15 Commits

Author SHA1 Message Date
hoangvv 1f824d03e2 update 2024-09-12 11:21:36 +07:00
hoangvv 87aab176d2 update code (Add middleware handle authentik server suddenly offline) 2024-08-29 11:57:36 +07:00
hoangvv 0af7c28227 update 2024-08-15 18:24:17 +07:00
hoangvv bb2cbabc59 update 2024-08-15 12:36:43 +07:00
hoangvv c1242916f9 update logout flow 2024-08-15 11:58:26 +07:00
hoangvv 3e775a86e0 update 2024-08-14 18:52:21 +07:00
hoangvv bcaa226c29 update 2024-08-14 12:06:18 +07:00
hoangvv 74786ca6c8 update code (integrate with authentik) 2024-08-14 10:00:32 +07:00
hoangvv c385748979 update 2024-08-13 12:19:14 +07:00
hoangvv 634c492519 update code (add OIDC client to CASAOS) 2024-08-10 12:36:08 +07:00
hoangvv 5f1a220316 update 2024-07-10 14:39:12 +07:00
hoangvv 75abc7e651 update 2024-07-10 12:14:25 +07:00
hoangvv 465e77832e update 2024-07-05 18:35:54 +07:00
hoangvv 219fa12590 update code 2024-07-04 12:19:44 +07:00
hoangvv dd3a9ebea4 omv intergration 2024-07-03 17:58:27 +07:00
28 changed files with 1499 additions and 146 deletions
+11
View File
@@ -31,3 +31,14 @@ linux-amd64-nextzenos-user-service-v1.3.tar.gz
build/sysroot/usr/bin/casaos-user-service
dist/casaos-user-service-amd64_linux_amd64_v1/build/sysroot/usr/bin/casaos-user-service
/build/scripts/setup
dist/casaos-user-service-amd64_linux_amd64_v1/build/sysroot/usr/bin/casaos-user-service
linux-amd64-nextzenos-user-service-v1.3.0.tar.gz
dist/casaos-user-service-amd64_linux_amd64_v1/build/sysroot/usr/bin/casaos-user-service
dist/casaos-user-service-amd64_linux_amd64_v1/build/sysroot/usr/bin/casaos-user-service
linux-amd64-nextzenos-user-service-v1.2.3.tar.gz
dist/casaos-user-service-amd64_linux_amd64_v1/build/sysroot/usr/bin/casaos-user-service
linux-amd64-nextzenos-user-service-v1.2.4.tar.gz
dist/casaos-user-service-amd64_linux_amd64_v1/build/sysroot/usr/bin/casaos-user-service
dist/casaos-user-service-amd64_linux_amd64_v1/build/sysroot/usr/bin/casaos-user-service
dist/casaos-user-service-amd64_linux_amd64_v1/build/sysroot/usr/bin/casaos-user-service
@@ -56,12 +56,12 @@ __is_migration_needed() {
__is_version_gt "${version2}" "${version1}"
}
__get_download_domain(){
__get_download_domain() {
local region
# Use ipconfig.io/country and https://ifconfig.io/country_code to get the country code
region=$(curl --connect-timeout 2 -s ipconfig.io/country || echo "")
if [ "${region}" = "" ]; then
region=$(curl --connect-timeout 2 -s https://ifconfig.io/country_code || echo "")
region=$(curl --connect-timeout 2 -s https://ifconfig.io/country_code || echo "")
fi
if [[ "${region}" = "China" ]] || [[ "${region}" = "CN" ]]; then
echo "https://casaos.oss-cn-shanghai.aliyuncs.com/"
@@ -95,7 +95,7 @@ readonly CURRENT_BIN_FILE_LEGACY
SOURCE_VERSION="$(${SOURCE_BIN_FILE} -v)"
readonly SOURCE_VERSION
CURRENT_VERSION="$(${CURRENT_BIN_FILE} -v || ${CURRENT_BIN_FILE_LEGACY} -v || (stat "${CURRENT_BIN_FILE_LEGACY}" > /dev/null && echo LEGACY_WITHOUT_VERSION) || echo CURRENT_VERSION_NOT_FOUND)"
CURRENT_VERSION="$(${CURRENT_BIN_FILE} -v || ${CURRENT_BIN_FILE_LEGACY} -v || (stat "${CURRENT_BIN_FILE_LEGACY}" >/dev/null && echo LEGACY_WITHOUT_VERSION) || echo CURRENT_VERSION_NOT_FOUND)"
readonly CURRENT_VERSION
__info_done "CURRENT_VERSION: ${CURRENT_VERSION}"
@@ -112,18 +112,18 @@ fi
ARCH="unknown"
case $(uname -m) in
x86_64)
ARCH="amd64"
;;
aarch64)
ARCH="arm64"
;;
armv7l)
ARCH="arm-7"
;;
*)
__error "Unsupported architecture"
;;
x86_64)
ARCH="amd64"
;;
aarch64)
ARCH="arm64"
;;
armv7l)
ARCH="arm-7"
;;
*)
__error "Unsupported architecture"
;;
esac
__info "ARCH: ${ARCH}"
@@ -161,7 +161,7 @@ while read -r VERSION_PAIR; do
if [ "${CURRENT_VERSION_FOUND}" = "true" ]; then
MIGRATION_PATH+=("${URL// /}")
fi
done < "${MIGRATION_LIST_FILE}"
done <"${MIGRATION_LIST_FILE}"
if [ ${#MIGRATION_PATH[@]} -eq 0 ]; then
__warning "No migration path found from ${CURRENT_VERSION} to ${SOURCE_VERSION}"
@@ -7,3 +7,6 @@ LogSaveName = user-service
LogFileExt = log
DBPath = /var/lib/casaos/db
UserDataPath = /var/lib/casaos
OMVServer = http://10.0.0.4:1081/rpc.php
SecretKey = N1PCdw3M2B1TfJhoaY2mL736p2vCUc47
AuthentikServer = http://10.0.0.26:9000
+417 -47
View File
@@ -26,6 +26,21 @@ const (
Access_tokenScopes = "access_token.Scopes"
)
// Defines values for YSKCardCardType.
const (
YSKCardCardTypeLongNotice YSKCardCardType = "long-notice"
YSKCardCardTypeShortNotice YSKCardCardType = "short-notice"
YSKCardCardTypeTask YSKCardCardType = "task"
)
// Defines values for YSKCardRenderType.
const (
YSKCardRenderTypeIconTextNotice YSKCardRenderType = "icon-text-notice"
YSKCardRenderTypeListNotice YSKCardRenderType = "list-notice"
YSKCardRenderTypeMarkdownNotice YSKCardRenderType = "markdown-notice"
YSKCardRenderTypeTask YSKCardRenderType = "task"
)
// Action defines model for Action.
type Action struct {
// Name action name
@@ -104,6 +119,69 @@ type PropertyType struct {
Name string `json:"name"`
}
// YSKCard defines model for YSKCard.
type YSKCard struct {
CardType YSKCardCardType `json:"cardType"`
Content YSKCardContent `json:"content"`
Id string `json:"id"`
RenderType YSKCardRenderType `json:"renderType"`
}
// YSKCardCardType defines model for YSKCard.CardType.
type YSKCardCardType string
// YSKCardRenderType defines model for YSKCard.RenderType.
type YSKCardRenderType string
// YSKCardContent defines model for YSKCardContent.
type YSKCardContent struct {
BodyIconWithText *YSKCardIconWithText `json:"bodyIconWithText,omitempty"`
BodyList *[]YSKCardListItem `json:"bodyList,omitempty"`
BodyProgress *YSKCardProgress `json:"bodyProgress,omitempty"`
FooterActions *[]YSKCardFooterAction `json:"footerActions,omitempty"`
TitleIcon YSKCardIcon `json:"titleIcon"`
TitleText string `json:"titleText"`
}
// YSKCardFooterAction defines model for YSKCardFooterAction.
type YSKCardFooterAction struct {
MessageBus YSKCardMessageBusAction `json:"messageBus"`
Side string `json:"side"`
Style string `json:"style"`
Text string `json:"text"`
}
// YSKCardIcon defines model for YSKCardIcon.
type YSKCardIcon = string
// YSKCardIconWithText defines model for YSKCardIconWithText.
type YSKCardIconWithText struct {
Description string `json:"description"`
Icon YSKCardIcon `json:"icon"`
}
// YSKCardList defines model for YSKCardList.
type YSKCardList = []YSKCard
// YSKCardListItem defines model for YSKCardListItem.
type YSKCardListItem struct {
Description string `json:"description"`
Icon YSKCardIcon `json:"icon"`
RightText string `json:"rightText"`
}
// YSKCardMessageBusAction defines model for YSKCardMessageBusAction.
type YSKCardMessageBusAction struct {
Key string `json:"key"`
Payload string `json:"payload"`
}
// YSKCardProgress defines model for YSKCardProgress.
type YSKCardProgress struct {
Label string `json:"label"`
Progress int `json:"progress"`
}
// ActionName defines model for ActionName.
type ActionName = string
@@ -140,6 +218,14 @@ type ResponseBadRequest = BaseResponse
// ResponseConflict defines model for ResponseConflict.
type ResponseConflict = BaseResponse
// ResponseGetYSKCardListOK defines model for ResponseGetYSKCardListOK.
type ResponseGetYSKCardListOK struct {
Data *YSKCardList `json:"data,omitempty"`
// Message message returned by server side if there is any
Message *string `json:"message,omitempty"`
}
// ResponseInternalServerError defines model for ResponseInternalServerError.
type ResponseInternalServerError = BaseResponse
@@ -326,6 +412,12 @@ type ClientInterface interface {
// PollSIO2 request
PollSIO2(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error)
// GetYskCard request
GetYskCard(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error)
// DeleteYskCard request
DeleteYskCard(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*http.Response, error)
}
func (c *Client) SubscribeActionWS(ctx context.Context, sourceId SourceID, params *SubscribeActionWSParams, reqEditors ...RequestEditorFn) (*http.Response, error) {
@@ -568,6 +660,30 @@ func (c *Client) PollSIO2(ctx context.Context, reqEditors ...RequestEditorFn) (*
return c.Client.Do(req)
}
func (c *Client) GetYskCard(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) {
req, err := NewGetYskCardRequest(c.Server)
if err != nil {
return nil, err
}
req = req.WithContext(ctx)
if err := c.applyEditors(ctx, req, reqEditors); err != nil {
return nil, err
}
return c.Client.Do(req)
}
func (c *Client) DeleteYskCard(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*http.Response, error) {
req, err := NewDeleteYskCardRequest(c.Server, id)
if err != nil {
return nil, err
}
req = req.WithContext(ctx)
if err := c.applyEditors(ctx, req, reqEditors); err != nil {
return nil, err
}
return c.Client.Do(req)
}
// NewSubscribeActionWSRequest generates requests for SubscribeActionWS
func NewSubscribeActionWSRequest(server string, sourceId SourceID, params *SubscribeActionWSParams) (*http.Request, error) {
var err error
@@ -1176,6 +1292,67 @@ func NewPollSIO2Request(server string) (*http.Request, error) {
return req, nil
}
// NewGetYskCardRequest generates requests for GetYskCard
func NewGetYskCardRequest(server string) (*http.Request, error) {
var err error
serverURL, err := url.Parse(server)
if err != nil {
return nil, err
}
operationPath := fmt.Sprintf("/ysk")
if operationPath[0] == '/' {
operationPath = "." + operationPath
}
queryURL, err := serverURL.Parse(operationPath)
if err != nil {
return nil, err
}
req, err := http.NewRequest("GET", queryURL.String(), nil)
if err != nil {
return nil, err
}
return req, nil
}
// NewDeleteYskCardRequest generates requests for DeleteYskCard
func NewDeleteYskCardRequest(server string, id string) (*http.Request, error) {
var err error
var pathParam0 string
pathParam0, err = runtime.StyleParamWithLocation("simple", false, "id", runtime.ParamLocationPath, id)
if err != nil {
return nil, err
}
serverURL, err := url.Parse(server)
if err != nil {
return nil, err
}
operationPath := fmt.Sprintf("/ysk/%s", pathParam0)
if operationPath[0] == '/' {
operationPath = "." + operationPath
}
queryURL, err := serverURL.Parse(operationPath)
if err != nil {
return nil, err
}
req, err := http.NewRequest("DELETE", queryURL.String(), nil)
if err != nil {
return nil, err
}
return req, nil
}
func (c *Client) applyEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error {
for _, r := range c.RequestEditors {
if err := r(ctx, req); err != nil {
@@ -1274,6 +1451,12 @@ type ClientWithResponsesInterface interface {
// PollSIO2 request
PollSIO2WithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*PollSIO2Response, error)
// GetYskCard request
GetYskCardWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetYskCardResponse, error)
// DeleteYskCard request
DeleteYskCardWithResponse(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*DeleteYskCardResponse, error)
}
type SubscribeActionWSResponse struct {
@@ -1635,6 +1818,57 @@ func (r PollSIO2Response) StatusCode() int {
return 0
}
type GetYskCardResponse struct {
Body []byte
HTTPResponse *http.Response
JSON200 *struct {
Data *YSKCardList `json:"data,omitempty"`
// Message message returned by server side if there is any
Message *string `json:"message,omitempty"`
}
JSON500 *BaseResponse
}
// Status returns HTTPResponse.Status
func (r GetYskCardResponse) Status() string {
if r.HTTPResponse != nil {
return r.HTTPResponse.Status
}
return http.StatusText(0)
}
// StatusCode returns HTTPResponse.StatusCode
func (r GetYskCardResponse) StatusCode() int {
if r.HTTPResponse != nil {
return r.HTTPResponse.StatusCode
}
return 0
}
type DeleteYskCardResponse struct {
Body []byte
HTTPResponse *http.Response
JSON200 *BaseResponse
JSON500 *BaseResponse
}
// Status returns HTTPResponse.Status
func (r DeleteYskCardResponse) Status() string {
if r.HTTPResponse != nil {
return r.HTTPResponse.Status
}
return http.StatusText(0)
}
// StatusCode returns HTTPResponse.StatusCode
func (r DeleteYskCardResponse) StatusCode() int {
if r.HTTPResponse != nil {
return r.HTTPResponse.StatusCode
}
return 0
}
// SubscribeActionWSWithResponse request returning *SubscribeActionWSResponse
func (c *ClientWithResponses) SubscribeActionWSWithResponse(ctx context.Context, sourceId SourceID, params *SubscribeActionWSParams, reqEditors ...RequestEditorFn) (*SubscribeActionWSResponse, error) {
rsp, err := c.SubscribeActionWS(ctx, sourceId, params, reqEditors...)
@@ -1811,6 +2045,24 @@ func (c *ClientWithResponses) PollSIO2WithResponse(ctx context.Context, reqEdito
return ParsePollSIO2Response(rsp)
}
// GetYskCardWithResponse request returning *GetYskCardResponse
func (c *ClientWithResponses) GetYskCardWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetYskCardResponse, error) {
rsp, err := c.GetYskCard(ctx, reqEditors...)
if err != nil {
return nil, err
}
return ParseGetYskCardResponse(rsp)
}
// DeleteYskCardWithResponse request returning *DeleteYskCardResponse
func (c *ClientWithResponses) DeleteYskCardWithResponse(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*DeleteYskCardResponse, error) {
rsp, err := c.DeleteYskCard(ctx, id, reqEditors...)
if err != nil {
return nil, err
}
return ParseDeleteYskCardResponse(rsp)
}
// ParseSubscribeActionWSResponse parses an HTTP response from a SubscribeActionWSWithResponse call
func ParseSubscribeActionWSResponse(rsp *http.Response) (*SubscribeActionWSResponse, error) {
bodyBytes, err := io.ReadAll(rsp.Body)
@@ -2258,6 +2510,77 @@ func ParsePollSIO2Response(rsp *http.Response) (*PollSIO2Response, error) {
return response, nil
}
// ParseGetYskCardResponse parses an HTTP response from a GetYskCardWithResponse call
func ParseGetYskCardResponse(rsp *http.Response) (*GetYskCardResponse, error) {
bodyBytes, err := io.ReadAll(rsp.Body)
defer func() { _ = rsp.Body.Close() }()
if err != nil {
return nil, err
}
response := &GetYskCardResponse{
Body: bodyBytes,
HTTPResponse: rsp,
}
switch {
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
var dest struct {
Data *YSKCardList `json:"data,omitempty"`
// Message message returned by server side if there is any
Message *string `json:"message,omitempty"`
}
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
return nil, err
}
response.JSON200 = &dest
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500:
var dest BaseResponse
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
return nil, err
}
response.JSON500 = &dest
}
return response, nil
}
// ParseDeleteYskCardResponse parses an HTTP response from a DeleteYskCardWithResponse call
func ParseDeleteYskCardResponse(rsp *http.Response) (*DeleteYskCardResponse, error) {
bodyBytes, err := io.ReadAll(rsp.Body)
defer func() { _ = rsp.Body.Close() }()
if err != nil {
return nil, err
}
response := &DeleteYskCardResponse{
Body: bodyBytes,
HTTPResponse: rsp,
}
switch {
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
var dest BaseResponse
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
return nil, err
}
response.JSON200 = &dest
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500:
var dest BaseResponse
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
return nil, err
}
response.JSON500 = &dest
}
return response, nil
}
// ServerInterface represents all server handlers.
type ServerInterface interface {
// Subscribe to actions by source ID (WebSocket)
@@ -2308,6 +2631,12 @@ type ServerInterface interface {
// Poll events and actions (SocketIO)
// (POST /socket.io/)
PollSIO2(ctx echo.Context) error
// (GET /ysk)
GetYskCard(ctx echo.Context) error
// (DELETE /ysk/{id})
DeleteYskCard(ctx echo.Context, id string) error
}
// ServerInterfaceWrapper converts echo contexts to parameters.
@@ -2597,6 +2926,35 @@ func (w *ServerInterfaceWrapper) PollSIO2(ctx echo.Context) error {
return err
}
// GetYskCard converts echo context to params.
func (w *ServerInterfaceWrapper) GetYskCard(ctx echo.Context) error {
var err error
ctx.Set(Access_tokenScopes, []string{""})
// Invoke the callback with all the unmarshalled arguments
err = w.Handler.GetYskCard(ctx)
return err
}
// DeleteYskCard converts echo context to params.
func (w *ServerInterfaceWrapper) DeleteYskCard(ctx echo.Context) error {
var err error
// ------------- Path parameter "id" -------------
var id string
err = runtime.BindStyledParameterWithLocation("simple", false, "id", runtime.ParamLocationPath, ctx.Param("id"), &id)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err))
}
ctx.Set(Access_tokenScopes, []string{""})
// Invoke the callback with all the unmarshalled arguments
err = w.Handler.DeleteYskCard(ctx, id)
return err
}
// This is a simple interface which specifies echo.Route addition functions which
// are present on both echo.Echo and echo.Group, since we want to allow using
// either of them for path registration
@@ -2641,59 +2999,71 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL
router.POST(baseURL+"/socket.io", wrapper.PollSIO)
router.GET(baseURL+"/socket.io/", wrapper.SubscribeSIO2)
router.POST(baseURL+"/socket.io/", wrapper.PollSIO2)
router.GET(baseURL+"/ysk", wrapper.GetYskCard)
router.DELETE(baseURL+"/ysk/:id", wrapper.DeleteYskCard)
}
// Base64 encoded, gzipped, json marshaled Swagger object
var swaggerSpec = []string{
"H4sIAAAAAAAC/+xbXW8bt9L+KwTf98IBVpLjuOe0AnoR121q9LQOogA5QGxY3N2RlvEuuSG5spVA//2A",
"5H5wP2St5LUNFL1KJJEzw5lnZh5++DsOeJJyBkxJPP2OUyJIAgqE+fQ2UJSzv0gC+hNleIpToiLsYWa+",
"s/94WMDXjAoI8VSJDDwsgwgSoufAPUnSWA+NeUDikVRckCVMUyIU1cKnCy4SorCH1TrV46QSlC3xZuM5",
"6uU+srxtPyc8Y1qTWcjXDMS6vhKJXdOpgsTobRhWWkqEIGtj6K8rYOop3BRSeTslYQhhp4NKvbKfEK/j",
"BwEJXxn5g7tlxjMRwMX5Fq9I8/MNDQ9xTYc/NlYMSHXGQ2p98j7zYyoj4yj9OeBM5f8laRrTgGhkTL5I",
"zvR3lVoShgY0JH4veApC5QJbqw5BBoKmeiyeYtCKUFpN8aoFfG/EJeEhxHiKfxHZNxDYa/ycZTTEU3x6",
"egLH8IM/+il8A6PTBfw48k//tRj9FJy+Pn29CCEI/92auwIWcoGneEbYOZW3Tni4/wUCZd1Vt/3o4+X5",
"5atWMDYe/gBLKhUIm5Af16n1RW9nlpj5fwELPMX/N6mqzsQOk5NKeAeYDjDWxPyJbC1lP87Uj4Iul4Vb",
"nwOexGjqic99APgYfJm8lSln0q7hHagKC5d/7OWXvgBrW3f5h15ETbfcU/kTwLy0qkTcgA5xUNxH85O4",
"Y69Msja5NX1ob2zX+iGH6BkJP9gu00Ozk1sJSKn71hSfkRAVIjZeT+vOiITChC4jG0KLob9wtohpcLCt",
"5fzBDHUlFuMumALBSDwDsQLxqxC6dw0U1l325FXJseYvrn7jGQsP9dlfXCErYDCn1UQWAwfE/i79NgVq",
"7WrwwrxNb+lEZ0titiq1xsdy9t3Z73L+/SBB3rYP8RqK/vbE0MuJueXtDX9KyQNKFITIDkKGvffm6B5W",
"NAGpSJK2hZc/IRVRifLYKc5vURqTQIvLgzTFIVEw0hM690UVwfhcLcYrtmFOEK47Vu806P1RdsWu2JGK",
"QACiEjGuv6RsiQLONAD0sAUXyJkhPeRnClGlJwgIeJIACyFEykwGBCSIEJFIKpEFKhMkRoSFqDRhBfrX",
"lEtJ/RhePRrna730/1Db33r18PfOxHYbfxBP1g8llvSiaagdtVijO+NGFUHhrjsax0iRW9gHcn3hUC27",
"CxS1AtmCRVn7m+vLf0ACVCYYhMhfI2maHJI0BEQXqAQLYevaugxRJuEli9fFrrhjx1vubPsA1RahPtVw",
"y5nDP8XwhYqh9djetdDD1j/dQDC/uQb39OLABbfaAuwN457ltprw3NV2Vx49Z7G1XthVa+2ogCcg0ULw",
"5PlrbW2FLUjU1uQeD/5ORHhHBCCb1ogvbOo8kH9O0ankFMWgY3w3HItFlYi8yo6P3wC6aIHMtH5UG6+j",
"4BtMbYHYGF0yQNwo0/IUR6mABb1Hd1RFiFhYpySAcal5hC5MY6lUaT/YwMsUArqggVcTk/94cT5Gv+mE",
"sd7w0LyrYs4dRZc6++6ohLq8uV40Z9N5U17+PSSExqWcUt6MJ4B8EhYTJNIBndNw7qE5Wek5Hpr78e18",
"PB7fRTSIUECYcWDi02XGM2nSNeBskUnKluMHEjQv/7tQbILexqlONQgyQdV6plMzb4hBAFLeKH4LrDz2",
"joCEpnXlB99vMxVxQb+ZvVKln6T0D8jPPShb8DbSjJeClOrCBIXrEELI/pAHMYGQkp+v8JEOCAg5CnjM",
"xcgUEJiikIjbV1cYSRFIUD9f4UipVE4nE0HuxkuqoszPJIh8ZzcOeDK5COBTRGL4CEE0ifmSTxJC2SQg",
"knCZ/3PjE8ZA3GjxN4wuI3Xz4/Fxej9O2fIKH2psrAU9obXqjhoVN36cwcMG02SJSKxN+IVIcjmzRj2/",
"RdaaSQMFV8xahf7MKeeZToUggFTl7MFmhqXStrSjFRFUp4yNhcw7XUxXIKSuH4muNjLzNQZ9EFJXGKbz",
"mUqZFeOpDDIptVAPpTEQCWhFJVWmAH1+R9XvmY8EpFxSxcX6+qjwl/VV20F2Ia8QF+gLpwx95plA51QG",
"XITV7NB+MV4uJ7fs61vfP/Phv6/GVyahqDL53uGSt+8vsIf1Am1KrU50YecpMJJSPMVvxsfjN7pTERWZ",
"jJ5Yj02+l9dVG/31ElQ7P2eFq7TfCk/7azQv587RihL0CfwZD25BjZ12MTeFfF4UtLxOQ4goM640N3PI",
"FqlaXMyHvKqXu+Z1CtI6Q3cAU2kuQtdEu839NDNrrS59P3ezj2rIpLzV23g7x7o3uJvrxjn/6+PXbRd+",
"jEBXbwbOls8HlKVLQXKe5vjPHarBqP0UxFQzGDNTQACmn+axIAbWa9NVlD3NglC7yZTzLEmIWD8Qx7JL",
"oqPSBE0BFVlq1+HKrl9ZmHLKlMTXWnQHiCbfdbwNllIuO8CUn7YhwoqgcguEYlPpZ50Rrl8qPU908+AW",
"l6/rbSS2dj87qVvavAY6OT7eLiYfN2keSW48fNpnXsdpvpl62n9qeUxcx04rbA4+rJkoARXxsA6NG5Wz",
"3c7CorkyInFcS2+kIqIMlINMCGAqXiORX35CaHlYD7zUr7zwIVFo3ZrVXWKtdyxvuUTPc9zibUmJ4moX",
"cU2JBUq4AMTgru6WvgvvutU+AMVdYg7CsnOu/1gY/9R/anUfU4tZp6v3iWED2r365zt4COU7sJ3vrspO",
"WG1pdoL+bD2rtqqH1svrYTJnmCpkPOl60e1ejwid07W2R5C5qm38zEb4sPA5tNXsdXeG83l73qMiPnDA",
"646vERbtxPwQZmfszXZhf8JrT46el+7m56I72a45Y3xisuu8xntJrlts9hyqm9oXEzupbj71UUy3BZ6d",
"RDd/z6HhWxxz92nftad9zxLXw1huzc6DiEHjwcvLc9xmxBxgGCO76klPgusk9HD81nkKeGDJrj2C2nj4",
"h30C0PW8pYMhO0tvOvSx/Nj16r70uOG8A9mxI+VvSo77h6+eE3sx4870GJgYV6F6UV7cTLmhWJLrwi2s",
"eL+g9eTEld5hKHF1qbkrjs/aHR8T6cHpsOPzXWx4W8ylZa6U70GC60ftmgJbsnRxaRjwqPyI5oLzZI5k",
"xLM41OQPqIpAoLmRM9fFZW7lzB+it7OLSzwY4yyNG4pwegUSGryPx7Em95RRRUlMv+U3pIX+XlzVdfRR",
"MdNlquVqXKK6rYFqk7ok++tmFNt0lMdxRxgeXHnAmaIs0+sWPNm+8G1m9V9wDcmTrVAurmFJYu6g59Wc",
"OYL7AFLzdMI+Dsivn5Ug1CxGxkRGaD6ZoxEi6I6LWyJ0ZupgJeSeJvSbBl+SEkV9GlO13oHok38g7UZ4",
"K2hfPGg59E9eCvsb9zLe9Lf6Nfzna92a7Ls72/8yEeMpnqxOJnm7vfEzu2vPs6j9kM/0BBPMhDCy1Etw",
"mISnPX8HcWyeblio6CHa4uLkIj/IsGupXgPUd0y64/ZQ7Z7t7aE7d57zFKF+I9HWXhQRZC5EjRGd62l1",
"vPKEoFL2CXzZPDQYUGMBiEphRyls67P36eYNBzmHhUldzpB9b/UBFj9f7fjLlSuMJjYHG0HNn2gNqnCr",
"sgEVVQeDXdpqf8A0rMrt6gZU5T4v61JYf2B37eH7kSLLd4Jnqa0M+bg/85x5iEJ6jez2ug5dvfa9oFdq",
"qZD78OGbt4XolIJm+d9QdJhbmFkzr/yAGy/yrjfXm/8FAAD//yaxUhg3PgAA",
"H4sIAAAAAAAC/+xcbW/bOBL+K4TuPrSAbKdturs1sB+aptsNetsUdXC9RRPElDS2WUuklqTiqIH/+4Gk",
"Xqg3W3acBFjsp9Y2OTN85pnhDEXlzvFZFDMKVApnfOfEmOMIJHD96a0vCaOfcATqE6HO2ImxXDiuQ/V3",
"5h/X4fBXQjgEzljyBFxH+AuIsJoDtziKQzU0ZD4OB0IyjucwjjGXRAkfzxiPsHRcR6axGickJ3TurNeu",
"pV7sIsvt+jliCVWa9EL+SoCn1ZUIxzadSIi03pphhaWYc5xqQ9/fAJUPAVNAxHKMgwCCVoAKvaKfELfl",
"Bw4Ru9HyDw7LhCXch7PTDlSE/vmaBPtA04LH2ogBIU9YQAwmnxMvJGKhgVKffUZl9l8cxyHxsWLG6Ltg",
"VH1XqsVBoEmDw8+cxcBlJrCx6gCEz0msxjpjB5QiFJdT3HIBdzW/RCyA0Bk773jyA7jj1n5OEhI4Y+f4",
"+CUcwWtv8CZ4BYPjGfwy8I5/mg3e+Mcvjl/MAvCDnxtzb4AGjDtjZ4LpKRFLyz3M+w6+NHBVbX92cX56",
"/rzhjLXrfIE5ERK4CciLNDZY9Aaz4My/OcycsfOvUZl1RmaYGJXCW8i0h7Ha5w9kayH7fqZecDKf57A+",
"Bj2x1tSTn7sQ8D780nErYkaFWcMHkCUXzj/uhEtfgjWtO/+oFlHRLXZU/gA0L6wqGHdAQCwW99H8IHDs",
"FEnGJjunHxqNbq1fMoqe4OCL2WV6aLZiKwIh1L41dk5wgHIRa7endSdYQG5Cm5E1ofnQd4zOQuLvbWsx",
"/2CG2hLzcR9A/jn5+A7z4D9E7OpTHIbnM2f8bRez3DsnruTNAMutK7NMVAu72kqVMyqBUxxOgN8Af8+5",
"2pIPxNZtMGfJ1rLmE5O/sYQG+1LhE5PICDgYFyoi84EHDOlt+o27KrvwwfebLr0FiFanpTuwCi9p1lS0",
"buNZW7Gx7u9qr9yaor99vetm/YZpR2p4CsF8giUEyAxCuinp3Xq4jiQRCImjuCm8+AnJBREo851kbIni",
"EPtKXOaksUpDMFATWtu9sm76Vi7GzbtLywlXLau36o7dWXZJL+kzuQAOiAhEmfqS0DnyGVUEUMNmjCNr",
"hnCRl0hEpJrAwWdRBDSAAEk9GRBgf4GwQELyxJcJxyHCNECFCTegfo2ZEMQL4fm9eZ6qpevc3bc0+WxN",
"bFYnG/lkcCi4pBZNAgXULEUrDaNcQA7XioQhkngJu1CuLx3KZbeRopIgG7Qocn99fdkPiINMOIUAeSkS",
"epNDggSAyAwVZME0raxL1/84OKdhmjf7LY180bD3IapJQn2yYcdRyj/J8ImSoUFs51zoOgafdiLo32yD",
"e6J44IRbdjY707hnui0nPHa23RZHj5lsDQrbcq0Z5bMIBJpxFj1+rq2ssEGJyprsU8/fMQ9WmAMyYY3Y",
"zITOhvizkk4pJ08GLePb6ZgvqmDkZXJ09ArQWYNkeutHlfHKC57mVAfFhuicAmJamZInGYo5zMgtWhG5",
"QNjQOsY+DAvNA3SmN5ZSlcLBOF7E4JMZ8d2KmOzHs9Mh+k0FjEHDRdO2jDm1FJ2r6FsRAVV5U7VoRsfT",
"urzse4gwCQs5hbwJiwB5OMgnCKQcOiXB1EVTfKPmuGjqhcvpcDhcLYi/QD6mGsDII/OEJUKHq8/oLBGE",
"zocbAjRL/9tYrJ3extOszW1S1Mc8yMkLNImUFIk1pUJG5wPKJNEpXCwYl/nHqxbCWe1Vj4b7XTZ67Tom",
"6Zcrf/Hy1fHrn37+5c1RG6+5oifvMpkIWZpMfEYHEm6tryLMlwFb0e6F1BDVu04BUkV9ueQNiL8rUakC",
"77EgPfMZ/Urk4gJu++JWmbJ2tZidcrJ14HEmIWpLy0rmZ87mHERfccXwtdrsWfFYQexq12/W5DbbJJEh",
"KBB2wKuYl+Nccu0dFvh8sjWuSq22pA1uryyjqwY/Sfqi8kcxoURGFeXVxfyXwAqdgsQkFG2hI2Ra30Bu",
"CKwGQfcU2UBsIWUsxqORjwVmYujrXXfL3qoszdVnMl0bhA1A5q5uGsDxajgncpF4iQCehaKyZ3Tmw9cF",
"Vk7yF6OQzdkowoRmFmf/XHuYUuDXckVCMl/Iay9M4PqXo6P4dhjTeRsWbRHYf8vvYppJUzuxuZ6iDC9t",
"zRsA3SdZtAViPY88CRKuw5XzmnF9jC5OtvKyBThb4AYQG/HYWP0S0qpFLAY6SHjYWmDjNGQ4uHegKaWl",
"tA3229m9aneIPdXC2oacUSFxGKpe5TuEYTojtKNLKGQWk18fFSMJlTAH3jDaaLTmN+1WuQv8hBOZThQN",
"sobe90GIa8mWQIvbCAvAgW69s/sIbxO5YJz8wJl7cxrH5CNkj6MInbFmpayrPD8mqrGCvPRDCCHzQ1aE",
"RhAQ/Oul80wVlMDFwGch4wNNVhijAPPl80sHCe4LkL9eHjh5KfHXVKevInNdOvsaq/PgA1rbkWrbDCbR",
"HOFQmWCShTHq8S0y1oxqLLikxiqUpQF0okp534dYZqcfprI3R4GmNUU3mBNV8htfiKxTD8kNcKH6n0h1",
"SyLxFAc94EJ1SFT1I0SIJB9PhJ8IoYS6KA4BC0A3RBCpG6hvH4j8PfEQh5gJIhlPr57leBmsmgCZhTxH",
"jKPvjFD0jSUcnRLhMx6UswPzxXA+Hy3pX28978SD/z0fXhblUJHVK5C8/XzmuI5aoAmpm5cqT6hEiGPi",
"jJ1Xw6PhK52w5EJH9MggNrorbhGt1ddzkM34nORQKdxypL0UTYu5U3RDMPoK3oT5S5BDq92d6kZ0mjdk",
"WZ8JASJUQ6kvTCGT2yp+0R+yrrQ49U9jEAYMlUl1pjkLbBPNJvF1otda3sXreLpZDhkVl63W7tax9sW6",
"9VXt+sWLoxdNCC8WoLpPCtaRtQcoieccZ+dMFn72UEVGhZMfEqDSzOTggz4PyHyBNa1T3RVL8zQOAgWT",
"TudJFGGebvBj0eWjZ4UJzxXf8FxB55R2vadBzAiVwrlSoltINLpT/tZciploIVP2tBBhmjuVGSLkh+Je",
"0urh6l2fx/Fu5tz8TlzaVTBVrs2NqpbWb+e8PDrqFpONG9Ufqa5d57jPvJZLFnrqcf+pxWPuKncabrP4",
"YcxEEcgFC6rUuJbZ6UFrYlEVLcJhWAlvJBdYair7CedAZZgint1Jg8CcI/XgS/UmkrOPFxqXmaqQGOst",
"yxuQqHkWLG5HSOQ37hCjoLaHiHFAFFZVWPouvO2y4R4sbhOzF5etewn3pfGb/lPLazIVn7VCvYsPa9Tu",
"tX9+gE0s38Lt7HS42AnLI9mtpD9JJ+VR+7758uowkXOYLKSRtFG0d697uM7atbo9SG3Vxn/6IH8/91ll",
"qz6r3+rOx93z7uXxAzu8CnylYFEgZg+Rtvpetwu7F7zmydfjlrvZc92t1a5+RvrAxa71ksRT1rp5s2eV",
"urG5yLq11M2m3qvSbZBna6GbXbNV9M0f0/fZvitvXDyKX/ercit27lUY1O4hP32NW/eYRQxtZFs+6Vng",
"WgF9uPrWekNjz5RduZu+dp3Xuzig7XpuS4VsLb0O6H3rYxvVXcvjGnh7VseWlL9pcdzffdWY2Kkybg2P",
"AxfGpauetC6uh9yhqiQbwo6qeDen9ayJS72HKYnLS1nb/Piou+N9PH3wctjCfFs13OVzYSpXwnYogqtH",
"7aoENsXS2bmugAfFRzTljEVTJBYsCQNV/AGRC+BoquVMVXKZGjnTTeXt5OzcOVjFWRh3qILTzZlQq/uY",
"eYBHKJEEh+RHdsMr19+rVrWBfpbPtCvVYjV2odq1gSqT2iR7ad2LzXKUhWGLGzau3GdUEpqodXMWdS+8",
"y6z+C64wedRJ5fwaGY70HbppOWeK4NaHWF/9NJcbs+tzkmOiFyNCLBZoOpqiAcJoxfgScxWZylkRviUR",
"+aHIF8VYEo+ERKZbGP3yH0rbHu4k7ZM7LaP+y6fivmZ3KpYbd2AKoB9WxiFO0Z+Tj8jHPGjbNv8Uy3fm",
"p/2r1MZ7hodqGfIgVwuox3cqlqO7rIgMIATZctf2E5PbQTjVk0scasVDVWIuxVzeb/nrC1v+7EL99sjV",
"/buDh0Xauvih4ahe+fh2pVZg3lExcCU8dMbO6OblKCvtrr3EnBBlKpovvej6QyeOCFM8V+FiVa2uivIV",
"hKG+5mzSkhqioiM/JcsOzUzclL6oduequuuh2j5H3kF3FqjWtZfq06+m9hxmpB++ayNa19OororTqFLZ",
"V/BE/YDqgBrz5FMqbNl2e+kr4keJ/fJ+cjFLwuyWQia5ysCmUHMhRF9Cwqcw03sPo8iE2BeY/Xq55Y34",
"SweNzCZSY0r2jsRBFXYqO6Ci8mS7TVvlDyMcVmW3ugOqst/vaFNYe8OlV4xbe4HFuiJUr1zndiDx/ANn",
"SWxyVjbsj2zIpkbKreUdt+3Rg1vPD27FhCu1kJwuRTBsPpB224LSbWT0UvAke0+6ZTH5IirGFx+c2ls3",
"V+ur9f8DAAD//z3VnfHySgAA",
}
// GetSwagger returns the content of the embedded swagger specification file
+1 -1
View File
@@ -1 +1 @@
{"project_name":"casaos-user-service","tag":"v1.0.0","previous_tag":"","version":"1.0.1","commit":"11de9608cd1105075bc3ac12d2130eb30d3c8004","date":"2024-07-01T02:10:13.880324864+07:00","runtime":{"goos":"linux","goarch":"amd64"}}
{"project_name":"casaos-user-service","tag":"v1.0.0","previous_tag":"","version":"1.0.1","commit":"87aab176d2716419944d8d34c9a5ee3d7bc63acd","date":"2024-09-07T11:45:04.418487229+07:00","runtime":{"goos":"linux","goarch":"amd64"}}
View File
+22 -17
View File
@@ -1,9 +1,12 @@
module github.com/IceWhaleTech/CasaOS-UserService
go 1.20
go 1.21
toolchain go1.22.0
require (
github.com/IceWhaleTech/CasaOS-Common v0.4.8-alpha12
github.com/coreos/go-oidc/v3 v3.11.0
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
github.com/deepmap/oapi-codegen v1.12.4
github.com/getkin/kin-openapi v0.117.0
@@ -14,27 +17,30 @@ require (
github.com/satori/go.uuid v1.2.0
github.com/tidwall/gjson v1.14.4
go.uber.org/zap v1.24.0
golang.org/x/net v0.17.0
golang.org/x/net v0.27.0
golang.org/x/oauth2 v0.21.0
golang.org/x/time v0.3.0
gopkg.in/ini.v1 v1.67.0
gorm.io/gorm v1.25.0
gorm.io/gorm v1.25.8
)
require (
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
github.com/benbjohnson/clock v1.3.1 // indirect
github.com/bytedance/sonic v1.9.1 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/bytedance/sonic v1.11.3 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/chenzhuoyu/iasm v0.9.1 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/glebarez/go-sqlite v1.21.1 // indirect
github.com/go-jose/go-jose/v4 v4.0.2 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.14.0 // indirect
github.com/go-playground/validator/v10 v10.19.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
@@ -47,36 +53,35 @@ require (
github.com/jinzhu/now v1.1.5 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/labstack/gommon v0.4.0 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/pelletier/go-toml/v2 v2.2.0 // indirect
github.com/perimeterx/marshmallow v1.1.4 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rogpeppe/go-internal v1.10.0 // indirect
github.com/samber/lo v1.38.1 // indirect
github.com/stretchr/testify v1.8.4 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/crypto v0.14.0 // indirect
golang.org/x/arch v0.7.0 // indirect
golang.org/x/crypto v0.25.0 // indirect
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect
golang.org/x/sys v0.14.0 // indirect
golang.org/x/text v0.13.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/text v0.16.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
modernc.org/libc v1.22.4 // indirect
+51 -33
View File
@@ -7,11 +7,18 @@ github.com/benbjohnson/clock v1.3.1 h1:Heo0FGXzOxUHquZbraxt+tT7UXVDhesUQH5ISbsOk
github.com/benbjohnson/clock v1.3.1/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
github.com/bytedance/sonic v1.11.3 h1:jRN+yEjakWh8aK5FzrciUHG8OFXK+4/KrAX/ysEtHAA=
github.com/bytedance/sonic v1.11.3/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA=
github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0=
github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
github.com/coreos/go-oidc/v3 v3.11.0 h1:Ia3MxdwpSw702YW0xgfmP1GVCMA9aEFWu12XUZ3/OtI=
github.com/coreos/go-oidc/v3 v3.11.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0=
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU=
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
@@ -24,8 +31,8 @@ github.com/deepmap/oapi-codegen v1.12.4 h1:pPmn6qI9MuOtCz82WY2Xaw46EQjgvxednXXrP
github.com/deepmap/oapi-codegen v1.12.4/go.mod h1:3lgHGMu6myQ2vqbbTXH2H1o4eXFTGnFiDaOaKKl5yas=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/getkin/kin-openapi v0.117.0 h1:QT2DyGujAL09F4NrKDHJGsUoIprlIcFVHWDVDcUFE8A=
github.com/getkin/kin-openapi v0.117.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc=
github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
@@ -39,6 +46,8 @@ github.com/glebarez/go-sqlite v1.21.1 h1:7MZyUPh2XTrHS7xNEHQbrhfMZuPSzhkm2A1qgg0
github.com/glebarez/go-sqlite v1.21.1/go.mod h1:ISs8MF6yk5cL4n/43rSOmVMGJJjHYr7L2MbZZ5Q4E2E=
github.com/glebarez/sqlite v1.8.0 h1:02X12E2I/4C1n+v90yTqrjRa8yuo7c3KeHI3FRznCvc=
github.com/glebarez/sqlite v1.8.0/go.mod h1:bpET16h1za2KOOMb8+jCp6UBP/iahDpfPQqSaYLTLx8=
github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk=
github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
@@ -47,6 +56,7 @@ github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
@@ -54,8 +64,8 @@ github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4=
github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
@@ -74,6 +84,7 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
@@ -91,8 +102,9 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
@@ -107,8 +119,8 @@ github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
@@ -128,12 +140,13 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
github.com/pelletier/go-toml/v2 v2.2.0 h1:QLgLl2yMN7N+ruc31VynXs1vhMZa7CeHHejIeBAsoHo=
github.com/pelletier/go-toml/v2 v2.2.0/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/perimeterx/marshmallow v1.1.4 h1:pZLDH9RjlLGGorbXhcaQLhfuV0pFMNfPO55FuFkxqLw=
github.com/perimeterx/marshmallow v1.1.4/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
@@ -152,16 +165,16 @@ github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKk
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
@@ -173,8 +186,8 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
@@ -183,45 +196,48 @@ github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc=
golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw=
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
@@ -238,9 +254,10 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/gorm v1.25.0 h1:+KtYtb2roDz14EQe4bla8CbQlmb9dN3VejSai3lprfU=
gorm.io/gorm v1.25.0/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gorm.io/gorm v1.25.8 h1:WAGEZ/aEcznN4D03laj8DKnehe1e9gYQAjW8xyPRdeo=
gorm.io/gorm v1.25.8/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
modernc.org/libc v1.22.4 h1:wymSbZb0AlrjdAVX3cjreCHTPCpPARbQXNz6BHPzdwQ=
modernc.org/libc v1.22.4/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
@@ -249,4 +266,5 @@ modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
modernc.org/sqlite v1.21.2 h1:ixuUG0QS413Vfzyx6FWx6PYTmHaOegTY+hjzhn7L+a0=
modernc.org/sqlite v1.21.2/go.mod h1:cxbLkB5WS32DnQqeH4h4o1B0eMr8W/y8/RGuxQ3JsC0=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
+1 -1
View File
@@ -48,6 +48,7 @@ var (
)
func init() {
configFlag := flag.String("c", "", "config address")
dbFlag := flag.String("db", "", "db path")
resetUserFlag := flag.Bool("ru", false, "reset user")
@@ -101,7 +102,6 @@ func main() {
v1Router := route.InitRouter()
v2Router := route.InitV2Router()
v2DocRouter := route.InitV2DocRouter(_docHTML, _docYAML)
_, publicKey := service.MyService.User().GetKeyPair()
jswkJSON, err := jwt.GenerateJwksJSON(publicKey)
+14
View File
@@ -0,0 +1,14 @@
package middleware
import (
"log"
"github.com/gin-gonic/gin"
)
func CheckConnection() gin.HandlerFunc {
return func(c *gin.Context) {
log.Println("OK")
c.Next()
}
}
+8 -5
View File
@@ -5,11 +5,14 @@ type CommonModel struct {
}
type APPModel struct {
LogPath string
LogSaveName string
LogFileExt string
UserDataPath string
DBPath string
LogPath string
LogSaveName string
LogFileExt string
UserDataPath string
DBPath string
OMVServer string
AuthentikServer string
SecretKey string
}
type Result struct {
+2
View File
@@ -23,6 +23,8 @@ var (
LogPath: constants.DefaultLogPath,
LogSaveName: "user",
LogFileExt: "log",
OMVServer: constants.DefaultOMVServer,
SecretKey: constants.DefaultSecretKey,
}
Cfg *ini.File
+1 -1
View File
@@ -41,7 +41,7 @@ func GetDb(dbPath string) *gorm.DB {
gdb = db
err = db.AutoMigrate(model2.UserDBModel{}, model.EventModel{})
err = db.AutoMigrate(model2.UserDBModel{}, model.EventModel{}, model2.AuthentikCredentialsDBModel{})
if err != nil {
logger.Error("check or create db error", zap.Any("error", err))
}
+63
View File
@@ -10,8 +10,20 @@
package encryption
import (
"crypto/aes"
"crypto/cipher"
"crypto/md5"
"crypto/rand"
"encoding/hex"
"github.com/IceWhaleTech/CasaOS-UserService/pkg/config"
)
var (
// We're using a 32 byte long secret key.
// This is probably something you generate first
// then put into and environment variable.
secretKey string = config.AppInfo.SecretKey
)
func GetMD5ByStr(str string) string {
@@ -19,3 +31,54 @@ func GetMD5ByStr(str string) string {
h.Write([]byte(str))
return hex.EncodeToString(h.Sum(nil))
}
func Encrypt(plaintext string) string {
aes, err := aes.NewCipher([]byte(secretKey))
if err != nil {
panic(err)
}
gcm, err := cipher.NewGCM(aes)
if err != nil {
panic(err)
}
// We need a 12-byte nonce for GCM (modifiable if you use cipher.NewGCMWithNonceSize())
// A nonce should always be randomly generated for every encryption.
nonce := make([]byte, gcm.NonceSize())
_, err = rand.Read(nonce)
if err != nil {
panic(err)
}
// ciphertext here is actually nonce+ciphertext
// So that when we decrypt, just knowing the nonce size
// is enough to separate it from the ciphertext.
ciphertext := gcm.Seal(nonce, nonce, []byte(plaintext), nil)
return string(ciphertext)
}
func Decrypt(ciphertext string) string {
aes, err := aes.NewCipher([]byte(secretKey))
if err != nil {
panic(err)
}
gcm, err := cipher.NewGCM(aes)
if err != nil {
panic(err)
}
// Since we know the ciphertext is actually nonce+ciphertext
// And len(nonce) == NonceSize(). We can separate the two.
nonceSize := gcm.NonceSize()
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
plaintext, err := gcm.Open(nil, []byte(nonce), []byte(ciphertext), nil)
if err != nil {
panic(err)
}
return string(plaintext)
}
+1 -1
View File
@@ -63,7 +63,7 @@ func EventListen() {
if event.Name == "local-storage:raid_status" {
continue
}
service.MyService.Event().CreateEvemt(model)
service.MyService.Event().CreateEvent(model)
// logger.Info("info", zap.Any("写入信息1", model))
// output, err := json.MarshalIndent(event, "", " ")
// if err != nil {
+12 -6
View File
@@ -15,7 +15,7 @@ import (
func InitRouter() *gin.Engine {
r := gin.Default()
r.Use(middleware.Cors())
// r.Use(middleware.WriteLog())
r.Use(v1.CheckOIDCInit())
r.Use(gzip.Gzip(gzip.DefaultCompression))
// check if environment variable is set
@@ -24,16 +24,22 @@ func InitRouter() *gin.Engine {
} else {
gin.SetMode(gin.ReleaseMode)
}
go v1.InitOIDC()
r.POST("/v1/users/register", v1.PostUserRegister)
r.POST("/v1/users/login", v1.PostUserLogin)
r.POST("/v1/users/omvlogin", v1.PostOMVLogin)
r.POST("/v1/users/logout", v1.PostLogout)
r.GET("/v1/users/name", v1.GetUserAllUsername) // all/name
r.POST("/v1/users/refresh", v1.PostUserRefreshToken)
// No short-term modifications
r.GET("/v1/users/image", v1.GetUserImage)
r.GET("/v1/users/:username", v1.GetUserInfoByUsername)
r.GET("/v1/users/status", v1.GetUserStatus) // init/check
r.POST("/v1/users/oidc/login", v1.OIDCLogin)
r.GET("/v1/users/oidc/callback", v1.OIDCCallback)
r.GET("/v1/users/oidc/profile", v1.OIDCProfile)
r.GET("/v1/users/oidc/userinfo", v1.OIDCUserInfo)
r.POST("/v1/users/oidc/validateToken", v1.OIDCValidateToken)
r.POST("/v1/users/oidc/logout", v1.OIDCLogout)
v1Group := r.Group("/v1")
v1Group.Use(jwt.JWT(
@@ -63,7 +69,7 @@ func InitRouter() *gin.Engine {
v1UsersGroup.GET("/avatar", v1.GetUserAvatar)
v1UsersGroup.DELETE("/:id", v1.DeleteUser)
v1UsersGroup.GET("/:username", v1.GetUserInfoByUsername)
// v1UsersGroup.GET("/:username", v1.GetUserInfoByUsername)
v1UsersGroup.DELETE("", v1.DeleteUserAll)
}
}
+407 -9
View File
@@ -3,13 +3,17 @@ package v1
import (
"context"
"crypto/ecdsa"
"crypto/rand"
"encoding/base64"
"encoding/json"
json2 "encoding/json"
"fmt"
"image"
"image/png"
"io"
"log"
"net/http"
"net/url"
url2 "net/url"
"os"
"path"
@@ -29,14 +33,26 @@ import (
"github.com/IceWhaleTech/CasaOS-UserService/pkg/config"
"github.com/IceWhaleTech/CasaOS-UserService/pkg/utils/encryption"
"github.com/IceWhaleTech/CasaOS-UserService/pkg/utils/file"
"github.com/IceWhaleTech/CasaOS-UserService/service"
model2 "github.com/IceWhaleTech/CasaOS-UserService/service/model"
"github.com/coreos/go-oidc/v3/oidc"
"github.com/gin-gonic/gin"
uuid "github.com/satori/go.uuid"
"github.com/tidwall/gjson"
"go.uber.org/zap"
"golang.org/x/oauth2"
"golang.org/x/time/rate"
)
"github.com/IceWhaleTech/CasaOS-UserService/service"
"github.com/gin-gonic/gin"
var (
//authServer = "http://10.0.0.26:9000"
authServer = "http://accessmanager.local"
clientID = "6KwKSxLCtaQ4r6HoAn3gdNMbNOAf75j3SejLIAx7"
clientSecret = "PE05fcDP4qESUmyZ1TNYpZNBxRPq70VpFI81vehsoJ6WhGz5yPXMljrFrOdMRdRhrYmF03fHWTZHgO9ZdNENrLN13BzL8CAgtEkTsyjXfgx9GvISheIjYfpSfvo219fL"
authURL = "http://accessmanager.local/application/o/nextzenos-oidc/"
//authURL = "http://10.0.0.26:9000/application/o/nextzenos-oidc/"
callbackURL = "http://nextzenos.local/v1/users/oidc/callback"
//callbackURL = "http://172.20.60.244:8080/v1/users/oidc/callback"
)
// @Summary register user
@@ -76,7 +92,6 @@ func PostUserRegister(c *gin.Context) {
user.Username = username
user.Password = encryption.GetMD5ByStr(pwd)
user.Role = role
// user.Role = "user"
user = service.MyService.User().CreateUser(user)
if user.Id == 0 {
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR)})
@@ -109,9 +124,7 @@ func PostUserLogin(c *gin.Context) {
json := make(map[string]string)
c.ShouldBind(&json)
username := json["username"]
password := json["password"]
// check params is empty
if len(username) == 0 || len(password) == 0 {
@@ -133,7 +146,6 @@ func PostUserLogin(c *gin.Context) {
model.Result{Success: common_err.USER_NOT_EXIST_OR_PWD_INVALID, Message: common_err.GetMsg(common_err.USER_NOT_EXIST_OR_PWD_INVALID)})
return
}
// clean limit
limiter = rate.NewLimiter(rate.Every(time.Minute), 5)
@@ -141,13 +153,13 @@ func PostUserLogin(c *gin.Context) {
token := system_model.VerifyInformation{}
accessToken, err := jwt.GetAccessToken(user.Username, privateKey, user.Id)
accessToken, err := jwt.GetAccessToken(username, privateKey, user.Id)
if err != nil {
c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()})
}
token.AccessToken = accessToken
refreshToken, err := jwt.GetRefreshToken(user.Username, privateKey, user.Id)
refreshToken, err := jwt.GetRefreshToken(username, privateKey, user.Id)
if err != nil {
c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()})
}
@@ -168,6 +180,387 @@ func PostUserLogin(c *gin.Context) {
Data: data,
})
}
func randString(nByte int) (string, error) {
b := make([]byte, nByte)
if _, err := io.ReadFull(rand.Reader, b); err != nil {
return "", err
}
return base64.RawURLEncoding.EncodeToString(b), nil
}
var oauth2Config oauth2.Config
var oidcInit bool
func InitOIDC() {
const (
maxSleep = 60 * time.Second
minSleep = 10 * time.Second
maxRetryBackoff = 5 // Cap retry backoff to 5 attempts
)
var (
successCount int
failCount int
sleepTime = minSleep
)
ticker := time.NewTicker(sleepTime)
defer ticker.Stop()
for {
select {
case <-ticker.C:
if err := OIDC(); err == nil {
if !oidcInit {
log.Println("OIDC provider initialized successfully")
} else {
log.Println("OIDC provider renewed successfully")
}
oidcInit = true
failCount = 0
successCount++
// Exponential backoff with a cap
sleepTime = minSleep * time.Duration(successCount)
if sleepTime > maxSleep {
sleepTime = maxSleep
}
} else {
oidcInit = false
successCount = 0
failCount++
// Exponential backoff with a cap
sleepTime = minSleep * time.Duration(failCount)
if failCount > maxRetryBackoff {
sleepTime = minSleep * time.Duration(maxRetryBackoff)
}
log.Printf("OIDC initialization failed: %v. Retrying in %v", err, sleepTime)
}
log.Printf("Waiting for %v before next check", sleepTime)
ticker.Reset(sleepTime)
}
}
}
func CheckOIDCInit() gin.HandlerFunc {
return func(c *gin.Context) {
if !oidcInit {
log.Println("Provider is Offline")
c.JSON(http.StatusServiceUnavailable, model.Result{Success: http.StatusServiceUnavailable, Message: "Authentik Server is Offline"})
return
}
c.Next()
}
}
// Use an init function to initialize the oauth2Config variable.
func OIDC() error {
ctx := context.Background()
provider, err := oidc.NewProvider(ctx, authURL)
if err != nil {
return err
}
oauth2Config = oauth2.Config{
ClientID: clientID,
ClientSecret: clientSecret,
RedirectURL: callbackURL,
Endpoint: provider.Endpoint(),
Scopes: []string{oidc.ScopeOpenID, "profile", "email", "goauthentik.io/api"},
//add offline access for refresh token
}
return nil
}
func OIDCLogin(c *gin.Context) {
json := make(map[string]string)
c.ShouldBind(&json)
state := json["state"]
// w := c.Writer
// r := c.Request
// setCallbackCookie(w, r, "state", state)
// c.Redirect(http.StatusFound, oauth2Config.AuthCodeURL(state))
c.JSON(common_err.SUCCESS,
model.Result{
Success: common_err.SUCCESS,
Message: common_err.GetMsg(common_err.SUCCESS),
Data: oauth2Config.AuthCodeURL(state),
})
}
func OIDCCallback(c *gin.Context) {
w := c.Writer
r := c.Request
// Verify state cookie
state := c.Query("state")
if r.URL.Query().Get("state") != state {
http.Error(w, "state did not match", http.StatusBadRequest)
return
}
// Exchange authorization code for token
oauth2Token, err := oauth2Config.Exchange(context.Background(), r.URL.Query().Get("code"))
if err != nil {
http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusInternalServerError)
return
}
expiryDuration := time.Until(oauth2Token.Expiry)
c.SetCookie("accessToken", oauth2Token.AccessToken, int(expiryDuration.Seconds()), "/", "", false, true)
// c.SetCookie("refreshToken", oauth2Token.RefreshToken, int(expiryDuration.Seconds()), "/", "", false, true)
c.Redirect(http.StatusFound, state)
}
func OIDCUserInfo(c *gin.Context) {
json := make(map[string]string)
c.ShouldBind(&json)
accessToken, err := c.Cookie("accessToken")
if err != nil {
c.Redirect(http.StatusFound, "/#/oidc")
}
authentikUser, err := service.MyService.Authentik().GetUserInfo(accessToken, authServer)
if err != nil {
c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.ERROR_AUTH_TOKEN, Message: common_err.GetMsg(common_err.ERROR_AUTH_TOKEN)})
return
}
c.JSON(common_err.SUCCESS,
model.Result{
Success: common_err.SUCCESS,
Message: common_err.GetMsg(common_err.SUCCESS),
Data: authentikUser,
})
}
func OIDCValidateToken(c *gin.Context) {
json := make(map[string]string)
c.ShouldBind(&json)
accessToken := json["authentikToken"]
var validateToken model2.AuthentikToken
validateToken, err := service.MyService.Authentik().ValidateToken(clientID, clientSecret, accessToken, authServer)
if err != nil {
c.JSON(http.StatusUnauthorized, model.Result{Success: common_err.ERROR_AUTH_TOKEN, Message: common_err.GetMsg(common_err.ERROR_AUTH_TOKEN)})
return
}
if !validateToken.Active {
c.JSON(http.StatusUnauthorized, model.Result{Success: common_err.ERROR_AUTH_TOKEN, Message: common_err.GetMsg(common_err.ERROR_AUTH_TOKEN)})
return
}
c.JSON(http.StatusOK, model.Result{Success: common_err.ERROR_AUTH_TOKEN, Message: common_err.GetMsg(common_err.ERROR_AUTH_TOKEN)})
}
func OIDCLogout(c *gin.Context) {
json := make(map[string]string)
c.ShouldBind(&json)
accessToken := json["authentikToken"]
fmt.Println(accessToken)
flow := "/if/flow/default-authentication-flow/"
next := "/application/o/authorize/"
client := "client_id=" + clientID
redirect_uri := "&redirect_uri=" + url.QueryEscape(callbackURL)
response_type := "&response_type=code"
scope := "&scope=openid+profile+email+" + url.QueryEscape("goauthentik.io/api")
state := "&state=" + url.QueryEscape("/#/profile")
fullURL := authServer + flow + "?" + "next=" + url.QueryEscape(next+"?"+client+redirect_uri+response_type+scope+state)
c.JSON(http.StatusOK, model.Result{Success: common_err.ERROR_AUTH_TOKEN, Message: common_err.GetMsg(common_err.ERROR_AUTH_TOKEN), Data: fullURL})
}
func OIDCProfile(c *gin.Context) {
if !oidcInit {
}
json := make(map[string]string)
c.ShouldBind(&json)
accessToken, err := c.Cookie("accessToken")
if err != nil {
c.Redirect(http.StatusFound, "/#/oidc")
}
// r := c.Request
// Get Authentik user info
authentikUser, err := service.MyService.Authentik().GetUserInfo(accessToken, authServer)
if err != nil {
c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.ERROR_AUTH_TOKEN, Message: common_err.GetMsg(common_err.ERROR_AUTH_TOKEN)})
return
}
// Handle user data in local database
user := service.MyService.User().GetUserInfoByUserName(authentikUser.User.Username)
if user.Id > 0 {
// Update existing user
user.Nickname = authentikUser.User.Username
user.Email = authentikUser.User.Email
user.Role = determineUserRole(authentikUser.User.IsSuperuser)
user.Avatar = authentikUser.User.Avatar
service.MyService.User().UpdateUser(user)
} else {
// Create new user
user = model2.UserDBModel{
Username: authentikUser.User.Username,
Password: hashPassword(),
Role: determineUserRole(authentikUser.User.IsSuperuser),
Avatar: authentikUser.User.Avatar,
}
user = service.MyService.User().CreateUser(user)
if user.Id == 0 {
c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR)})
return
}
}
// Generate tokens
token, err := generateTokens(user)
if err != nil {
c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()})
return
}
data := make(map[string]interface{}, 2)
data["token"] = token
data["user"] = user
data["authToken"] = accessToken
c.JSON(common_err.SUCCESS,
model.Result{
Success: common_err.SUCCESS,
Message: common_err.GetMsg(common_err.SUCCESS),
Data: data,
})
}
func determineUserRole(isSuperuser bool) string {
if isSuperuser {
return "admin"
}
return "user"
}
func hashPassword() string {
generatePassword, err := randString(16)
if err != nil {
return ""
}
return encryption.GetMD5ByStr(generatePassword)
}
func generateTokens(user model2.UserDBModel) (system_model.VerifyInformation, error) {
privateKey, _ := service.MyService.User().GetKeyPair()
accessToken, err := jwt.GetAccessToken(user.Username, privateKey, user.Id)
if err != nil {
return system_model.VerifyInformation{}, err
}
refreshToken, err := jwt.GetRefreshToken(user.Username, privateKey, user.Id)
if err != nil {
return system_model.VerifyInformation{}, err
}
return system_model.VerifyInformation{
AccessToken: accessToken,
RefreshToken: refreshToken,
ExpiresAt: time.Now().Add(3 * time.Hour * time.Duration(1)).Unix(),
}, nil
}
// func setCallbackCookie(w http.ResponseWriter, r *http.Request, name, value string) {
// c := &http.Cookie{
// Name: name,
// Value: value,
// MaxAge: int(time.Hour.Seconds()),
// Secure: r.TLS != nil,
// HttpOnly: true,
// }
// http.SetCookie(w, c)
// }
// @Summary login user to openmediavault
// @Produce application/json
// @Tags user
// @Param username password
// @Security SessionID
// @Success 200 {string} string "ok"
// @Router /users/omvLogin [post]
func PostOMVLogin(c *gin.Context) {
if !limiter.Allow() {
c.JSON(common_err.TOO_MANY_REQUEST,
model.Result{
Success: common_err.TOO_MANY_LOGIN_REQUESTS,
Message: common_err.GetMsg(common_err.TOO_MANY_LOGIN_REQUESTS),
})
return
}
json := make(map[string]string)
c.ShouldBind(&json)
username := json["username"]
password := json["password"]
res, cookies := service.MyService.OMV().LoginSession(username, password)
var resData model2.OMVLogin
err := json2.Unmarshal([]byte(res), &resData)
if err != nil {
log.Printf("Error getting user: %v", err)
return
}
if !resData.Response.Authenticated {
c.JSON(common_err.CLIENT_ERROR,
model.Result{Success: common_err.USER_NOT_EXIST_OR_PWD_INVALID, Message: common_err.GetMsg(common_err.USER_NOT_EXIST_OR_PWD_INVALID)})
return
}
getUser, err := service.MyService.OMV().AuthUser(username, password, resData.Response.SessionID)
if err != nil {
// Handle the error, for example, log it or return it
log.Printf("Error getting user: %v", err)
return // or handle it in a way that fits your application's error handling strategy
}
var userData model2.OMVUser
err = json2.Unmarshal([]byte(getUser), &userData)
if err != nil {
// Handle the error, for example, log it or return it
log.Printf("Error getting user: %v", err)
return // or handle it in a way that fits your application's error handling strategy
}
if isEmpty(userData.Response) {
c.JSON(common_err.CLIENT_ERROR,
model.Result{
Success: common_err.USER_NOT_EXIST_OR_PWD_INVALID,
Message: common_err.GetMsg(common_err.USER_NOT_EXIST_OR_PWD_INVALID)})
return
}
// cookie_value, err := c.Cookie("sessionID")
// decrypt := encryption.Decrypt(cookie_value)
// fmt.Printf(decrypt)
// sessionId := encryption.Encrypt(resData.Response.SessionID)
for _, cookie := range cookies {
c.SetCookie(cookie.Name, cookie.Value, 3600, "/", "", false, true)
}
c.JSON(common_err.SUCCESS,
model.Result{
Success: common_err.SUCCESS,
Message: common_err.GetMsg(common_err.SUCCESS),
Data: userData,
})
}
func PostLogout(c *gin.Context) {
cookies := c.Request.Cookies()
for _, cookie := range cookies {
// Set the cookie to expire immediately
c.SetCookie(cookie.Name, "", -1, "/", "", false, true)
}
c.JSON(common_err.SUCCESS,
model.Result{
Success: common_err.SUCCESS,
Message: common_err.GetMsg(common_err.SUCCESS),
})
}
func isEmpty(obj interface{}) bool {
jsonData, err := json.Marshal(obj)
if err != nil && string(jsonData) == "{}" {
return true
}
return false
}
// @Summary edit user head
// @Produce application/json
@@ -431,7 +824,12 @@ func GetUserInfoByUsername(c *gin.Context) {
}
user := service.MyService.User().GetUserInfoByUserName(username)
if user.Id == 0 {
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.USER_NOT_EXIST, Message: common_err.GetMsg(common_err.USER_NOT_EXIST)})
c.JSON(common_err.SUCCESS,
model.Result{
Success: common_err.SUCCESS,
Message: common_err.GetMsg(common_err.USER_NOT_EXIST),
Data: nil,
})
return
}
+153
View File
@@ -0,0 +1,153 @@
package service
import (
"encoding/base64"
"encoding/json"
"fmt"
"log"
"net/http"
"net/url"
"strings"
model2 "github.com/IceWhaleTech/CasaOS-UserService/service/model"
"gorm.io/gorm"
)
type AuthentikService interface {
GetUserInfo(accessToken string, baseURL string) (model2.AuthentikUser, error)
GetUserApp(accessToken string, baseURL string) (model2.AuthentikApplication, error)
CreateCredential(m model2.AuthentikCredentialsDBModel) model2.AuthentikCredentialsDBModel
UpdateCredential(m model2.AuthentikCredentialsDBModel) model2.AuthentikCredentialsDBModel
GetCredential(id int) model2.AuthentikCredentialsDBModel
ValidateToken(clientId string, clientSecret string, accessToken string, baseURL string) (model2.AuthentikToken, error)
}
type authentikService struct {
db *gorm.DB
}
var (
APICorePrefix = "/api/v3/core"
)
func (a *authentikService) CreateCredential(m model2.AuthentikCredentialsDBModel) model2.AuthentikCredentialsDBModel {
a.db.Create(&m)
return m
}
func (a *authentikService) UpdateCredential(m model2.AuthentikCredentialsDBModel) model2.AuthentikCredentialsDBModel {
a.db.Model(&m).Where("id = ?", m.Id).Updates(m)
return m
}
func (a *authentikService) GetCredential(id int) model2.AuthentikCredentialsDBModel {
var m model2.AuthentikCredentialsDBModel
a.db.Limit(1).Where("id = ?", id).First(&m)
return m
}
func (a *authentikService) ValidateToken(clientId string, clientSecret string, accessToken string, baseURL string) (model2.AuthentikToken, error) {
auth := clientId + ":" + clientSecret
basicAuth := "Basic " + base64.StdEncoding.EncodeToString([]byte(auth))
path := baseURL + "/application/o/introspect/"
formData := url.Values{}
formData.Set("token", accessToken)
responseBody := strings.NewReader(formData.Encode())
req, err := http.NewRequest("POST", path, responseBody)
if err != nil {
return model2.AuthentikToken{}, fmt.Errorf("error creating request: %v", err)
}
req.Header.Set("Authorization", basicAuth)
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Add("Accept", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return model2.AuthentikToken{}, fmt.Errorf("error making request: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
log.Println("HTTP error:", resp.Status)
return model2.AuthentikToken{}, fmt.Errorf("HTTP error: %s", resp.Status)
}
var token model2.AuthentikToken
if err := json.NewDecoder(resp.Body).Decode(&token); err != nil {
log.Println("Error decoding response:", err)
return model2.AuthentikToken{}, err
}
return token, nil
}
func (a *authentikService) GetUserApp(accessToken string, baseURL string) (model2.AuthentikApplication, error) {
bearer := "Bearer " + accessToken
path := baseURL + APICorePrefix + "/applications/"
req, err := http.NewRequest("GET", path, nil)
if err != nil {
return model2.AuthentikApplication{}, err
}
req.Header.Set("Authorization", bearer)
req.Header.Add("Accept", "application/json")
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
// Always follow redirects
return nil
},
}
resp, err := client.Do(req)
if err != nil {
log.Println("Error on request:", err)
return model2.AuthentikApplication{}, err
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
log.Println("HTTP error:", resp.Status)
return model2.AuthentikApplication{}, fmt.Errorf("HTTP error: %s", resp.Status)
}
var app model2.AuthentikApplication
if err := json.NewDecoder(resp.Body).Decode(&app); err != nil {
log.Println("Error decoding response:", err)
return model2.AuthentikApplication{}, err
}
return app, nil
}
func (a *authentikService) GetUserInfo(accessToken string, baseURL string) (model2.AuthentikUser, error) {
bearer := "Bearer " + accessToken
path := baseURL + APICorePrefix + "/users/me/"
req, err := http.NewRequest("GET", path, nil) // No need for bytes.NewBuffer(nil) for GET requests without a body
if err != nil {
return model2.AuthentikUser{}, err
}
req.Header.Set("Authorization", bearer)
req.Header.Add("Accept", "application/json")
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
// Always follow redirects
return nil
},
}
resp, err := client.Do(req)
if err != nil {
log.Println("Error on request:", err)
return model2.AuthentikUser{}, err
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
log.Println("HTTP error:", resp.Status)
return model2.AuthentikUser{}, fmt.Errorf("HTTP error: %s", resp.Status)
}
var user model2.AuthentikUser
if err := json.NewDecoder(resp.Body).Decode(&user); err != nil {
log.Println("Error decoding response:", err)
return model2.AuthentikUser{}, err
}
return user, nil
}
func NewAuthentikService(db *gorm.DB) AuthentikService {
return &authentikService{
db: db,
}
}
+2 -2
View File
@@ -8,7 +8,7 @@ import (
)
type EventService interface {
CreateEvemt(m model.EventModel) model.EventModel
CreateEvent(m model.EventModel) model.EventModel
GetEvents() (list []model.EventModel)
GetEventByUUID(uuid string) (m model.EventModel)
DeleteEvent(uuid string)
@@ -19,7 +19,7 @@ type eventService struct {
db *gorm.DB
}
func (e *eventService) CreateEvemt(m model.EventModel) model.EventModel {
func (e *eventService) CreateEvent(m model.EventModel) model.EventModel {
e.db.Create(&m)
return m
}
+42
View File
@@ -0,0 +1,42 @@
package model
type AuthentikApplication struct {
Pagination struct {
Count int64 `json:"count"`
Current int64 `json:"current"`
EndIndex int64 `json:"end_index"`
Next int64 `json:"next"`
Previous int64 `json:"previous"`
StartIndex int64 `json:"start_index"`
TotalPages int64 `json:"total_pages"`
} `json:"pagination"`
Results []struct {
BackchannelProviders []interface{} `json:"backchannel_providers"`
BackchannelProvidersObj []interface{} `json:"backchannel_providers_obj"`
Group string `json:"group"`
LaunchURL string `json:"launch_url"`
MetaDescription string `json:"meta_description"`
MetaIcon string `json:"meta_icon"`
MetaLaunchURL string `json:"meta_launch_url"`
MetaPublisher string `json:"meta_publisher"`
Name string `json:"name"`
OpenInNewTab bool `json:"open_in_new_tab"`
Pk string `json:"pk"`
PolicyEngineMode string `json:"policy_engine_mode"`
Provider int64 `json:"provider"`
ProviderObj struct {
AssignedApplicationName string `json:"assigned_application_name"`
AssignedApplicationSlug string `json:"assigned_application_slug"`
AuthenticationFlow string `json:"authentication_flow"`
AuthorizationFlow string `json:"authorization_flow"`
Component string `json:"component"`
MetaModelName string `json:"meta_model_name"`
Name string `json:"name"`
Pk int64 `json:"pk"`
PropertyMappings []string `json:"property_mappings"`
VerboseName string `json:"verbose_name"`
VerboseNamePlural string `json:"verbose_name_plural"`
} `json:"provider_obj"`
Slug string `json:"slug"`
} `json:"results"`
}
+17
View File
@@ -0,0 +1,17 @@
package model
import "time"
// Soon to be removed
type AuthentikCredentialsDBModel struct {
Id int `gorm:"column:id;primary_key" json:"id"`
ClientID string `json:"clientId"`
ClientSecret string `json:"clientSecret"`
Server string `json:"server"`
CreatedAt time.Time `gorm:"<-:create;autoCreateTime" json:"created_at,omitempty"`
UpdatedAt time.Time `gorm:"<-:create;<-:update;autoUpdateTime" json:"updated_at,omitempty"`
}
func (p *AuthentikCredentialsDBModel) TableName() string {
return "o_authentik_credentials"
}
+21
View File
@@ -0,0 +1,21 @@
package model
type AuthentikToken struct {
Acr string `json:"acr"`
Active bool `json:"active"`
Aud string `json:"aud"`
AuthTime int64 `json:"auth_time"`
ClientID string `json:"client_id"`
Email string `json:"email"`
EmailVerified bool `json:"email_verified"`
Exp int64 `json:"exp"`
GivenName string `json:"given_name"`
Groups []string `json:"groups"`
Iat int64 `json:"iat"`
Iss string `json:"iss"`
Name string `json:"name"`
Nickname string `json:"nickname"`
PreferredUsername string `json:"preferred_username"`
Scope string `json:"scope"`
Sub string `json:"sub"`
}
+25
View File
@@ -0,0 +1,25 @@
package model
type AuthentikUser struct {
User struct {
Avatar string `json:"avatar"`
Email string `json:"email"`
Groups []struct {
Name string `json:"name"`
Pk string `json:"pk"`
} `json:"groups"`
IsActive bool `json:"is_active"`
IsSuperuser bool `json:"is_superuser"`
Name string `json:"name"`
Pk int64 `json:"pk"`
Settings struct {
Theme struct {
Base string `json:"base"`
} `json:"theme"`
} `json:"settings"`
SystemPermissions []string `json:"system_permissions"`
Type string `json:"type"`
UID string `json:"uid"`
Username string `json:"username"`
} `json:"user"`
}
+23
View File
@@ -0,0 +1,23 @@
package model
type OMVLogin struct {
Response struct {
Authenticated bool `json:"authenticated"`
Username string `json:"username"`
Permissions struct {
Role string `json:"role"`
} `json:"permissions"`
SessionID string `json:"sessionid"`
} `json:"response"`
Error interface{} `json:"error"`
}
type OMVUser struct {
Response struct {
Authenticated bool `json:"authenticated"`
Username string `json:"username"`
Permissions struct {
Role string `json:"role"`
} `json:"permissions"`
} `json:"response"`
Error interface{} `json:"error"`
}
+1 -1
View File
@@ -11,7 +11,7 @@ package model
import "time"
//Soon to be removed
// Soon to be removed
type UserDBModel struct {
Id int `gorm:"column:id;primary_key" json:"id"`
Username string `json:"username"`
+167
View File
@@ -0,0 +1,167 @@
package service
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"github.com/IceWhaleTech/CasaOS-UserService/pkg/config"
"github.com/IceWhaleTech/CasaOS-UserService/service/model"
)
type OMVService interface {
LoginSession(userName string, password string) (string, []*http.Cookie)
Logout(sessionID string) (string, error)
GetUser(username string, sessionID string) (string, error)
AuthUser(username string, password string, sessionID string) (string, error)
SetUser(m model.UserDBModel) model.UserDBModel
ApplyChange()
}
type omvService struct {
}
// AuthUser implements OMVService.
func (o *omvService) LoginSession(username string, password string) (string, []*http.Cookie) {
postBody, _ := json.Marshal(map[string]interface{}{
"service": "session",
"method": "login",
"params": map[string]string{
"username": username,
"password": password,
},
})
responseBody := bytes.NewBuffer(postBody)
response, err := http.Post(config.AppInfo.OMVServer, "application/json", responseBody)
cookies := response.Cookies()
if err != nil {
fmt.Print(err.Error())
os.Exit(1)
}
responseData, err := io.ReadAll(response.Body)
if err != nil {
log.Fatal(err)
}
return string(responseData), cookies
}
func (o *omvService) Logout(sessionID string) (string, error) {
postBody, _ := json.Marshal(map[string]interface{}{
"service": "UserMgmt",
"method": "logout",
"params": nil,
})
responseBody := bytes.NewBuffer(postBody)
req, err := http.NewRequest("POST", config.AppInfo.OMVServer, responseBody)
if err != nil {
return "", fmt.Errorf("error creating request: %v", err)
}
req.Header.Set("X-OPENMEDIAVAULT-SESSIONID", sessionID) // Set session ID header
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return "", fmt.Errorf("error making request: %v", err)
}
defer resp.Body.Close() // Ensure the response body is closed
// Check for HTTP errors
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("HTTP error: %s", resp.Status)
}
// Read the response body
responseData, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("error reading response body: %v", err)
}
return string(responseData), nil
}
func (o *omvService) AuthUser(username string, password string, sessionID string) (string, error) {
postBody, _ := json.Marshal(map[string]interface{}{
"service": "session",
"method": "login",
"params": map[string]string{
"username": username,
"password": password,
},
})
responseBody := bytes.NewBuffer(postBody)
req, err := http.NewRequest("POST", config.AppInfo.OMVServer, responseBody)
if err != nil {
return "", fmt.Errorf("error creating request: %v", err)
}
req.Header.Set("X-OPENMEDIAVAULT-SESSIONID", sessionID) // Set session ID header
// Send the request
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return "", fmt.Errorf("error making request: %v", err)
}
defer resp.Body.Close()
// Check for HTTP errors
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("HTTP error: %s", resp.Status)
}
// Read the response body
responseData, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("error reading response body: %v", err)
}
return string(responseData), nil
}
func (o *omvService) GetUser(username string, sessionID string) (string, error) {
// Prepare the RPC request
postBody, _ := json.Marshal(map[string]interface{}{
"service": "UserMgmt",
"method": "getUser",
"params": map[string]string{
"name": username,
},
})
responseBody := bytes.NewBuffer(postBody)
// Create HTTP request and set session ID header
req, err := http.NewRequest("POST", config.AppInfo.OMVServer, responseBody)
if err != nil {
return "", fmt.Errorf("error creating request: %v", err)
}
req.Header.Set("X-OPENMEDIAVAULT-SESSIONID", sessionID) // Set session ID header
// Send the request
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return "", fmt.Errorf("error making request: %v", err)
}
defer resp.Body.Close() // Ensure the response body is closed
// Check for HTTP errors
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("HTTP error: %s", resp.Status)
}
// Read the response body
responseData, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("error reading response body: %v", err)
}
return string(responseData), nil
}
func (o *omvService) SetUser(m model.UserDBModel) model.UserDBModel {
// Implement SetUser logic here
return m // Assuming m is the modified user
}
func (o *omvService) ApplyChange() {
// Implement ApplyChange logic here
}
func NewOMVService() OMVService {
return &omvService{}
}
+18 -6
View File
@@ -14,6 +14,8 @@ type Repository interface {
User() UserService
MessageBus() *message_bus.ClientWithResponses
Event() EventService
OMV() OMVService
Authentik() AuthentikService
}
func NewService(db *gorm.DB, RuntimePath string) Repository {
@@ -24,16 +26,20 @@ func NewService(db *gorm.DB, RuntimePath string) Repository {
}
return &store{
gateway: gatewayManagement,
user: NewUserService(db),
event: NewEventService(db),
gateway: gatewayManagement,
user: NewUserService(db),
event: NewEventService(db),
omv: NewOMVService(),
authentik: NewAuthentikService(db),
}
}
type store struct {
gateway external.ManagementService
user UserService
event EventService
gateway external.ManagementService
user UserService
event EventService
omv OMVService
authentik AuthentikService
}
func (c *store) Event() EventService {
@@ -46,6 +52,12 @@ func (c *store) Gateway() external.ManagementService {
func (c *store) User() UserService {
return c.user
}
func (c *store) OMV() OMVService {
return c.omv
}
func (c *store) Authentik() AuthentikService {
return c.authentik
}
func (c *store) MessageBus() *message_bus.ClientWithResponses {
client, _ := message_bus.NewClientWithResponses("", func(c *message_bus.Client) error {
// error will never be returned, as we always want to return a client, even with wrong address,