From 8137813831de07a7c1aa81c179b6ceb5b86ac82b Mon Sep 17 00:00:00 2001 From: hanif salafi Date: Fri, 24 Jan 2025 01:49:01 +0700 Subject: [PATCH] feat: add reset password, forgot password, request otp in users --- .../entity/forgot_passwords.entity.go | 12 + .../entity/registration_otps.entity.go | 12 + app/database/index.database.go | 2 + .../users/controller/users.controller.go | 147 +++++++++ .../users/repository/users.repository.go | 49 +++ app/module/users/request/users.request.go | 25 ++ app/module/users/service/users.service.go | 138 ++++++++ app/module/users/users.module.go | 6 + config/config/keycloak.config.go | 24 ++ docs/swagger/docs.go | 310 ++++++++++++++++++ docs/swagger/swagger.json | 310 ++++++++++++++++++ docs/swagger/swagger.yaml | 195 +++++++++++ utils/service/string.service.go | 17 + 13 files changed, 1247 insertions(+) create mode 100644 app/database/entity/forgot_passwords.entity.go create mode 100644 app/database/entity/registration_otps.entity.go diff --git a/app/database/entity/forgot_passwords.entity.go b/app/database/entity/forgot_passwords.entity.go new file mode 100644 index 0000000..1f95270 --- /dev/null +++ b/app/database/entity/forgot_passwords.entity.go @@ -0,0 +1,12 @@ +package entity + +import "time" + +type ForgotPasswords struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + KeycloakID string `json:"keycloak_id" gorm:"type:varchar"` + CodeRequest string `json:"code_request" gorm:"type:varchar"` + IsActive bool `json:"is_active" gorm:"type:bool"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` +} diff --git a/app/database/entity/registration_otps.entity.go b/app/database/entity/registration_otps.entity.go new file mode 100644 index 0000000..3161420 --- /dev/null +++ b/app/database/entity/registration_otps.entity.go @@ -0,0 +1,12 @@ +package entity + +import "time" + +type RegistrationOtps struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + Email string `json:"email" gorm:"type:varchar"` + OtpCode string `json:"otp_code" gorm:"type:varchar"` + ValidUntil time.Time `json:"valid_until" gorm:"default:(NOW() + INTERVAL '10 minutes')"` + IsActive bool `json:"is_active" gorm:"type:bool"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` +} diff --git a/app/database/index.database.go b/app/database/index.database.go index be9830a..755627d 100644 --- a/app/database/index.database.go +++ b/app/database/index.database.go @@ -83,6 +83,8 @@ func Models() []interface{} { entity.MasterStatuses{}, entity.MasterApprovalStatuses{}, entity.Provinces{}, + entity.ForgotPasswords{}, + entity.RegistrationOtps{}, entity.UserLevels{}, entity.UserRoles{}, entity.UserRoleAccesses{}, diff --git a/app/module/users/controller/users.controller.go b/app/module/users/controller/users.controller.go index 3f9a667..2f876bb 100644 --- a/app/module/users/controller/users.controller.go +++ b/app/module/users/controller/users.controller.go @@ -25,6 +25,11 @@ type UsersController interface { Login(c *fiber.Ctx) error ParetoLogin(c *fiber.Ctx) error Delete(c *fiber.Ctx) error + SavePassword(c *fiber.Ctx) error + ResetPassword(c *fiber.Ctx) error + ForgotPassword(c *fiber.Ctx) error + OtpRequest(c *fiber.Ctx) error + OtpValidation(c *fiber.Ctx) error } func NewUsersController(usersService service.UsersService) UsersController { @@ -294,3 +299,145 @@ func (_i *usersController) Delete(c *fiber.Ctx) error { Messages: utilRes.Messages{"Users successfully deleted"}, }) } + +// SavePassword Users +// @Summary SavePassword Users +// @Description API for SavePassword Users +// @Tags Users +// @Security Bearer +// @Param payload body request.UserSavePassword true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /users/save-password [post] +func (_i *usersController) SavePassword(c *fiber.Ctx) error { + req := new(request.UserSavePassword) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + authToken := c.Get("Authorization") + + err := _i.usersService.SavePassword(*req, authToken) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Users password successfully update"}, + }) +} + +// ResetPassword Users +// @Summary ResetPassword Users +// @Description API for ResetPassword Users +// @Tags Users +// @Security Bearer +// @Param payload body request.UserResetPassword true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /users/reset-password [post] +func (_i *usersController) ResetPassword(c *fiber.Ctx) error { + req := new(request.UserResetPassword) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err := _i.usersService.ResetPassword(*req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Users password successfully reset"}, + }) +} + +// ForgotPassword Users +// @Summary ForgotPassword Users +// @Description API for ForgotPassword Users +// @Tags Users +// @Security Bearer +// @Param payload body request.UserForgotPassword true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /users/forgot-password [post] +func (_i *usersController) ForgotPassword(c *fiber.Ctx) error { + req := new(request.UserForgotPassword) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err := _i.usersService.ForgotPassword(*req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Users forgot password has sent"}, + }) +} + +// OtpRequest Users +// @Summary OtpRequest Users +// @Description API for OtpRequest Users +// @Tags Users +// @Security Bearer +// @Param payload body request.UserOtpRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /users/otp-request [post] +func (_i *usersController) OtpRequest(c *fiber.Ctx) error { + req := new(request.UserOtpRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err := _i.usersService.OtpRequest(*req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Otp has sent"}, + }) +} + +// OtpValidation Users +// @Summary OtpValidation Users +// @Description API for OtpValidation Users +// @Tags Users +// @Security Bearer +// @Param payload body request.UserOtpValidation true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /users/otp-validation [post] +func (_i *usersController) OtpValidation(c *fiber.Ctx) error { + req := new(request.UserOtpValidation) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err := _i.usersService.OtpValidation(*req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"OTP is valid"}, + }) +} diff --git a/app/module/users/repository/users.repository.go b/app/module/users/repository/users.repository.go index e3242e4..260346b 100644 --- a/app/module/users/repository/users.repository.go +++ b/app/module/users/repository/users.repository.go @@ -20,9 +20,15 @@ type UsersRepository interface { GetAll(req request.UsersQueryRequest) (userss []*entity.Users, paging paginator.Pagination, err error) FindOne(id uint) (users *entity.Users, err error) FindByKeycloakId(keycloakId string) (users *entity.Users, err error) + FindByUsername(username string) (users *entity.Users, err error) Create(users *entity.Users) (userReturn *entity.Users, err error) Update(id uint, users *entity.Users) (err error) Delete(id uint) (err error) + CreateForgotPassword(forgotPasswords *entity.ForgotPasswords) (err error) + UpdateForgotPassword(id uint, forgotPasswords *entity.ForgotPasswords) (err error) + FindForgotPassword(keycloakId string, code string) (forgotPasswords *entity.ForgotPasswords, err error) + CreateRegistrationOtps(registrationOtps *entity.RegistrationOtps) (err error) + FindRegistrationOtps(email string, code string) (registrationOtps *entity.RegistrationOtps, err error) } func NewUsersRepository(db *database.Database, log zerolog.Logger) UsersRepository { @@ -116,6 +122,14 @@ func (_i *usersRepository) FindByKeycloakId(keycloakId string) (users *entity.Us return users, nil } +func (_i *usersRepository) FindByUsername(username string) (users *entity.Users, err error) { + if err := _i.DB.DB.Where("username = ?", username).First(&users).Error; err != nil { + return nil, err + } + + return users, nil +} + func (_i *usersRepository) Create(users *entity.Users) (userReturn *entity.Users, err error) { result := _i.DB.DB.Create(users) return users, result.Error @@ -130,3 +144,38 @@ func (_i *usersRepository) Update(id uint, users *entity.Users) (err error) { func (_i *usersRepository) Delete(id uint) error { return _i.DB.DB.Delete(&entity.Users{}, id).Error } + +func (_i *usersRepository) CreateForgotPassword(forgotPasswords *entity.ForgotPasswords) (err error) { + result := _i.DB.DB.Create(forgotPasswords) + return result.Error +} + +func (_i *usersRepository) UpdateForgotPassword(id uint, forgotPasswords *entity.ForgotPasswords) (err error) { + _i.Log.Info().Interface("forgotPasswords", id).Msg("") + _i.Log.Info().Interface("forgotPasswords", forgotPasswords).Msg("") + + return _i.DB.DB.Model(&entity.ForgotPasswords{}). + Where(&entity.ForgotPasswords{ID: id}). + Updates(forgotPasswords).Error +} + +func (_i *usersRepository) FindForgotPassword(keycloakId string, code string) (forgotPasswords *entity.ForgotPasswords, err error) { + if err := _i.DB.DB.Where("keycloak_id = ?", keycloakId).Where("code_request = ?", code).Where("is_active = ?", true).First(&forgotPasswords).Error; err != nil { + return nil, err + } + + return forgotPasswords, nil +} + +func (_i *usersRepository) CreateRegistrationOtps(registrationOtps *entity.RegistrationOtps) (err error) { + result := _i.DB.DB.Create(registrationOtps) + return result.Error +} + +func (_i *usersRepository) FindRegistrationOtps(email string, code string) (registrationOtps *entity.RegistrationOtps, err error) { + if err := _i.DB.DB.Where("email = ?", email).Where("otp_code = ?", code).First(®istrationOtps).Error; err != nil { + return nil, err + } + + return registrationOtps, nil +} diff --git a/app/module/users/request/users.request.go b/app/module/users/request/users.request.go index 3a3c4d2..9fddd32 100644 --- a/app/module/users/request/users.request.go +++ b/app/module/users/request/users.request.go @@ -114,6 +114,31 @@ type UserLogin struct { RefreshToken *string `json:"refreshToken"` } +type UserForgotPassword struct { + Username string `json:"username"` +} + +type UserSavePassword struct { + Password string `json:"password"` + ConfirmPassword string `json:"confirmPassword"` +} + +type UserResetPassword struct { + CodeRequest string `json:"codeRequest"` + UserId string `json:"userId"` + Password string `json:"password"` + ConfirmPassword string `json:"confirmPassword"` +} + +type UserOtpRequest struct { + Email string `json:"email"` +} + +type UserOtpValidation struct { + Email string `json:"email"` + OtpCode string `json:"otpCode"` +} + type UsersQueryRequestContext struct { Username string `json:"username"` Email string `json:"email"` diff --git a/app/module/users/service/users.service.go b/app/module/users/service/users.service.go index 8e48020..df0bf50 100644 --- a/app/module/users/service/users.service.go +++ b/app/module/users/service/users.service.go @@ -38,6 +38,11 @@ type UsersService interface { ParetoLogin(req request.UserLogin) (res *response.ParetoLoginResponse, err error) Update(id uint, req request.UsersUpdateRequest) (err error) Delete(id uint) error + SavePassword(req request.UserSavePassword, authToken string) (err error) + ResetPassword(req request.UserResetPassword) (err error) + ForgotPassword(req request.UserForgotPassword) (err error) + OtpRequest(req request.UserOtpRequest) (err error) + OtpValidation(req request.UserOtpValidation) (err error) } // NewUsersService init UsersService @@ -220,6 +225,139 @@ func (_i *usersService) Delete(id uint) error { return _i.Repo.Update(id, result) } +func (_i *usersService) SavePassword(req request.UserSavePassword, authToken string) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + + _i.Log.Info().Interface("AUTH TOKEN", authToken).Msg("") + + if authToken != "" { + createdBy := utilSvc.GetUserInfo(_i.Log, _i.Repo, authToken) + + err := _i.Keycloak.SetPassword(authToken, *createdBy.KeycloakId, req.Password) + if err != nil { + return err + } + + return nil + } else { + return fmt.Errorf("Invalid token") + } +} + +func (_i *usersService) ResetPassword(req request.UserResetPassword) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + + if req.Password != req.ConfirmPassword { + return fmt.Errorf("Invalid Password") + } + + user, err := _i.Repo.FindByKeycloakId(req.UserId) + if err != nil { + return fmt.Errorf("User Id Not Found") + } + + forgotPassword, err := _i.Repo.FindForgotPassword(req.UserId, req.CodeRequest) + if err != nil { + return fmt.Errorf("Invalid Request") + } + + _i.Log.Info().Interface("data", forgotPassword).Msg("") + + _i.Log.Info().Interface("dataForgotPassword", forgotPassword).Msg("") + + if user != nil { + err := _i.Keycloak.SetPasswordWithoutToken(req.UserId, req.Password) + if err != nil { + return err + } + + forgotPassword.IsActive = false + forgotPassword.UpdatedAt = time.Now() + err = _i.Repo.UpdateForgotPassword(forgotPassword.ID, forgotPassword) + if err != nil { + return err + } + + return nil + } else { + return fmt.Errorf("User not found") + } +} + +func (_i *usersService) ForgotPassword(req request.UserForgotPassword) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + + user, err := _i.Repo.FindByUsername(req.Username) + if err != nil { + return err + } + + if user != nil { + codeRequest, err := utilSvc.GenerateNumericCode(8) + if err != nil { + return nil + } + forgotPasswordReq := entity.ForgotPasswords{ + KeycloakID: *user.KeycloakId, + CodeRequest: codeRequest, + IsActive: true, + } + + err = _i.Repo.CreateForgotPassword(&forgotPasswordReq) + if err != nil { + return err + } + + // send email forgot password + + return nil + } else { + return fmt.Errorf("User not found") + } +} + +func (_i *usersService) OtpRequest(req request.UserOtpRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + + codeRequest, err := utilSvc.GenerateNumericCode(6) + if err != nil { + return err + } + otpReq := entity.RegistrationOtps{ + Email: req.Email, + OtpCode: codeRequest, + IsActive: true, + } + + err = _i.Repo.CreateRegistrationOtps(&otpReq) + if err != nil { + return err + } + + // send otp to email + + return nil +} + +func (_i *usersService) OtpValidation(req request.UserOtpValidation) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + + registrationOtp, err := _i.Repo.FindRegistrationOtps(req.Email, req.OtpCode) + if err != nil { + return fmt.Errorf("OTP is not valid") + } + + if registrationOtp != nil { + if registrationOtp.ValidUntil.Before(time.Now()) { + return fmt.Errorf("OTP has expired") + } + + return nil + } else { + return fmt.Errorf("OTP is not valid") + } +} + func ParseJWTToken(token string) (map[string]interface{}, error) { // Pisahkan JWT menjadi 3 bagian: header, payload, dan signature parts := strings.Split(token, ".") diff --git a/app/module/users/users.module.go b/app/module/users/users.module.go index ba8e856..025dca0 100644 --- a/app/module/users/users.module.go +++ b/app/module/users/users.module.go @@ -52,5 +52,11 @@ func (_i *UsersRouter) RegisterUsersRoutes() { router.Post("/login", usersController.Login) router.Post("/pareto-login", usersController.ParetoLogin) router.Delete("/:id", usersController.Delete) + router.Post("/save-password", usersController.SavePassword) + router.Post("/reset-password", usersController.ResetPassword) + router.Post("/forgot-password", usersController.ForgotPassword) + router.Post("/otp-request", usersController.OtpRequest) + router.Post("/otp-validation", usersController.OtpValidation) + }) } diff --git a/config/config/keycloak.config.go b/config/config/keycloak.config.go index f02d442..85fb4d5 100644 --- a/config/config/keycloak.config.go +++ b/config/config/keycloak.config.go @@ -137,3 +137,27 @@ func (_keycloak *KeycloakConfig) SetPassword(token string, keycloakId string, pa return nil } + +func (_keycloak *KeycloakConfig) SetPasswordWithoutToken(keycloakId string, password string) error { + ctx := context.Background() + client := gocloak.NewClient(_keycloak.Cfg.Keycloak.Endpoint) + + token, err := client.Login( + ctx, + _keycloak.Cfg.Keycloak.ClientId, + _keycloak.Cfg.Keycloak.ClientSecret, + _keycloak.Cfg.Keycloak.Realm, + _keycloak.Cfg.Keycloak.AdminUsername, + _keycloak.Cfg.Keycloak.AdminPassword, + ) + if err != nil { + panic("Something wrong with the credentials or url") + } + + err = client.SetPassword(ctx, token.AccessToken, keycloakId, _keycloak.Cfg.Keycloak.Realm, password, false) + if err != nil { + panic("Oh no!, failed to set password :(") + } + + return nil +} diff --git a/docs/swagger/docs.go b/docs/swagger/docs.go index 52a01ad..47fb79b 100644 --- a/docs/swagger/docs.go +++ b/docs/swagger/docs.go @@ -6024,6 +6024,57 @@ const docTemplate = `{ } } }, + "/users/forgot-password": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for ForgotPassword Users", + "tags": [ + "Users" + ], + "summary": "ForgotPassword Users", + "parameters": [ + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserForgotPassword" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, "/users/info": { "get": { "security": [ @@ -6125,6 +6176,108 @@ const docTemplate = `{ } } }, + "/users/otp-request": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for OtpRequest Users", + "tags": [ + "Users" + ], + "summary": "OtpRequest Users", + "parameters": [ + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserOtpRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/users/otp-validation": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for OtpValidation Users", + "tags": [ + "Users" + ], + "summary": "OtpValidation Users", + "parameters": [ + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserOtpValidation" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, "/users/pareto-login": { "post": { "security": [ @@ -6176,6 +6329,108 @@ const docTemplate = `{ } } }, + "/users/reset-password": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for ResetPassword Users", + "tags": [ + "Users" + ], + "summary": "ResetPassword Users", + "parameters": [ + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserResetPassword" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/users/save-password": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for SavePassword Users", + "tags": [ + "Users" + ], + "summary": "SavePassword Users", + "parameters": [ + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserSavePassword" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, "/users/{id}": { "put": { "security": [ @@ -6839,6 +7094,14 @@ const docTemplate = `{ } } }, + "request.UserForgotPassword": { + "type": "object", + "properties": { + "username": { + "type": "string" + } + } + }, "request.UserLevelsCreateRequest": { "type": "object", "required": [ @@ -6910,6 +7173,42 @@ const docTemplate = `{ } } }, + "request.UserOtpRequest": { + "type": "object", + "properties": { + "email": { + "type": "string" + } + } + }, + "request.UserOtpValidation": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "otpCode": { + "type": "string" + } + } + }, + "request.UserResetPassword": { + "type": "object", + "properties": { + "codeRequest": { + "type": "string" + }, + "confirmPassword": { + "type": "string" + }, + "password": { + "type": "string" + }, + "userId": { + "type": "string" + } + } + }, "request.UserRoleAccessesCreateRequest": { "type": "object", "required": [ @@ -7059,6 +7358,17 @@ const docTemplate = `{ } } }, + "request.UserSavePassword": { + "type": "object", + "properties": { + "confirmPassword": { + "type": "string" + }, + "password": { + "type": "string" + } + } + }, "request.UsersCreateRequest": { "type": "object", "required": [ diff --git a/docs/swagger/swagger.json b/docs/swagger/swagger.json index 6bc6edd..2926a49 100644 --- a/docs/swagger/swagger.json +++ b/docs/swagger/swagger.json @@ -6013,6 +6013,57 @@ } } }, + "/users/forgot-password": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for ForgotPassword Users", + "tags": [ + "Users" + ], + "summary": "ForgotPassword Users", + "parameters": [ + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserForgotPassword" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, "/users/info": { "get": { "security": [ @@ -6114,6 +6165,108 @@ } } }, + "/users/otp-request": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for OtpRequest Users", + "tags": [ + "Users" + ], + "summary": "OtpRequest Users", + "parameters": [ + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserOtpRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/users/otp-validation": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for OtpValidation Users", + "tags": [ + "Users" + ], + "summary": "OtpValidation Users", + "parameters": [ + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserOtpValidation" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, "/users/pareto-login": { "post": { "security": [ @@ -6165,6 +6318,108 @@ } } }, + "/users/reset-password": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for ResetPassword Users", + "tags": [ + "Users" + ], + "summary": "ResetPassword Users", + "parameters": [ + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserResetPassword" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/users/save-password": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for SavePassword Users", + "tags": [ + "Users" + ], + "summary": "SavePassword Users", + "parameters": [ + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserSavePassword" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, "/users/{id}": { "put": { "security": [ @@ -6828,6 +7083,14 @@ } } }, + "request.UserForgotPassword": { + "type": "object", + "properties": { + "username": { + "type": "string" + } + } + }, "request.UserLevelsCreateRequest": { "type": "object", "required": [ @@ -6899,6 +7162,42 @@ } } }, + "request.UserOtpRequest": { + "type": "object", + "properties": { + "email": { + "type": "string" + } + } + }, + "request.UserOtpValidation": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "otpCode": { + "type": "string" + } + } + }, + "request.UserResetPassword": { + "type": "object", + "properties": { + "codeRequest": { + "type": "string" + }, + "confirmPassword": { + "type": "string" + }, + "password": { + "type": "string" + }, + "userId": { + "type": "string" + } + } + }, "request.UserRoleAccessesCreateRequest": { "type": "object", "required": [ @@ -7048,6 +7347,17 @@ } } }, + "request.UserSavePassword": { + "type": "object", + "properties": { + "confirmPassword": { + "type": "string" + }, + "password": { + "type": "string" + } + } + }, "request.UsersCreateRequest": { "type": "object", "required": [ diff --git a/docs/swagger/swagger.yaml b/docs/swagger/swagger.yaml index b801057..e0433f8 100644 --- a/docs/swagger/swagger.yaml +++ b/docs/swagger/swagger.yaml @@ -384,6 +384,11 @@ definitions: - pathUrl - statusId type: object + request.UserForgotPassword: + properties: + username: + type: string + type: object request.UserLevelsCreateRequest: properties: aliasName: @@ -433,6 +438,29 @@ definitions: username: type: string type: object + request.UserOtpRequest: + properties: + email: + type: string + type: object + request.UserOtpValidation: + properties: + email: + type: string + otpCode: + type: string + type: object + request.UserResetPassword: + properties: + codeRequest: + type: string + confirmPassword: + type: string + password: + type: string + userId: + type: string + type: object request.UserRoleAccessesCreateRequest: properties: isAdminEnabled: @@ -539,6 +567,13 @@ definitions: - status_id - userLevelIds type: object + request.UserSavePassword: + properties: + confirmPassword: + type: string + password: + type: string + type: object request.UsersCreateRequest: properties: address: @@ -4541,6 +4576,38 @@ paths: summary: Get one Users tags: - Users + /users/forgot-password: + post: + description: API for ForgotPassword Users + parameters: + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.UserForgotPassword' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: ForgotPassword Users + tags: + - Users /users/info: get: description: API for ShowUserInfo @@ -4605,6 +4672,70 @@ paths: summary: Login Users tags: - Users + /users/otp-request: + post: + description: API for OtpRequest Users + parameters: + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.UserOtpRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: OtpRequest Users + tags: + - Users + /users/otp-validation: + post: + description: API for OtpValidation Users + parameters: + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.UserOtpValidation' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: OtpValidation Users + tags: + - Users /users/pareto-login: post: description: API for ParetoLogin Users @@ -4637,4 +4768,68 @@ paths: summary: ParetoLogin Users tags: - Users + /users/reset-password: + post: + description: API for ResetPassword Users + parameters: + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.UserResetPassword' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: ResetPassword Users + tags: + - Users + /users/save-password: + post: + description: API for SavePassword Users + parameters: + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.UserSavePassword' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: SavePassword Users + tags: + - Users swagger: "2.0" diff --git a/utils/service/string.service.go b/utils/service/string.service.go index 9ed2bec..73e4c4e 100644 --- a/utils/service/string.service.go +++ b/utils/service/string.service.go @@ -1,6 +1,8 @@ package service import ( + "crypto/rand" + "math/big" "strings" "unicode" ) @@ -29,3 +31,18 @@ func MakeSlug(s string) string { return -1 }, s) } + +func GenerateNumericCode(codeLength int) (string, error) { + const digits = "0123456789" + result := make([]byte, codeLength) + + for i := 0; i < codeLength; i++ { + num, err := rand.Int(rand.Reader, big.NewInt(int64(len(digits)))) + if err != nil { + return "", err + } + result[i] = digits[num.Int64()] + } + + return string(result), nil +}