diff --git a/api/actions/app.go b/api/actions/app.go index 03eeb518..648ef805 100644 --- a/api/actions/app.go +++ b/api/actions/app.go @@ -44,13 +44,17 @@ func App() *buffalo.App { apiPath := "/api" auth := app.Group(apiPath + "/auth") - auth.Middleware.Skip(SetContextMiddleware, AuthLogin) + + auth.Middleware.Skip(SetContextMiddleware, AuthLogin, AuthLoginRefresh) auth.POST("/login", AuthLogin) - auth.POST("/refresh", AuthLoginRefresh) auth.POST("/validate", AuthValidate) auth.POST("/logout", AuthLogout) auth.POST("/userinfo", AuthUserinfo) + refresh := auth.Group("/refresh") + refresh.Use(SetRefreshCtxMiddleware) + refresh.POST("", AuthLoginRefresh) + api := app.Group(apiPath) api.POST("/disklookup", DiskLookup) api.POST("/availabledisktypebyproviderregion", AvailableDiskTypeByProviderRegion) diff --git a/api/actions/auth.go b/api/actions/auth.go index d2864271..feecf2d8 100644 --- a/api/actions/auth.go +++ b/api/actions/auth.go @@ -8,6 +8,7 @@ import ( "log" "github.com/gobuffalo/buffalo" + "github.com/gobuffalo/buffalo/render" "github.com/gobuffalo/pop/v6" ) @@ -54,16 +55,21 @@ func AuthLoginRefresh(c buffalo.Context) error { return c.Render(commonResponse.Status.StatusCode, r.JSON(commonResponse)) } + refreshToken := c.Value("refreshToken").(string) + if refreshToken != sess.RefreshToken { + return c.Render(http.StatusForbidden, render.JSON(map[string]interface{}{"error": http.StatusText(http.StatusForbidden)})) + } + tokenSet, err := handler.RefreshAccessToken(sess.RefreshToken) if err != nil { app.Logger.Error(err.Error()) - commonResponse := handler.CommonResponseStatusBadRequest(err.Error()) + commonResponse := handler.CommonResponseStatusForbidden(err.Error()) return c.Render(commonResponse.Status.StatusCode, r.JSON(commonResponse)) } sess.AccessToken = tokenSet.Accesstoken sess.ExpiresIn = float64(tokenSet.ExpiresIn) - sess.RefreshToken = tokenSet.Accesstoken + sess.RefreshToken = tokenSet.RefreshToken sess.RefreshExpiresIn = float64(tokenSet.RefreshExpiresIn) _, err = handler.UpdateUserSess(tx, sess) diff --git a/api/actions/middleware.go b/api/actions/middleware.go index 327eaf5f..76677df5 100644 --- a/api/actions/middleware.go +++ b/api/actions/middleware.go @@ -37,3 +37,34 @@ func SetContextMiddleware(next buffalo.Handler) buffalo.Handler { return next(c) } } + +func SetRefreshCtxMiddleware(next buffalo.Handler) buffalo.Handler { + return func(c buffalo.Context) error { + accessToken := strings.TrimPrefix(c.Request().Header.Get("Authorization"), "Bearer ") + _, err := handler.GetTokenClaims(accessToken) + if errMsg := err.Error(); err != nil && !strings.Contains(errMsg, "token is expired") { + app.Logger.Error(errMsg) + app.Logger.Error("error occured from token claim") + return c.Render(http.StatusUnauthorized, render.JSON(map[string]interface{}{"error": "Unauthorized"})) + } + + commonRequest := &handler.CommonRequest{} + if err := c.Bind(commonRequest); err != nil { + app.Logger.Error(err.Error()) + return c.Render(http.StatusBadRequest, render.JSON(map[string]interface{}{"error": http.StatusText(http.StatusBadRequest)})) + } + + refreshToken := commonRequest.Request.(map[string]interface{})["refresh_token"].(string) + + refreshTokenClaim, err := handler.GetRefreshTokenClaims(refreshToken) + if err != nil { + app.Logger.Error(err.Error()) + return c.Render(http.StatusForbidden, render.JSON(map[string]interface{}{"error": http.StatusText(http.StatusForbidden)})) + } + + c.Set("UserId", refreshTokenClaim.Upn) + c.Set("refreshToken", refreshToken) + + return next(c) + } +} diff --git a/api/handler/auth.go b/api/handler/auth.go index d6f64551..8e34c6d1 100644 --- a/api/handler/auth.go +++ b/api/handler/auth.go @@ -214,9 +214,10 @@ func generateJWT() (*CmigUserLoginResponse, error) { refreshExp := time.Now().Add(refreshTokenExpired).Unix() refreshClaims := CmigRefreshtokenClaims{ - Exp: exp, + Exp: refreshExp, + Upn: user.Id, MapClaims: &jwt.MapClaims{ - "exp": exp, + "exp": refreshExp, }, } refreshToken := jwt.NewWithClaims(jwt.SigningMethodHS256, refreshClaims) @@ -242,7 +243,7 @@ func GetUserToken(id string, password string) (*CmigUserLoginResponse, error) { } func RefreshAccessToken(refreshToken string) (*CmigUserLoginResponse, error) { - token, err := jwt.ParseWithClaims(refreshToken, &CmigAccesstokenClaims{}, func(token *jwt.Token) (interface{}, error) { + token, err := jwt.ParseWithClaims(refreshToken, &CmigRefreshtokenClaims{}, func(token *jwt.Token) (interface{}, error) { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) } @@ -251,7 +252,7 @@ func RefreshAccessToken(refreshToken string) (*CmigUserLoginResponse, error) { if err != nil { return nil, fmt.Errorf("token is invalid : %s", err.Error()) } - if claims, ok := token.Claims.(*CmigAccesstokenClaims); ok && token.Valid { + if claims, ok := token.Claims.(*CmigRefreshtokenClaims); ok && token.Valid { if time.Now().Unix() > claims.Exp { return nil, fmt.Errorf("refresh token expired") } @@ -261,15 +262,32 @@ func RefreshAccessToken(refreshToken string) (*CmigUserLoginResponse, error) { } } -func GetTokenClaims(tokenString string) (*CmigAccesstokenClaims, error) { - token, err := jwt.ParseWithClaims(tokenString, &CmigAccesstokenClaims{}, func(token *jwt.Token) (interface{}, error) { +func GetRefreshTokenClaims(tokenString string) (*CmigRefreshtokenClaims, error) { + token, err := jwt.ParseWithClaims(tokenString, &CmigRefreshtokenClaims{}, func(token *jwt.Token) (interface{}, error) { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) } return encryptionKey, nil }) if err != nil { - return nil, fmt.Errorf("token is invalid : %s", err.Error()) + return nil, err + } + if claims, ok := token.Claims.(*CmigRefreshtokenClaims); ok && token.Valid { + return claims, nil + } else { + return nil, fmt.Errorf("token is invalid") + } +} + +func GetTokenClaims(tokenString string) (*CmigAccesstokenClaims, error) { + token, err := jwt.ParseWithClaims(tokenString, &CmigAccesstokenClaims{}, func(t *jwt.Token) (interface{}, error) { + if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"]) + } + return encryptionKey, nil + }) + if err != nil { + return nil, err } if claims, ok := token.Claims.(*CmigAccesstokenClaims); ok && token.Valid { return claims, nil diff --git a/api/handler/http-util.go b/api/handler/http-util.go index 1ec9849f..b9ac3c59 100644 --- a/api/handler/http-util.go +++ b/api/handler/http-util.go @@ -65,7 +65,7 @@ type ApiYaml struct { // //////////////////////////////////////////////////////////////// var ( - ApiYamlSet ApiYaml + ApiYamlSet ApiYaml ) func init() { @@ -359,6 +359,17 @@ func CommonResponseStatusBadRequest(responseData interface{}) *CommonResponse { } } +func CommonResponseStatusForbidden(responseData interface{}) *CommonResponse { + webStatus := WebStatus{ + StatusCode: http.StatusForbidden, + Message: http.StatusText(http.StatusForbidden), + } + return &CommonResponse{ + ResponseData: responseData, + Status: webStatus, + } +} + func CommonResponseStatusInternalServerError(responseData interface{}) *CommonResponse { webStatus := WebStatus{ StatusCode: http.StatusInternalServerError, diff --git a/front/.env.local b/front/.env.local index 54258825..582e8b5a 100644 --- a/front/.env.local +++ b/front/.env.local @@ -1,4 +1,4 @@ VITE_BACKEND_ENDPOINT = '/api' -VITE_BACKEND_URL = 'http://cm-butterfly-api:4000' +VITE_BACKEND_URL = 'https://devmigapi.onecloudcon.com' VITE_PROJECT_NAME = 'MIGRATOR' VITE_LANGUAGE = 'en' diff --git a/front/src/app/providers/router/index.ts b/front/src/app/providers/router/index.ts index 3e869397..ba13c13a 100644 --- a/front/src/app/providers/router/index.ts +++ b/front/src/app/providers/router/index.ts @@ -13,10 +13,14 @@ import { AuthorizationType } from '../../../shared/libs/store/auth'; import { useAuthStore } from '../../../shared/libs/store/auth'; import { ROLE_TYPE } from '../../../shared/libs/accessControl/pageAccessHelper/constant'; import { RoleType } from '../../../shared/libs/accessControl/pageAccessHelper/types'; -import { getMinimalPageAccessPermissionList } from '../../../shared/libs'; +import { + axiosPost, + getMinimalPageAccessPermissionList, +} from '../../../shared/libs'; import { toLower } from 'lodash'; import { tempRoutes } from './routes/temp.ts'; import NotFound from '@/pages/error/404/NotFound.vue'; +import { axiosInstance, createInstance } from '@/shared/libs/api/instance.ts'; //TODO admin부분 고려 const accessiblePagesWithRoles = [] as any[]; @@ -62,6 +66,7 @@ export class McmpRouter { mode: 'history', routes: McmpRouter.rootRoute, }); + McmpRouter.router.beforeEach((to: Route, from: Route, next) => { const requiresAuth = to.matched.some( record => record.meta?.requiresAuth, diff --git a/front/src/features/workflow/workflowEditor/sequential/designer/editor/model/beetleTaskEditorModel.ts b/front/src/features/sequential/designer/editor/model/beetleTaskEditorModel.ts similarity index 100% rename from front/src/features/workflow/workflowEditor/sequential/designer/editor/model/beetleTaskEditorModel.ts rename to front/src/features/sequential/designer/editor/model/beetleTaskEditorModel.ts diff --git a/front/src/features/workflow/workflowEditor/sequential/designer/editor/model/editorProviders.ts b/front/src/features/sequential/designer/editor/model/editorProviders.ts similarity index 88% rename from front/src/features/workflow/workflowEditor/sequential/designer/editor/model/editorProviders.ts rename to front/src/features/sequential/designer/editor/model/editorProviders.ts index 06a066bd..0c9cb8f4 100644 --- a/front/src/features/workflow/workflowEditor/sequential/designer/editor/model/editorProviders.ts +++ b/front/src/features/sequential/designer/editor/model/editorProviders.ts @@ -1,6 +1,6 @@ import { insertDynamicComponent } from '@/shared/utils'; -import { getSequencePath } from '@/features/workflow/workflowEditor/sequential/designer/editor/model/utils.ts'; -import BeetleTaskEditor from '@/features/workflow/workflowEditor/sequential/designer/editor/ui/BeetleTaskEditor.vue'; +import { getSequencePath } from '@/features/sequential/designer/editor/model/utils.ts'; +import BeetleTaskEditor from '@/features/sequential/designer/editor/ui/BeetleTaskEditor.vue'; export function editorProviders() { const editor = document.createElement('div'); diff --git a/front/src/features/workflow/workflowEditor/sequential/designer/editor/model/utils.ts b/front/src/features/sequential/designer/editor/model/utils.ts similarity index 100% rename from front/src/features/workflow/workflowEditor/sequential/designer/editor/model/utils.ts rename to front/src/features/sequential/designer/editor/model/utils.ts diff --git a/front/src/features/workflow/workflowEditor/sequential/designer/editor/ui/BeetleTaskEditor.vue b/front/src/features/sequential/designer/editor/ui/BeetleTaskEditor.vue similarity index 97% rename from front/src/features/workflow/workflowEditor/sequential/designer/editor/ui/BeetleTaskEditor.vue rename to front/src/features/sequential/designer/editor/ui/BeetleTaskEditor.vue index 3dcc89c9..1ab5d08e 100644 --- a/front/src/features/workflow/workflowEditor/sequential/designer/editor/ui/BeetleTaskEditor.vue +++ b/front/src/features/sequential/designer/editor/ui/BeetleTaskEditor.vue @@ -13,9 +13,9 @@ import Vue, { watch, } from 'vue'; import { useInputModel } from '@/shared/hooks/input/useInputModel.ts'; -import { useTaskEditorModel } from '@/features/workflow/workflowEditor/sequential/designer/editor/model/beetleTaskEditorModel.ts'; +import { useTaskEditorModel } from '@/features/sequential/designer/editor/model/beetleTaskEditorModel.ts'; import BAccordion from '@/shared/ui/Input/Accordian/BAccordion.vue'; -import SequentialShortCut from '@/features/workflow/workflowEditor/sequential/designer/shortcut/ui/SequentialShortCut.vue'; +import SequentialShortCut from '@/features/sequential/designer/shortcut/ui/SequentialShortCut.vue'; import { Step } from '@/features/workflow/workflowEditor/model/types.ts'; interface IProps { diff --git a/front/src/features/workflow/workflowEditor/sequential/designer/editor/ui/TaskGroupEditor.vue b/front/src/features/sequential/designer/editor/ui/TaskGroupEditor.vue similarity index 100% rename from front/src/features/workflow/workflowEditor/sequential/designer/editor/ui/TaskGroupEditor.vue rename to front/src/features/sequential/designer/editor/ui/TaskGroupEditor.vue diff --git a/front/src/features/workflow/workflowEditor/sequential/designer/model/sequentialDesignerModel.ts b/front/src/features/sequential/designer/model/sequentialDesignerModel.ts similarity index 95% rename from front/src/features/workflow/workflowEditor/sequential/designer/model/sequentialDesignerModel.ts rename to front/src/features/sequential/designer/model/sequentialDesignerModel.ts index c63b750b..8b229db7 100644 --- a/front/src/features/workflow/workflowEditor/sequential/designer/model/sequentialDesignerModel.ts +++ b/front/src/features/sequential/designer/model/sequentialDesignerModel.ts @@ -5,8 +5,8 @@ import { } from 'sequential-workflow-designer'; import { Definition, Step } from 'sequential-workflow-model'; import getRandomId from '@/shared/utils/uuid'; -import { toolboxSteps } from '@/features/workflow/workflowEditor/sequential/designer/toolbox/model/toolboxSteps.ts'; -import { editorProviders } from '@/features/workflow/workflowEditor/sequential/designer/editor/model/editorProviders.ts'; +import { toolboxSteps } from '@/features/sequential/designer/toolbox/model/toolboxSteps.ts'; +import { editorProviders } from '@/features/sequential/designer/editor/model/editorProviders.ts'; import testSvg from '@/shared/asset/image/testSvg.svg'; export function useSequentialDesignerModel(refs: any) { diff --git a/front/src/features/workflow/workflowEditor/sequential/designer/shortcut/ui/SequentialShortCut.vue b/front/src/features/sequential/designer/shortcut/ui/SequentialShortCut.vue similarity index 100% rename from front/src/features/workflow/workflowEditor/sequential/designer/shortcut/ui/SequentialShortCut.vue rename to front/src/features/sequential/designer/shortcut/ui/SequentialShortCut.vue diff --git a/front/src/features/workflow/workflowEditor/sequential/designer/toolbox/model/api/index.ts b/front/src/features/sequential/designer/toolbox/model/api/index.ts similarity index 100% rename from front/src/features/workflow/workflowEditor/sequential/designer/toolbox/model/api/index.ts rename to front/src/features/sequential/designer/toolbox/model/api/index.ts diff --git a/front/src/features/workflow/workflowEditor/sequential/designer/toolbox/model/toolboxModel.ts b/front/src/features/sequential/designer/toolbox/model/toolboxModel.ts similarity index 92% rename from front/src/features/workflow/workflowEditor/sequential/designer/toolbox/model/toolboxModel.ts rename to front/src/features/sequential/designer/toolbox/model/toolboxModel.ts index 27867f83..8b9f34ee 100644 --- a/front/src/features/workflow/workflowEditor/sequential/designer/toolbox/model/toolboxModel.ts +++ b/front/src/features/sequential/designer/toolbox/model/toolboxModel.ts @@ -1,14 +1,14 @@ import { getTaskComponentList, ITaskComponentInfoResponse, -} from '@/features/workflow/workflowEditor/sequential/designer/toolbox/model/api'; +} from '@/features/sequential/designer/toolbox/model/api'; import { parseRequestBody } from '@/shared/utils/stringToObject'; import getRandomId from '@/shared/utils/uuid'; import { fixedModel, Step, } from '@/features/workflow/workflowEditor/model/types.ts'; -import { toolboxSteps } from '@/features/workflow/workflowEditor/sequential/designer/toolbox/model/toolboxSteps.ts'; +import { toolboxSteps } from '@/features/sequential/designer/toolbox/model/toolboxSteps.ts'; import { ITaskResponse } from '@/entities'; export function useSequentialToolboxModel() { diff --git a/front/src/features/workflow/workflowEditor/sequential/designer/toolbox/model/toolboxSteps.ts b/front/src/features/sequential/designer/toolbox/model/toolboxSteps.ts similarity index 100% rename from front/src/features/workflow/workflowEditor/sequential/designer/toolbox/model/toolboxSteps.ts rename to front/src/features/sequential/designer/toolbox/model/toolboxSteps.ts diff --git a/front/src/features/workflow/workflowEditor/sequential/designer/ui/SequentialDesigner.vue b/front/src/features/sequential/designer/ui/SequentialDesigner.vue similarity index 78% rename from front/src/features/workflow/workflowEditor/sequential/designer/ui/SequentialDesigner.vue rename to front/src/features/sequential/designer/ui/SequentialDesigner.vue index ee31ebe4..ea9e92b2 100644 --- a/front/src/features/workflow/workflowEditor/sequential/designer/ui/SequentialDesigner.vue +++ b/front/src/features/sequential/designer/ui/SequentialDesigner.vue @@ -1,11 +1,11 @@