diff --git a/api/swagger/docs.go b/api/swagger/docs.go index c7022264..24779acb 100644 --- a/api/swagger/docs.go +++ b/api/swagger/docs.go @@ -1,4 +1,5 @@ -// Code generated by swaggo/swag. DO NOT EDIT +// Package swagger GENERATED BY SWAG; DO NOT EDIT +// This file was generated by swaggo/swag package swagger import "github.com/swaggo/swag" @@ -504,7 +505,7 @@ const docTemplate = `{ "JWT": [] } ], - "description": "Get CloudSetting", + "description": "Get CloudSettings", "consumes": [ "application/json" ], @@ -514,12 +515,15 @@ const docTemplate = `{ "tags": [ "CloudSettings" ], - "summary": "Get CloudSetting", + "summary": "Get CloudSettings", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.CloudSetting" + "type": "array", + "items": { + "$ref": "#/definitions/domain.CloudSetting" + } } } } @@ -569,7 +573,7 @@ const docTemplate = `{ "JWT": [] } ], - "description": "Get cloudSetting by cloudSettingId", + "description": "Get CloudSetting", "consumes": [ "application/json" ], @@ -579,7 +583,7 @@ const docTemplate = `{ "tags": [ "CloudSettings" ], - "summary": "Get cloudSetting by cloudSettingId", + "summary": "Get CloudSetting", "responses": { "200": { "description": "OK", @@ -589,6 +593,43 @@ const docTemplate = `{ } } }, + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update CloudSetting", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "CloudSettings" + ], + "summary": "Update CloudSetting", + "parameters": [ + { + "description": "Update cloud setting request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/domain.UpdateCloudSettingRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object" + } + } + } + }, "delete": { "security": [ { @@ -1066,6 +1107,9 @@ const docTemplate = `{ "domain.CloudSetting": { "type": "object", "properties": { + "clusters": { + "type": "integer" + }, "createdAt": { "type": "string" }, @@ -1092,6 +1136,9 @@ const docTemplate = `{ }, "updatedAt": { "type": "string" + }, + "updator": { + "type": "string" } } }, @@ -1178,13 +1225,12 @@ const docTemplate = `{ }, "domain.CreateCloudSettingRequest": { "type": "object", + "required": [ + "name", + "secretKey", + "secretKeyId" + ], "properties": { - "accessKey": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, "description": { "type": "string" }, @@ -1194,11 +1240,16 @@ const docTemplate = `{ "secretKey": { "type": "string" }, - "type": { + "secretKeyId": { "type": "string" }, - "updatedAt": { - "type": "string" + "type": { + "type": "string", + "enum": [ + "AWS", + "AZZURE", + "GCP" + ] } } }, @@ -1357,6 +1408,20 @@ const docTemplate = `{ } } }, + "domain.UpdateCloudSettingRequest": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "secretKey": { + "type": "string" + }, + "secretKeyId": { + "type": "string" + } + } + }, "domain.User": { "type": "object", "properties": { diff --git a/api/swagger/swagger.json b/api/swagger/swagger.json index 5dd586d7..3dde3100 100644 --- a/api/swagger/swagger.json +++ b/api/swagger/swagger.json @@ -498,7 +498,7 @@ "JWT": [] } ], - "description": "Get CloudSetting", + "description": "Get CloudSettings", "consumes": [ "application/json" ], @@ -508,12 +508,15 @@ "tags": [ "CloudSettings" ], - "summary": "Get CloudSetting", + "summary": "Get CloudSettings", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.CloudSetting" + "type": "array", + "items": { + "$ref": "#/definitions/domain.CloudSetting" + } } } } @@ -563,7 +566,7 @@ "JWT": [] } ], - "description": "Get cloudSetting by cloudSettingId", + "description": "Get CloudSetting", "consumes": [ "application/json" ], @@ -573,7 +576,7 @@ "tags": [ "CloudSettings" ], - "summary": "Get cloudSetting by cloudSettingId", + "summary": "Get CloudSetting", "responses": { "200": { "description": "OK", @@ -583,6 +586,43 @@ } } }, + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update CloudSetting", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "CloudSettings" + ], + "summary": "Update CloudSetting", + "parameters": [ + { + "description": "Update cloud setting request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/domain.UpdateCloudSettingRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object" + } + } + } + }, "delete": { "security": [ { @@ -1060,6 +1100,9 @@ "domain.CloudSetting": { "type": "object", "properties": { + "clusters": { + "type": "integer" + }, "createdAt": { "type": "string" }, @@ -1086,6 +1129,9 @@ }, "updatedAt": { "type": "string" + }, + "updator": { + "type": "string" } } }, @@ -1172,13 +1218,12 @@ }, "domain.CreateCloudSettingRequest": { "type": "object", + "required": [ + "name", + "secretKey", + "secretKeyId" + ], "properties": { - "accessKey": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, "description": { "type": "string" }, @@ -1188,11 +1233,16 @@ "secretKey": { "type": "string" }, - "type": { + "secretKeyId": { "type": "string" }, - "updatedAt": { - "type": "string" + "type": { + "type": "string", + "enum": [ + "AWS", + "AZZURE", + "GCP" + ] } } }, @@ -1351,6 +1401,20 @@ } } }, + "domain.UpdateCloudSettingRequest": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "secretKey": { + "type": "string" + }, + "secretKeyId": { + "type": "string" + } + } + }, "domain.User": { "type": "object", "properties": { diff --git a/api/swagger/swagger.yaml b/api/swagger/swagger.yaml index e69ef630..9c985cf4 100644 --- a/api/swagger/swagger.yaml +++ b/api/swagger/swagger.yaml @@ -61,6 +61,8 @@ definitions: type: object domain.CloudSetting: properties: + clusters: + type: integer createdAt: type: string creator: @@ -79,6 +81,8 @@ definitions: type: string updatedAt: type: string + updator: + type: string type: object domain.Cluster: properties: @@ -135,20 +139,24 @@ definitions: type: object domain.CreateCloudSettingRequest: properties: - accessKey: - type: string - createdAt: - type: string description: type: string name: type: string secretKey: type: string - type: - type: string - updatedAt: + secretKeyId: type: string + type: + enum: + - AWS + - AZZURE + - GCP + type: string + required: + - name + - secretKey + - secretKeyId type: object domain.CreateClusterRequest: properties: @@ -251,6 +259,15 @@ definitions: metadata: type: string type: object + domain.UpdateCloudSettingRequest: + properties: + description: + type: string + secretKey: + type: string + secretKeyId: + type: string + type: object domain.User: properties: accountId: @@ -595,17 +612,19 @@ paths: get: consumes: - application/json - description: Get CloudSetting + description: Get CloudSettings produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/domain.CloudSetting' + items: + $ref: '#/definitions/domain.CloudSetting' + type: array security: - JWT: [] - summary: Get CloudSetting + summary: Get CloudSettings tags: - CloudSettings post: @@ -657,7 +676,7 @@ paths: get: consumes: - application/json - description: Get cloudSetting by cloudSettingId + description: Get CloudSetting produces: - application/json responses: @@ -667,7 +686,30 @@ paths: $ref: '#/definitions/domain.CloudSetting' security: - JWT: [] - summary: Get cloudSetting by cloudSettingId + summary: Get CloudSetting + tags: + - CloudSettings + put: + consumes: + - application/json + description: Update CloudSetting + parameters: + - description: Update cloud setting request + in: body + name: body + required: true + schema: + $ref: '#/definitions/domain.UpdateCloudSettingRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + type: object + security: + - JWT: [] + summary: Update CloudSetting tags: - CloudSettings /clusters: diff --git a/go.mod b/go.mod index a771328d..49d88118 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,8 @@ go 1.18 require ( github.com/Nerzal/gocloak/v13 v13.1.0 github.com/dgrijalva/jwt-go v3.2.0+incompatible - github.com/go-playground/validator/v10 v10.12.0 + github.com/go-playground/validator v9.31.0+incompatible + github.com/gofrs/uuid v4.0.0+incompatible github.com/golang-jwt/jwt v3.2.2+incompatible github.com/golang-jwt/jwt/v4 v4.5.0 github.com/golang/mock v1.6.0 @@ -40,7 +41,6 @@ require ( github.com/go-openapi/swag v0.19.15 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator v9.31.0+incompatible // indirect github.com/go-resty/resty/v2 v2.7.0 // indirect github.com/go-sql-driver/mysql v1.7.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect @@ -88,6 +88,7 @@ require ( golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.28.1 // indirect + gopkg.in/go-playground/assert.v1 v1.2.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.66.4 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 12646406..79652510 100644 --- a/go.sum +++ b/go.sum @@ -123,15 +123,12 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator v9.31.0+incompatible h1:UA72EPEogEnq76ehGdEDp4Mit+3FDh548oRqwVgNsHA= github.com/go-playground/validator v9.31.0+incompatible/go.mod h1:yrEkQXlcI+PugkyDjY2bRrL/UBU4f3rvrgkN3V8JEig= -github.com/go-playground/validator/v10 v10.12.0 h1:E4gtWgxWxp8YSxExrQFv5BpCahla0PVF2oTTEYaWQGI= -github.com/go-playground/validator/v10 v10.12.0/go.mod h1:hCAPuzYvKdP33pxWa+2+6AIKXEKqjIUyqsNCtbsSJrA= github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= @@ -827,6 +824,8 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8X gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= +gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= diff --git a/internal/auth/user/user.go b/internal/auth/user/user.go index a90bb6f6..a394d7b4 100644 --- a/internal/auth/user/user.go +++ b/internal/auth/user/user.go @@ -1,20 +1,28 @@ package user +import "github.com/google/uuid" + // Info describes a user that has been authenticated to the system. type Info interface { - GetOrganization() string + GetUserId() uuid.UUID + GetOrganizationId() string GetRoleProjectMapping() map[string]string } // DefaultInfo provides a simple user information exchange object // for components that implement the UserInfo interface. type DefaultInfo struct { - Organization string + UserId uuid.UUID + OrganizationId string RoleProjectMapping map[string]string } -func (i *DefaultInfo) GetOrganization() string { - return i.Organization +func (i *DefaultInfo) GetUserId() uuid.UUID { + return i.UserId +} + +func (i *DefaultInfo) GetOrganizationId() string { + return i.OrganizationId } // GetRoleGroupMapping key is project name, value is role name diff --git a/internal/delivery/http/cloud-setting.go b/internal/delivery/http/cloud-setting.go index 7e796981..0d45626b 100644 --- a/internal/delivery/http/cloud-setting.go +++ b/internal/delivery/http/cloud-setting.go @@ -1,13 +1,12 @@ package http import ( - "encoding/json" "fmt" - "io" "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" @@ -36,22 +35,20 @@ func NewCloudSettingHandler(h usecase.ICloudSettingUsecase) *CloudSettingHandler // @Router /cloud-settings [post] // @Security JWT func (h *CloudSettingHandler) CreateCloudSetting(w http.ResponseWriter, r *http.Request) { - organizationId, userId, _ := GetSession(r) - - input := domain.CreateCloudSettingRequest{} - body, err := io.ReadAll(r.Body) - if err != nil { - log.Error(err) + user, ok := request.UserFrom(r.Context()) + if !ok { + ErrorJSON(w, httpErrors.NewBadRequestError(fmt.Errorf("Invalid token"))) return } - err = json.Unmarshal(body, &input) + + input := domain.CreateCloudSettingRequest{} + err := UnmarshalRequestInput(r, &input) if err != nil { ErrorJSON(w, httpErrors.NewBadRequestError(err)) return } - resource := "" - cloudSettingId, err := h.usecase.Create(organizationId, input, resource, userId) + cloudSettingId, err := h.usecase.Create(user.GetOrganizationId(), input, user.GetUserId()) if err != nil { ErrorJSON(w, err) return @@ -65,46 +62,49 @@ func (h *CloudSettingHandler) CreateCloudSetting(w http.ResponseWriter, r *http. out.CloudSettingId = cloudSettingId.String() ResponseJSON(w, http.StatusOK, out) - } // GetCloudSetting godoc // @Tags CloudSettings -// @Summary Get CloudSetting -// @Description Get CloudSetting +// @Summary Get CloudSettings +// @Description Get CloudSettings // @Accept json // @Produce json -// @Success 200 {object} domain.CloudSetting +// @Success 200 {object} []domain.CloudSetting // @Router /cloud-settings [get] // @Security JWT -func (h *CloudSettingHandler) GetCloudSetting(w http.ResponseWriter, r *http.Request) { - organizationId, _, _ := GetSession(r) +func (h *CloudSettingHandler) GetCloudSettings(w http.ResponseWriter, r *http.Request) { + user, ok := request.UserFrom(r.Context()) + if !ok { + ErrorJSON(w, httpErrors.NewBadRequestError(fmt.Errorf("Invalid token"))) + return + } - cloudSetting, err := h.usecase.GetByOrganizationId(organizationId) + cloudSettings, err := h.usecase.Fetch(user.GetOrganizationId()) if err != nil { ErrorJSON(w, err) return } var out struct { - CloudSetting domain.CloudSetting `json:"cloudSetting"` + CloudSettings []domain.CloudSetting `json:"cloudSettings"` } - out.CloudSetting = cloudSetting + out.CloudSettings = cloudSettings ResponseJSON(w, http.StatusOK, out) } -// GetCloudSettingById godoc +// GetCloudSetting godoc // @Tags CloudSettings -// @Summary Get cloudSetting by cloudSettingId -// @Description Get cloudSetting by cloudSettingId +// @Summary Get CloudSetting +// @Description Get CloudSetting // @Accept json // @Produce json // @Success 200 {object} domain.CloudSetting // @Router /cloud-settings/{cloudSettingId} [get] // @Security JWT -func (h *CloudSettingHandler) GetCloudSettingById(w http.ResponseWriter, r *http.Request) { +func (h *CloudSettingHandler) GetCloudSetting(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) cloudSettingId, ok := vars["cloudSettingId"] if !ok { @@ -112,13 +112,13 @@ func (h *CloudSettingHandler) GetCloudSettingById(w http.ResponseWriter, r *http return } - parsedId, err := uuid.Parse(cloudSettingId) + parsedUuid, err := uuid.Parse(cloudSettingId) if err != nil { - ErrorJSON(w, httpErrors.NewBadRequestError(errors.WithMessage(err, "Failed to parse uuid"))) + ErrorJSON(w, httpErrors.NewBadRequestError(errors.Wrap(err, "Failed to parse uuid %s"))) return } - cloudSetting, err := h.usecase.Get(parsedId) + cloudSetting, err := h.usecase.Get(parsedUuid) if err != nil { ErrorJSON(w, err) return @@ -133,6 +133,52 @@ func (h *CloudSettingHandler) GetCloudSettingById(w http.ResponseWriter, r *http ResponseJSON(w, http.StatusOK, out) } +// UpdateCloudSetting godoc +// @Tags CloudSettings +// @Summary Update CloudSetting +// @Description Update CloudSetting +// @Accept json +// @Produce json +// @Param body body domain.UpdateCloudSettingRequest true "Update cloud setting request" +// @Success 200 {object} object +// @Router /cloud-settings/{cloudSettingId} [put] +// @Security JWT +func (h *CloudSettingHandler) UpdateCloudSetting(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + cloudSettingId, ok := vars["cloudSettingId"] + if !ok { + ErrorJSON(w, httpErrors.NewBadRequestError(fmt.Errorf("Invalid cloudSettingId"))) + return + } + + parsedUuid, err := uuid.Parse(cloudSettingId) + if err != nil { + ErrorJSON(w, httpErrors.NewBadRequestError(errors.Wrap(err, "Failed to parse uuid %s"))) + return + } + + user, ok := request.UserFrom(r.Context()) + if !ok { + ErrorJSON(w, httpErrors.NewBadRequestError(fmt.Errorf("Invalid token"))) + return + } + + input := domain.UpdateCloudSettingRequest{} + err = UnmarshalRequestInput(r, &input) + if err != nil { + ErrorJSON(w, httpErrors.NewBadRequestError(err)) + return + } + + err = h.usecase.Update(parsedUuid, input, user.GetUserId()) + if err != nil { + ErrorJSON(w, err) + return + } + + ResponseJSON(w, http.StatusOK, nil) +} + // DeleteCloudSetting godoc // @Tags CloudSettings // @Summary Delete CloudSetting @@ -153,7 +199,7 @@ func (h *CloudSettingHandler) DeleteCloudSetting(w http.ResponseWriter, r *http. parsedId, err := uuid.Parse(cloudSettingId) if err != nil { - ErrorJSON(w, httpErrors.NewBadRequestError(errors.WithMessage(err, "Failed to parse uuid"))) + ErrorJSON(w, httpErrors.NewBadRequestError(errors.Wrap(err, "Failed to parse uuid"))) return } diff --git a/internal/delivery/http/handler.go b/internal/delivery/http/handler.go index db1d5ac9..f7fa8989 100644 --- a/internal/delivery/http/handler.go +++ b/internal/delivery/http/handler.go @@ -7,7 +7,6 @@ import ( "net/http" "github.com/go-playground/validator" - "github.com/google/uuid" "github.com/openinfradev/tks-api/internal/helper" "github.com/openinfradev/tks-api/pkg/httpErrors" "github.com/openinfradev/tks-api/pkg/log" @@ -27,15 +26,6 @@ func ResponseJSON(w http.ResponseWriter, httpStatus int, data interface{}) { json.NewEncoder(w).Encode(out) } -func GetSession(r *http.Request) (organizationId string, userId uuid.UUID, accountId string) { - userId, err := uuid.Parse(r.Header.Get("ID")) - if err != nil { - userId = uuid.Nil - } - - return r.Header.Get("OrganizationId"), userId, r.Header.Get("AccountId") -} - func UnmarshalRequestInput(r *http.Request, in any) error { body, err := io.ReadAll(r.Body) if err != nil { @@ -50,7 +40,6 @@ func UnmarshalRequestInput(r *http.Request, in any) error { validate := validator.New() err = validate.Struct(in) if err != nil { - fmt.Println(err) return err } return nil diff --git a/internal/delivery/http/history.go b/internal/delivery/http/history.go index c8f9e463..8c0d2205 100644 --- a/internal/delivery/http/history.go +++ b/internal/delivery/http/history.go @@ -1,12 +1,9 @@ package http import ( - "fmt" "net/http" "github.com/openinfradev/tks-api/internal/usecase" - "github.com/openinfradev/tks-api/pkg/domain" - "github.com/openinfradev/tks-api/pkg/httpErrors" ) type HistoryHandler struct { @@ -29,28 +26,30 @@ func NewHistoryHandler(h usecase.IHistoryUsecase) *HistoryHandler { // @Router /histories [get] // @Security JWT func (h *HistoryHandler) GetHistories(w http.ResponseWriter, r *http.Request) { - var err error - - _, userId, _ := GetSession(r) - urlParams := r.URL.Query() - - userId = userId - urlParams = urlParams - - projectId := urlParams.Get("projectId") - if projectId == "" { - ErrorJSON(w, httpErrors.NewBadRequestError(fmt.Errorf("Invalid projectId"))) - return - } - - var out struct { - Histories []domain.History `json:"histories"` - } - out.Histories, err = h.usecase.Fetch() - if err != nil { - ErrorJSON(w, err) - return - } - - ResponseJSON(w, http.StatusOK, out) + /* + var err error + + _, userId, _ := GetSession(r) + urlParams := r.URL.Query() + + userId = userId + urlParams = urlParams + + projectId := urlParams.Get("projectId") + if projectId == "" { + ErrorJSON(w, httpErrors.NewBadRequestError(fmt.Errorf("Invalid projectId"))) + return + } + + var out struct { + Histories []domain.History `json:"histories"` + } + out.Histories, err = h.usecase.Fetch() + if err != nil { + ErrorJSON(w, err) + return + } + + ResponseJSON(w, http.StatusOK, out) + */ } diff --git a/internal/delivery/http/organization.go b/internal/delivery/http/organization.go index 3d674fdc..7b7d5dca 100644 --- a/internal/delivery/http/organization.go +++ b/internal/delivery/http/organization.go @@ -39,7 +39,6 @@ func NewOrganizationHandler(o usecase.IOrganizationUsecase, u usecase.IUserUseca // @Router /organizations [post] // @Security JWT func (h *OrganizationHandler) CreateOrganization(w http.ResponseWriter, r *http.Request) { - _, userId, _ := GetSession(r) input := domain.CreateOrganizationRequest{} @@ -47,7 +46,6 @@ func (h *OrganizationHandler) CreateOrganization(w http.ResponseWriter, r *http. if err != nil { ErrorJSON(w, httpErrors.NewBadRequestError(err)) return - } /* @@ -78,7 +76,7 @@ func (h *OrganizationHandler) CreateOrganization(w http.ResponseWriter, r *http. } organizationId, err := h.usecase.Create(domain.Organization{ Name: input.Name, - Creator: userId.String(), + Creator: "", Description: input.Description, }, token) if err != nil { diff --git a/internal/delivery/http/user.go b/internal/delivery/http/user.go index e1aabf97..b8073e4d 100644 --- a/internal/delivery/http/user.go +++ b/internal/delivery/http/user.go @@ -3,14 +3,15 @@ package http import ( "encoding/json" "fmt" + "io" + "net/http" + "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" "github.com/openinfradev/tks-api/pkg/log" - "io" - "net/http" ) type IUserHandler interface { @@ -53,7 +54,7 @@ func (u UserHandler) Create(w http.ResponseWriter, r *http.Request) { ctx := r.Context() user := input.ToUser() user.Organization = domain.Organization{ - ID: userInfo.GetOrganization(), + ID: userInfo.GetOrganizationId(), } user, err = u.usecase.Create(ctx, user) if err != nil { @@ -161,7 +162,7 @@ func (u UserHandler) Update(w http.ResponseWriter, r *http.Request) { ctx := r.Context() user := input.ToUser() user.Organization = domain.Organization{ - ID: userInfo.GetOrganization(), + ID: userInfo.GetOrganizationId(), } originUser, err := u.usecase.GetByAccountId(ctx, userId) diff --git a/internal/keycloak/keycloak.go b/internal/keycloak/keycloak.go index f6c946ce..824b01b7 100644 --- a/internal/keycloak/keycloak.go +++ b/internal/keycloak/keycloak.go @@ -351,7 +351,7 @@ func (k *Keycloak) VerifyAccessToken(token string, organizationName string) erro //TODO implement me ctx := context.Background() - log.Info(token) + //log.Info(token) rptResult, err := k.client.RetrospectToken(ctx, token, DefaultClientID, DefaultClientSecret, organizationName) if err != nil { return err diff --git a/internal/repository/cloud-setting.go b/internal/repository/cloud-setting.go index a02e51df..9c57886a 100644 --- a/internal/repository/cloud-setting.go +++ b/internal/repository/cloud-setting.go @@ -12,8 +12,9 @@ import ( // Interfaces type ICloudSettingRepository interface { Get(cloudSettingId uuid.UUID) (domain.CloudSetting, error) - GetByOrganizationId(organizationId string) (domain.CloudSetting, error) + Fetch(organizationId string) ([]domain.CloudSetting, error) Create(organizationId string, input domain.CreateCloudSettingRequest, resource string, creator uuid.UUID) (cloudSettingId uuid.UUID, err error) + Update(cloudSettingId uuid.UUID, input domain.UpdateCloudSettingRequest, resource string, updator uuid.UUID) (err error) Delete(cloudSettingId uuid.UUID) (err error) } @@ -39,6 +40,7 @@ type CloudSetting struct { Resource string Type domain.CloudType Creator uuid.UUID + Updator uuid.UUID } func (c *CloudSetting) BeforeCreate(tx *gorm.DB) (err error) { @@ -57,14 +59,18 @@ func (r *CloudSettingRepository) Get(cloudSettingId uuid.UUID) (domain.CloudSett return resCloudSetting, nil } -func (r *CloudSettingRepository) GetByOrganizationId(organizationId string) (domain.CloudSetting, error) { - var cloudSetting CloudSetting - res := r.db.First(&cloudSetting, "organization_id = ?", organizationId) +func (r *CloudSettingRepository) Fetch(organizationId string) (out []domain.CloudSetting, err error) { + var cloudSettings []CloudSetting + res := r.db.Find(&cloudSettings, "organization_id = ?", organizationId) if res.RowsAffected == 0 || res.Error != nil { - return domain.CloudSetting{}, fmt.Errorf("Not found cloudSetting for organizationId %s", organizationId) + return nil, fmt.Errorf("Not found cloudSettings for organizationId %s", organizationId) } - resCloudSetting := r.reflect(cloudSetting) - return resCloudSetting, nil + + for _, cloudSetting := range cloudSettings { + outCloudSetting := r.reflect(cloudSetting) + out = append(out, outCloudSetting) + } + return out, nil } func (r *CloudSettingRepository) Create(organizationId string, input domain.CreateCloudSettingRequest, resource string, creator uuid.UUID) (cloudSettingId uuid.UUID, err error) { @@ -72,6 +78,7 @@ func (r *CloudSettingRepository) Create(organizationId string, input domain.Crea OrganizationId: organizationId, Name: input.Name, Description: input.Description, + Type: input.Type, Resource: resource, Creator: creator} res := r.db.Create(&cloudSetting) @@ -81,6 +88,16 @@ func (r *CloudSettingRepository) Create(organizationId string, input domain.Crea return cloudSetting.ID, nil } +func (r *CloudSettingRepository) Update(cloudSettingId uuid.UUID, in domain.UpdateCloudSettingRequest, resource string, updator uuid.UUID) (err error) { + res := r.db.Model(&CloudSetting{}). + Where("id = ?", cloudSettingId). + Updates(map[string]interface{}{"Description": in.Description, "Resource": resource, "Updator": updator}) + if res.Error != nil { + return res.Error + } + return nil +} + func (r *CloudSettingRepository) Delete(cloudSettingId uuid.UUID) (err error) { res := r.db.Delete(&CloudSetting{}, "id = ?", cloudSettingId) if res.Error != nil { @@ -96,7 +113,9 @@ func (r *CloudSettingRepository) reflect(cloudSetting CloudSetting) domain.Cloud Name: cloudSetting.Name, Description: cloudSetting.Description, Resource: cloudSetting.Resource, + Type: cloudSetting.Type, Creator: cloudSetting.Creator.String(), + Updator: cloudSetting.Updator.String(), CreatedAt: cloudSetting.CreatedAt, UpdatedAt: cloudSetting.UpdatedAt, } diff --git a/internal/route/route.go b/internal/route/route.go index 820e8460..8d13a50f 100644 --- a/internal/route/route.go +++ b/internal/route/route.go @@ -9,6 +9,7 @@ import ( "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" @@ -168,9 +169,10 @@ func SetupRouter(db *gorm.DB, argoClient argowf.ArgoClient, asset http.Handler, r.Handle(API_PREFIX+API_VERSION+"/histories", authMiddleware(http.HandlerFunc(historyHandler.GetHistories), kc)).Methods(http.MethodGet) cloudSettingHandler := delivery.NewCloudSettingHandler(usecase.NewCloudSettingUsecase(repository.NewCloudSettingRepository(db), argoClient)) - r.Handle(API_PREFIX+API_VERSION+"/cloud-settings", authMiddleware(http.HandlerFunc(cloudSettingHandler.GetCloudSetting), kc)).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/cloud-settings", authMiddleware(http.HandlerFunc(cloudSettingHandler.GetCloudSettings), kc)).Methods(http.MethodGet) r.Handle(API_PREFIX+API_VERSION+"/cloud-settings", authMiddleware(http.HandlerFunc(cloudSettingHandler.CreateCloudSetting), kc)).Methods(http.MethodPost) - r.Handle(API_PREFIX+API_VERSION+"/cloud-settings/{cloudSettingId}", authMiddleware(http.HandlerFunc(cloudSettingHandler.GetCloudSettingById), kc)).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/cloud-settings/{cloudSettingId}", authMiddleware(http.HandlerFunc(cloudSettingHandler.GetCloudSetting), kc)).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/cloud-settings/{cloudSettingId}", authMiddleware(http.HandlerFunc(cloudSettingHandler.UpdateCloudSetting), kc)).Methods(http.MethodPut) r.Handle(API_PREFIX+API_VERSION+"/cloud-settings/{cloudSettingId}", authMiddleware(http.HandlerFunc(cloudSettingHandler.DeleteCloudSetting), kc)).Methods(http.MethodDelete) // assets @@ -261,6 +263,7 @@ func authMiddleware(next http.Handler, kc keycloak.IKeycloak) http.Handler { if err != nil { log.Error("failed to parse access token: ", err) w.WriteHeader(http.StatusUnauthorized) + w.Write([]byte(err.Error())) return } organization := parsedToken.Claims.(jwtWithouKey.MapClaims)["organization"].(string) @@ -274,10 +277,13 @@ func authMiddleware(next http.Handler, kc keycloak.IKeycloak) http.Handler { if err != nil { log.Error("failed to parse access token: ", err) w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(err.Error())) return } + if jwtToken == nil || mapClaims == nil || mapClaims.Valid() != nil { w.WriteHeader(http.StatusUnauthorized) + w.Write([]byte("Error message TODO")) return } roleProjectMapping := make(map[string]string) @@ -286,13 +292,20 @@ func authMiddleware(next http.Handler, kc keycloak.IKeycloak) http.Handler { if len(slice) != 2 { log.Error("invalid role format: ", role) w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(fmt.Sprintf("invalid role format: %s", role))) return } // key is projectName and value is roleName roleProjectMapping[slice[1]] = slice[0] } + userId, err := uuid.Parse(jwtToken.Claims.(jwt.MapClaims)["sid"].(string)) + if err != nil { + userId = uuid.Nil + } + userInfo := &user.DefaultInfo{ - Organization: jwtToken.Claims.(jwt.MapClaims)["organization"].(string), + OrganizationId: jwtToken.Claims.(jwt.MapClaims)["organization"].(string), + UserId: userId, RoleProjectMapping: roleProjectMapping, } diff --git a/internal/usecase/cloud-setting.go b/internal/usecase/cloud-setting.go index c0adbf6d..9cfc3e6c 100644 --- a/internal/usecase/cloud-setting.go +++ b/internal/usecase/cloud-setting.go @@ -1,9 +1,6 @@ package usecase import ( - "fmt" - "net/http" - "github.com/google/uuid" "github.com/openinfradev/tks-api/internal/repository" argowf "github.com/openinfradev/tks-api/pkg/argo-client" @@ -14,8 +11,9 @@ import ( type ICloudSettingUsecase interface { Get(cloudSettingId uuid.UUID) (domain.CloudSetting, error) - GetByOrganizationId(organizationId string) (domain.CloudSetting, error) - Create(organizationId string, in domain.CreateCloudSettingRequest, resource string, creator uuid.UUID) (cloudSettingId uuid.UUID, err error) + Fetch(organizationId string) ([]domain.CloudSetting, error) + Create(organizationId string, in domain.CreateCloudSettingRequest, creator uuid.UUID) (cloudSettingId uuid.UUID, err error) + Update(cloudSettingId uuid.UUID, in domain.UpdateCloudSettingRequest, updator uuid.UUID) (err error) Delete(cloudSettingId uuid.UUID) error } @@ -31,12 +29,8 @@ func NewCloudSettingUsecase(r repository.ICloudSettingRepository, argoClient arg } } -func (u *CloudSettingUsecase) Create(organizationId string, in domain.CreateCloudSettingRequest, resource string, creator uuid.UUID) (cloudSettingId uuid.UUID, err error) { - _, err = u.repo.GetByOrganizationId(organizationId) - if err == nil { - return uuid.Nil, httpErrors.NewRestError(http.StatusForbidden, "", fmt.Errorf("Already exist cloudSetting for organization")) - } - +func (u *CloudSettingUsecase) Create(organizationId string, in domain.CreateCloudSettingRequest, creator uuid.UUID) (cloudSettingId uuid.UUID, err error) { + resource := "TODO server result or additional information" cloudSettingId, err = u.repo.Create(organizationId, in, resource, creator) if err != nil { return uuid.Nil, httpErrors.NewInternalServerError(err) @@ -64,6 +58,15 @@ func (u *CloudSettingUsecase) Create(organizationId string, in domain.CreateClou return cloudSettingId, nil } +func (u *CloudSettingUsecase) Update(cloudSettingId uuid.UUID, in domain.UpdateCloudSettingRequest, updator uuid.UUID) (err error) { + resource := "TODO server result or additional information" + err = u.repo.Update(cloudSettingId, in, resource, updator) + if err != nil { + return httpErrors.NewInternalServerError(err) + } + return nil +} + func (u *CloudSettingUsecase) Get(cloudSettingId uuid.UUID) (res domain.CloudSetting, err error) { res, err = u.repo.Get(cloudSettingId) if err != nil { @@ -72,10 +75,10 @@ func (u *CloudSettingUsecase) Get(cloudSettingId uuid.UUID) (res domain.CloudSet return res, nil } -func (u *CloudSettingUsecase) GetByOrganizationId(organizationId string) (res domain.CloudSetting, err error) { - res, err = u.repo.GetByOrganizationId(organizationId) +func (u *CloudSettingUsecase) Fetch(organizationId string) (res []domain.CloudSetting, err error) { + res, err = u.repo.Fetch(organizationId) if err != nil { - return domain.CloudSetting{}, err + return nil, err } return res, nil } diff --git a/internal/usecase/user.go b/internal/usecase/user.go index 909741ae..d7f46429 100644 --- a/internal/usecase/user.go +++ b/internal/usecase/user.go @@ -3,6 +3,7 @@ package usecase import ( "context" "fmt" + "github.com/Nerzal/gocloak/v13" "github.com/google/uuid" "github.com/openinfradev/tks-api/internal/auth/request" @@ -158,21 +159,21 @@ func (u *UserUsecase) UpdatePasswordByAccountId(ctx context.Context, accountId s return fmt.Errorf("user in the context is empty") } - originUser, err := u.kc.GetUser(userInfo.GetOrganization(), accountId, token) + originUser, err := u.kc.GetUser(userInfo.GetOrganizationId(), accountId, token) if err != nil { return errors.Wrap(err, "getting user from keycloak failed") } (*originUser.Credentials)[0].Value = gocloak.StringP(newPassword) - err = u.kc.UpdateUser(userInfo.GetOrganization(), originUser, token) + err = u.kc.UpdateUser(userInfo.GetOrganizationId(), originUser, token) if err != nil { return errors.Wrap(err, "updating user in keycloak failed") } // update password in DB - user, err := u.repo.GetUserByAccountId(accountId, userInfo.GetOrganization()) + user, err := u.repo.GetUserByAccountId(accountId, userInfo.GetOrganizationId()) if err != nil { return errors.Wrap(err, "getting user from repository failed") } @@ -200,7 +201,7 @@ func (u *UserUsecase) List(ctx context.Context) (*[]domain.User, error) { return nil, fmt.Errorf("user in the context is empty") } - users, err := u.repo.List(u.repo.OrganizationFilter(userInfo.GetOrganization())) + users, err := u.repo.List(u.repo.OrganizationFilter(userInfo.GetOrganizationId())) if err != nil { return nil, errors.Wrap(err, "getting users from repository failed") } @@ -214,7 +215,7 @@ func (u *UserUsecase) GetByAccountId(ctx context.Context, accountId string) (*do return nil, fmt.Errorf("user in the context is empty") } - users, err := u.repo.List(u.repo.OrganizationFilter(userInfo.GetOrganization()), + users, err := u.repo.List(u.repo.OrganizationFilter(userInfo.GetOrganizationId()), u.repo.AccountIdFilter(accountId)) if err != nil { return nil, errors.Wrap(err, "getting users from repository failed") @@ -234,7 +235,7 @@ func (u *UserUsecase) UpdateByAccountId(ctx context.Context, accountId string, u return nil, fmt.Errorf("user in the context is empty") } - users, err := u.repo.List(u.repo.OrganizationFilter(userInfo.GetOrganization()), + users, err := u.repo.List(u.repo.OrganizationFilter(userInfo.GetOrganizationId()), u.repo.AccountIdFilter(accountId)) if err != nil { return nil, errors.Wrap(err, "getting users from repository failed") @@ -267,7 +268,7 @@ func (u *UserUsecase) DeleteByAccountId(ctx context.Context, accountId string) e return fmt.Errorf("user in the context is empty") } - users, err := u.repo.List(u.repo.OrganizationFilter(userInfo.GetOrganization()), + users, err := u.repo.List(u.repo.OrganizationFilter(userInfo.GetOrganizationId()), u.repo.AccountIdFilter(accountId)) if err != nil { return errors.Wrap(err, "getting users from repository failed") @@ -292,7 +293,7 @@ func (u *UserUsecase) DeleteByAccountId(ctx context.Context, accountId string) e if ok == false { return fmt.Errorf("token in the context is empty") } - err = u.kc.DeleteUser(userInfo.GetOrganization(), accountId, token) + err = u.kc.DeleteUser(userInfo.GetOrganizationId(), accountId, token) if err != nil { return errors.Wrap(err, "deleting user in keycloak failed") } @@ -370,7 +371,7 @@ func (u *UserUsecase) Create(ctx context.Context, user *domain.User) (*domain.Us return nil, err } - //err = u.repo.AssignRole(user.AccountId, userInfo.GetOrganization(), user.Role.Name) + //err = u.repo.AssignRole(user.AccountId, userInfo.GetOrganizationId(), user.Role.Name) //if err != nil { // return nil, err //} diff --git a/pkg/domain/cloud-setting.go b/pkg/domain/cloud-setting.go index 7dc2c3c1..2207528b 100644 --- a/pkg/domain/cloud-setting.go +++ b/pkg/domain/cloud-setting.go @@ -11,6 +11,7 @@ const ( CloudType_UNDEFINED = "UNDEFINED" CloudType_AWS = "AWS" CloudType_AZURE = "AZZURE" + CloudType_GCP = "GCP" ) type CloudSetting = struct { @@ -20,17 +21,23 @@ type CloudSetting = struct { Description string `json:"description"` Type CloudType `json:"type"` Resource string `json:"resource"` + Clusters int `json:"clusters"` Creator string `json:"creator"` + Updator string `json:"updator"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` } type CreateCloudSettingRequest struct { - Name string `json:"name"` + Name string `json:"name" validate:"required"` Description string `json:"description"` - Type CloudType `json:"type"` - AccessKey string `json:"accessKey"` - SecretKey string `json:"secretKey"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` + Type CloudType `json:"type" validate:"oneof=AWS AZZURE GCP"` + SecretKeyId string `json:"secretKeyId" validate:"required"` + SecretKey string `json:"secretKey" validate:"required"` +} + +type UpdateCloudSettingRequest struct { + Description string `json:"description"` + SecretKeyId string `json:"secretKeyId"` + SecretKey string `json:"secretKey"` }