diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 49824a05..cb9660a3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -64,7 +64,7 @@ jobs: ./scripts/run_minio.sh sleep 5 - name: Download cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: /home/runner/.cache/go-build key: ${{ runner.os }}-go-test-sqlite3-${{ github.sha }} diff --git a/Makefile b/Makefile index c475d51a..594f0a73 100644 --- a/Makefile +++ b/Makefile @@ -104,6 +104,7 @@ gormgen: ## Generate gorm model from database @$(GOCMD) run ./pkg/dal/cmd/gen.go swagen: ## Generate swagger from code comments +# go install github.com/swaggo/swag/cmd/swag@latest @swag fmt @swag init --output pkg/handlers/apidocs diff --git a/go.mod b/go.mod index 660a187a..6f859568 100644 --- a/go.mod +++ b/go.mod @@ -238,7 +238,7 @@ require ( github.com/googleapis/gax-go/v2 v2.12.3 // indirect github.com/gopherjs/gopherjs v1.17.2 // indirect github.com/gorilla/mux v1.8.1 // indirect - github.com/gorilla/schema v1.3.0 // indirect + github.com/gorilla/schema v1.4.1 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/gosuri/uitable v0.0.4 // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect diff --git a/go.sum b/go.sum index 0435688f..6f58f7b4 100644 --- a/go.sum +++ b/go.sum @@ -987,8 +987,8 @@ github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyE github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= -github.com/gorilla/schema v1.3.0 h1:rbciOzXAx3IB8stEFnfTwO3sYa6EWlQk79XdyustPDA= -github.com/gorilla/schema v1.3.0/go.mod h1:Dg5SSm5PV60mhF2NFaTV1xuYYj8tV8NOPRo4FggUMnM= +github.com/gorilla/schema v1.4.1 h1:jUg5hUjCSDZpNGLuXQOgIWGdlgrIdYvgQ0wZtdK1M3E= +github.com/gorilla/schema v1.4.1/go.mod h1:Dg5SSm5PV60mhF2NFaTV1xuYYj8tV8NOPRo4FggUMnM= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= diff --git a/pkg/handlers/apidocs/docs.go b/pkg/handlers/apidocs/docs.go index daf92b45..b2dfeb70 100644 --- a/pkg/handlers/apidocs/docs.go +++ b/pkg/handlers/apidocs/docs.go @@ -4599,6 +4599,17 @@ const docTemplate = `{ "User" ], "summary": "Login user", + "parameters": [ + { + "description": "User login object", + "name": "message", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/types.PostUserLoginRequest" + } + } + ], "responses": { "200": { "description": "OK", @@ -7120,6 +7131,27 @@ const docTemplate = `{ } } }, + "types.PostUserLoginRequest": { + "type": "object", + "required": [ + "password", + "username" + ], + "properties": { + "password": { + "type": "string", + "maxLength": 20, + "minLength": 5, + "example": "Admin@123" + }, + "username": { + "type": "string", + "maxLength": 20, + "minLength": 2, + "example": "sigma" + } + } + }, "types.PostUserLoginResponse": { "type": "object", "properties": { diff --git a/pkg/handlers/apidocs/swagger.json b/pkg/handlers/apidocs/swagger.json index 70f69813..f0d5c741 100644 --- a/pkg/handlers/apidocs/swagger.json +++ b/pkg/handlers/apidocs/swagger.json @@ -4591,6 +4591,17 @@ "User" ], "summary": "Login user", + "parameters": [ + { + "description": "User login object", + "name": "message", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/types.PostUserLoginRequest" + } + } + ], "responses": { "200": { "description": "OK", @@ -7112,6 +7123,27 @@ } } }, + "types.PostUserLoginRequest": { + "type": "object", + "required": [ + "password", + "username" + ], + "properties": { + "password": { + "type": "string", + "maxLength": 20, + "minLength": 5, + "example": "Admin@123" + }, + "username": { + "type": "string", + "maxLength": 20, + "minLength": 2, + "example": "sigma" + } + } + }, "types.PostUserLoginResponse": { "type": "object", "properties": { diff --git a/pkg/handlers/apidocs/swagger.yaml b/pkg/handlers/apidocs/swagger.yaml index 791bbb04..0a81029b 100644 --- a/pkg/handlers/apidocs/swagger.yaml +++ b/pkg/handlers/apidocs/swagger.yaml @@ -1046,6 +1046,22 @@ definitions: example: 21911 type: integer type: object + types.PostUserLoginRequest: + properties: + password: + example: Admin@123 + maxLength: 20 + minLength: 5 + type: string + username: + example: sigma + maxLength: 20 + minLength: 2 + type: string + required: + - password + - username + type: object types.PostUserLoginResponse: properties: email: @@ -4725,6 +4741,13 @@ paths: post: consumes: - application/json + parameters: + - description: User login object + in: body + name: message + required: true + schema: + $ref: '#/definitions/types.PostUserLoginRequest' produces: - application/json responses: diff --git a/pkg/handlers/users/users_login.go b/pkg/handlers/users/users_login.go index 0b800479..485cf8e1 100644 --- a/pkg/handlers/users/users_login.go +++ b/pkg/handlers/users/users_login.go @@ -37,9 +37,10 @@ import ( // @Accept json // @Produce json // @Router /users/login [post] -// @Failure 500 {object} xerrors.ErrCode -// @Failure 401 {object} xerrors.ErrCode -// @Success 200 {object} types.PostUserLoginResponse +// @Param message body types.PostUserLoginRequest true "User login object" +// @Failure 500 {object} xerrors.ErrCode +// @Failure 401 {object} xerrors.ErrCode +// @Success 200 {object} types.PostUserLoginResponse func (h *handler) Login(c echo.Context) error { ctx := log.Logger.WithContext(c.Request().Context()) diff --git a/pkg/handlers/users/users_login_test.go b/pkg/handlers/users/users_login_test.go index b4950b36..f94d644c 100644 --- a/pkg/handlers/users/users_login_test.go +++ b/pkg/handlers/users/users_login_test.go @@ -14,235 +14,74 @@ package users -// func TestLogin(t *testing.T) { -// logger.SetLevel("debug") -// e := echo.New() -// validators.Initialize(e) -// err := tests.Initialize(t) -// assert.NoError(t, err) -// err = tests.DB.Init() -// assert.NoError(t, err) -// defer func() { -// conn, err := dal.DB.DB() -// assert.NoError(t, err) -// err = conn.Close() -// assert.NoError(t, err) -// // err = tests.DB.DeInit() -// // assert.NoError(t, err) -// }() - -// viper.Reset() -// miniRedis := miniredis.RunT(t) -// viper.SetDefault("redis.url", "redis://"+miniRedis.Addr()) - -// viper.SetDefault("auth.internalUser.password", "internal-sigma") -// viper.SetDefault("auth.internalUser.username", "internal-sigma") -// viper.SetDefault("auth.admin.password", "Admin@123") -// viper.SetDefault("auth.admin.username", "sigma") -// err = inits.Initialize() -// assert.NoError(t, err) - -// _, err = handlerNew() -// assert.Error(t, err) - -// viper.SetDefault("auth.jwt.privateKey", privateKeyString) -// userHandler, err := handlerNew() -// assert.NoError(t, err) - -// userService := dao.NewUserServiceFactory().New() -// userObj, err := userService.GetByUsername(log.Logger.WithContext(context.Background()), "sigma") -// assert.NoError(t, err) - -// req := httptest.NewRequest(http.MethodPost, "/", bytes.NewBufferString(`{"username":"sigma","password":"Admin@123"}`)) -// req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) -// rec := httptest.NewRecorder() -// c := e.NewContext(req, rec) -// c.Set(consts.ContextUser, userObj) -// err = userHandler.Login(c) -// assert.NoError(t, err) -// assert.Equal(t, http.StatusOK, c.Response().Status) - -// // req = httptest.NewRequest(http.MethodPost, "/", bytes.NewBufferString(`{"username":"sigma","password":""}`)) -// // req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) -// // rec = httptest.NewRecorder() -// // c = e.NewContext(req, rec) -// // err = userHandler.Login(c) -// // assert.NoError(t, err) -// // assert.Equal(t, http.StatusBadRequest, c.Response().Status) -// } - -// func TestLoginMockToken(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() - -// logger.SetLevel("debug") -// e := echo.New() -// validators.Initialize(e) -// err := tests.Initialize(t) -// assert.NoError(t, err) -// err = tests.DB.Init() -// assert.NoError(t, err) -// defer func() { -// conn, err := dal.DB.DB() -// assert.NoError(t, err) -// err = conn.Close() -// assert.NoError(t, err) -// err = tests.DB.DeInit() -// assert.NoError(t, err) -// }() - -// viper.Reset() -// miniRedis := miniredis.RunT(t) -// viper.SetDefault("redis.url", "redis://"+miniRedis.Addr()) - -// viper.SetDefault("auth.internalUser.password", "internal-sigma") -// viper.SetDefault("auth.internalUser.username", "internal-sigma") -// viper.SetDefault("auth.admin.password", "Admin@123") -// viper.SetDefault("auth.admin.username", "sigma") -// err = inits.Initialize() -// assert.NoError(t, err) - -// var times int -// tokenMock := tokenmock.NewMockTokenService(ctrl) -// tokenMock.EXPECT().New(gomock.Any(), gomock.Any()).DoAndReturn(func(_ int64, _ time.Duration) (string, error) { -// times++ -// if times == 2 { -// return "test", nil -// } else { -// return "", fmt.Errorf("test") -// } -// }).Times(3) - -// viper.SetDefault("auth.jwt.privateKey", privateKeyString) -// userHandler, err := handlerNew(inject{tokenService: tokenMock}) -// assert.NoError(t, err) - -// userService := dao.NewUserServiceFactory().New() -// userObj, err := userService.GetByUsername(log.Logger.WithContext(context.Background()), "sigma") -// assert.NoError(t, err) - -// req := httptest.NewRequest(http.MethodPost, "/", bytes.NewBufferString(`{"username":"sigma","password":"Admin@123"}`)) -// req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) -// rec := httptest.NewRecorder() -// c := e.NewContext(req, rec) -// c.Set(consts.ContextUser, userObj) -// err = userHandler.Login(c) -// assert.NoError(t, err) -// assert.Equal(t, http.StatusInternalServerError, c.Response().Status) - -// req = httptest.NewRequest(http.MethodPost, "/", bytes.NewBufferString(`{"username":"sigma","password":"Admin@123"}`)) -// req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) -// rec = httptest.NewRecorder() -// c = e.NewContext(req, rec) -// c.Set(consts.ContextUser, userObj) -// err = userHandler.Login(c) -// assert.NoError(t, err) -// assert.Equal(t, http.StatusInternalServerError, c.Response().Status) -// } - -// func TestLoginMockPassword(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() - -// // passwordMock := passwordmock.NewMockPassword(ctrl) -// // passwordMock.EXPECT().Verify(gomock.Any(), gomock.Any()).DoAndReturn(func(_, _ string) bool { -// // return false -// // }).Times(1) - -// logger.SetLevel("debug") -// e := echo.New() -// validators.Initialize(e) -// err := tests.Initialize(t) -// assert.NoError(t, err) -// err = tests.DB.Init() -// assert.NoError(t, err) -// defer func() { -// conn, err := dal.DB.DB() -// assert.NoError(t, err) -// err = conn.Close() -// assert.NoError(t, err) -// err = tests.DB.DeInit() -// assert.NoError(t, err) -// }() - -// viper.Reset() -// miniRedis := miniredis.RunT(t) -// viper.SetDefault("redis.url", "redis://"+miniRedis.Addr()) - -// viper.SetDefault("auth.internalUser.password", "internal-sigma") -// viper.SetDefault("auth.internalUser.username", "internal-sigma") -// viper.SetDefault("auth.admin.password", "Admin@123") -// viper.SetDefault("auth.admin.username", "sigma") -// err = inits.Initialize() -// assert.NoError(t, err) - -// viper.SetDefault("auth.jwt.privateKey", privateKeyString) -// userHandler, err := handlerNew(inject{}) -// assert.NoError(t, err) - -// req := httptest.NewRequest(http.MethodPost, "/", bytes.NewBufferString(`{"username":"sigma","password":"Admin@123"}`)) -// req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) -// rec := httptest.NewRecorder() -// c := e.NewContext(req, rec) -// err = userHandler.Login(c) -// assert.NoError(t, err) -// assert.Equal(t, http.StatusUnauthorized, c.Response().Status) -// } - -// func TestLoginMockDAO(t *testing.T) { -// viper.Reset() -// logger.SetLevel("debug") -// e := echo.New() -// e.HideBanner = true -// e.HidePort = true -// validators.Initialize(e) -// err := tests.Initialize(t) -// assert.NoError(t, err) -// err = tests.DB.Init() -// assert.NoError(t, err) -// defer func() { -// conn, err := dal.DB.DB() -// assert.NoError(t, err) -// err = conn.Close() -// assert.NoError(t, err) -// err = tests.DB.DeInit() -// assert.NoError(t, err) -// }() - -// viper.SetDefault("auth.internalUser.password", "internal-sigma") -// viper.SetDefault("auth.internalUser.username", "internal-sigma") -// viper.SetDefault("auth.admin.password", "Admin@123") -// viper.SetDefault("auth.admin.username", "sigma") -// viper.SetDefault("auth.jwt.privateKey", privateKeyString) - -// miniRedis := miniredis.RunT(t) -// viper.SetDefault("redis.url", "redis://"+miniRedis.Addr()) - -// err = inits.Initialize() -// assert.NoError(t, err) - -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() - -// // daoMockUserService := daomock.NewMockUserService(ctrl) -// // daoMockUserService.EXPECT().GetByUsername(gomock.Any(), gomock.Any()).DoAndReturn(func(_ context.Context, _ string) (*models.User, error) { -// // return nil, fmt.Errorf("test") -// // }).Times(1) - -// // daoMockUserServiceFactory := daomock.NewMockUserServiceFactory(ctrl) -// // daoMockUserServiceFactory.EXPECT().New(gomock.Any()).DoAndReturn(func(txs ...*query.Query) dao.UserService { -// // return daoMockUserService -// // }).Times(1) - -// viper.SetDefault("auth.jwt.privateKey", privateKeyString) -// userHandler, err := handlerNew(inject{}) -// assert.NoError(t, err) - -// req := httptest.NewRequest(http.MethodPost, "/", bytes.NewBufferString(`{"username":"test","password":"123498712311Aa!"}`)) -// req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) -// rec := httptest.NewRecorder() -// c := e.NewContext(req, rec) -// err = userHandler.Login(c) -// assert.NoError(t, err) -// assert.Equal(t, http.StatusUnauthorized, c.Response().Status) -// } +import ( + "bytes" + "context" + "net/http" + "net/http/httptest" + "testing" + + "github.com/labstack/echo/v4" + "github.com/rs/zerolog/log" + "github.com/stretchr/testify/assert" + + "github.com/go-sigma/sigma/pkg/configs" + "github.com/go-sigma/sigma/pkg/consts" + "github.com/go-sigma/sigma/pkg/dal" + "github.com/go-sigma/sigma/pkg/dal/dao" + "github.com/go-sigma/sigma/pkg/inits" + "github.com/go-sigma/sigma/pkg/logger" + "github.com/go-sigma/sigma/pkg/tests" + "github.com/go-sigma/sigma/pkg/utils/ptr" +) + +func TestLogin(t *testing.T) { + logger.SetLevel("debug") + + assert.NoError(t, tests.Initialize(t)) + assert.NoError(t, tests.DB.Init()) + defer func() { + conn, err := dal.DB.DB() + assert.NoError(t, err) + assert.NoError(t, conn.Close()) + assert.NoError(t, tests.DB.DeInit()) + }() + + config := &configs.Configuration{ + Auth: configs.ConfigurationAuth{ + Admin: configs.ConfigurationAuthAdmin{ + Username: "sigma", + Password: "sigma", + Email: "sigma@gmail.com", + }, + Jwt: configs.ConfigurationAuthJwt{ + PrivateKey: privateKeyString, + }, + Token: configs.ConfigurationAuthToken{ + Realm: "http://localhost:8080/user/token", + Service: "sigma-dev", + }, + }, + } + configs.SetConfiguration(config) + + assert.NoError(t, inits.Initialize(ptr.To(config))) + + userHandler, err := handlerNew() + assert.NoError(t, err) + + userService := dao.NewUserServiceFactory().New() + userObj, err := userService.GetByUsername(log.Logger.WithContext(context.Background()), "sigma") + assert.NoError(t, err) + + e := echo.New() + + req := httptest.NewRequest(http.MethodPost, "/", bytes.NewBufferString(`{"username":"sigma","password":"sigma"}`)) + req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) + rec := httptest.NewRecorder() + c := e.NewContext(req, rec) + c.Set(consts.ContextUser, userObj) + err = userHandler.Login(c) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, c.Response().Status) +} diff --git a/pkg/handlers/users/users_signup_test.go b/pkg/handlers/users/users_signup_test.go index 21bf6786..dc798585 100644 --- a/pkg/handlers/users/users_signup_test.go +++ b/pkg/handlers/users/users_signup_test.go @@ -55,9 +55,6 @@ func TestSignup(t *testing.T) { assert.NoError(t, tests.DB.DeInit()) }() - _, err := handlerNew() - assert.Error(t, err) - config := &configs.Configuration{ Auth: configs.ConfigurationAuth{ Admin: configs.ConfigurationAuthAdmin{ diff --git a/pkg/types/user.go b/pkg/types/user.go index a55f53ce..c7bbaf34 100644 --- a/pkg/types/user.go +++ b/pkg/types/user.go @@ -64,6 +64,12 @@ type UserItem struct { UpdatedAt string `json:"updated_at" example:"2006-01-02 15:04:05"` } +// PostUserLoginRequest ... +type PostUserLoginRequest struct { + Username string `json:"username" validate:"required,is_valid_username,min=2,max=20" example:"sigma"` + Password string `json:"password" validate:"required,min=5,max=20,is_valid_password" example:"Admin@123"` +} + // PostUserLoginResponse ... type PostUserLoginResponse struct { RefreshToken string `json:"refresh_token"`