From 6ff3ed3ddb1493f2071249cd9a14a2ecc5c9bea9 Mon Sep 17 00:00:00 2001 From: donggyu Date: Wed, 12 Apr 2023 17:18:57 +0900 Subject: [PATCH 01/11] add golangci-lint config --- .golangci.yaml | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 .golangci.yaml diff --git a/.golangci.yaml b/.golangci.yaml new file mode 100644 index 00000000..ff85739a --- /dev/null +++ b/.golangci.yaml @@ -0,0 +1,68 @@ +run: + timeout: 5m + issues-exit-code: 2 + tests: true + build-tags: [] + skip-dirs: [] + skip-dirs-use-default: false + skip-files: [] + allow-parallel-runners: true +output: + format: colored-line-number + print-issued-lines: true + print-linter-name: true + uniq-by-line: true + sort-results: true +linters-settings: + godot: + scope: all + gofmt: + simplify: false + staticcheck: + checks: + - all + - "-SA9004" # +linters: + enable: + - errcheck # checks unchecked errors + - gosimple # simplify code + - govet # examines Go source code and reports suspicious constructs + - ineffassign # detect unused assign + - staticcheck # cover Go vet edge cases + - typecheck # type-checks Go code + - unused # checks Go code for unused constants, variables, functions and types + + + disable: + - deadcode # enabled-default. but duplicated & deprecated someday + - structcheck # enabled-default. but duplicated & deprecated someday + - varcheck # enabled-default. but duplicated & deprecated someday + - bidichk # checks for dangerous unicode character sequences + - bodyclose # checks whether HTTP response body is closed successfully + - contextcheck # check the function whether use a non-inherited context + - dupl # code clone detection + - durationcheck # check for two durations multiplied together + - errname # checks that sentinel errors are prefixed with the Err and error types are suffixed with the Error + - errorlint # find code that will cause problems with the error wrapping scheme + - exportloopref # checks for pointers to enclosing loop variables + - goconst # finds repeated strings that could be replaced by a constant + - gocritic # provides diagnostics that check for bugs, performance and style issues + - godot # check if comments end in a period + - gofmt # checks whether code was gofmt-ed + - goimports # fix imports, formats your code in the same style as gofmt + - ifshort # checks that your code uses short syntax for if-statements whenever possible + - misspell # finds commonly misspelled English words in comments + - noctx # finds sending http request without context.Context + - predeclared # find code that shadows one of Go's predeclared identifiers + - revive # replacement of golint + - rowserrcheck # checks whether Err of rows is checked successfully + - sqlclosecheck # checks that sql.Rows and sql.Stmt are closed. + - unconvert # remove unnecessary type conversions + - wastedassign # finds wasted assignment statements. + - whitespace # tool for detection of leading and trailing whitespace + - wrapcheck # check that errors from external packages are wrapped during return to help identify the error source. + +issues: + exclude: [] +severity: + default-severity: warning \ No newline at end of file From 74d55d2608d81a4c3bed17ff289f265be9bdd3b7 Mon Sep 17 00:00:00 2001 From: donggyu Date: Wed, 12 Apr 2023 17:21:31 +0900 Subject: [PATCH 02/11] change all login response error to unauthorized error --- internal/usecase/auth.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/usecase/auth.go b/internal/usecase/auth.go index 0d18cb1c..31f45b6e 100644 --- a/internal/usecase/auth.go +++ b/internal/usecase/auth.go @@ -32,10 +32,10 @@ func (r *AuthUsecase) Login(accountId string, password string, organizationId st // Authentication with DB user, err := r.repo.Get(accountId, organizationId) if err != nil { - return domain.User{}, err + return domain.User{}, httpErrors.NewUnauthorizedError(err) } if !helper.CheckPasswordHash(user.Password, password) { - return domain.User{}, httpErrors.NewUnauthorizedError(fmt.Errorf("password is not correct")) + return domain.User{}, httpErrors.NewUnauthorizedError(fmt.Errorf("")) } // Authentication with Keycloak From 062f58b510236b4f5f56640832e56a7d91d608aa Mon Sep 17 00:00:00 2001 From: donggyu Date: Wed, 12 Apr 2023 17:40:57 +0900 Subject: [PATCH 03/11] trivial change for lint --- internal/delivery/http/app-serve-app.go | 1 - internal/delivery/http/auth.go | 1 - internal/delivery/http/stack-template.go | 11 +++++------ internal/helper/jwt.go | 5 ++++- internal/keycloak/keycloak.go | 1 - internal/repository/app-serve-app.go | 4 +--- internal/repository/cluster.go | 1 - internal/repository/user.go | 4 ++-- internal/route/route.go | 10 +++++++++- internal/usecase/app-serve-app.go | 1 - pkg/api-client/api-client.go | 1 - pkg/httpErrors/httpErrors.go | 1 - 12 files changed, 21 insertions(+), 20 deletions(-) diff --git a/internal/delivery/http/app-serve-app.go b/internal/delivery/http/app-serve-app.go index 1f2e26e8..521f4245 100644 --- a/internal/delivery/http/app-serve-app.go +++ b/internal/delivery/http/app-serve-app.go @@ -139,7 +139,6 @@ func (h *AppServeAppHandler) GetAppServeApps(w http.ResponseWriter, r *http.Requ out.AppServeApps = apps ResponseJSON(w, http.StatusOK, out) - } // GetAppServeApp godoc diff --git a/internal/delivery/http/auth.go b/internal/delivery/http/auth.go index cde40bce..3a1b13ee 100644 --- a/internal/delivery/http/auth.go +++ b/internal/delivery/http/auth.go @@ -59,7 +59,6 @@ func (h *AuthHandler) Login(w http.ResponseWriter, r *http.Request) { } ResponseJSON(w, http.StatusOK, out) - } // Logout godoc diff --git a/internal/delivery/http/stack-template.go b/internal/delivery/http/stack-template.go index fbb66952..4eee3b2c 100644 --- a/internal/delivery/http/stack-template.go +++ b/internal/delivery/http/stack-template.go @@ -35,7 +35,7 @@ func NewStackTemplateHandler(h usecase.IStackTemplateUsecase) *StackTemplateHand // @Router /stack-templates [post] // @Security JWT func (h *StackTemplateHandler) CreateStackTemplate(w http.ResponseWriter, r *http.Request) { - ErrorJSON(w, fmt.Errorf("Need implentation")) + ErrorJSON(w, fmt.Errorf("need implementation")) } // GetStackTemplate godoc @@ -84,7 +84,7 @@ func (h *StackTemplateHandler) GetStackTemplate(w http.ResponseWriter, r *http.R vars := mux.Vars(r) strId, ok := vars["stackTemplateId"] if !ok { - ErrorJSON(w, httpErrors.NewBadRequestError(fmt.Errorf("Invalid stackTemplateId"))) + ErrorJSON(w, httpErrors.NewBadRequestError(fmt.Errorf("invalid stackTemplateId"))) return } @@ -111,7 +111,6 @@ func (h *StackTemplateHandler) GetStackTemplate(w http.ResponseWriter, r *http.R } ResponseJSON(w, http.StatusOK, out) - } // UpdateStackTemplate godoc @@ -152,7 +151,7 @@ func (h *StackTemplateHandler) UpdateStackTemplate(w http.ResponseWriter, r *htt } */ - ErrorJSON(w, fmt.Errorf("Need implentation")) + ErrorJSON(w, fmt.Errorf("need implementation")) } // DeleteStackTemplate godoc @@ -169,9 +168,9 @@ func (h *StackTemplateHandler) DeleteStackTemplate(w http.ResponseWriter, r *htt vars := mux.Vars(r) _, ok := vars["stackTemplateId"] if !ok { - ErrorJSON(w, httpErrors.NewBadRequestError(fmt.Errorf("Invalid stackTemplateId"))) + ErrorJSON(w, httpErrors.NewBadRequestError(fmt.Errorf("invalid stackTemplateId"))) return } - ErrorJSON(w, fmt.Errorf("Need implentation")) + ErrorJSON(w, fmt.Errorf("need implementation")) } diff --git a/internal/helper/jwt.go b/internal/helper/jwt.go index 8bfcd316..68170a55 100644 --- a/internal/helper/jwt.go +++ b/internal/helper/jwt.go @@ -11,7 +11,10 @@ func CreateJWT(accountId string, uId string, organizationId string) (string, err signingKey := []byte(viper.GetString("jwt-secret")) aToken := jwt.New(jwt.SigningMethodHS256) - claims := aToken.Claims.(jwt.MapClaims) + claims, ok := aToken.Claims.(jwt.MapClaims) + if !ok { + return "", nil + } claims["AccountId"] = accountId claims["ID"] = uId claims["OrganizationId"] = organizationId diff --git a/internal/keycloak/keycloak.go b/internal/keycloak/keycloak.go index 69ed3374..999150f7 100644 --- a/internal/keycloak/keycloak.go +++ b/internal/keycloak/keycloak.go @@ -341,7 +341,6 @@ func (k *Keycloak) GetAccessTokenByIdPassword(accountId string, password string, } func (k *Keycloak) VerifyAccessToken(token string, organizationName string) error { - //TODO implement me ctx := context.Background() //log.Info(token) diff --git a/internal/repository/app-serve-app.go b/internal/repository/app-serve-app.go index 046ba614..8af3a6a7 100644 --- a/internal/repository/app-serve-app.go +++ b/internal/repository/app-serve-app.go @@ -27,8 +27,7 @@ func NewAppServeAppRepository(db *gorm.DB) IAppServeAppRepository { } } -func (r *AppServeAppRepository) CreateAppServeApp( - app *domain.AppServeApp) (appId string, taskId string, err error) { +func (r *AppServeAppRepository) CreateAppServeApp(app *domain.AppServeApp) (appId string, taskId string, err error) { res := r.db.Create(&app) if res.Error != nil { @@ -41,7 +40,6 @@ func (r *AppServeAppRepository) CreateAppServeApp( // Update creates new appServeApp Task for existing appServeApp. func (r *AppServeAppRepository) CreateTask( task *domain.AppServeAppTask) (string, error) { - res := r.db.Create(task) if res.Error != nil { return "", res.Error diff --git a/internal/repository/cluster.go b/internal/repository/cluster.go index 54609444..1556fea8 100644 --- a/internal/repository/cluster.go +++ b/internal/repository/cluster.go @@ -199,7 +199,6 @@ func (r *ClusterRepository) InitWorkflow(clusterId domain.ClusterId, workflowId } return nil - } func reflectCluster(cluster Cluster) domain.Cluster { diff --git a/internal/repository/user.go b/internal/repository/user.go index cdf68926..4b08c2f5 100644 --- a/internal/repository/user.go +++ b/internal/repository/user.go @@ -11,9 +11,9 @@ import ( // Interface type IUserRepository interface { - Create(accountId string, organizationId string, paasword string, name string) (domain.User, error) + Create(accountId string, organizationId string, password string, name string) (domain.User, error) CreateWithUuid(uuid uuid.UUID, accountId string, name string, password string, email string, - department string, description string, orgainzationId string, roleId uuid.UUID) (domain.User, error) + department string, description string, organizationId string, roleId uuid.UUID) (domain.User, error) List(...FilterFunc) (out *[]domain.User, err error) Get(accountId string, organizationId string) (domain.User, error) GetByUuid(userId uuid.UUID) (domain.User, error) diff --git a/internal/route/route.go b/internal/route/route.go index 4b8e3a45..df383215 100644 --- a/internal/route/route.go +++ b/internal/route/route.go @@ -227,7 +227,15 @@ func authMiddleware(next http.Handler, kc keycloak.IKeycloak) http.Handler { } return } - organization := parsedToken.Claims.(jwtWithouKey.MapClaims)["organization"].(string) + organization, ok := parsedToken.Claims.(jwtWithouKey.MapClaims)["organization"].(string) + if !ok { + log.Error("failed to parse access token: ", err) + w.WriteHeader(http.StatusUnauthorized) + if _, err := w.Write([]byte(err.Error())); err != nil { + log.Error(err) + } + return + } if err := kc.VerifyAccessToken(token, organization); err != nil { log.Error("failed to verify access token: ", err) w.WriteHeader(http.StatusUnauthorized) diff --git a/internal/usecase/app-serve-app.go b/internal/usecase/app-serve-app.go index 22c7a02b..195ccbb6 100644 --- a/internal/usecase/app-serve-app.go +++ b/internal/usecase/app-serve-app.go @@ -367,7 +367,6 @@ func (u *AppServeAppUsecase) PromoteAppServeApp(appId string) (ret string, err e return fmt.Sprintf("The app '%s' is being promoted. "+ "Confirm result by checking the app status after a while.", app.Name), nil - } func (u *AppServeAppUsecase) AbortAppServeApp(appId string) (ret string, err error) { diff --git a/pkg/api-client/api-client.go b/pkg/api-client/api-client.go index 0ae6cf6c..fb94d413 100644 --- a/pkg/api-client/api-client.go +++ b/pkg/api-client/api-client.go @@ -135,5 +135,4 @@ func (c *ApiClientImpl) callWithBody(method string, path string, input interface } return resJson, nil - } diff --git a/pkg/httpErrors/httpErrors.go b/pkg/httpErrors/httpErrors.go index 4c9bc640..0b19d1c9 100644 --- a/pkg/httpErrors/httpErrors.go +++ b/pkg/httpErrors/httpErrors.go @@ -141,7 +141,6 @@ func reflectVariableName(interface{}) string { */ func parseErrors(err error) IRestError { - switch { case strings.Contains(err.Error(), "SQLSTATE"): return parseSqlError(err) From aa6b59205d9ef41326875b2750ea638d455628fe Mon Sep 17 00:00:00 2001 From: donggyu Date: Wed, 12 Apr 2023 17:41:23 +0900 Subject: [PATCH 04/11] update golangci-lint conf --- .golangci.yaml | 49 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index ff85739a..ba322e69 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -22,6 +22,47 @@ linters-settings: checks: - all - "-SA9004" # + errcheck: + check-type-assertions: true + check-blank: false + + govet: + check-shadowing: false + settings: + printf: + funcs: + - (github.com/xxx/xxx/pkg/log).Infof + - (github.com/xxx/xxx/pkg/log).Warnf + - (github.com/xxx/xxx/pkg/log).Errorf + - (github.com/xxx/xxx/pkg/log).Fatalf + - (github.com/xxx/xxx/pkg/log).Panicf + - (github.com/xxx/xxx/pkg/log).Debugf + - (github.com/xxx/xxx/pkg/log).Info + - (github.com/xxx/xxx/pkg/log).Warn + - (github.com/xxx/xxx/pkg/log).Error + - (github.com/xxx/xxx/pkg/log).Fatal + - (github.com/xxx/xxx/pkg/log).Panic + - (github.com/xxx/xxx/pkg/log).Debug + - (github.com/xxx/xxx/pkg/log).InfoDepth + - (github.com/xxx/xxx/pkg/log).WarnDepth + - (github.com/xxx/xxx/pkg/log).ErrorDepth + - (github.com/xxx/xxx/pkg/log).FatalDepth + - (github.com/xxx/xxx/pkg/log).PanicDepth + - (github.com/xxx/xxx/pkg/log).DebugDepth + - (github.com/xxx/xxx/pkg/log).InfofDepth + - (github.com/xxx/xxx/pkg/log).WarnfDepth + - (github.com/xxx/xxx/pkg/log).ErrorfDepth + - (github.com/xxx/xxx/pkg/log).FatalfDepth + - (github.com/xxx/xxx/pkg/log).PanicfDepth + - (github.com/xxx/xxx/pkg/log).DebugfDepth + - (github.com/xxx/xxx/pkg/log).InfoWithFields + - (github.com/xxx/xxx/pkg/log).WarnWithFields + - (github.com/xxx/xxx/pkg/log).ErrorWithFields + - (github.com/xxx/xxx/pkg/log).FatalWithFields + - (github.com/xxx/xxx/pkg/log).PanicWithFields + - (github.com/xxx/xxx/pkg/log).DebugWithFields + - (github.com/xxx/xxx/pkg/log).InfofWithFields + linters: enable: - errcheck # checks unchecked errors @@ -31,7 +72,9 @@ linters: - staticcheck # cover Go vet edge cases - typecheck # type-checks Go code - unused # checks Go code for unused constants, variables, functions and types - + - gosimple # specializes in simplifying a code + - errcheck # checks unchecked errors + - misspell # finds commonly misspelled English words in comments disable: - deadcode # enabled-default. but duplicated & deprecated someday @@ -51,7 +94,7 @@ linters: - gofmt # checks whether code was gofmt-ed - goimports # fix imports, formats your code in the same style as gofmt - ifshort # checks that your code uses short syntax for if-statements whenever possible - - misspell # finds commonly misspelled English words in comments +# - misspell # finds commonly misspelled English words in comments - noctx # finds sending http request without context.Context - predeclared # find code that shadows one of Go's predeclared identifiers - revive # replacement of golint @@ -59,7 +102,7 @@ linters: - sqlclosecheck # checks that sql.Rows and sql.Stmt are closed. - unconvert # remove unnecessary type conversions - wastedassign # finds wasted assignment statements. - - whitespace # tool for detection of leading and trailing whitespace + - whitespace # checks for unnecessary whitespace - wrapcheck # check that errors from external packages are wrapped during return to help identify the error source. issues: From 2a8c145550a6e7cacdf47a2f86e95e56f8b58835 Mon Sep 17 00:00:00 2001 From: donggyu Date: Wed, 12 Apr 2023 18:10:45 +0900 Subject: [PATCH 05/11] change organization status code --- pkg/domain/mapper.go | 4 ++-- pkg/domain/mapper_test.go | 31 ++++++++++++++++++++++++------ pkg/domain/organization.go | 39 ++++++++++++++++++++++++-------------- pkg/domain/user.go | 2 ++ 4 files changed, 54 insertions(+), 22 deletions(-) diff --git a/pkg/domain/mapper.go b/pkg/domain/mapper.go index 20927d44..cbd61531 100644 --- a/pkg/domain/mapper.go +++ b/pkg/domain/mapper.go @@ -77,10 +77,10 @@ func Map(src interface{}, dst interface{}) error { return val, nil }, {srcType: reflect.TypeOf((*OrganizationStatus)(nil)).Elem(), dstType: reflect.TypeOf("")}: func(i interface{}) (interface{}, error) { - return string(i.(OrganizationStatus)), nil + return i.(OrganizationStatus).String(), nil }, {srcType: reflect.TypeOf(""), dstType: reflect.TypeOf((*OrganizationStatus)(nil)).Elem()}: func(i interface{}) (interface{}, error) { - return i.(OrganizationStatus).String(), nil + return organizationStatusMap[i.(string)], nil }, {srcType: reflect.TypeOf((*Role)(nil)).Elem(), dstType: reflect.TypeOf("")}: func(i interface{}) (interface{}, error) { return i.(Role).Name, nil diff --git a/pkg/domain/mapper_test.go b/pkg/domain/mapper_test.go index 5b476e0b..818f2ee4 100644 --- a/pkg/domain/mapper_test.go +++ b/pkg/domain/mapper_test.go @@ -18,7 +18,7 @@ func TestConvert(t *testing.T) { wantErr bool }{ { - name: "test case 1", + name: "test case: CreateOrganizationRequest->Organization", args: args{ src: CreateOrganizationRequest{ Name: "test", @@ -30,7 +30,7 @@ func TestConvert(t *testing.T) { wantErr: false, }, { - name: "test case 2", + name: "test case Organization->CreateOrganizationResponse", args: args{ src: Organization{ ID: "", @@ -47,7 +47,25 @@ func TestConvert(t *testing.T) { wantErr: false, }, { - name: "test case 3", + name: "test case Organization->GetOrganizationResponse", + args: args{ + src: Organization{ + ID: "", + Name: "test", + Description: "test", + Phone: "test", + Status: OrganizationStatus_CREATE, + StatusDescription: "good", + Creator: "", + CreatedAt: time.Time{}, + UpdatedAt: time.Time{}, + }, + dst: &(&GetOrganizationResponse{}).Organization, + }, + wantErr: false, + }, + { + name: "test case CreateUserRequest->User", args: args{ src: CreateUserRequest{ AccountId: "testAccount", @@ -63,7 +81,7 @@ func TestConvert(t *testing.T) { wantErr: false, }, { - name: "test case 4", + name: "test case User->GetUserResponse", args: args{ src: User{ ID: "", @@ -80,7 +98,7 @@ func TestConvert(t *testing.T) { Department: "", Description: "", }, - dst: &User{}, + dst: &GetUserResponse{}, }, wantErr: false, }, @@ -90,7 +108,8 @@ func TestConvert(t *testing.T) { if err := Map(tt.args.src, tt.args.dst); (err != nil) != tt.wantErr { t.Errorf("Map() error = %v, wantErr %v", err, tt.wantErr) } else { - fmt.Println(tt.args.dst) + fmt.Printf("Input: %+v\n", tt.args.src) + fmt.Printf("Output: %+v\n\n", tt.args.dst) } }) } diff --git a/pkg/domain/organization.go b/pkg/domain/organization.go index a281e3ff..4c6f2823 100644 --- a/pkg/domain/organization.go +++ b/pkg/domain/organization.go @@ -29,12 +29,21 @@ var organizationStatus = [...]string{ "ERROR", } +var organizationStatusMap = map[string]OrganizationStatus{ + "PENDING": OrganizationStatus_PENDING, + "CREATE": OrganizationStatus_CREATE, + "CREATING": OrganizationStatus_CREATING, + "CREATED": OrganizationStatus_CREATED, + "DELETE": OrganizationStatus_DELETE, + "DELETING": OrganizationStatus_DELETING, + "DELETED": OrganizationStatus_DELETED, + "ERROR": OrganizationStatus_ERROR, +} + func (m OrganizationStatus) String() string { return organizationStatus[(m)] } func (m OrganizationStatus) FromString(s string) OrganizationStatus { - for i, v := range organizationStatus { - if v == s { - return OrganizationStatus(i) - } + if v, ok := organizationStatusMap[s]; ok { + return v } return OrganizationStatus_ERROR } @@ -64,16 +73,16 @@ type CreateOrganizationResponse struct { type GetOrganizationResponse struct { Organization struct { - ID string `json:"id"` - Name string `json:"name"` - Description string `json:"description"` - Phone string `json:"phone"` - PrimaryClusterId string `json:"primaryClusterId"` - Status OrganizationStatus `json:"status"` - StatusDescription string `json:"statusDescription"` - Creator string `json:"creator"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Phone string `json:"phone"` + PrimaryClusterId string `json:"primaryClusterId"` + Status string `json:"status"` + StatusDescription string `json:"statusDescription"` + Creator string `json:"creator"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` } `json:"organization"` } type ListOrganizationResponse struct { @@ -86,6 +95,8 @@ type ListOrganizationBody struct { Phone string `json:"phone"` PrimaryClusterId string `json:"primaryClusterId"` Status OrganizationStatus `json:"status"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` } type UpdateOrganizationRequest struct { diff --git a/pkg/domain/user.go b/pkg/domain/user.go index 5dc5255e..d73cf3a0 100644 --- a/pkg/domain/user.go +++ b/pkg/domain/user.go @@ -155,6 +155,8 @@ type UpdateUserResponse struct { Email string `json:"email"` Department string `json:"department"` Description string `json:"description"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` } `json:"user"` } From 13d754f6d7616ee037a1a1ed363cae0a6ec7a662 Mon Sep 17 00:00:00 2001 From: donggyu Date: Mon, 17 Apr 2023 20:15:35 +0900 Subject: [PATCH 06/11] minor fix: refactoring for auth middleware --- internal/auth/authenticator/interface.go | 19 -- .../auth/authenticator/keycloak/keycloak.go | 1 - internal/delivery/http/organization.go | 3 +- internal/keycloak/keycloak.go | 1 - internal/middleware/auth/auth.go | 10 + .../auth/authenticator/interface.go | 48 +++ .../auth/authenticator/keycloak/keycloak.go | 85 ++++++ .../auth/authorization/interface.go | 25 ++ .../{ => middleware}/auth/request/context.go | 2 +- internal/{ => middleware}/auth/user/user.go | 0 internal/middleware/middleware.go | 27 ++ internal/route/route.go | 286 +++++++++--------- internal/route/route_test.go | 33 ++ internal/usecase/app-group.go | 2 +- internal/usecase/cloud-account.go | 2 +- internal/usecase/cluster.go | 2 +- internal/usecase/organization.go | 2 +- internal/usecase/user.go | 2 +- 18 files changed, 379 insertions(+), 171 deletions(-) delete mode 100644 internal/auth/authenticator/interface.go delete mode 100644 internal/auth/authenticator/keycloak/keycloak.go create mode 100644 internal/middleware/auth/auth.go create mode 100644 internal/middleware/auth/authenticator/interface.go create mode 100644 internal/middleware/auth/authenticator/keycloak/keycloak.go create mode 100644 internal/middleware/auth/authorization/interface.go rename internal/{ => middleware}/auth/request/context.go (92%) rename internal/{ => middleware}/auth/user/user.go (100%) create mode 100644 internal/middleware/middleware.go create mode 100644 internal/route/route_test.go diff --git a/internal/auth/authenticator/interface.go b/internal/auth/authenticator/interface.go deleted file mode 100644 index e32da109..00000000 --- a/internal/auth/authenticator/interface.go +++ /dev/null @@ -1,19 +0,0 @@ -package authenticator - -import ( - "context" - "github.com/openinfradev/tks-api/internal/auth/user" - "net/http" -) - -type Token interface { - AuthenticateToken(ctx context.Context, token string) (*Response, bool, error) -} - -type Request interface { - AuthenticateRequest(req *http.Request) (*Response, bool, error) -} - -type Response struct { - User user.Info -} diff --git a/internal/auth/authenticator/keycloak/keycloak.go b/internal/auth/authenticator/keycloak/keycloak.go deleted file mode 100644 index cf172a0e..00000000 --- a/internal/auth/authenticator/keycloak/keycloak.go +++ /dev/null @@ -1 +0,0 @@ -package keycloak diff --git a/internal/delivery/http/organization.go b/internal/delivery/http/organization.go index 40e9bc5a..feb0dfde 100644 --- a/internal/delivery/http/organization.go +++ b/internal/delivery/http/organization.go @@ -2,12 +2,12 @@ package http import ( "fmt" + "github.com/openinfradev/tks-api/internal/middleware/auth/request" "net/http" "github.com/openinfradev/tks-api/pkg/httpErrors" "github.com/gorilla/mux" - "github.com/openinfradev/tks-api/internal/auth/request" "github.com/openinfradev/tks-api/internal/usecase" "github.com/openinfradev/tks-api/pkg/domain" "github.com/openinfradev/tks-api/pkg/log" @@ -36,6 +36,7 @@ func NewOrganizationHandler(o usecase.IOrganizationUsecase, u usecase.IUserUseca // @Router /organizations [post] // @Security JWT func (h *OrganizationHandler) CreateOrganization(w http.ResponseWriter, r *http.Request) { + log.Info("called Create") input := domain.CreateOrganizationRequest{} err := UnmarshalRequestInput(r, &input) diff --git a/internal/keycloak/keycloak.go b/internal/keycloak/keycloak.go index 999150f7..14bdc622 100644 --- a/internal/keycloak/keycloak.go +++ b/internal/keycloak/keycloak.go @@ -35,7 +35,6 @@ type IKeycloak interface { VerifyAccessToken(token string, organizationName string) error ParseAccessToken(token string, organization string) (*jwt.Token, *jwt.MapClaims, error) } - type Keycloak struct { config *Config client *gocloak.GoCloak diff --git a/internal/middleware/auth/auth.go b/internal/middleware/auth/auth.go new file mode 100644 index 00000000..03a7278f --- /dev/null +++ b/internal/middleware/auth/auth.go @@ -0,0 +1,10 @@ +package auth + +import ( + "net/http" +) + +type Interface interface { + WithAuthentication(handler http.Handler) http.Handler + WithAuthorization(handler http.Handler) http.Handler +} diff --git a/internal/middleware/auth/authenticator/interface.go b/internal/middleware/auth/authenticator/interface.go new file mode 100644 index 00000000..d546a72a --- /dev/null +++ b/internal/middleware/auth/authenticator/interface.go @@ -0,0 +1,48 @@ +package authenticator + +import ( + internalHttp "github.com/openinfradev/tks-api/internal/delivery/http" + "github.com/openinfradev/tks-api/internal/middleware/auth/request" + "github.com/openinfradev/tks-api/internal/middleware/auth/user" + "github.com/openinfradev/tks-api/pkg/httpErrors" + "github.com/openinfradev/tks-api/pkg/log" + "net/http" +) + +type Interface interface { + WithAuthentication(handler http.Handler) http.Handler +} + +type defaultAuthenticator struct { + auth Request +} + +func NewDefaultAuthenticator(auth Request) *defaultAuthenticator { + return &defaultAuthenticator{ + auth: auth, + } +} + +func (a *defaultAuthenticator) WithAuthentication(handler http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + + log.Info("called Authentication") + + resp, ok, err := a.auth.AuthenticateRequest(r) + if !ok { + internalHttp.ErrorJSON(w, httpErrors.NewUnauthorizedError(err)) + return + } + log.Info(request.TokenFrom(r.Context())) + r = r.WithContext(request.WithUser(r.Context(), resp.User)) + handler.ServeHTTP(w, r) + }) +} + +type Request interface { + AuthenticateRequest(req *http.Request) (*Response, bool, error) +} + +type Response struct { + User user.Info +} diff --git a/internal/middleware/auth/authenticator/keycloak/keycloak.go b/internal/middleware/auth/authenticator/keycloak/keycloak.go new file mode 100644 index 00000000..241c929c --- /dev/null +++ b/internal/middleware/auth/authenticator/keycloak/keycloak.go @@ -0,0 +1,85 @@ +package keycloak + +import ( + "fmt" + jwtWithouKey "github.com/dgrijalva/jwt-go" + "github.com/google/uuid" + "github.com/openinfradev/tks-api/internal/keycloak" + "github.com/openinfradev/tks-api/internal/middleware/auth/authenticator" + "github.com/openinfradev/tks-api/internal/middleware/auth/request" + "github.com/openinfradev/tks-api/internal/middleware/auth/user" + "github.com/openinfradev/tks-api/pkg/httpErrors" + "github.com/openinfradev/tks-api/pkg/log" + "net/http" + "strings" +) + +type keycloakAuthenticator struct { + kc keycloak.IKeycloak +} + +func NewKeycloakAuthenticator(kc keycloak.IKeycloak) *keycloakAuthenticator { + return &keycloakAuthenticator{ + kc: kc, + } +} + +func (a *keycloakAuthenticator) AuthenticateRequest(r *http.Request) (*authenticator.Response, bool, error) { + authHeader := strings.TrimSpace(r.Header.Get("Authorization")) + if authHeader == "" { + return nil, false, httpErrors.NewUnauthorizedError(fmt.Errorf("authorization header is invalid")) + } + parts := strings.SplitN(authHeader, " ", 3) + if len(parts) < 2 || strings.ToLower(parts[0]) != "bearer" { + return nil, false, httpErrors.NewUnauthorizedError(fmt.Errorf("authorization header is invalid")) + } + + token := parts[1] + + if len(token) == 0 { + // The space before the token case + if len(parts) == 3 { + log.Warn("the provided Authorization header contains extra space before the bearer token, and is ignored") + } + return nil, false, httpErrors.NewUnauthorizedError(fmt.Errorf("authorization header is invalid")) + } + + return a.AuthenticateToken(r, token) +} + +func (a *keycloakAuthenticator) AuthenticateToken(r *http.Request, token string) (*authenticator.Response, bool, error) { + parsedToken, _, err := new(jwtWithouKey.Parser).ParseUnverified(token, jwtWithouKey.MapClaims{}) + if err != nil { + return nil, false, err + } + organizationId, ok := parsedToken.Claims.(jwtWithouKey.MapClaims)["organization"].(string) + if !ok { + return nil, false, fmt.Errorf("organization is not found in token") + } + if err := a.kc.VerifyAccessToken(token, organizationId); err != nil { + return nil, false, err + } + + roleProjectMapping := make(map[string]string) + for _, role := range parsedToken.Claims.(jwtWithouKey.MapClaims)["tks-role"].([]interface{}) { + slice := strings.Split(role.(string), "@") + if len(slice) != 2 { + return nil, false, fmt.Errorf("invalid tks-role format") + } + // key is projectName and value is roleName + roleProjectMapping[slice[1]] = slice[0] + } + userAccountId, err := uuid.Parse(parsedToken.Claims.(jwtWithouKey.MapClaims)["sub"].(string)) + if err != nil { + return nil, false, err + } + + userInfo := &user.DefaultInfo{ + UserId: userAccountId, + OrganizationId: organizationId, + RoleProjectMapping: roleProjectMapping, + } + *r = *(r.WithContext(request.WithToken(r.Context(), token))) + + return &authenticator.Response{User: userInfo}, true, nil +} diff --git a/internal/middleware/auth/authorization/interface.go b/internal/middleware/auth/authorization/interface.go new file mode 100644 index 00000000..85827d03 --- /dev/null +++ b/internal/middleware/auth/authorization/interface.go @@ -0,0 +1,25 @@ +package authorization + +import ( + "github.com/openinfradev/tks-api/pkg/log" + "net/http" +) + +type Interface interface { + WithAuthorization(handler http.Handler) http.Handler +} + +type defaultAuthorization struct { +} + +func NewDefaultAuthorization() *defaultAuthorization { + return &defaultAuthorization{} +} + +func (a *defaultAuthorization) WithAuthorization(handler http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + log.Info("called Authorization") + + handler.ServeHTTP(w, r) + }) +} diff --git a/internal/auth/request/context.go b/internal/middleware/auth/request/context.go similarity index 92% rename from internal/auth/request/context.go rename to internal/middleware/auth/request/context.go index 4ef49334..75658148 100644 --- a/internal/auth/request/context.go +++ b/internal/middleware/auth/request/context.go @@ -2,7 +2,7 @@ package request import ( "context" - "github.com/openinfradev/tks-api/internal/auth/user" + "github.com/openinfradev/tks-api/internal/middleware/auth/user" ) type key int diff --git a/internal/auth/user/user.go b/internal/middleware/auth/user/user.go similarity index 100% rename from internal/auth/user/user.go rename to internal/middleware/auth/user/user.go diff --git a/internal/middleware/middleware.go b/internal/middleware/middleware.go new file mode 100644 index 00000000..e33941e4 --- /dev/null +++ b/internal/middleware/middleware.go @@ -0,0 +1,27 @@ +package middleware + +import ( + "github.com/openinfradev/tks-api/internal/middleware/auth/authenticator" + "github.com/openinfradev/tks-api/internal/middleware/auth/authorization" + "net/http" +) + +type defaultMiddleware struct { + authenticator authenticator.Interface + authorizer authorization.Interface +} + +func NewDefaultMiddleware(authenticator authenticator.Interface, + authorizer authorization.Interface) *defaultMiddleware { + ret := &defaultMiddleware{ + authenticator: authenticator, + authorizer: authorizer, + } + return ret +} + +func (m *defaultMiddleware) Handle(handle http.Handler) http.Handler { + handler := m.authorizer.WithAuthorization(handle) + handler = m.authenticator.WithAuthentication(handler) + return handler +} diff --git a/internal/route/route.go b/internal/route/route.go index df383215..8ca7547d 100644 --- a/internal/route/route.go +++ b/internal/route/route.go @@ -151,149 +151,149 @@ func SetupRouter(db *gorm.DB, argoClient argowf.ArgoClient, asset http.Handler, return handlers.CORS(credentials, headersOk, originsOk, methodsOk)(r) } -func authMiddleware(next http.Handler, kc keycloak.IKeycloak) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // Possible values : "basic", "keycloak" - authType := r.Header.Get("Authorization-Type") - - switch authType { - case "basic": - tokenString := r.Header.Get("Authorization") - if len(tokenString) == 0 { - w.WriteHeader(http.StatusUnauthorized) - if _, err := w.Write([]byte("Missing Authorization Header")); err != nil { - log.Error(err) - } - - return - } - tokenString = strings.Replace(tokenString, "Bearer ", "", 1) - token, err := helper.VerifyToken(tokenString) - if err != nil { - w.WriteHeader(http.StatusUnauthorized) - if _, err := w.Write([]byte("Error verifying JWT token: " + err.Error())); err != nil { - log.Error(err) - } - return - } - - accountId := token.Claims.(jwt.MapClaims)["AccountId"] - organizationId := token.Claims.(jwt.MapClaims)["OrganizationId"] - id := token.Claims.(jwt.MapClaims)["ID"] - - log.Debug("[authMiddleware] accountId : ", accountId) - log.Debug("[authMiddleware] Id : ", id) - log.Debug("[authMiddleware] organizationId : ", organizationId) - - r.Header.Set("OrganizationId", fmt.Sprint(organizationId)) - r.Header.Set("AccountId", fmt.Sprint(accountId)) - r.Header.Set("ID", fmt.Sprint(id)) - - next.ServeHTTP(w, r) - return - - case "keycloak": - default: - auth := strings.TrimSpace(r.Header.Get("Authorization")) - if auth == "" { - w.WriteHeader(http.StatusUnauthorized) - return - } - parts := strings.SplitN(auth, " ", 3) - if len(parts) < 2 || strings.ToLower(parts[0]) != "bearer" { - w.WriteHeader(http.StatusUnauthorized) - return - } - - token := parts[1] - - // Empty bearer tokens aren't valid - if len(token) == 0 { - // The space before the token case - if len(parts) == 3 { - log.Warn("the provided Authorization header contains extra space before the bearer token, and is ignored") - } - w.WriteHeader(http.StatusUnauthorized) - return - } - - parsedToken, _, err := new(jwtWithouKey.Parser).ParseUnverified(token, jwtWithouKey.MapClaims{}) - - if err != nil { - log.Error("failed to parse access token: ", err) - w.WriteHeader(http.StatusUnauthorized) - if _, err := w.Write([]byte(err.Error())); err != nil { - log.Error(err) - } - return - } - organization, ok := parsedToken.Claims.(jwtWithouKey.MapClaims)["organization"].(string) - if !ok { - log.Error("failed to parse access token: ", err) - w.WriteHeader(http.StatusUnauthorized) - if _, err := w.Write([]byte(err.Error())); err != nil { - log.Error(err) - } - return - } - if err := kc.VerifyAccessToken(token, organization); err != nil { - log.Error("failed to verify access token: ", err) - w.WriteHeader(http.StatusUnauthorized) - if _, err := w.Write([]byte("failed to verify access token: " + err.Error())); err != nil { - log.Error(err) - } - return - } - jwtToken, mapClaims, err := kc.ParseAccessToken(token, organization) - if err != nil { - log.Error("failed to parse access token: ", err) - w.WriteHeader(http.StatusUnauthorized) - if _, err := w.Write([]byte(err.Error())); err != nil { - log.Error(err) - } - return - } - - if jwtToken == nil || mapClaims == nil || mapClaims.Valid() != nil { - w.WriteHeader(http.StatusUnauthorized) - if _, err := w.Write([]byte("Error message TODO")); err != nil { - log.Error(err) - } - return - } - roleProjectMapping := make(map[string]string) - for _, role := range jwtToken.Claims.(jwt.MapClaims)["tks-role"].([]interface{}) { - slice := strings.Split(role.(string), "@") - if len(slice) != 2 { - log.Error("invalid role format: ", role) - w.WriteHeader(http.StatusUnauthorized) - if _, err := w.Write([]byte(fmt.Sprintf("invalid role format: %s", role))); err != nil { - log.Error(err) - } - return - } - // key is projectName and value is roleName - roleProjectMapping[slice[1]] = slice[0] - } - userId, err := uuid.Parse(jwtToken.Claims.(jwt.MapClaims)["sub"].(string)) - if err != nil { - userId = uuid.Nil - } - - userInfo := &user.DefaultInfo{ - OrganizationId: jwtToken.Claims.(jwt.MapClaims)["organization"].(string), - UserId: userId, - RoleProjectMapping: roleProjectMapping, - } - - r = r.WithContext(request.WithToken(r.Context(), token)) - r = r.WithContext(request.WithUser(r.Context(), userInfo)) - - next.ServeHTTP(w, r) - } - - }) -} +//func authMiddleware(next http.Handler, kc keycloak.IKeycloak) http.Handler { +// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { +// // Possible values : "basic", "keycloak" +// authType := r.Header.Get("Authorization-Type") +// +// switch authType { +// case "basic": +// tokenString := r.Header.Get("Authorization") +// if len(tokenString) == 0 { +// w.WriteHeader(http.StatusUnauthorized) +// if _, err := w.Write([]byte("Missing Authorization Header")); err != nil { +// log.Error(err) +// } +// +// return +// } +// tokenString = strings.Replace(tokenString, "Bearer ", "", 1) +// token, err := helper.VerifyToken(tokenString) +// if err != nil { +// w.WriteHeader(http.StatusUnauthorized) +// if _, err := w.Write([]byte("Error verifying JWT token: " + err.Error())); err != nil { +// log.Error(err) +// } +// return +// } +// +// accountId := token.Claims.(jwt.MapClaims)["AccountId"] +// organizationId := token.Claims.(jwt.MapClaims)["OrganizationId"] +// id := token.Claims.(jwt.MapClaims)["ID"] +// +// log.Debug("[authMiddleware] accountId : ", accountId) +// log.Debug("[authMiddleware] Id : ", id) +// log.Debug("[authMiddleware] organizationId : ", organizationId) +// +// r.Header.Set("OrganizationId", fmt.Sprint(organizationId)) +// r.Header.Set("AccountId", fmt.Sprint(accountId)) +// r.Header.Set("ID", fmt.Sprint(id)) +// +// next.ServeHTTP(w, r) +// return +// +// case "keycloak": +// default: +// auth := strings.TrimSpace(r.Header.Get("Authorization")) +// if auth == "" { +// w.WriteHeader(http.StatusUnauthorized) +// return +// } +// parts := strings.SplitN(auth, " ", 3) +// if len(parts) < 2 || strings.ToLower(parts[0]) != "bearer" { +// w.WriteHeader(http.StatusUnauthorized) +// return +// } +// +// token := parts[1] +// +// // Empty bearer tokens aren't valid +// if len(token) == 0 { +// // The space before the token case +// if len(parts) == 3 { +// log.Warn("the provided Authorization header contains extra space before the bearer token, and is ignored") +// } +// w.WriteHeader(http.StatusUnauthorized) +// return +// } +// +// parsedToken, _, err := new(jwtWithouKey.Parser).ParseUnverified(token, jwtWithouKey.MapClaims{}) +// +// if err != nil { +// log.Error("failed to parse access token: ", err) +// w.WriteHeader(http.StatusUnauthorized) +// if _, err := w.Write([]byte(err.Error())); err != nil { +// log.Error(err) +// } +// return +// } +// organization, ok := parsedToken.Claims.(jwtWithouKey.MapClaims)["organization"].(string) +// if !ok { +// log.Error("failed to parse access token: ", err) +// w.WriteHeader(http.StatusUnauthorized) +// if _, err := w.Write([]byte(err.Error())); err != nil { +// log.Error(err) +// } +// return +// } +// if err := kc.VerifyAccessToken(token, organization); err != nil { +// log.Error("failed to verify access token: ", err) +// w.WriteHeader(http.StatusUnauthorized) +// if _, err := w.Write([]byte("failed to verify access token: " + err.Error())); err != nil { +// log.Error(err) +// } +// return +// } +// jwtToken, mapClaims, err := kc.ParseAccessToken(token, organization) +// if err != nil { +// log.Error("failed to parse access token: ", err) +// w.WriteHeader(http.StatusUnauthorized) +// if _, err := w.Write([]byte(err.Error())); err != nil { +// log.Error(err) +// } +// return +// } +// +// if jwtToken == nil || mapClaims == nil || mapClaims.Valid() != nil { +// w.WriteHeader(http.StatusUnauthorized) +// if _, err := w.Write([]byte("Error message TODO")); err != nil { +// log.Error(err) +// } +// return +// } +// roleProjectMapping := make(map[string]string) +// for _, role := range jwtToken.Claims.(jwt.MapClaims)["tks-role"].([]interface{}) { +// slice := strings.Split(role.(string), "@") +// if len(slice) != 2 { +// log.Error("invalid role format: ", role) +// w.WriteHeader(http.StatusUnauthorized) +// if _, err := w.Write([]byte(fmt.Sprintf("invalid role format: %s", role))); err != nil { +// log.Error(err) +// } +// return +// } +// // key is projectName and value is roleName +// roleProjectMapping[slice[1]] = slice[0] +// } +// userId, err := uuid.Parse(jwtToken.Claims.(jwt.MapClaims)["sub"].(string)) +// if err != nil { +// userId = uuid.Nil +// } +// +// userInfo := &user.DefaultInfo{ +// OrganizationId: jwtToken.Claims.(jwt.MapClaims)["organization"].(string), +// UserId: userId, +// RoleProjectMapping: roleProjectMapping, +// } +// +// r = r.WithContext(request.WithToken(r.Context(), token)) +// r = r.WithContext(request.WithUser(r.Context(), userInfo)) +// +// next.ServeHTTP(w, r) +// } +// +// }) +//} func loggingMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/internal/route/route_test.go b/internal/route/route_test.go new file mode 100644 index 00000000..6fcdedbd --- /dev/null +++ b/internal/route/route_test.go @@ -0,0 +1,33 @@ +package route + +import ( + "net/http" + "net/url" + "testing" +) + +func TestAuthMiddleware(t *testing.T) { + type args struct { + next *http.Request + } + tests := []struct { + name string + args args + }{ + { + name: "test case: AuthMiddleware", + args: args{ + next: &http.Request{ + URL: &url.URL{ + Path: "/api/v1/organizations", + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + AuthMiddleware(tt.args.next) + }) + } +} diff --git a/internal/usecase/app-group.go b/internal/usecase/app-group.go index 71dfcb1e..111e917d 100644 --- a/internal/usecase/app-group.go +++ b/internal/usecase/app-group.go @@ -3,8 +3,8 @@ package usecase import ( "context" "fmt" + "github.com/openinfradev/tks-api/internal/middleware/auth/request" - "github.com/openinfradev/tks-api/internal/auth/request" "github.com/openinfradev/tks-api/internal/repository" argowf "github.com/openinfradev/tks-api/pkg/argo-client" "github.com/openinfradev/tks-api/pkg/domain" diff --git a/internal/usecase/cloud-account.go b/internal/usecase/cloud-account.go index 9adf84c3..604f8872 100644 --- a/internal/usecase/cloud-account.go +++ b/internal/usecase/cloud-account.go @@ -3,9 +3,9 @@ package usecase import ( "context" "fmt" + "github.com/openinfradev/tks-api/internal/middleware/auth/request" "github.com/google/uuid" - "github.com/openinfradev/tks-api/internal/auth/request" "github.com/openinfradev/tks-api/internal/repository" argowf "github.com/openinfradev/tks-api/pkg/argo-client" "github.com/openinfradev/tks-api/pkg/domain" diff --git a/internal/usecase/cluster.go b/internal/usecase/cluster.go index b9e25d92..a130ea9b 100644 --- a/internal/usecase/cluster.go +++ b/internal/usecase/cluster.go @@ -3,10 +3,10 @@ package usecase import ( "context" "fmt" + "github.com/openinfradev/tks-api/internal/middleware/auth/request" "strings" "github.com/google/uuid" - "github.com/openinfradev/tks-api/internal/auth/request" "github.com/openinfradev/tks-api/internal/repository" argowf "github.com/openinfradev/tks-api/pkg/argo-client" "github.com/openinfradev/tks-api/pkg/domain" diff --git a/internal/usecase/organization.go b/internal/usecase/organization.go index 382adec0..13d5e581 100644 --- a/internal/usecase/organization.go +++ b/internal/usecase/organization.go @@ -3,8 +3,8 @@ package usecase import ( "context" "fmt" + "github.com/openinfradev/tks-api/internal/middleware/auth/request" - "github.com/openinfradev/tks-api/internal/auth/request" "github.com/openinfradev/tks-api/internal/helper" "github.com/openinfradev/tks-api/internal/keycloak" "github.com/openinfradev/tks-api/pkg/httpErrors" diff --git a/internal/usecase/user.go b/internal/usecase/user.go index dbb251b4..73ec6aad 100644 --- a/internal/usecase/user.go +++ b/internal/usecase/user.go @@ -3,11 +3,11 @@ package usecase import ( "context" "fmt" + "github.com/openinfradev/tks-api/internal/middleware/auth/request" "net/http" "github.com/Nerzal/gocloak/v13" "github.com/google/uuid" - "github.com/openinfradev/tks-api/internal/auth/request" "github.com/openinfradev/tks-api/internal/helper" "github.com/openinfradev/tks-api/internal/keycloak" "github.com/openinfradev/tks-api/internal/repository" From 3486c5d6c803c9957d2cd5d414adc123748dbf52 Mon Sep 17 00:00:00 2001 From: donggyu Date: Mon, 17 Apr 2023 21:04:56 +0900 Subject: [PATCH 07/11] minor fix: minor bug fix --- internal/keycloak/config.go | 1 + internal/keycloak/keycloak.go | 219 +++++++++--------- internal/middleware/auth/auth.go | 10 - .../{interface.go => authenticator.go} | 12 +- .../auth/authenticator/keycloak/keycloak.go | 7 +- .../interface.go => authorizer/authorizer.go} | 2 +- internal/middleware/middleware.go | 6 +- internal/route/route.go | 21 +- internal/usecase/auth.go | 2 +- 9 files changed, 127 insertions(+), 153 deletions(-) delete mode 100644 internal/middleware/auth/auth.go rename internal/middleware/auth/authenticator/{interface.go => authenticator.go} (89%) rename internal/middleware/auth/{authorization/interface.go => authorizer/authorizer.go} (95%) diff --git a/internal/keycloak/config.go b/internal/keycloak/config.go index 30802ba0..d769752e 100644 --- a/internal/keycloak/config.go +++ b/internal/keycloak/config.go @@ -11,4 +11,5 @@ const ( DefaultMasterRealm = "master" DefaultClientID = "tks" DefaultClientSecret = "secret" + accessTokenLifespan = 60 * 60 * 24 ) diff --git a/internal/keycloak/keycloak.go b/internal/keycloak/keycloak.go index 14bdc622..f1c393cd 100644 --- a/internal/keycloak/keycloak.go +++ b/internal/keycloak/keycloak.go @@ -4,12 +4,9 @@ import ( "context" "crypto/tls" "fmt" - "time" - "github.com/openinfradev/tks-api/pkg/httpErrors" "github.com/Nerzal/gocloak/v13" - "github.com/golang-jwt/jwt/v4" "github.com/openinfradev/tks-api/pkg/domain" "github.com/openinfradev/tks-api/pkg/log" ) @@ -18,30 +15,30 @@ type IKeycloak interface { InitializeKeycloak() error LoginAdmin() (string, error) + Login(accountId string, password string, organizationId string) (*domain.User, error) - CreateRealm(organizationName string, organizationConfig domain.Organization, token string) (string, error) - GetRealm(organizationName string, token string) (*domain.Organization, error) - GetRealms(token string) ([]*domain.Organization, error) - DeleteRealm(organizationName string, token string) error - UpdateRealm(organizationName string, organizationConfig domain.Organization, token string) error + CreateRealm(organizationName string) (string, error) + GetRealm(organizationName string) (*domain.Organization, error) + GetRealms() ([]*domain.Organization, error) + DeleteRealm(organizationName string) error + UpdateRealm(organizationName string, organizationConfig domain.Organization) error - CreateUser(organizationName string, user *gocloak.User, token string) error - GetUser(organizationName string, userAccountId string, token string) (*gocloak.User, error) - GetUsers(organizationName string, token string) ([]*gocloak.User, error) - DeleteUser(organizationName string, userAccountId string, token string) error - UpdateUser(organizationName string, user *gocloak.User, accessToken string) error + CreateUser(organizationName string, user *gocloak.User) error + GetUser(organizationName string, userAccountId string) (*gocloak.User, error) + GetUsers(organizationName string) ([]*gocloak.User, error) + DeleteUser(organizationName string, userAccountId string) error + UpdateUser(organizationName string, user *gocloak.User) error - GetAccessTokenByIdPassword(accountId string, password string, organizationName string) (*domain.User, error) VerifyAccessToken(token string, organizationName string) error - ParseAccessToken(token string, organization string) (*jwt.Token, *jwt.MapClaims, error) } type Keycloak struct { config *Config client *gocloak.GoCloak } -func (c *Keycloak) LoginAdmin() (string, error) { - token, err := c.client.LoginAdmin(context.Background(), c.config.AdminId, c.config.AdminPassword, DefaultMasterRealm) +func (k *Keycloak) LoginAdmin() (string, error) { + token, err := k.client.LoginAdmin(context.Background(), k.config.AdminId, k.config.AdminPassword, DefaultMasterRealm) + log.Infof("LoginAdmin: %s", token.AccessToken) if err != nil { return "", err } @@ -54,56 +51,50 @@ func New(config *Config) IKeycloak { config: config, } } -func (c *Keycloak) InitializeKeycloak() error { - c.client = gocloak.NewClient(c.config.Address) +func (k *Keycloak) InitializeKeycloak() error { + k.client = gocloak.NewClient(k.config.Address) ctx := context.Background() - restyClient := c.client.RestyClient() - //for debugging - - //if os.Getenv("LOG_LEVEL") == "DEBUG" { - // restyClient.SetDebug(true) - //} - + restyClient := k.client.RestyClient() restyClient.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true}) - token, err := c.loginAdmin(ctx) + token, err := k.loginAdmin(ctx) if err != nil { log.Fatal(err) return err } - group, err := c.ensureGroupByName(ctx, token, DefaultMasterRealm, "tks-admin@master") + group, err := k.ensureGroupByName(ctx, token, DefaultMasterRealm, "tks-admin@master") if err != nil { log.Fatal(err) return err } - user, err := c.ensureUserByName(ctx, token, DefaultMasterRealm, c.config.AdminId, c.config.AdminPassword) + user, err := k.ensureUserByName(ctx, token, DefaultMasterRealm, k.config.AdminId, k.config.AdminPassword) if err != nil { log.Fatal(err) return err } - if err := c.addUserToGroup(ctx, token, DefaultMasterRealm, *user.ID, *group.ID); err != nil { + if err := k.addUserToGroup(ctx, token, DefaultMasterRealm, *user.ID, *group.ID); err != nil { log.Fatal(err) return err } - keycloakClient, err := c.ensureClient(ctx, token, DefaultMasterRealm, DefaultClientID, DefaultClientSecret) + keycloakClient, err := k.ensureClient(ctx, token, DefaultMasterRealm, DefaultClientID, DefaultClientSecret) if err != nil { log.Fatal(err) return err } for _, defaultMapper := range defaultProtocolTksMapper { - if err := c.ensureClientProtocolMappers(ctx, token, DefaultMasterRealm, *keycloakClient.ClientID, "openid", defaultMapper); err != nil { + if err := k.ensureClientProtocolMappers(ctx, token, DefaultMasterRealm, *keycloakClient.ClientID, "openid", defaultMapper); err != nil { log.Fatal(err) return err } } - if _, err := c.client.Login(ctx, DefaultClientID, DefaultClientSecret, DefaultMasterRealm, - c.config.AdminId, c.config.AdminPassword); err != nil { + if _, err := k.client.Login(ctx, DefaultClientID, DefaultClientSecret, DefaultMasterRealm, + k.config.AdminId, k.config.AdminPassword); err != nil { log.Fatal(err) return err } @@ -111,31 +102,36 @@ func (c *Keycloak) InitializeKeycloak() error { return nil } -func (k *Keycloak) CreateRealm(organizationName string, organizationConfig domain.Organization, accessToken string) (string, error) { +func (k *Keycloak) CreateRealm(organizationName string) (string, error) { //TODO implement me ctx := context.Background() + token, err := k.loginAdmin(ctx) + if err != nil { + return "", err + } + accessToken := token.AccessToken realmConfig := gocloak.RealmRepresentation{ Realm: &organizationName, Enabled: gocloak.BoolP(true), - AccessTokenLifespan: gocloak.IntP(60 * 60 * 24), + AccessTokenLifespan: gocloak.IntP(accessTokenLifespan), } realmUUID, err := k.client.CreateRealm(ctx, accessToken, realmConfig) if err != nil { - return realmUUID, err + return "", err } + // After Create Realm, accesstoken got changed so that old token doesn't work properly. - token, err := k.loginAdmin(ctx) + token, err = k.loginAdmin(ctx) if err != nil { - return realmUUID, err + return "", err } accessToken = token.AccessToken - time.Sleep(time.Second * 3) clientUUID, err := k.createDefaultClient(context.Background(), accessToken, organizationName, DefaultClientID, DefaultClientSecret) if err != nil { log.Error(err, "createDefaultClient") - return realmUUID, err + return "", err } token, err = k.loginAdmin(ctx) @@ -156,7 +152,7 @@ func (k *Keycloak) CreateRealm(organizationName string, organizationConfig domai } } if _, err := k.createClientProtocolMapper(ctx, accessToken, organizationName, clientUUID, defaultMapper); err != nil { - return realmUUID, err + return "", err } } adminGroupUuid, err := k.createGroup(ctx, accessToken, organizationName, "admin@"+organizationName) @@ -191,18 +187,18 @@ func (k *Keycloak) CreateRealm(organizationName string, organizationConfig domai }) if err != nil { - return realmUUID, err + return "", err } userGroupUuid, err := k.createGroup(ctx, accessToken, organizationName, "user@"+organizationName) if err != nil { - return realmUUID, err + return "", err } //adminGroup, err := k.ensureGroup(ctx, accessToken, organizationName, "admin@"+organizationName) viewUserRole, err := k.getClientRole(ctx, accessToken, organizationName, realmManagementClientUuid, "view-users") if err != nil { - return realmUUID, err + return "", err } err = k.addClientRoleToGroup(ctx, accessToken, organizationName, realmManagementClientUuid, userGroupUuid, @@ -212,7 +208,7 @@ func (k *Keycloak) CreateRealm(organizationName string, organizationConfig domai }) if err != nil { - return realmUUID, err + return "", err } // TODO: implement leader, member, viewer @@ -223,18 +219,30 @@ func (k *Keycloak) CreateRealm(organizationName string, organizationConfig domai return realmUUID, nil } -func (k *Keycloak) GetRealm(organizationName string, accessToken string) (*domain.Organization, error) { +func (k *Keycloak) Login(accountId string, password string, organizationId string) (*domain.User, error) { ctx := context.Background() - realm, err := k.client.GetRealm(ctx, accessToken, organizationName) + JWTToken, err := k.client.Login(ctx, DefaultClientID, DefaultClientSecret, organizationId, accountId, password) + if err != nil { + log.Error(err) + return nil, err + } + return &domain.User{Token: JWTToken.AccessToken}, nil +} + +func (k *Keycloak) GetRealm(organizationName string) (*domain.Organization, error) { + ctx := context.Background() + token, err := k.loginAdmin(ctx) + realm, err := k.client.GetRealm(ctx, token.AccessToken, organizationName) if err != nil { return nil, err } return k.reflectOrganization(*realm), nil } -func (k *Keycloak) GetRealms(accessToken string) ([]*domain.Organization, error) { +func (k *Keycloak) GetRealms() ([]*domain.Organization, error) { ctx := context.Background() - realms, err := k.client.GetRealms(ctx, accessToken) + token, err := k.loginAdmin(ctx) + realms, err := k.client.GetRealms(ctx, token.AccessToken) if err != nil { return nil, err } @@ -245,39 +253,44 @@ func (k *Keycloak) GetRealms(accessToken string) ([]*domain.Organization, error) return organization, nil } -func (k *Keycloak) UpdateRealm(organizationName string, organizationConfig domain.Organization, accessToken string) error { +func (k *Keycloak) UpdateRealm(organizationName string, organizationConfig domain.Organization) error { ctx := context.Background() + token, err := k.loginAdmin(ctx) realm := k.reflectRealmRepresentation(organizationConfig) - err := k.client.UpdateRealm(ctx, accessToken, *realm) + err = k.client.UpdateRealm(ctx, token.AccessToken, *realm) if err != nil { return err } return nil } -func (k *Keycloak) DeleteRealm(organizationName string, accessToken string) error { +func (k *Keycloak) DeleteRealm(organizationName string) error { ctx := context.Background() - err := k.client.DeleteRealm(ctx, accessToken, organizationName) + token, err := k.loginAdmin(ctx) + err = k.client.DeleteRealm(ctx, token.AccessToken, organizationName) if err != nil { return err } return nil } -func (k *Keycloak) CreateUser(organizationName string, user *gocloak.User, accessToken string) error { +func (k *Keycloak) CreateUser(organizationName string, user *gocloak.User) error { ctx := context.Background() + token, err := k.loginAdmin(ctx) user.Enabled = gocloak.BoolP(true) - _, err := k.client.CreateUser(ctx, accessToken, organizationName, *user) + _, err = k.client.CreateUser(ctx, token.AccessToken, organizationName, *user) if err != nil { return err } return nil } -func (k *Keycloak) GetUser(organizationName string, accountId string, accessToken string) (*gocloak.User, error) { +func (k *Keycloak) GetUser(organizationName string, accountId string) (*gocloak.User, error) { ctx := context.Background() + token, err := k.loginAdmin(ctx) + //TODO: this is rely on the fact that username is the same as userAccountId and unique - users, err := k.client.GetUsers(ctx, accessToken, organizationName, gocloak.GetUsersParams{ + users, err := k.client.GetUsers(ctx, token.AccessToken, organizationName, gocloak.GetUsersParams{ Username: gocloak.StringP(accountId), }) if err != nil { @@ -289,10 +302,11 @@ func (k *Keycloak) GetUser(organizationName string, accountId string, accessToke return users[0], nil } -func (k *Keycloak) GetUsers(organizationName string, accessToken string) ([]*gocloak.User, error) { +func (k *Keycloak) GetUsers(organizationName string) ([]*gocloak.User, error) { ctx := context.Background() + token, err := k.loginAdmin(ctx) //TODO: this is rely on the fact that username is the same as userAccountId and unique - users, err := k.client.GetUsers(ctx, accessToken, organizationName, gocloak.GetUsersParams{}) + users, err := k.client.GetUsers(ctx, token.AccessToken, organizationName, gocloak.GetUsersParams{}) if err != nil { return nil, err } @@ -303,42 +317,33 @@ func (k *Keycloak) GetUsers(organizationName string, accessToken string) ([]*goc return users, nil } -func (k *Keycloak) UpdateUser(organizationName string, user *gocloak.User, accessToken string) error { +func (k *Keycloak) UpdateUser(organizationName string, user *gocloak.User) error { ctx := context.Background() + token, err := k.loginAdmin(ctx) user.Enabled = gocloak.BoolP(true) - err := k.client.UpdateUser(ctx, accessToken, organizationName, *user) + err = k.client.UpdateUser(ctx, token.AccessToken, organizationName, *user) if err != nil { return err } return nil } -func (k *Keycloak) DeleteUser(organizationName string, userAccountId string, accessToken string) error { +func (k *Keycloak) DeleteUser(organizationName string, userAccountId string) error { ctx := context.Background() - u, err := k.GetUser(organizationName, userAccountId, accessToken) + token, err := k.loginAdmin(ctx) + + u, err := k.GetUser(organizationName, userAccountId) if err != nil { log.Errorf("error is :%s(%T)", err.Error(), err) return httpErrors.NewNotFoundError(err) } - err = k.client.DeleteUser(ctx, accessToken, organizationName, *u.ID) + err = k.client.DeleteUser(ctx, token.AccessToken, organizationName, *u.ID) if err != nil { return err } return nil } -func (k *Keycloak) GetAccessTokenByIdPassword(accountId string, password string, organizationName string) (*domain.User, error) { - ctx := context.Background() - JWTToken, err := k.client.Login(ctx, DefaultClientID, DefaultClientSecret, organizationName, accountId, password) - if err != nil { - log.Error(err) - return nil, err - } - return &domain.User{ - Token: JWTToken.AccessToken, - }, nil -} - func (k *Keycloak) VerifyAccessToken(token string, organizationName string) error { //TODO implement me ctx := context.Background() @@ -354,18 +359,8 @@ func (k *Keycloak) VerifyAccessToken(token string, organizationName string) erro return nil } -func (k *Keycloak) ParseAccessToken(token string, organization string) (*jwt.Token, *jwt.MapClaims, error) { - ctx := context.Background() - jwtToken, mapClaim, err := k.client.DecodeAccessToken(ctx, token, organization) - if err != nil { - fmt.Println(err) - } - - return jwtToken, mapClaim, err -} - -func (c *Keycloak) loginAdmin(ctx context.Context) (*gocloak.JWT, error) { - token, err := c.client.LoginAdmin(ctx, c.config.AdminId, c.config.AdminPassword, DefaultMasterRealm) +func (k *Keycloak) loginAdmin(ctx context.Context) (*gocloak.JWT, error) { + token, err := k.client.LoginAdmin(ctx, k.config.AdminId, k.config.AdminPassword, DefaultMasterRealm) if err != nil { log.Error("Login to keycloak as Admin is failed", err) } @@ -373,10 +368,10 @@ func (c *Keycloak) loginAdmin(ctx context.Context) (*gocloak.JWT, error) { return token, err } -func (c *Keycloak) ensureClientProtocolMappers(ctx context.Context, token *gocloak.JWT, realm string, clientId string, +func (k *Keycloak) ensureClientProtocolMappers(ctx context.Context, token *gocloak.JWT, realm string, clientId string, scope string, mapper gocloak.ProtocolMapperRepresentation) error { //TODO: Check current logic(if exist, do nothing) is fine - clients, err := c.client.GetClients(ctx, token.AccessToken, realm, gocloak.GetClientsParams{ + clients, err := k.client.GetClients(ctx, token.AccessToken, realm, gocloak.GetClientsParams{ ClientID: &clientId, }) if err != nil { @@ -392,15 +387,15 @@ func (c *Keycloak) ensureClientProtocolMappers(ctx context.Context, token *goclo } } - if _, err := c.client.CreateClientProtocolMapper(ctx, token.AccessToken, realm, *clients[0].ID, mapper); err != nil { + if _, err := k.client.CreateClientProtocolMapper(ctx, token.AccessToken, realm, *clients[0].ID, mapper); err != nil { log.Error("Creating Client Protocol Mapper is failed", err) return err } return nil } -func (c *Keycloak) ensureClient(ctx context.Context, token *gocloak.JWT, realm string, clientId string, secret string) (*gocloak.Client, error) { - keycloakClient, err := c.client.GetClients(ctx, token.AccessToken, realm, gocloak.GetClientsParams{ +func (k *Keycloak) ensureClient(ctx context.Context, token *gocloak.JWT, realm string, clientId string, secret string) (*gocloak.Client, error) { + keycloakClient, err := k.client.GetClients(ctx, token.AccessToken, realm, gocloak.GetClientsParams{ ClientID: &clientId, }) if err != nil { @@ -408,7 +403,7 @@ func (c *Keycloak) ensureClient(ctx context.Context, token *gocloak.JWT, realm s } if len(keycloakClient) == 0 { - _, err = c.client.CreateClient(ctx, token.AccessToken, realm, gocloak.Client{ + _, err = k.client.CreateClient(ctx, token.AccessToken, realm, gocloak.Client{ ClientID: gocloak.StringP(clientId), Enabled: gocloak.BoolP(true), DirectAccessGrantsEnabled: gocloak.BoolP(true), @@ -416,7 +411,7 @@ func (c *Keycloak) ensureClient(ctx context.Context, token *gocloak.JWT, realm s if err != nil { log.Error("Creating Client is failed", err) } - keycloakClient, err = c.client.GetClients(ctx, token.AccessToken, realm, gocloak.GetClientsParams{ + keycloakClient, err = k.client.GetClients(ctx, token.AccessToken, realm, gocloak.GetClientsParams{ ClientID: &clientId, }) if err != nil { @@ -426,7 +421,7 @@ func (c *Keycloak) ensureClient(ctx context.Context, token *gocloak.JWT, realm s if *keycloakClient[0].Secret != secret { log.Warn("Client secret is not matched. Overwrite it") keycloakClient[0].Secret = gocloak.StringP(secret) - if err := c.client.UpdateClient(ctx, token.AccessToken, realm, *keycloakClient[0]); err != nil { + if err := k.client.UpdateClient(ctx, token.AccessToken, realm, *keycloakClient[0]); err != nil { log.Error("Updating Client is failed", err) } } @@ -434,8 +429,8 @@ func (c *Keycloak) ensureClient(ctx context.Context, token *gocloak.JWT, realm s return keycloakClient[0], err } -func (c *Keycloak) addUserToGroup(ctx context.Context, token *gocloak.JWT, realm string, userID string, groupID string) error { - groups, err := c.client.GetUserGroups(ctx, token.AccessToken, realm, userID, gocloak.GetGroupsParams{}) +func (k *Keycloak) addUserToGroup(ctx context.Context, token *gocloak.JWT, realm string, userID string, groupID string) error { + groups, err := k.client.GetUserGroups(ctx, token.AccessToken, realm, userID, gocloak.GetGroupsParams{}) if err != nil { log.Error("Getting User Groups is failed") } @@ -445,23 +440,23 @@ func (c *Keycloak) addUserToGroup(ctx context.Context, token *gocloak.JWT, realm } } - err = c.client.AddUserToGroup(ctx, token.AccessToken, realm, userID, groupID) + err = k.client.AddUserToGroup(ctx, token.AccessToken, realm, userID, groupID) if err != nil { log.Error("Assigning User to Group is failed", err) } return err } -func (c *Keycloak) ensureUserByName(ctx context.Context, token *gocloak.JWT, realm string, userName string, password string) (*gocloak.User, error) { - user, err := c.ensureUser(ctx, token, realm, userName, password) +func (k *Keycloak) ensureUserByName(ctx context.Context, token *gocloak.JWT, realm string, userName string, password string) (*gocloak.User, error) { + user, err := k.ensureUser(ctx, token, realm, userName, password) return user, err } -func (c *Keycloak) ensureUser(ctx context.Context, token *gocloak.JWT, realm string, userName string, password string) (*gocloak.User, error) { +func (k *Keycloak) ensureUser(ctx context.Context, token *gocloak.JWT, realm string, userName string, password string) (*gocloak.User, error) { searchParam := gocloak.GetUsersParams{ Search: gocloak.StringP(userName), } - users, err := c.client.GetUsers(ctx, token.AccessToken, realm, searchParam) + users, err := k.client.GetUsers(ctx, token.AccessToken, realm, searchParam) if err != nil { log.Error("Getting User is failed", err) } @@ -477,12 +472,12 @@ func (c *Keycloak) ensureUser(ctx context.Context, token *gocloak.JWT, realm str }, }, } - _, err = c.client.CreateUser(ctx, token.AccessToken, realm, user) + _, err = k.client.CreateUser(ctx, token.AccessToken, realm, user) if err != nil { log.Error("Creating User is failed", err) } - users, err = c.client.GetUsers(ctx, token.AccessToken, realm, searchParam) + users, err = k.client.GetUsers(ctx, token.AccessToken, realm, searchParam) if err != nil { log.Error("Getting User is failed", err) } @@ -491,12 +486,12 @@ func (c *Keycloak) ensureUser(ctx context.Context, token *gocloak.JWT, realm str return users[0], err } -func (c *Keycloak) ensureGroupByName(ctx context.Context, token *gocloak.JWT, realm string, groupName string, groupParam ...gocloak.Group) (*gocloak.Group, error) { - group, err := c.ensureGroup(ctx, token, realm, groupName) +func (k *Keycloak) ensureGroupByName(ctx context.Context, token *gocloak.JWT, realm string, groupName string, groupParam ...gocloak.Group) (*gocloak.Group, error) { + group, err := k.ensureGroup(ctx, token, realm, groupName) return group, err } -func (c *Keycloak) ensureGroup(ctx context.Context, token *gocloak.JWT, realm string, groupName string) (*gocloak.Group, error) { +func (k *Keycloak) ensureGroup(ctx context.Context, token *gocloak.JWT, realm string, groupName string) (*gocloak.Group, error) { searchParam := gocloak.GetGroupsParams{ Search: gocloak.StringP(groupName), } @@ -504,16 +499,16 @@ func (c *Keycloak) ensureGroup(ctx context.Context, token *gocloak.JWT, realm st Name: gocloak.StringP(groupName), } - groups, err := c.client.GetGroups(ctx, token.AccessToken, realm, searchParam) + groups, err := k.client.GetGroups(ctx, token.AccessToken, realm, searchParam) if err != nil { log.Error("Getting Group is failed", err) } if len(groups) == 0 { - _, err = c.client.CreateGroup(ctx, token.AccessToken, realm, groupParam) + _, err = k.client.CreateGroup(ctx, token.AccessToken, realm, groupParam) if err != nil { log.Error("Creating Group is failed", err) } - groups, err = c.client.GetGroups(ctx, token.AccessToken, realm, searchParam) + groups, err = k.client.GetGroups(ctx, token.AccessToken, realm, searchParam) if err != nil { log.Error("Getting Group is failed", err) } diff --git a/internal/middleware/auth/auth.go b/internal/middleware/auth/auth.go deleted file mode 100644 index 03a7278f..00000000 --- a/internal/middleware/auth/auth.go +++ /dev/null @@ -1,10 +0,0 @@ -package auth - -import ( - "net/http" -) - -type Interface interface { - WithAuthentication(handler http.Handler) http.Handler - WithAuthorization(handler http.Handler) http.Handler -} diff --git a/internal/middleware/auth/authenticator/interface.go b/internal/middleware/auth/authenticator/authenticator.go similarity index 89% rename from internal/middleware/auth/authenticator/interface.go rename to internal/middleware/auth/authenticator/authenticator.go index d546a72a..2251f4f9 100644 --- a/internal/middleware/auth/authenticator/interface.go +++ b/internal/middleware/auth/authenticator/authenticator.go @@ -5,13 +5,15 @@ import ( "github.com/openinfradev/tks-api/internal/middleware/auth/request" "github.com/openinfradev/tks-api/internal/middleware/auth/user" "github.com/openinfradev/tks-api/pkg/httpErrors" - "github.com/openinfradev/tks-api/pkg/log" "net/http" ) type Interface interface { WithAuthentication(handler http.Handler) http.Handler } +type Request interface { + AuthenticateRequest(req *http.Request) (*Response, bool, error) +} type defaultAuthenticator struct { auth Request @@ -25,24 +27,16 @@ func NewDefaultAuthenticator(auth Request) *defaultAuthenticator { func (a *defaultAuthenticator) WithAuthentication(handler http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - - log.Info("called Authentication") - resp, ok, err := a.auth.AuthenticateRequest(r) if !ok { internalHttp.ErrorJSON(w, httpErrors.NewUnauthorizedError(err)) return } - log.Info(request.TokenFrom(r.Context())) r = r.WithContext(request.WithUser(r.Context(), resp.User)) handler.ServeHTTP(w, r) }) } -type Request interface { - AuthenticateRequest(req *http.Request) (*Response, bool, error) -} - type Response struct { User user.Info } diff --git a/internal/middleware/auth/authenticator/keycloak/keycloak.go b/internal/middleware/auth/authenticator/keycloak/keycloak.go index 241c929c..cdd60001 100644 --- a/internal/middleware/auth/authenticator/keycloak/keycloak.go +++ b/internal/middleware/auth/authenticator/keycloak/keycloak.go @@ -8,7 +8,6 @@ import ( "github.com/openinfradev/tks-api/internal/middleware/auth/authenticator" "github.com/openinfradev/tks-api/internal/middleware/auth/request" "github.com/openinfradev/tks-api/internal/middleware/auth/user" - "github.com/openinfradev/tks-api/pkg/httpErrors" "github.com/openinfradev/tks-api/pkg/log" "net/http" "strings" @@ -27,11 +26,11 @@ func NewKeycloakAuthenticator(kc keycloak.IKeycloak) *keycloakAuthenticator { func (a *keycloakAuthenticator) AuthenticateRequest(r *http.Request) (*authenticator.Response, bool, error) { authHeader := strings.TrimSpace(r.Header.Get("Authorization")) if authHeader == "" { - return nil, false, httpErrors.NewUnauthorizedError(fmt.Errorf("authorization header is invalid")) + return nil, false, fmt.Errorf("authorizer header is invalid") } parts := strings.SplitN(authHeader, " ", 3) if len(parts) < 2 || strings.ToLower(parts[0]) != "bearer" { - return nil, false, httpErrors.NewUnauthorizedError(fmt.Errorf("authorization header is invalid")) + return nil, false, fmt.Errorf("authorizer header is invalid") } token := parts[1] @@ -41,7 +40,7 @@ func (a *keycloakAuthenticator) AuthenticateRequest(r *http.Request) (*authentic if len(parts) == 3 { log.Warn("the provided Authorization header contains extra space before the bearer token, and is ignored") } - return nil, false, httpErrors.NewUnauthorizedError(fmt.Errorf("authorization header is invalid")) + return nil, false, fmt.Errorf("token is empty") } return a.AuthenticateToken(r, token) diff --git a/internal/middleware/auth/authorization/interface.go b/internal/middleware/auth/authorizer/authorizer.go similarity index 95% rename from internal/middleware/auth/authorization/interface.go rename to internal/middleware/auth/authorizer/authorizer.go index 85827d03..df237fce 100644 --- a/internal/middleware/auth/authorization/interface.go +++ b/internal/middleware/auth/authorizer/authorizer.go @@ -1,4 +1,4 @@ -package authorization +package authorizer import ( "github.com/openinfradev/tks-api/pkg/log" diff --git a/internal/middleware/middleware.go b/internal/middleware/middleware.go index e33941e4..170c09f1 100644 --- a/internal/middleware/middleware.go +++ b/internal/middleware/middleware.go @@ -2,17 +2,17 @@ package middleware import ( "github.com/openinfradev/tks-api/internal/middleware/auth/authenticator" - "github.com/openinfradev/tks-api/internal/middleware/auth/authorization" + "github.com/openinfradev/tks-api/internal/middleware/auth/authorizer" "net/http" ) type defaultMiddleware struct { authenticator authenticator.Interface - authorizer authorization.Interface + authorizer authorizer.Interface } func NewDefaultMiddleware(authenticator authenticator.Interface, - authorizer authorization.Interface) *defaultMiddleware { + authorizer authorizer.Interface) *defaultMiddleware { ret := &defaultMiddleware{ authenticator: authenticator, authorizer: authorizer, diff --git a/internal/route/route.go b/internal/route/route.go index 8ca7547d..6405ba1e 100644 --- a/internal/route/route.go +++ b/internal/route/route.go @@ -3,26 +3,21 @@ package route import ( "bytes" "fmt" - "io" - "io/ioutil" - "net/http" - "strings" - - "github.com/google/uuid" - "github.com/openinfradev/tks-api/internal/auth/request" - user "github.com/openinfradev/tks-api/internal/auth/user" - "github.com/openinfradev/tks-api/internal/keycloak" - - jwtWithouKey "github.com/dgrijalva/jwt-go" - "github.com/golang-jwt/jwt/v4" "github.com/gorilla/handlers" "github.com/gorilla/mux" + "github.com/openinfradev/tks-api/internal/keycloak" + "github.com/openinfradev/tks-api/internal/middleware" + "github.com/openinfradev/tks-api/internal/middleware/auth/authenticator" + authenticatorKeycloak "github.com/openinfradev/tks-api/internal/middleware/auth/authenticator/keycloak" + "github.com/openinfradev/tks-api/internal/middleware/auth/authorization" "gorm.io/gorm" + "io" + "io/ioutil" + "net/http" "github.com/swaggo/http-swagger" delivery "github.com/openinfradev/tks-api/internal/delivery/http" - "github.com/openinfradev/tks-api/internal/helper" "github.com/openinfradev/tks-api/internal/repository" "github.com/openinfradev/tks-api/internal/usecase" argowf "github.com/openinfradev/tks-api/pkg/argo-client" diff --git a/internal/usecase/auth.go b/internal/usecase/auth.go index 31f45b6e..b573247b 100644 --- a/internal/usecase/auth.go +++ b/internal/usecase/auth.go @@ -39,7 +39,7 @@ func (r *AuthUsecase) Login(accountId string, password string, organizationId st } // Authentication with Keycloak - accountToken, err := r.kc.GetAccessTokenByIdPassword(accountId, password, organizationId) + accountToken, err := r.kc.Login(accountId, password, organizationId) if err != nil { //TODO: implement not found handling return domain.User{}, httpErrors.NewUnauthorizedError(err) From a1b81d5ddb9223826c8859ec9c8dbc5d864542c2 Mon Sep 17 00:00:00 2001 From: donggyu Date: Mon, 17 Apr 2023 21:57:36 +0900 Subject: [PATCH 08/11] minor fix: minor bug fix --- internal/keycloak/keycloak.go | 3 -- .../auth/authenticator/keycloak/keycloak.go | 10 ++++ internal/usecase/organization.go | 10 +--- internal/usecase/user.go | 54 +++++-------------- 4 files changed, 24 insertions(+), 53 deletions(-) diff --git a/internal/keycloak/keycloak.go b/internal/keycloak/keycloak.go index f1c393cd..41255514 100644 --- a/internal/keycloak/keycloak.go +++ b/internal/keycloak/keycloak.go @@ -38,7 +38,6 @@ type Keycloak struct { func (k *Keycloak) LoginAdmin() (string, error) { token, err := k.client.LoginAdmin(context.Background(), k.config.AdminId, k.config.AdminPassword, DefaultMasterRealm) - log.Infof("LoginAdmin: %s", token.AccessToken) if err != nil { return "", err } @@ -345,9 +344,7 @@ func (k *Keycloak) DeleteUser(organizationName string, userAccountId string) err } func (k *Keycloak) VerifyAccessToken(token string, organizationName string) error { - //TODO implement me ctx := context.Background() - //log.Info(token) rptResult, err := k.client.RetrospectToken(ctx, token, DefaultClientID, DefaultClientSecret, organizationName) if err != nil { return err diff --git a/internal/middleware/auth/authenticator/keycloak/keycloak.go b/internal/middleware/auth/authenticator/keycloak/keycloak.go index cdd60001..cc902d1a 100644 --- a/internal/middleware/auth/authenticator/keycloak/keycloak.go +++ b/internal/middleware/auth/authenticator/keycloak/keycloak.go @@ -51,11 +51,21 @@ func (a *keycloakAuthenticator) AuthenticateToken(r *http.Request, token string) if err != nil { return nil, false, err } + + if parsedToken.Method.Alg() != "RS256" { + return nil, false, fmt.Errorf("invalid token") + } + + if parsedToken.Claims.Valid() != nil { + return nil, false, fmt.Errorf("invalid token") + } + organizationId, ok := parsedToken.Claims.(jwtWithouKey.MapClaims)["organization"].(string) if !ok { return nil, false, fmt.Errorf("organization is not found in token") } if err := a.kc.VerifyAccessToken(token, organizationId); err != nil { + log.Errorf("failed to verify access token: %v", err) return nil, false, err } diff --git a/internal/usecase/organization.go b/internal/usecase/organization.go index 13d5e581..f421a210 100644 --- a/internal/usecase/organization.go +++ b/internal/usecase/organization.go @@ -3,8 +3,6 @@ package usecase import ( "context" "fmt" - "github.com/openinfradev/tks-api/internal/middleware/auth/request" - "github.com/openinfradev/tks-api/internal/helper" "github.com/openinfradev/tks-api/internal/keycloak" "github.com/openinfradev/tks-api/pkg/httpErrors" @@ -48,13 +46,9 @@ func (u *OrganizationUsecase) Create(ctx context.Context, in *domain.Organizatio return "", err } } - token, ok := request.TokenFrom(ctx) - if !ok { - return "", fmt.Errorf("token in the context is empty") - } // Create realm in keycloak - if organizationId, err = u.kc.CreateRealm(helper.GenerateOrganizationId(), domain.Organization{}, token); err != nil { + if organizationId, err = u.kc.CreateRealm(helper.GenerateOrganizationId()); err != nil { return "", err } @@ -103,7 +97,7 @@ func (u *OrganizationUsecase) Delete(organizationId string, accessToken string) } // Delete realm in keycloak - if err := u.kc.DeleteRealm(organizationId, accessToken); err != nil { + if err := u.kc.DeleteRealm(organizationId); err != nil { return err } diff --git a/internal/usecase/user.go b/internal/usecase/user.go index 73ec6aad..6f3b5f01 100644 --- a/internal/usecase/user.go +++ b/internal/usecase/user.go @@ -47,17 +47,12 @@ func (u *UserUsecase) DeleteAll(ctx context.Context, organizationId string) erro } func (u *UserUsecase) DeleteAdmin(organizationId string) error { - token, err := u.kc.LoginAdmin() - if err != nil { - return errors.Wrap(err, "login admin failed") - } - - user, err := u.kc.GetUser(organizationId, "admin", token) + user, err := u.kc.GetUser(organizationId, "admin") if err != nil { return errors.Wrap(err, "get user failed") } - err = u.kc.DeleteUser(organizationId, "admin", token) + err = u.kc.DeleteUser(organizationId, "admin") if err != nil { return errors.Wrap(err, "delete user failed") } @@ -76,10 +71,6 @@ func (u *UserUsecase) DeleteAdmin(organizationId string) error { } func (u *UserUsecase) CreateAdmin(orgainzationId string) (*domain.User, error) { - token, err := u.kc.LoginAdmin() - if err != nil { - return nil, errors.Wrap(err, "login admin failed") - } user := domain.User{ AccountId: "admin", Password: "admin", @@ -94,7 +85,7 @@ func (u *UserUsecase) CreateAdmin(orgainzationId string) (*domain.User, error) { // Create user in keycloak groups := []string{fmt.Sprintf("%s@%s", user.Role.Name, orgainzationId)} - err = u.kc.CreateUser(orgainzationId, &gocloak.User{ + err := u.kc.CreateUser(orgainzationId, &gocloak.User{ Username: gocloak.StringP(user.AccountId), Credentials: &[]gocloak.CredentialRepresentation{ { @@ -104,11 +95,11 @@ func (u *UserUsecase) CreateAdmin(orgainzationId string) (*domain.User, error) { }, }, Groups: &groups, - }, token) + }) if err != nil { return nil, errors.Wrap(err, "creating user in keycloak failed") } - keycloakUser, err := u.kc.GetUser(user.Organization.ID, user.AccountId, token) + keycloakUser, err := u.kc.GetUser(user.Organization.ID, user.AccountId) if err != nil { return nil, errors.Wrap(err, "getting user from keycloak failed") } @@ -147,13 +138,7 @@ func (u *UserUsecase) CreateAdmin(orgainzationId string) (*domain.User, error) { func (u *UserUsecase) UpdatePasswordByAccountId(ctx context.Context, accountId string, newPassword string, organizationId string) error { - - token, ok := request.TokenFrom(ctx) - if !ok { - return fmt.Errorf("token in the context is empty") - } - - originUser, err := u.kc.GetUser(organizationId, accountId, token) + originUser, err := u.kc.GetUser(organizationId, accountId) if err != nil { return err } @@ -166,7 +151,7 @@ func (u *UserUsecase) UpdatePasswordByAccountId(ctx context.Context, accountId s }, } - err = u.kc.UpdateUser(organizationId, originUser, token) + err = u.kc.UpdateUser(organizationId, originUser) if err != nil { return errors.Wrap(err, "updating user in keycloak failed") } @@ -233,10 +218,6 @@ func (u *UserUsecase) UpdateByAccountId(ctx context.Context, accountId string, u if !ok { return nil, fmt.Errorf("user in the context is empty") } - token, ok := request.TokenFrom(ctx) - if !ok { - return nil, fmt.Errorf("token in the context is empty") - } users, err := u.repo.List(u.repo.OrganizationFilter(userInfo.GetOrganizationId()), u.repo.AccountIdFilter(accountId)) @@ -257,7 +238,7 @@ func (u *UserUsecase) UpdateByAccountId(ctx context.Context, accountId string, u if err := u.kc.UpdateUser(userInfo.GetOrganizationId(), &gocloak.User{ ID: &(*users)[0].ID, Groups: &groups, - }, token); err != nil { + }); err != nil { log.Errorf("updating user in keycloak failed: %v", err) return nil, httpErrors.NewInternalServerError(err) } @@ -309,11 +290,7 @@ func (u *UserUsecase) DeleteByAccountId(ctx context.Context, accountId string, o } // Delete user in keycloak - token, ok := request.TokenFrom(ctx) - if !ok { - return fmt.Errorf("token in the context is empty") - } - err = u.kc.DeleteUser(organizationId, accountId, token) + err = u.kc.DeleteUser(organizationId, accountId) if err != nil { return err } @@ -322,13 +299,6 @@ func (u *UserUsecase) DeleteByAccountId(ctx context.Context, accountId string, o } func (u *UserUsecase) Create(ctx context.Context, user *domain.User) (*domain.User, error) { - // Validation check - - token, ok := request.TokenFrom(ctx) - if !ok { - return nil, fmt.Errorf("token in the context is empty") - } - // Create user in keycloak groups := []string{fmt.Sprintf("%s@%s", user.Role.Name, user.Organization.ID)} err := u.kc.CreateUser(user.Organization.ID, &gocloak.User{ @@ -341,15 +311,15 @@ func (u *UserUsecase) Create(ctx context.Context, user *domain.User) (*domain.Us }, }, Groups: &groups, - }, token) + }) if err != nil { - if _, err := u.kc.GetUser(user.Organization.ID, user.AccountId, token); err == nil { + if _, err := u.kc.GetUser(user.Organization.ID, user.AccountId); err == nil { return nil, httpErrors.NewConflictError(errors.New("user already exists")) } return nil, errors.Wrap(err, "creating user in keycloak failed") } - keycloakUser, err := u.kc.GetUser(user.Organization.ID, user.AccountId, token) + keycloakUser, err := u.kc.GetUser(user.Organization.ID, user.AccountId) if err != nil { return nil, errors.Wrap(err, "getting user from keycloak failed") } From 8d23ac01298582a6e9882128b0b069deb00df86a Mon Sep 17 00:00:00 2001 From: donggyu Date: Tue, 18 Apr 2023 14:57:24 +0900 Subject: [PATCH 09/11] bug fix: solve too long master token --- api/swagger/docs.go | 132 ++++++++- api/swagger/swagger.json | 129 ++++++++- api/swagger/swagger.yaml | 110 +++++++- internal/delivery/http/cloud-account.go | 2 +- internal/keycloak/config.go | 1 + internal/keycloak/keycloak.go | 57 ++-- .../auth/authenticator/authenticator.go | 4 +- internal/middleware/{ => auth}/middleware.go | 12 +- internal/route/route.go | 267 +++++------------- internal/usecase/auth.go | 8 +- internal/usecase/stack.go | 2 +- 11 files changed, 468 insertions(+), 256 deletions(-) rename internal/middleware/{ => auth}/middleware.go (62%) diff --git a/api/swagger/docs.go b/api/swagger/docs.go index 08aa189c..c97d28d1 100644 --- a/api/swagger/docs.go +++ b/api/swagger/docs.go @@ -1,5 +1,4 @@ -// Package swagger GENERATED BY SWAG; DO NOT EDIT -// This file was generated by swaggo/swag +// Code generated by swaggo/swag. DO NOT EDIT package swagger import "github.com/swaggo/swag" @@ -2055,7 +2054,7 @@ const docTemplate = `{ "type": "object", "properties": { "appGroupType": { - "type": "integer" + "$ref": "#/definitions/domain.AppGroupType" }, "clusterId": { "type": "string" @@ -2076,7 +2075,7 @@ const docTemplate = `{ "type": "string" }, "status": { - "type": "integer" + "$ref": "#/definitions/domain.AppGroupStatus" }, "statusDescription": { "type": "string" @@ -2092,6 +2091,38 @@ const docTemplate = `{ } } }, + "domain.AppGroupStatus": { + "type": "integer", + "enum": [ + 0, + 1, + 2, + 3, + 4, + 5 + ], + "x-enum-varnames": [ + "AppGroupStatus_PENDING", + "AppGroupStatus_INSTALLING", + "AppGroupStatus_RUNNING", + "AppGroupStatus_DELETING", + "AppGroupStatus_DELETED", + "AppGroupStatus_ERROR" + ] + }, + "domain.AppGroupType": { + "type": "integer", + "enum": [ + 0, + 1, + 2 + ], + "x-enum-varnames": [ + "AppGroupType_UNSPECIFIED", + "AppGroupType_LMA", + "AppGroupType_SERVICE_MESH" + ] + }, "domain.AppServeApp": { "type": "object", "properties": { @@ -2248,7 +2279,7 @@ const docTemplate = `{ "type": "string" }, "applicationType": { - "type": "integer" + "$ref": "#/definitions/domain.ApplicationType" }, "createdAt": { "type": "string" @@ -2267,6 +2298,35 @@ const docTemplate = `{ } } }, + "domain.ApplicationType": { + "type": "integer", + "enum": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10 + ], + "x-enum-varnames": [ + "ApplicationType_UNSPECIFIED", + "ApplicationType_THANOS", + "ApplicationType_PROMETHEUS", + "ApplicationType_GRAFANA", + "ApplicationType_KIALI", + "ApplicationType_KIBANA", + "ApplicationType_ELASTICSERCH", + "ApplicationType_CLOUD_CONSOLE", + "ApplicationType_HORIZON", + "ApplicationType_JAEGER", + "ApplicationType_KUBERNETES_DASHBOARD" + ] + }, "domain.ChartData": { "type": "object", "properties": { @@ -2428,7 +2488,7 @@ const docTemplate = `{ "type": "string" }, "status": { - "type": "integer" + "$ref": "#/definitions/domain.ClusterStatus" }, "statusDesc": { "type": "string" @@ -2540,6 +2600,25 @@ const docTemplate = `{ } } }, + "domain.ClusterStatus": { + "type": "integer", + "enum": [ + 0, + 1, + 2, + 3, + 4, + 5 + ], + "x-enum-varnames": [ + "ClusterStatus_PENDING", + "ClusterStatus_INSTALLING", + "ClusterStatus_RUNNING", + "ClusterStatus_DELETING", + "ClusterStatus_DELETED", + "ClusterStatus_ERROR" + ] + }, "domain.CreateAppGroupRequest": { "type": "object", "required": [ @@ -3079,7 +3158,7 @@ const docTemplate = `{ "type": "string" }, "status": { - "type": "integer" + "type": "string" }, "statusDescription": { "type": "string" @@ -3198,6 +3277,9 @@ const docTemplate = `{ "domain.ListOrganizationBody": { "type": "object", "properties": { + "createdAt": { + "type": "string" + }, "description": { "type": "string" }, @@ -3214,7 +3296,10 @@ const docTemplate = `{ "type": "string" }, "status": { - "type": "integer" + "$ref": "#/definitions/domain.OrganizationStatus" + }, + "updatedAt": { + "type": "string" } } }, @@ -3325,7 +3410,7 @@ const docTemplate = `{ "type": "string" }, "status": { - "type": "integer" + "$ref": "#/definitions/domain.OrganizationStatus" }, "statusDescription": { "type": "string" @@ -3335,6 +3420,29 @@ const docTemplate = `{ } } }, + "domain.OrganizationStatus": { + "type": "integer", + "enum": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7 + ], + "x-enum-varnames": [ + "OrganizationStatus_PENDING", + "OrganizationStatus_CREATE", + "OrganizationStatus_CREATING", + "OrganizationStatus_CREATED", + "OrganizationStatus_DELETE", + "OrganizationStatus_DELETING", + "OrganizationStatus_DELETED", + "OrganizationStatus_ERROR" + ] + }, "domain.Role": { "type": "object", "properties": { @@ -3842,6 +3950,9 @@ const docTemplate = `{ "accountId": { "type": "string" }, + "createdAt": { + "type": "string" + }, "department": { "type": "string" }, @@ -3862,6 +3973,9 @@ const docTemplate = `{ }, "role": { "$ref": "#/definitions/domain.Role" + }, + "updatedAt": { + "type": "string" } } } diff --git a/api/swagger/swagger.json b/api/swagger/swagger.json index 810181b7..a6756030 100644 --- a/api/swagger/swagger.json +++ b/api/swagger/swagger.json @@ -2048,7 +2048,7 @@ "type": "object", "properties": { "appGroupType": { - "type": "integer" + "$ref": "#/definitions/domain.AppGroupType" }, "clusterId": { "type": "string" @@ -2069,7 +2069,7 @@ "type": "string" }, "status": { - "type": "integer" + "$ref": "#/definitions/domain.AppGroupStatus" }, "statusDescription": { "type": "string" @@ -2085,6 +2085,38 @@ } } }, + "domain.AppGroupStatus": { + "type": "integer", + "enum": [ + 0, + 1, + 2, + 3, + 4, + 5 + ], + "x-enum-varnames": [ + "AppGroupStatus_PENDING", + "AppGroupStatus_INSTALLING", + "AppGroupStatus_RUNNING", + "AppGroupStatus_DELETING", + "AppGroupStatus_DELETED", + "AppGroupStatus_ERROR" + ] + }, + "domain.AppGroupType": { + "type": "integer", + "enum": [ + 0, + 1, + 2 + ], + "x-enum-varnames": [ + "AppGroupType_UNSPECIFIED", + "AppGroupType_LMA", + "AppGroupType_SERVICE_MESH" + ] + }, "domain.AppServeApp": { "type": "object", "properties": { @@ -2241,7 +2273,7 @@ "type": "string" }, "applicationType": { - "type": "integer" + "$ref": "#/definitions/domain.ApplicationType" }, "createdAt": { "type": "string" @@ -2260,6 +2292,35 @@ } } }, + "domain.ApplicationType": { + "type": "integer", + "enum": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10 + ], + "x-enum-varnames": [ + "ApplicationType_UNSPECIFIED", + "ApplicationType_THANOS", + "ApplicationType_PROMETHEUS", + "ApplicationType_GRAFANA", + "ApplicationType_KIALI", + "ApplicationType_KIBANA", + "ApplicationType_ELASTICSERCH", + "ApplicationType_CLOUD_CONSOLE", + "ApplicationType_HORIZON", + "ApplicationType_JAEGER", + "ApplicationType_KUBERNETES_DASHBOARD" + ] + }, "domain.ChartData": { "type": "object", "properties": { @@ -2421,7 +2482,7 @@ "type": "string" }, "status": { - "type": "integer" + "$ref": "#/definitions/domain.ClusterStatus" }, "statusDesc": { "type": "string" @@ -2533,6 +2594,25 @@ } } }, + "domain.ClusterStatus": { + "type": "integer", + "enum": [ + 0, + 1, + 2, + 3, + 4, + 5 + ], + "x-enum-varnames": [ + "ClusterStatus_PENDING", + "ClusterStatus_INSTALLING", + "ClusterStatus_RUNNING", + "ClusterStatus_DELETING", + "ClusterStatus_DELETED", + "ClusterStatus_ERROR" + ] + }, "domain.CreateAppGroupRequest": { "type": "object", "required": [ @@ -3072,7 +3152,7 @@ "type": "string" }, "status": { - "type": "integer" + "type": "string" }, "statusDescription": { "type": "string" @@ -3191,6 +3271,9 @@ "domain.ListOrganizationBody": { "type": "object", "properties": { + "createdAt": { + "type": "string" + }, "description": { "type": "string" }, @@ -3207,7 +3290,10 @@ "type": "string" }, "status": { - "type": "integer" + "$ref": "#/definitions/domain.OrganizationStatus" + }, + "updatedAt": { + "type": "string" } } }, @@ -3318,7 +3404,7 @@ "type": "string" }, "status": { - "type": "integer" + "$ref": "#/definitions/domain.OrganizationStatus" }, "statusDescription": { "type": "string" @@ -3328,6 +3414,29 @@ } } }, + "domain.OrganizationStatus": { + "type": "integer", + "enum": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7 + ], + "x-enum-varnames": [ + "OrganizationStatus_PENDING", + "OrganizationStatus_CREATE", + "OrganizationStatus_CREATING", + "OrganizationStatus_CREATED", + "OrganizationStatus_DELETE", + "OrganizationStatus_DELETING", + "OrganizationStatus_DELETED", + "OrganizationStatus_ERROR" + ] + }, "domain.Role": { "type": "object", "properties": { @@ -3835,6 +3944,9 @@ "accountId": { "type": "string" }, + "createdAt": { + "type": "string" + }, "department": { "type": "string" }, @@ -3855,6 +3967,9 @@ }, "role": { "$ref": "#/definitions/domain.Role" + }, + "updatedAt": { + "type": "string" } } } diff --git a/api/swagger/swagger.yaml b/api/swagger/swagger.yaml index 411a96ef..84c0a704 100644 --- a/api/swagger/swagger.yaml +++ b/api/swagger/swagger.yaml @@ -3,7 +3,7 @@ definitions: domain.AppGroupResponse: properties: appGroupType: - type: integer + $ref: '#/definitions/domain.AppGroupType' clusterId: type: string createdAt: @@ -17,7 +17,7 @@ definitions: name: type: string status: - type: integer + $ref: '#/definitions/domain.AppGroupStatus' statusDescription: type: string updatedAt: @@ -27,6 +27,32 @@ definitions: workflowId: type: string type: object + domain.AppGroupStatus: + enum: + - 0 + - 1 + - 2 + - 3 + - 4 + - 5 + type: integer + x-enum-varnames: + - AppGroupStatus_PENDING + - AppGroupStatus_INSTALLING + - AppGroupStatus_RUNNING + - AppGroupStatus_DELETING + - AppGroupStatus_DELETED + - AppGroupStatus_ERROR + domain.AppGroupType: + enum: + - 0 + - 1 + - 2 + type: integer + x-enum-varnames: + - AppGroupType_UNSPECIFIED + - AppGroupType_LMA + - AppGroupType_SERVICE_MESH domain.AppServeApp: properties: app_serve_app_tasks: @@ -139,7 +165,7 @@ definitions: appGroupId: type: string applicationType: - type: integer + $ref: '#/definitions/domain.ApplicationType' createdAt: type: string endpoint: @@ -151,6 +177,32 @@ definitions: updatedAt: type: string type: object + domain.ApplicationType: + enum: + - 0 + - 1 + - 2 + - 3 + - 4 + - 5 + - 6 + - 7 + - 8 + - 9 + - 10 + type: integer + x-enum-varnames: + - ApplicationType_UNSPECIFIED + - ApplicationType_THANOS + - ApplicationType_PROMETHEUS + - ApplicationType_GRAFANA + - ApplicationType_KIALI + - ApplicationType_KIBANA + - ApplicationType_ELASTICSERCH + - ApplicationType_CLOUD_CONSOLE + - ApplicationType_HORIZON + - ApplicationType_JAEGER + - ApplicationType_KUBERNETES_DASHBOARD domain.ChartData: properties: series: @@ -257,7 +309,7 @@ definitions: stackTemplateId: type: string status: - type: integer + $ref: '#/definitions/domain.ClusterStatus' statusDesc: type: string updatedAt: @@ -330,6 +382,22 @@ definitions: updator: $ref: '#/definitions/domain.SimpleUserResponse' type: object + domain.ClusterStatus: + enum: + - 0 + - 1 + - 2 + - 3 + - 4 + - 5 + type: integer + x-enum-varnames: + - ClusterStatus_PENDING + - ClusterStatus_INSTALLING + - ClusterStatus_RUNNING + - ClusterStatus_DELETING + - ClusterStatus_DELETED + - ClusterStatus_ERROR domain.CreateAppGroupRequest: properties: appGroupType: @@ -696,7 +764,7 @@ definitions: primaryClusterId: type: string status: - type: integer + type: string statusDescription: type: string updatedAt: @@ -772,6 +840,8 @@ definitions: type: object domain.ListOrganizationBody: properties: + createdAt: + type: string description: type: string id: @@ -783,7 +853,9 @@ definitions: primaryClusterId: type: string status: - type: integer + $ref: '#/definitions/domain.OrganizationStatus' + updatedAt: + type: string type: object domain.ListUserBody: properties: @@ -856,12 +928,32 @@ definitions: primaryClusterId: type: string status: - type: integer + $ref: '#/definitions/domain.OrganizationStatus' statusDescription: type: string updatedAt: type: string type: object + domain.OrganizationStatus: + enum: + - 0 + - 1 + - 2 + - 3 + - 4 + - 5 + - 6 + - 7 + type: integer + x-enum-varnames: + - OrganizationStatus_PENDING + - OrganizationStatus_CREATE + - OrganizationStatus_CREATING + - OrganizationStatus_CREATED + - OrganizationStatus_DELETE + - OrganizationStatus_DELETING + - OrganizationStatus_DELETED + - OrganizationStatus_ERROR domain.Role: properties: createdAt: @@ -1199,6 +1291,8 @@ definitions: properties: accountId: type: string + createdAt: + type: string department: type: string description: @@ -1213,6 +1307,8 @@ definitions: $ref: '#/definitions/domain.Organization' role: $ref: '#/definitions/domain.Role' + updatedAt: + type: string type: object type: object domain.User: diff --git a/internal/delivery/http/cloud-account.go b/internal/delivery/http/cloud-account.go index 9b555123..bfb679f8 100644 --- a/internal/delivery/http/cloud-account.go +++ b/internal/delivery/http/cloud-account.go @@ -2,11 +2,11 @@ package http import ( "fmt" + "github.com/openinfradev/tks-api/internal/middleware/auth/request" "net/http" "github.com/google/uuid" "github.com/gorilla/mux" - "github.com/openinfradev/tks-api/internal/auth/request" "github.com/openinfradev/tks-api/internal/usecase" "github.com/openinfradev/tks-api/pkg/domain" "github.com/openinfradev/tks-api/pkg/httpErrors" diff --git a/internal/keycloak/config.go b/internal/keycloak/config.go index d769752e..bc0243d5 100644 --- a/internal/keycloak/config.go +++ b/internal/keycloak/config.go @@ -11,5 +11,6 @@ const ( DefaultMasterRealm = "master" DefaultClientID = "tks" DefaultClientSecret = "secret" + AdminCliClientID = "admin-cli" accessTokenLifespan = 60 * 60 * 24 ) diff --git a/internal/keycloak/keycloak.go b/internal/keycloak/keycloak.go index 41255514..9c4525e8 100644 --- a/internal/keycloak/keycloak.go +++ b/internal/keycloak/keycloak.go @@ -14,7 +14,7 @@ import ( type IKeycloak interface { InitializeKeycloak() error - LoginAdmin() (string, error) + LoginAdmin(accountId string, password string) (*domain.User, error) Login(accountId string, password string, organizationId string) (*domain.User, error) CreateRealm(organizationName string) (string, error) @@ -36,13 +36,27 @@ type Keycloak struct { client *gocloak.GoCloak } -func (k *Keycloak) LoginAdmin() (string, error) { - token, err := k.client.LoginAdmin(context.Background(), k.config.AdminId, k.config.AdminPassword, DefaultMasterRealm) +func (k *Keycloak) LoginAdmin(accountId string, password string) (*domain.User, error) { + ctx := context.Background() + log.Info("LoginAdmin called") + //JWTToken, err := k.client.Login(ctx, "admin-cli", "", DefaultMasterRealm, accountId, password) + JWTToken, err := k.client.LoginAdmin(ctx, accountId, password, DefaultMasterRealm) if err != nil { - return "", err + log.Error(err) + return nil, err } - return token.AccessToken, nil + return &domain.User{Token: JWTToken.AccessToken}, nil +} + +func (k *Keycloak) Login(accountId string, password string, organizationId string) (*domain.User, error) { + ctx := context.Background() + JWTToken, err := k.client.Login(ctx, DefaultClientID, DefaultClientSecret, organizationId, accountId, password) + if err != nil { + log.Error(err) + return nil, err + } + return &domain.User{Token: JWTToken.AccessToken}, nil } func New(config *Config) IKeycloak { @@ -98,6 +112,25 @@ func (k *Keycloak) InitializeKeycloak() error { return err } + adminCliClient, err := k.ensureClient(ctx, token, DefaultMasterRealm, AdminCliClientID, DefaultClientSecret) + if err != nil { + log.Fatal(err) + return err + } + + for _, defaultMapper := range defaultProtocolTksMapper { + if err := k.ensureClientProtocolMappers(ctx, token, DefaultMasterRealm, *adminCliClient.ClientID, "openid", defaultMapper); err != nil { + log.Fatal(err) + return err + } + } + + if _, err := k.client.Login(ctx, AdminCliClientID, DefaultClientSecret, DefaultMasterRealm, + k.config.AdminId, k.config.AdminPassword); err != nil { + log.Fatal(err) + return err + } + return nil } @@ -218,16 +251,6 @@ func (k *Keycloak) CreateRealm(organizationName string) (string, error) { return realmUUID, nil } -func (k *Keycloak) Login(accountId string, password string, organizationId string) (*domain.User, error) { - ctx := context.Background() - JWTToken, err := k.client.Login(ctx, DefaultClientID, DefaultClientSecret, organizationId, accountId, password) - if err != nil { - log.Error(err) - return nil, err - } - return &domain.User{Token: JWTToken.AccessToken}, nil -} - func (k *Keycloak) GetRealm(organizationName string) (*domain.Organization, error) { ctx := context.Background() token, err := k.loginAdmin(ctx) @@ -415,7 +438,7 @@ func (k *Keycloak) ensureClient(ctx context.Context, token *gocloak.JWT, realm s log.Error("Getting Client is failed", err) } } - if *keycloakClient[0].Secret != secret { + if keycloakClient[0].Secret == nil || *keycloakClient[0].Secret != secret { log.Warn("Client secret is not matched. Overwrite it") keycloakClient[0].Secret = gocloak.StringP(secret) if err := k.client.UpdateClient(ctx, token.AccessToken, realm, *keycloakClient[0]); err != nil { @@ -423,7 +446,7 @@ func (k *Keycloak) ensureClient(ctx context.Context, token *gocloak.JWT, realm s } } - return keycloakClient[0], err + return keycloakClient[0], nil } func (k *Keycloak) addUserToGroup(ctx context.Context, token *gocloak.JWT, realm string, userID string, groupID string) error { diff --git a/internal/middleware/auth/authenticator/authenticator.go b/internal/middleware/auth/authenticator/authenticator.go index 2251f4f9..892b3834 100644 --- a/internal/middleware/auth/authenticator/authenticator.go +++ b/internal/middleware/auth/authenticator/authenticator.go @@ -19,9 +19,9 @@ type defaultAuthenticator struct { auth Request } -func NewDefaultAuthenticator(auth Request) *defaultAuthenticator { +func NewAuthenticator(kc Request) *defaultAuthenticator { return &defaultAuthenticator{ - auth: auth, + auth: kc, } } diff --git a/internal/middleware/middleware.go b/internal/middleware/auth/middleware.go similarity index 62% rename from internal/middleware/middleware.go rename to internal/middleware/auth/middleware.go index 170c09f1..f9e6e977 100644 --- a/internal/middleware/middleware.go +++ b/internal/middleware/auth/middleware.go @@ -1,4 +1,4 @@ -package middleware +package auth import ( "github.com/openinfradev/tks-api/internal/middleware/auth/authenticator" @@ -6,21 +6,21 @@ import ( "net/http" ) -type defaultMiddleware struct { +type authMiddleware struct { authenticator authenticator.Interface authorizer authorizer.Interface } -func NewDefaultMiddleware(authenticator authenticator.Interface, - authorizer authorizer.Interface) *defaultMiddleware { - ret := &defaultMiddleware{ +func NewAuthMiddleware(authenticator authenticator.Interface, + authorizer authorizer.Interface) *authMiddleware { + ret := &authMiddleware{ authenticator: authenticator, authorizer: authorizer, } return ret } -func (m *defaultMiddleware) Handle(handle http.Handler) http.Handler { +func (m *authMiddleware) Handle(handle http.Handler) http.Handler { handler := m.authorizer.WithAuthorization(handle) handler = m.authenticator.WithAuthentication(handler) return handler diff --git a/internal/route/route.go b/internal/route/route.go index 6405ba1e..3a9bdfab 100644 --- a/internal/route/route.go +++ b/internal/route/route.go @@ -6,10 +6,10 @@ import ( "github.com/gorilla/handlers" "github.com/gorilla/mux" "github.com/openinfradev/tks-api/internal/keycloak" - "github.com/openinfradev/tks-api/internal/middleware" + "github.com/openinfradev/tks-api/internal/middleware/auth" "github.com/openinfradev/tks-api/internal/middleware/auth/authenticator" - authenticatorKeycloak "github.com/openinfradev/tks-api/internal/middleware/auth/authenticator/keycloak" - "github.com/openinfradev/tks-api/internal/middleware/auth/authorization" + authKeycloak "github.com/openinfradev/tks-api/internal/middleware/auth/authenticator/keycloak" + "github.com/openinfradev/tks-api/internal/middleware/auth/authorizer" "gorm.io/gorm" "io" "io/ioutil" @@ -41,13 +41,9 @@ func (r *StatusRecorder) WriteHeader(status int) { func SetupRouter(db *gorm.DB, argoClient argowf.ArgoClient, asset http.Handler, kc keycloak.IKeycloak) http.Handler { r := mux.NewRouter() - - r.Use(loggingMiddleware) - - // [TODO] Transaction - //r.Use(transactionMiddleware(db)) - - //r.PathPrefix("/swagger").Handler(httpSwagger.WrapHandler) + authMiddleware := auth.NewAuthMiddleware( + authenticator.NewAuthenticator(authKeycloak.NewKeycloakAuthenticator(kc)), + authorizer.NewDefaultAuthorization()) repoFactory := repository.Repository{ User: repository.NewUserRepository(db), @@ -59,78 +55,85 @@ func SetupRouter(db *gorm.DB, argoClient argowf.ArgoClient, asset http.Handler, StackTemplate: repository.NewStackTemplateRepository(db), History: repository.NewHistoryRepository(db), } - authHandler := delivery.NewAuthHandler(usecase.NewAuthUsecase(repoFactory, kc)) + + r.Use(loggingMiddleware) + //r.Use(authMiddleware.Handle) + // [TODO] Transaction + //r.Use(transactionMiddleware(db)) + + //r.PathPrefix("/swagger").Handler(httpSwagger.WrapHandler) + r.HandleFunc(API_PREFIX+API_VERSION+"/auth/login", authHandler.Login).Methods(http.MethodPost) r.HandleFunc(API_PREFIX+API_VERSION+"/auth/logout", authHandler.Logout).Methods(http.MethodPost) r.HandleFunc(API_PREFIX+API_VERSION+"/auth/find-id", authHandler.FindId).Methods(http.MethodPost) r.HandleFunc(API_PREFIX+API_VERSION+"/auth/find-password", authHandler.FindPassword).Methods(http.MethodPost) userHandler := delivery.NewUserHandler(usecase.NewUserUsecase(repoFactory, kc)) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/users", authMiddleware(http.HandlerFunc(userHandler.Create), kc)).Methods(http.MethodPost) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/users", authMiddleware(http.HandlerFunc(userHandler.List), kc)).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/users/{accountId}", authMiddleware(http.HandlerFunc(userHandler.Get), kc)).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/users/{accountId}", authMiddleware(http.HandlerFunc(userHandler.Delete), kc)).Methods(http.MethodDelete) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/users/{accountId}", authMiddleware(http.HandlerFunc(userHandler.Update), kc)).Methods(http.MethodPut) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/users/{accountId}/password", authMiddleware(http.HandlerFunc(userHandler.UpdatePassword), kc)).Methods(http.MethodPut) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/users/{accountId}/existence", authMiddleware(http.HandlerFunc(userHandler.CheckId), kc)).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/users", authMiddleware.Handle(http.HandlerFunc(userHandler.Create))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/users", authMiddleware.Handle(http.HandlerFunc(userHandler.List))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/users/{accountId}", authMiddleware.Handle(http.HandlerFunc(userHandler.Get))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/users/{accountId}", authMiddleware.Handle(http.HandlerFunc(userHandler.Delete))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/users/{accountId}", authMiddleware.Handle(http.HandlerFunc(userHandler.Update))).Methods(http.MethodPut) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/users/{accountId}/password", authMiddleware.Handle(http.HandlerFunc(userHandler.UpdatePassword))).Methods(http.MethodPut) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/users/{accountId}/existence", authMiddleware.Handle(http.HandlerFunc(userHandler.CheckId))).Methods(http.MethodGet) organizationHandler := delivery.NewOrganizationHandler(usecase.NewOrganizationUsecase(repoFactory, argoClient, kc), usecase.NewUserUsecase(repoFactory, kc)) - r.Handle(API_PREFIX+API_VERSION+"/organizations", authMiddleware(http.HandlerFunc(organizationHandler.CreateOrganization), kc)).Methods(http.MethodPost) - r.Handle(API_PREFIX+API_VERSION+"/organizations", authMiddleware(http.HandlerFunc(organizationHandler.GetOrganizations), kc)).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}", authMiddleware(http.HandlerFunc(organizationHandler.GetOrganization), kc)).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}", authMiddleware(http.HandlerFunc(organizationHandler.DeleteOrganization), kc)).Methods(http.MethodDelete) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}", authMiddleware(http.HandlerFunc(organizationHandler.UpdateOrganization), kc)).Methods(http.MethodPut) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/primary-cluster", authMiddleware(http.HandlerFunc(organizationHandler.UpdatePrimaryCluster), kc)).Methods(http.MethodPatch) + r.Handle(API_PREFIX+API_VERSION+"/organizations", authMiddleware.Handle(http.HandlerFunc(organizationHandler.CreateOrganization))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/organizations", authMiddleware.Handle(http.HandlerFunc(organizationHandler.GetOrganizations))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}", authMiddleware.Handle(http.HandlerFunc(organizationHandler.GetOrganization))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}", authMiddleware.Handle(http.HandlerFunc(organizationHandler.DeleteOrganization))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}", authMiddleware.Handle(http.HandlerFunc(organizationHandler.UpdateOrganization))).Methods(http.MethodPut) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/primary-cluster", authMiddleware.Handle(http.HandlerFunc(organizationHandler.UpdatePrimaryCluster))).Methods(http.MethodPatch) clusterHandler := delivery.NewClusterHandler(usecase.NewClusterUsecase(repoFactory, argoClient)) - r.Handle(API_PREFIX+API_VERSION+"/clusters", authMiddleware(http.HandlerFunc(clusterHandler.CreateCluster), kc)).Methods(http.MethodPost) - r.Handle(API_PREFIX+API_VERSION+"/clusters", authMiddleware(http.HandlerFunc(clusterHandler.GetClusters), kc)).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/clusters/{clusterId}", authMiddleware(http.HandlerFunc(clusterHandler.GetCluster), kc)).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/clusters/{clusterId}", authMiddleware(http.HandlerFunc(clusterHandler.DeleteCluster), kc)).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/clusters", authMiddleware.Handle(http.HandlerFunc(clusterHandler.CreateCluster))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/clusters", authMiddleware.Handle(http.HandlerFunc(clusterHandler.GetClusters))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/clusters/{clusterId}", authMiddleware.Handle(http.HandlerFunc(clusterHandler.GetCluster))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/clusters/{clusterId}", authMiddleware.Handle(http.HandlerFunc(clusterHandler.DeleteCluster))).Methods(http.MethodDelete) appGroupHandler := delivery.NewAppGroupHandler(usecase.NewAppGroupUsecase(repoFactory, argoClient)) - r.Handle(API_PREFIX+API_VERSION+"/app-groups", authMiddleware(http.HandlerFunc(appGroupHandler.CreateAppGroup), kc)).Methods(http.MethodPost) - r.Handle(API_PREFIX+API_VERSION+"/app-groups", authMiddleware(http.HandlerFunc(appGroupHandler.GetAppGroups), kc)).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/app-groups/{appGroupId}", authMiddleware(http.HandlerFunc(appGroupHandler.GetAppGroup), kc)).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/app-groups/{appGroupId}", authMiddleware(http.HandlerFunc(appGroupHandler.DeleteAppGroup), kc)).Methods(http.MethodDelete) - r.Handle(API_PREFIX+API_VERSION+"/app-groups/{appGroupId}/applications", authMiddleware(http.HandlerFunc(appGroupHandler.GetApplications), kc)).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/app-groups/{appGroupId}/applications", authMiddleware(http.HandlerFunc(appGroupHandler.UpdateApplication), kc)).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/app-groups", authMiddleware.Handle(http.HandlerFunc(appGroupHandler.CreateAppGroup))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/app-groups", authMiddleware.Handle(http.HandlerFunc(appGroupHandler.GetAppGroups))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/app-groups/{appGroupId}", authMiddleware.Handle(http.HandlerFunc(appGroupHandler.GetAppGroup))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/app-groups/{appGroupId}", authMiddleware.Handle(http.HandlerFunc(appGroupHandler.DeleteAppGroup))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/app-groups/{appGroupId}/applications", authMiddleware.Handle(http.HandlerFunc(appGroupHandler.GetApplications))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/app-groups/{appGroupId}/applications", authMiddleware.Handle(http.HandlerFunc(appGroupHandler.UpdateApplication))).Methods(http.MethodPost) appServeAppHandler := delivery.NewAppServeAppHandler(usecase.NewAppServeAppUsecase(repoFactory, argoClient)) - r.Handle(API_PREFIX+API_VERSION+"/app-serve-apps", authMiddleware(http.HandlerFunc(appServeAppHandler.CreateAppServeApp), kc)).Methods(http.MethodPost) - r.Handle(API_PREFIX+API_VERSION+"/app-serve-apps", authMiddleware(http.HandlerFunc(appServeAppHandler.GetAppServeApps), kc)).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/app-serve-apps/{appId}", authMiddleware(http.HandlerFunc(appServeAppHandler.GetAppServeApp), kc)).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/app-serve-apps/{appId}", authMiddleware(http.HandlerFunc(appServeAppHandler.DeleteAppServeApp), kc)).Methods(http.MethodDelete) - r.Handle(API_PREFIX+API_VERSION+"/app-serve-apps/{appId}", authMiddleware(http.HandlerFunc(appServeAppHandler.UpdateAppServeApp), kc)).Methods(http.MethodPut) - r.Handle(API_PREFIX+API_VERSION+"/app-serve-apps/{appId}/status", authMiddleware(http.HandlerFunc(appServeAppHandler.UpdateAppServeAppStatus), kc)).Methods(http.MethodPatch) - r.Handle(API_PREFIX+API_VERSION+"/app-serve-apps/{appId}/endpoint", authMiddleware(http.HandlerFunc(appServeAppHandler.UpdateAppServeAppEndpoint), kc)).Methods(http.MethodPatch) + r.Handle(API_PREFIX+API_VERSION+"/app-serve-apps", authMiddleware.Handle(http.HandlerFunc(appServeAppHandler.CreateAppServeApp))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/app-serve-apps", authMiddleware.Handle(http.HandlerFunc(appServeAppHandler.GetAppServeApps))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/app-serve-apps/{appId}", authMiddleware.Handle(http.HandlerFunc(appServeAppHandler.GetAppServeApp))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/app-serve-apps/{appId}", authMiddleware.Handle(http.HandlerFunc(appServeAppHandler.DeleteAppServeApp))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/app-serve-apps/{appId}", authMiddleware.Handle(http.HandlerFunc(appServeAppHandler.UpdateAppServeApp))).Methods(http.MethodPut) + r.Handle(API_PREFIX+API_VERSION+"/app-serve-apps/{appId}/status", authMiddleware.Handle(http.HandlerFunc(appServeAppHandler.UpdateAppServeAppStatus))).Methods(http.MethodPatch) + r.Handle(API_PREFIX+API_VERSION+"/app-serve-apps/{appId}/endpoint", authMiddleware.Handle(http.HandlerFunc(appServeAppHandler.UpdateAppServeAppEndpoint))).Methods(http.MethodPatch) cloudAccountHandler := delivery.NewCloudAccountHandler(usecase.NewCloudAccountUsecase(repoFactory, argoClient)) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/cloud-accounts", authMiddleware(http.HandlerFunc(cloudAccountHandler.GetCloudAccounts), kc)).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/cloud-accounts", authMiddleware(http.HandlerFunc(cloudAccountHandler.CreateCloudAccount), kc)).Methods(http.MethodPost) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/cloud-accounts/name/{name}/existence", authMiddleware(http.HandlerFunc(cloudAccountHandler.CheckCloudAccountName), kc)).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/cloud-accounts/{cloudAccountId}", authMiddleware(http.HandlerFunc(cloudAccountHandler.GetCloudAccount), kc)).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/cloud-accounts/{cloudAccountId}", authMiddleware(http.HandlerFunc(cloudAccountHandler.UpdateCloudAccount), kc)).Methods(http.MethodPut) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/cloud-accounts/{cloudAccountId}", authMiddleware(http.HandlerFunc(cloudAccountHandler.DeleteCloudAccount), kc)).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/cloud-accounts", authMiddleware.Handle(http.HandlerFunc(cloudAccountHandler.GetCloudAccounts))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/cloud-accounts", authMiddleware.Handle(http.HandlerFunc(cloudAccountHandler.CreateCloudAccount))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/cloud-accounts/name/{name}/existence", authMiddleware.Handle(http.HandlerFunc(cloudAccountHandler.CheckCloudAccountName))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/cloud-accounts/{cloudAccountId}", authMiddleware.Handle(http.HandlerFunc(cloudAccountHandler.GetCloudAccount))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/cloud-accounts/{cloudAccountId}", authMiddleware.Handle(http.HandlerFunc(cloudAccountHandler.UpdateCloudAccount))).Methods(http.MethodPut) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/cloud-accounts/{cloudAccountId}", authMiddleware.Handle(http.HandlerFunc(cloudAccountHandler.DeleteCloudAccount))).Methods(http.MethodDelete) stackTemplateHandler := delivery.NewStackTemplateHandler(usecase.NewStackTemplateUsecase(repoFactory)) - r.Handle(API_PREFIX+API_VERSION+"/stack-templates", authMiddleware(http.HandlerFunc(stackTemplateHandler.GetStackTemplates), kc)).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/stack-templates", authMiddleware(http.HandlerFunc(stackTemplateHandler.CreateStackTemplate), kc)).Methods(http.MethodPost) - r.Handle(API_PREFIX+API_VERSION+"/stack-templates/{stackTemplateId}", authMiddleware(http.HandlerFunc(stackTemplateHandler.GetStackTemplate), kc)).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/stack-templates/{stackTemplateId}", authMiddleware(http.HandlerFunc(stackTemplateHandler.UpdateStackTemplate), kc)).Methods(http.MethodPut) - r.Handle(API_PREFIX+API_VERSION+"/stack-templates/{stackTemplateId}", authMiddleware(http.HandlerFunc(stackTemplateHandler.DeleteStackTemplate), kc)).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/stack-templates", authMiddleware.Handle(http.HandlerFunc(stackTemplateHandler.GetStackTemplates))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/stack-templates", authMiddleware.Handle(http.HandlerFunc(stackTemplateHandler.CreateStackTemplate))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/stack-templates/{stackTemplateId}", authMiddleware.Handle(http.HandlerFunc(stackTemplateHandler.GetStackTemplate))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/stack-templates/{stackTemplateId}", authMiddleware.Handle(http.HandlerFunc(stackTemplateHandler.UpdateStackTemplate))).Methods(http.MethodPut) + r.Handle(API_PREFIX+API_VERSION+"/stack-templates/{stackTemplateId}", authMiddleware.Handle(http.HandlerFunc(stackTemplateHandler.DeleteStackTemplate))).Methods(http.MethodDelete) stackHandler := delivery.NewStackHandler(usecase.NewStackUsecase(repoFactory, argoClient)) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks", authMiddleware(http.HandlerFunc(stackHandler.GetStacks), kc)).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks", authMiddleware(http.HandlerFunc(stackHandler.CreateStack), kc)).Methods(http.MethodPost) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/name/{name}/existence", authMiddleware(http.HandlerFunc(stackHandler.CheckStackName), kc)).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/{stackId}", authMiddleware(http.HandlerFunc(stackHandler.GetStack), kc)).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/{stackId}", authMiddleware(http.HandlerFunc(stackHandler.UpdateStack), kc)).Methods(http.MethodPut) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/{stackId}", authMiddleware(http.HandlerFunc(stackHandler.DeleteStack), kc)).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks", authMiddleware.Handle(http.HandlerFunc(stackHandler.GetStacks))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks", authMiddleware.Handle(http.HandlerFunc(stackHandler.CreateStack))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/name/{name}/existence", authMiddleware.Handle(http.HandlerFunc(stackHandler.CheckStackName))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/{stackId}", authMiddleware.Handle(http.HandlerFunc(stackHandler.GetStack))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/{stackId}", authMiddleware.Handle(http.HandlerFunc(stackHandler.UpdateStack))).Methods(http.MethodPut) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/{stackId}", authMiddleware.Handle(http.HandlerFunc(stackHandler.DeleteStack))).Methods(http.MethodDelete) dashboardHandler := delivery.NewDashboardHandler(usecase.NewDashboardUsecase(repoFactory)) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/dashboard/charts", authMiddleware(http.HandlerFunc(dashboardHandler.GetCharts), kc)).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/dashboard/charts", authMiddleware.Handle(http.HandlerFunc(dashboardHandler.GetCharts))).Methods(http.MethodGet) // assets r.PathPrefix("/api/").HandlerFunc(http.NotFound) @@ -140,156 +143,12 @@ func SetupRouter(db *gorm.DB, argoClient argowf.ArgoClient, asset http.Handler, credentials := handlers.AllowCredentials() headersOk := handlers.AllowedHeaders([]string{"content-type", "Authorization", "Authorization-Type"}) - originsOk := handlers.AllowedOrigins([]string{"http://localhost:3000"}) + originsOk := handlers.AllowedOrigins([]string{"http://localhost"}) methodsOk := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS"}) return handlers.CORS(credentials, headersOk, originsOk, methodsOk)(r) } -//func authMiddleware(next http.Handler, kc keycloak.IKeycloak) http.Handler { -// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { -// // Possible values : "basic", "keycloak" -// authType := r.Header.Get("Authorization-Type") -// -// switch authType { -// case "basic": -// tokenString := r.Header.Get("Authorization") -// if len(tokenString) == 0 { -// w.WriteHeader(http.StatusUnauthorized) -// if _, err := w.Write([]byte("Missing Authorization Header")); err != nil { -// log.Error(err) -// } -// -// return -// } -// tokenString = strings.Replace(tokenString, "Bearer ", "", 1) -// token, err := helper.VerifyToken(tokenString) -// if err != nil { -// w.WriteHeader(http.StatusUnauthorized) -// if _, err := w.Write([]byte("Error verifying JWT token: " + err.Error())); err != nil { -// log.Error(err) -// } -// return -// } -// -// accountId := token.Claims.(jwt.MapClaims)["AccountId"] -// organizationId := token.Claims.(jwt.MapClaims)["OrganizationId"] -// id := token.Claims.(jwt.MapClaims)["ID"] -// -// log.Debug("[authMiddleware] accountId : ", accountId) -// log.Debug("[authMiddleware] Id : ", id) -// log.Debug("[authMiddleware] organizationId : ", organizationId) -// -// r.Header.Set("OrganizationId", fmt.Sprint(organizationId)) -// r.Header.Set("AccountId", fmt.Sprint(accountId)) -// r.Header.Set("ID", fmt.Sprint(id)) -// -// next.ServeHTTP(w, r) -// return -// -// case "keycloak": -// default: -// auth := strings.TrimSpace(r.Header.Get("Authorization")) -// if auth == "" { -// w.WriteHeader(http.StatusUnauthorized) -// return -// } -// parts := strings.SplitN(auth, " ", 3) -// if len(parts) < 2 || strings.ToLower(parts[0]) != "bearer" { -// w.WriteHeader(http.StatusUnauthorized) -// return -// } -// -// token := parts[1] -// -// // Empty bearer tokens aren't valid -// if len(token) == 0 { -// // The space before the token case -// if len(parts) == 3 { -// log.Warn("the provided Authorization header contains extra space before the bearer token, and is ignored") -// } -// w.WriteHeader(http.StatusUnauthorized) -// return -// } -// -// parsedToken, _, err := new(jwtWithouKey.Parser).ParseUnverified(token, jwtWithouKey.MapClaims{}) -// -// if err != nil { -// log.Error("failed to parse access token: ", err) -// w.WriteHeader(http.StatusUnauthorized) -// if _, err := w.Write([]byte(err.Error())); err != nil { -// log.Error(err) -// } -// return -// } -// organization, ok := parsedToken.Claims.(jwtWithouKey.MapClaims)["organization"].(string) -// if !ok { -// log.Error("failed to parse access token: ", err) -// w.WriteHeader(http.StatusUnauthorized) -// if _, err := w.Write([]byte(err.Error())); err != nil { -// log.Error(err) -// } -// return -// } -// if err := kc.VerifyAccessToken(token, organization); err != nil { -// log.Error("failed to verify access token: ", err) -// w.WriteHeader(http.StatusUnauthorized) -// if _, err := w.Write([]byte("failed to verify access token: " + err.Error())); err != nil { -// log.Error(err) -// } -// return -// } -// jwtToken, mapClaims, err := kc.ParseAccessToken(token, organization) -// if err != nil { -// log.Error("failed to parse access token: ", err) -// w.WriteHeader(http.StatusUnauthorized) -// if _, err := w.Write([]byte(err.Error())); err != nil { -// log.Error(err) -// } -// return -// } -// -// if jwtToken == nil || mapClaims == nil || mapClaims.Valid() != nil { -// w.WriteHeader(http.StatusUnauthorized) -// if _, err := w.Write([]byte("Error message TODO")); err != nil { -// log.Error(err) -// } -// return -// } -// roleProjectMapping := make(map[string]string) -// for _, role := range jwtToken.Claims.(jwt.MapClaims)["tks-role"].([]interface{}) { -// slice := strings.Split(role.(string), "@") -// if len(slice) != 2 { -// log.Error("invalid role format: ", role) -// w.WriteHeader(http.StatusUnauthorized) -// if _, err := w.Write([]byte(fmt.Sprintf("invalid role format: %s", role))); err != nil { -// log.Error(err) -// } -// return -// } -// // key is projectName and value is roleName -// roleProjectMapping[slice[1]] = slice[0] -// } -// userId, err := uuid.Parse(jwtToken.Claims.(jwt.MapClaims)["sub"].(string)) -// if err != nil { -// userId = uuid.Nil -// } -// -// userInfo := &user.DefaultInfo{ -// OrganizationId: jwtToken.Claims.(jwt.MapClaims)["organization"].(string), -// UserId: userId, -// RoleProjectMapping: roleProjectMapping, -// } -// -// r = r.WithContext(request.WithToken(r.Context(), token)) -// r = r.WithContext(request.WithUser(r.Context(), userInfo)) -// -// next.ServeHTTP(w, r) -// } -// -// }) -//} - func loggingMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { log.Info(fmt.Sprintf("***** START [%s %s] ***** ", r.Method, r.RequestURI)) diff --git a/internal/usecase/auth.go b/internal/usecase/auth.go index b573247b..a1b9a30f 100644 --- a/internal/usecase/auth.go +++ b/internal/usecase/auth.go @@ -37,9 +37,13 @@ func (r *AuthUsecase) Login(accountId string, password string, organizationId st if !helper.CheckPasswordHash(user.Password, password) { return domain.User{}, httpErrors.NewUnauthorizedError(fmt.Errorf("")) } - + var accountToken *domain.User // Authentication with Keycloak - accountToken, err := r.kc.Login(accountId, password, organizationId) + if organizationId == "master" && accountId == "admin" { + accountToken, err = r.kc.LoginAdmin(accountId, password) + } else { + accountToken, err = r.kc.Login(accountId, password, organizationId) + } if err != nil { //TODO: implement not found handling return domain.User{}, httpErrors.NewUnauthorizedError(err) diff --git a/internal/usecase/stack.go b/internal/usecase/stack.go index 4ecb9cce..4d4188ba 100644 --- a/internal/usecase/stack.go +++ b/internal/usecase/stack.go @@ -3,9 +3,9 @@ package usecase import ( "context" "fmt" + "github.com/openinfradev/tks-api/internal/middleware/auth/request" "time" - "github.com/openinfradev/tks-api/internal/auth/request" "github.com/openinfradev/tks-api/internal/repository" argowf "github.com/openinfradev/tks-api/pkg/argo-client" "github.com/openinfradev/tks-api/pkg/domain" From fab7c4be4d2f4b48b55543b79298b42501b026a5 Mon Sep 17 00:00:00 2001 From: donggyu Date: Tue, 18 Apr 2023 15:08:56 +0900 Subject: [PATCH 10/11] lint fix --- internal/delivery/http/cloud-account.go | 2 +- internal/delivery/http/organization.go | 5 ++-- internal/delivery/http/stack.go | 2 +- internal/delivery/http/user.go | 3 +-- internal/keycloak/keycloak.go | 28 ++++++++++++++++++++- internal/route/route_test.go | 33 ------------------------- 6 files changed, 32 insertions(+), 41 deletions(-) delete mode 100644 internal/route/route_test.go diff --git a/internal/delivery/http/cloud-account.go b/internal/delivery/http/cloud-account.go index bfb679f8..80399224 100644 --- a/internal/delivery/http/cloud-account.go +++ b/internal/delivery/http/cloud-account.go @@ -2,11 +2,11 @@ package http import ( "fmt" - "github.com/openinfradev/tks-api/internal/middleware/auth/request" "net/http" "github.com/google/uuid" "github.com/gorilla/mux" + "github.com/openinfradev/tks-api/internal/middleware/auth/request" "github.com/openinfradev/tks-api/internal/usecase" "github.com/openinfradev/tks-api/pkg/domain" "github.com/openinfradev/tks-api/pkg/httpErrors" diff --git a/internal/delivery/http/organization.go b/internal/delivery/http/organization.go index feb0dfde..95d71542 100644 --- a/internal/delivery/http/organization.go +++ b/internal/delivery/http/organization.go @@ -2,14 +2,13 @@ package http import ( "fmt" - "github.com/openinfradev/tks-api/internal/middleware/auth/request" "net/http" - "github.com/openinfradev/tks-api/pkg/httpErrors" - "github.com/gorilla/mux" + "github.com/openinfradev/tks-api/internal/middleware/auth/request" "github.com/openinfradev/tks-api/internal/usecase" "github.com/openinfradev/tks-api/pkg/domain" + "github.com/openinfradev/tks-api/pkg/httpErrors" "github.com/openinfradev/tks-api/pkg/log" ) diff --git a/internal/delivery/http/stack.go b/internal/delivery/http/stack.go index d3669890..3662d1de 100644 --- a/internal/delivery/http/stack.go +++ b/internal/delivery/http/stack.go @@ -153,7 +153,7 @@ func (h *StackHandler) GetStack(w http.ResponseWriter, r *http.Request) { // @Router /organizations/{organizationId}/stacks/{stackId} [put] // @Security JWT func (h *StackHandler) UpdateStack(w http.ResponseWriter, r *http.Request) { - ErrorJSON(w, httpErrors.NewInternalServerError(fmt.Errorf("Need implementaion"))) + ErrorJSON(w, httpErrors.NewInternalServerError(fmt.Errorf("need implementation"))) } // DeleteStack godoc diff --git a/internal/delivery/http/user.go b/internal/delivery/http/user.go index 7a351d35..6dd6b189 100644 --- a/internal/delivery/http/user.go +++ b/internal/delivery/http/user.go @@ -4,12 +4,11 @@ import ( "fmt" "net/http" - "github.com/openinfradev/tks-api/pkg/log" - "github.com/gorilla/mux" "github.com/openinfradev/tks-api/internal/usecase" "github.com/openinfradev/tks-api/pkg/domain" "github.com/openinfradev/tks-api/pkg/httpErrors" + "github.com/openinfradev/tks-api/pkg/log" ) type IUserHandler interface { diff --git a/internal/keycloak/keycloak.go b/internal/keycloak/keycloak.go index 9c4525e8..db139a47 100644 --- a/internal/keycloak/keycloak.go +++ b/internal/keycloak/keycloak.go @@ -254,6 +254,9 @@ func (k *Keycloak) CreateRealm(organizationName string) (string, error) { func (k *Keycloak) GetRealm(organizationName string) (*domain.Organization, error) { ctx := context.Background() token, err := k.loginAdmin(ctx) + if err != nil { + return nil, err + } realm, err := k.client.GetRealm(ctx, token.AccessToken, organizationName) if err != nil { return nil, err @@ -264,6 +267,9 @@ func (k *Keycloak) GetRealm(organizationName string) (*domain.Organization, erro func (k *Keycloak) GetRealms() ([]*domain.Organization, error) { ctx := context.Background() token, err := k.loginAdmin(ctx) + if err != nil { + return nil, err + } realms, err := k.client.GetRealms(ctx, token.AccessToken) if err != nil { return nil, err @@ -278,6 +284,9 @@ func (k *Keycloak) GetRealms() ([]*domain.Organization, error) { func (k *Keycloak) UpdateRealm(organizationName string, organizationConfig domain.Organization) error { ctx := context.Background() token, err := k.loginAdmin(ctx) + if err != nil { + return err + } realm := k.reflectRealmRepresentation(organizationConfig) err = k.client.UpdateRealm(ctx, token.AccessToken, *realm) if err != nil { @@ -289,6 +298,9 @@ func (k *Keycloak) UpdateRealm(organizationName string, organizationConfig domai func (k *Keycloak) DeleteRealm(organizationName string) error { ctx := context.Background() token, err := k.loginAdmin(ctx) + if err != nil { + return err + } err = k.client.DeleteRealm(ctx, token.AccessToken, organizationName) if err != nil { return err @@ -299,6 +311,9 @@ func (k *Keycloak) DeleteRealm(organizationName string) error { func (k *Keycloak) CreateUser(organizationName string, user *gocloak.User) error { ctx := context.Background() token, err := k.loginAdmin(ctx) + if err != nil { + return err + } user.Enabled = gocloak.BoolP(true) _, err = k.client.CreateUser(ctx, token.AccessToken, organizationName, *user) if err != nil { @@ -310,6 +325,9 @@ func (k *Keycloak) CreateUser(organizationName string, user *gocloak.User) error func (k *Keycloak) GetUser(organizationName string, accountId string) (*gocloak.User, error) { ctx := context.Background() token, err := k.loginAdmin(ctx) + if err != nil { + return nil, err + } //TODO: this is rely on the fact that username is the same as userAccountId and unique users, err := k.client.GetUsers(ctx, token.AccessToken, organizationName, gocloak.GetUsersParams{ @@ -327,6 +345,9 @@ func (k *Keycloak) GetUser(organizationName string, accountId string) (*gocloak. func (k *Keycloak) GetUsers(organizationName string) ([]*gocloak.User, error) { ctx := context.Background() token, err := k.loginAdmin(ctx) + if err != nil { + return nil, err + } //TODO: this is rely on the fact that username is the same as userAccountId and unique users, err := k.client.GetUsers(ctx, token.AccessToken, organizationName, gocloak.GetUsersParams{}) if err != nil { @@ -342,6 +363,9 @@ func (k *Keycloak) GetUsers(organizationName string) ([]*gocloak.User, error) { func (k *Keycloak) UpdateUser(organizationName string, user *gocloak.User) error { ctx := context.Background() token, err := k.loginAdmin(ctx) + if err != nil { + return err + } user.Enabled = gocloak.BoolP(true) err = k.client.UpdateUser(ctx, token.AccessToken, organizationName, *user) if err != nil { @@ -353,7 +377,9 @@ func (k *Keycloak) UpdateUser(organizationName string, user *gocloak.User) error func (k *Keycloak) DeleteUser(organizationName string, userAccountId string) error { ctx := context.Background() token, err := k.loginAdmin(ctx) - + if err != nil { + return err + } u, err := k.GetUser(organizationName, userAccountId) if err != nil { log.Errorf("error is :%s(%T)", err.Error(), err) diff --git a/internal/route/route_test.go b/internal/route/route_test.go deleted file mode 100644 index 6fcdedbd..00000000 --- a/internal/route/route_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package route - -import ( - "net/http" - "net/url" - "testing" -) - -func TestAuthMiddleware(t *testing.T) { - type args struct { - next *http.Request - } - tests := []struct { - name string - args args - }{ - { - name: "test case: AuthMiddleware", - args: args{ - next: &http.Request{ - URL: &url.URL{ - Path: "/api/v1/organizations", - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - AuthMiddleware(tt.args.next) - }) - } -} From 549df092ef1c8d1cda20a18f9b71357a95217f18 Mon Sep 17 00:00:00 2001 From: donggyu Date: Tue, 18 Apr 2023 15:19:21 +0900 Subject: [PATCH 11/11] trivial fix --- internal/route/route.go | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/internal/route/route.go b/internal/route/route.go index 3a9bdfab..f56d4a1d 100644 --- a/internal/route/route.go +++ b/internal/route/route.go @@ -3,25 +3,24 @@ package route import ( "bytes" "fmt" + "io" + "io/ioutil" + "net/http" + "github.com/gorilla/handlers" "github.com/gorilla/mux" + delivery "github.com/openinfradev/tks-api/internal/delivery/http" "github.com/openinfradev/tks-api/internal/keycloak" "github.com/openinfradev/tks-api/internal/middleware/auth" "github.com/openinfradev/tks-api/internal/middleware/auth/authenticator" authKeycloak "github.com/openinfradev/tks-api/internal/middleware/auth/authenticator/keycloak" "github.com/openinfradev/tks-api/internal/middleware/auth/authorizer" - "gorm.io/gorm" - "io" - "io/ioutil" - "net/http" - - "github.com/swaggo/http-swagger" - - delivery "github.com/openinfradev/tks-api/internal/delivery/http" "github.com/openinfradev/tks-api/internal/repository" "github.com/openinfradev/tks-api/internal/usecase" argowf "github.com/openinfradev/tks-api/pkg/argo-client" "github.com/openinfradev/tks-api/pkg/log" + "github.com/swaggo/http-swagger" + "gorm.io/gorm" ) const ( @@ -58,12 +57,9 @@ func SetupRouter(db *gorm.DB, argoClient argowf.ArgoClient, asset http.Handler, authHandler := delivery.NewAuthHandler(usecase.NewAuthUsecase(repoFactory, kc)) r.Use(loggingMiddleware) - //r.Use(authMiddleware.Handle) // [TODO] Transaction //r.Use(transactionMiddleware(db)) - //r.PathPrefix("/swagger").Handler(httpSwagger.WrapHandler) - r.HandleFunc(API_PREFIX+API_VERSION+"/auth/login", authHandler.Login).Methods(http.MethodPost) r.HandleFunc(API_PREFIX+API_VERSION+"/auth/logout", authHandler.Logout).Methods(http.MethodPost) r.HandleFunc(API_PREFIX+API_VERSION+"/auth/find-id", authHandler.FindId).Methods(http.MethodPost) @@ -143,7 +139,7 @@ func SetupRouter(db *gorm.DB, argoClient argowf.ArgoClient, asset http.Handler, credentials := handlers.AllowCredentials() headersOk := handlers.AllowedHeaders([]string{"content-type", "Authorization", "Authorization-Type"}) - originsOk := handlers.AllowedOrigins([]string{"http://localhost"}) + originsOk := handlers.AllowedOrigins([]string{"http://localhost:3000"}) methodsOk := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS"}) return handlers.CORS(credentials, headersOk, originsOk, methodsOk)(r)