562 lines
17 KiB
Go
562 lines
17 KiB
Go
|
|
package service
|
||
|
|
|
||
|
|
import (
|
||
|
|
"encoding/base64"
|
||
|
|
"encoding/json"
|
||
|
|
"fmt"
|
||
|
|
"strings"
|
||
|
|
"time"
|
||
|
|
"web-medols-be/app/database/entity"
|
||
|
|
"web-medols-be/app/database/entity/users"
|
||
|
|
userLevelsRepository "web-medols-be/app/module/user_levels/repository"
|
||
|
|
"web-medols-be/app/module/users/mapper"
|
||
|
|
"web-medols-be/app/module/users/repository"
|
||
|
|
"web-medols-be/app/module/users/request"
|
||
|
|
"web-medols-be/app/module/users/response"
|
||
|
|
"web-medols-be/config/config"
|
||
|
|
"web-medols-be/utils/paginator"
|
||
|
|
utilSvc "web-medols-be/utils/service"
|
||
|
|
|
||
|
|
paseto "aidanwoods.dev/go-paseto"
|
||
|
|
"github.com/Nerzal/gocloak/v13"
|
||
|
|
"github.com/google/uuid"
|
||
|
|
"github.com/rs/zerolog"
|
||
|
|
)
|
||
|
|
|
||
|
|
// UsersService
|
||
|
|
type usersService struct {
|
||
|
|
Repo repository.UsersRepository
|
||
|
|
UserLevelsRepo userLevelsRepository.UserLevelsRepository
|
||
|
|
Log zerolog.Logger
|
||
|
|
Keycloak *config.KeycloakConfig
|
||
|
|
Smtp *config.SmtpConfig
|
||
|
|
}
|
||
|
|
|
||
|
|
// UsersService define interface of IUsersService
|
||
|
|
type UsersService interface {
|
||
|
|
All(clientId *uuid.UUID, req request.UsersQueryRequest) (users []*response.UsersResponse, paging paginator.Pagination, err error)
|
||
|
|
Show(clientId *uuid.UUID, id uint) (users *response.UsersResponse, err error)
|
||
|
|
ShowByUsername(clientId *uuid.UUID, username string) (users *response.UsersResponse, err error)
|
||
|
|
ShowUserInfo(clientId *uuid.UUID, authToken string) (users *response.UsersResponse, err error)
|
||
|
|
Save(clientId *uuid.UUID, req request.UsersCreateRequest, authToken string) (userReturn *users.Users, err error)
|
||
|
|
Login(req request.UserLogin) (res *gocloak.JWT, err error)
|
||
|
|
ParetoLogin(req request.UserLogin) (res *response.ParetoLoginResponse, err error)
|
||
|
|
Update(clientId *uuid.UUID, id uint, req request.UsersUpdateRequest) (err error)
|
||
|
|
Delete(clientId *uuid.UUID, id uint) error
|
||
|
|
SavePassword(clientId *uuid.UUID, req request.UserSavePassword, authToken string) (err error)
|
||
|
|
ResetPassword(req request.UserResetPassword) (err error)
|
||
|
|
ForgotPassword(clientId *uuid.UUID, req request.UserForgotPassword) (err error)
|
||
|
|
EmailValidationPreLogin(clientId *uuid.UUID, req request.UserEmailValidationRequest) (msgResponse *string, err error)
|
||
|
|
SetupEmail(clientId *uuid.UUID, req request.UserEmailValidationRequest) (msgResponse *string, err error)
|
||
|
|
OtpRequest(req request.UserOtpRequest) (err error)
|
||
|
|
OtpValidation(req request.UserOtpValidation) (err error)
|
||
|
|
SendLoginOtp(name string, email string, otp string) error
|
||
|
|
SendRegistrationOtp(name string, email string, otp string) error
|
||
|
|
}
|
||
|
|
|
||
|
|
// NewUsersService init UsersService
|
||
|
|
func NewUsersService(repo repository.UsersRepository, userLevelsRepo userLevelsRepository.UserLevelsRepository, log zerolog.Logger, keycloak *config.KeycloakConfig, smtp *config.SmtpConfig) UsersService {
|
||
|
|
|
||
|
|
return &usersService{
|
||
|
|
Repo: repo,
|
||
|
|
UserLevelsRepo: userLevelsRepo,
|
||
|
|
Log: log,
|
||
|
|
Keycloak: keycloak,
|
||
|
|
Smtp: smtp,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// All implement interface of UsersService
|
||
|
|
func (_i *usersService) All(clientId *uuid.UUID, req request.UsersQueryRequest) (users []*response.UsersResponse, paging paginator.Pagination, err error) {
|
||
|
|
results, paging, err := _i.Repo.GetAll(clientId, req)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
for _, result := range results {
|
||
|
|
users = append(users, mapper.UsersResponseMapper(result, _i.UserLevelsRepo, clientId))
|
||
|
|
}
|
||
|
|
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
func (_i *usersService) Show(clientId *uuid.UUID, id uint) (users *response.UsersResponse, err error) {
|
||
|
|
result, err := _i.Repo.FindOne(clientId, id)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
return mapper.UsersResponseMapper(result, _i.UserLevelsRepo, clientId), nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (_i *usersService) ShowByUsername(clientId *uuid.UUID, username string) (users *response.UsersResponse, err error) {
|
||
|
|
result, err := _i.Repo.FindByUsername(clientId, username)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
return mapper.UsersResponseMapper(result, _i.UserLevelsRepo, clientId), nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (_i *usersService) ShowUserInfo(clientId *uuid.UUID, authToken string) (users *response.UsersResponse, err error) {
|
||
|
|
userInfo := utilSvc.GetUserInfo(_i.Log, _i.Repo, authToken)
|
||
|
|
|
||
|
|
return mapper.UsersResponseMapper(userInfo, _i.UserLevelsRepo, clientId), nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (_i *usersService) Save(clientId *uuid.UUID, req request.UsersCreateRequest, authToken string) (userReturn *users.Users, err error) {
|
||
|
|
_i.Log.Info().Interface("data", req).Msg("")
|
||
|
|
newReq := req.ToEntity()
|
||
|
|
|
||
|
|
_i.Log.Info().Interface("AUTH TOKEN", authToken).Msg("")
|
||
|
|
|
||
|
|
if authToken != "" {
|
||
|
|
createdBy := utilSvc.GetUserInfo(_i.Log, _i.Repo, authToken)
|
||
|
|
newReq.CreatedById = &createdBy.ID
|
||
|
|
}
|
||
|
|
|
||
|
|
keycloakId, err := _i.Keycloak.CreateUser(req.Fullname, req.Email, req.Username, req.Password)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
newReq.KeycloakId = &keycloakId
|
||
|
|
newReq.TempPassword = &req.Password
|
||
|
|
|
||
|
|
// Set ClientId on entity
|
||
|
|
newReq.ClientId = clientId
|
||
|
|
|
||
|
|
return _i.Repo.Create(newReq)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (_i *usersService) Login(req request.UserLogin) (res *gocloak.JWT, err error) {
|
||
|
|
_i.Log.Info().Interface("data", req).Msg("")
|
||
|
|
|
||
|
|
var loginResponse *gocloak.JWT
|
||
|
|
if req.RefreshToken == nil {
|
||
|
|
loginResponse, err = _i.Keycloak.Login(*req.Username, *req.Password)
|
||
|
|
} else {
|
||
|
|
loginResponse, err = _i.Keycloak.RefreshToken(*req.RefreshToken)
|
||
|
|
}
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
return loginResponse, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (_i *usersService) ParetoLogin(req request.UserLogin) (res *response.ParetoLoginResponse, err error) {
|
||
|
|
_i.Log.Info().Interface("data", req).Msg("")
|
||
|
|
|
||
|
|
var loginResponse *gocloak.JWT
|
||
|
|
token := paseto.NewToken()
|
||
|
|
secretKeyHex := "bdc42b1a0ba2bac3e27ba84241f9de06dee71b70f838af8d1beb0417f03d1d00"
|
||
|
|
secretKey, _ := paseto.V4SymmetricKeyFromHex(secretKeyHex)
|
||
|
|
// secretKey := paseto.NewV4SymmetricKey() // to change the secretKey periodically
|
||
|
|
|
||
|
|
if req.RefreshToken == nil {
|
||
|
|
loginResponse, err = _i.Keycloak.Login(*req.Username, *req.Password)
|
||
|
|
} else {
|
||
|
|
// Retrieve Refresh Token
|
||
|
|
parser := paseto.NewParser()
|
||
|
|
verifiedToken, err := parser.ParseV4Local(secretKey, *req.RefreshToken, nil)
|
||
|
|
if err != nil {
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
|
||
|
|
refreshToken, _ := verifiedToken.GetString("refresh_token")
|
||
|
|
_i.Log.Info().Interface("Pareto parse refresh token", refreshToken).Msg("")
|
||
|
|
|
||
|
|
loginResponse, err = _i.Keycloak.RefreshToken(refreshToken)
|
||
|
|
}
|
||
|
|
|
||
|
|
_i.Log.Info().Interface("loginResponse", loginResponse).Msg("")
|
||
|
|
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
parseToken, err := ParseJWTToken(loginResponse.AccessToken)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
issuedAt := parseToken["iat"].(float64)
|
||
|
|
expirationTime := parseToken["exp"].(float64)
|
||
|
|
issuer := parseToken["iss"].(string)
|
||
|
|
jti := parseToken["jti"].(string)
|
||
|
|
subject := parseToken["sub"].(string)
|
||
|
|
|
||
|
|
token.SetIssuedAt(time.Unix(int64(issuedAt), 0))
|
||
|
|
token.SetNotBefore(time.Unix(int64(issuedAt), 0))
|
||
|
|
token.SetExpiration(time.Unix(int64(expirationTime), 0))
|
||
|
|
token.SetIssuer(issuer)
|
||
|
|
token.SetJti(jti)
|
||
|
|
token.SetSubject(subject)
|
||
|
|
token.SetString("access_token", loginResponse.AccessToken)
|
||
|
|
token.SetString("refresh_token", loginResponse.RefreshToken)
|
||
|
|
|
||
|
|
_i.Log.Info().Interface("Pareto Generated Key", secretKey.ExportHex()).Msg("")
|
||
|
|
|
||
|
|
tokenEncrypted := token.V4Encrypt(secretKey, nil)
|
||
|
|
|
||
|
|
parser := paseto.NewParser()
|
||
|
|
verifiedToken, err := parser.ParseV4Local(secretKey, tokenEncrypted, nil)
|
||
|
|
if err != nil {
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
|
||
|
|
tokenParsing, _ := verifiedToken.GetString("access_token")
|
||
|
|
_i.Log.Info().Interface("Pareto parse token", tokenParsing).Msg("")
|
||
|
|
|
||
|
|
resLogin := &response.ParetoLoginResponse{
|
||
|
|
AccessToken: tokenEncrypted,
|
||
|
|
}
|
||
|
|
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
return resLogin, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (_i *usersService) Update(clientId *uuid.UUID, id uint, req request.UsersUpdateRequest) (err error) {
|
||
|
|
_i.Log.Info().Interface("data", req).Msg("")
|
||
|
|
newReq := req.ToEntity()
|
||
|
|
|
||
|
|
findUser, err := _i.Repo.FindOne(clientId, id)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
err = _i.Keycloak.UpdateUser(findUser.KeycloakId, req.Fullname, req.Email)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
// Set ClientId on entity
|
||
|
|
newReq.ClientId = clientId
|
||
|
|
|
||
|
|
return _i.Repo.Update(clientId, id, newReq)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (_i *usersService) Delete(clientId *uuid.UUID, id uint) error {
|
||
|
|
result, err := _i.Repo.FindOne(clientId, id)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
isActive := false
|
||
|
|
result.IsActive = &isActive
|
||
|
|
return _i.Repo.Update(clientId, id, result)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (_i *usersService) SavePassword(clientId *uuid.UUID, 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)
|
||
|
|
|
||
|
|
tokenString := strings.TrimPrefix(authToken, "Bearer ")
|
||
|
|
|
||
|
|
err := _i.Keycloak.SetPassword(tokenString, *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(clientId *uuid.UUID, req request.UserForgotPassword) (err error) {
|
||
|
|
_i.Log.Info().Interface("data", req).Msg("")
|
||
|
|
|
||
|
|
user, err := _i.Repo.FindByUsername(clientId, 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
|
||
|
|
url := fmt.Sprintf("https://kontenhumas.com/setup-password?userId=%s&code=%s", *user.KeycloakId, codeRequest)
|
||
|
|
|
||
|
|
subject := "[HUMAS POLRI] Forgot Password"
|
||
|
|
htmlBody := "<p>Anda telah mengirimkan permintaan untuk melakukan reset password.</p>"
|
||
|
|
htmlBody += "<p style='padding-bottom: 10px;'>Silahkan buat password akun anda dengan menekan tombol di bawah ini, untuk membuat password baru</p>"
|
||
|
|
htmlBody += fmt.Sprintf("<a style='padding:8px 50px;border-radius:28px;background-color:#37c2b6;text-decoration:none;color:#f4f5f5;' href='%s'>Reset Password</a>", url)
|
||
|
|
htmlBody += "<p style='padding-top: 10px;'>Terimakasih.</p>"
|
||
|
|
err = _i.Smtp.SendEmail(subject, user.Email, user.Fullname, htmlBody)
|
||
|
|
|
||
|
|
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 req.Name == nil {
|
||
|
|
req.Name = &req.Email
|
||
|
|
}
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
otpReq := entity.OneTimePasswords{
|
||
|
|
Email: req.Email,
|
||
|
|
Name: req.Name,
|
||
|
|
OtpCode: codeRequest,
|
||
|
|
IsActive: true,
|
||
|
|
}
|
||
|
|
|
||
|
|
err = _i.Repo.CreateOtp(&otpReq)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
err = _i.SendRegistrationOtp(*req.Name, req.Email, codeRequest)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (_i *usersService) OtpValidation(req request.UserOtpValidation) (err error) {
|
||
|
|
_i.Log.Info().Interface("data", req).Msg("")
|
||
|
|
|
||
|
|
var otp *entity.OneTimePasswords
|
||
|
|
if req.Email == nil {
|
||
|
|
otp, err = _i.Repo.FindOtpByIdentity(*req.Username, req.OtpCode)
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("OTP is not valid")
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
otp, err = _i.Repo.FindOtpByEmail(*req.Email, req.OtpCode)
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("OTP is not valid")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if otp != nil {
|
||
|
|
if otp.ValidUntil.Before(time.Now()) {
|
||
|
|
return fmt.Errorf("OTP has expired")
|
||
|
|
}
|
||
|
|
|
||
|
|
return nil
|
||
|
|
} else {
|
||
|
|
return fmt.Errorf("OTP is not valid")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func (_i *usersService) EmailValidationPreLogin(clientId *uuid.UUID, req request.UserEmailValidationRequest) (msgResponse *string, err error) {
|
||
|
|
_i.Log.Info().Interface("data", req).Msg("")
|
||
|
|
|
||
|
|
var loginResponse *gocloak.JWT
|
||
|
|
loginResponse, err = _i.Keycloak.Login(*req.Username, *req.Password)
|
||
|
|
|
||
|
|
if loginResponse == nil || err != nil {
|
||
|
|
return nil, fmt.Errorf("username / password incorrect")
|
||
|
|
}
|
||
|
|
|
||
|
|
findUser, err := _i.Repo.FindByUsername(clientId, *req.Username)
|
||
|
|
if findUser == nil || err != nil {
|
||
|
|
return nil, fmt.Errorf("username / password incorrect")
|
||
|
|
}
|
||
|
|
|
||
|
|
_i.Log.Info().Interface("data user", findUser).Msg("")
|
||
|
|
|
||
|
|
if *findUser.IsEmailUpdated != true {
|
||
|
|
message := "Continue to setup email"
|
||
|
|
msgResponse = &message
|
||
|
|
} else {
|
||
|
|
codeRequest, err := utilSvc.GenerateNumericCode(6)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
otpReq := entity.OneTimePasswords{
|
||
|
|
Email: findUser.Email,
|
||
|
|
Identity: &findUser.Username,
|
||
|
|
OtpCode: codeRequest,
|
||
|
|
IsActive: true,
|
||
|
|
}
|
||
|
|
|
||
|
|
err = _i.Repo.CreateOtp(&otpReq)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
err = _i.SendLoginOtp(findUser.Fullname, findUser.Email, codeRequest)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
} else {
|
||
|
|
msg := "Email is valid and OTP has been sent"
|
||
|
|
msgResponse = &msg
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return msgResponse, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (_i *usersService) SetupEmail(clientId *uuid.UUID, req request.UserEmailValidationRequest) (msgResponse *string, err error) {
|
||
|
|
_i.Log.Info().Interface("data", req).Msg("")
|
||
|
|
|
||
|
|
var loginResponse *gocloak.JWT
|
||
|
|
loginResponse, err = _i.Keycloak.Login(*req.Username, *req.Password)
|
||
|
|
|
||
|
|
if loginResponse == nil || err != nil {
|
||
|
|
return nil, fmt.Errorf("username / password incorrect")
|
||
|
|
}
|
||
|
|
|
||
|
|
_i.Log.Info().Interface("findUser", "").Msg("")
|
||
|
|
findUser, err := _i.Repo.FindByUsername(clientId, *req.Username)
|
||
|
|
|
||
|
|
_i.Log.Info().Interface("findUser", findUser).Msg("")
|
||
|
|
if findUser == nil || err != nil {
|
||
|
|
return nil, fmt.Errorf("username / password incorrect")
|
||
|
|
}
|
||
|
|
|
||
|
|
isTrue := true
|
||
|
|
if findUser.Email == *req.OldEmail {
|
||
|
|
findUser.Email = *req.NewEmail
|
||
|
|
findUser.IsEmailUpdated = &isTrue
|
||
|
|
_i.Log.Info().Interface("Update", "").Msg("")
|
||
|
|
err = _i.Repo.Update(clientId, findUser.ID, findUser)
|
||
|
|
_i.Log.Info().Interface("Update", err).Msg("")
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
codeRequest, err := utilSvc.GenerateNumericCode(6)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
otpReq := entity.OneTimePasswords{
|
||
|
|
Email: findUser.Email,
|
||
|
|
Identity: &findUser.Username,
|
||
|
|
OtpCode: codeRequest,
|
||
|
|
IsActive: true,
|
||
|
|
}
|
||
|
|
|
||
|
|
err = _i.Repo.CreateOtp(&otpReq)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
err = _i.SendLoginOtp(findUser.Fullname, findUser.Email, codeRequest)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
} else {
|
||
|
|
msg := "Email is valid and OTP has been sent"
|
||
|
|
msgResponse = &msg
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
return nil, fmt.Errorf("the old email is not same")
|
||
|
|
}
|
||
|
|
|
||
|
|
return msgResponse, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func ParseJWTToken(token string) (map[string]interface{}, error) {
|
||
|
|
// Pisahkan JWT menjadi 3 bagian: header, payload, dan signature
|
||
|
|
parts := strings.Split(token, ".")
|
||
|
|
if len(parts) != 3 {
|
||
|
|
return nil, fmt.Errorf("Invalid JWT token")
|
||
|
|
}
|
||
|
|
|
||
|
|
// Decode bagian payload (index ke-1)
|
||
|
|
payloadData, err := base64.RawURLEncoding.DecodeString(parts[1])
|
||
|
|
if err != nil {
|
||
|
|
return nil, fmt.Errorf("Failed to decode payload: %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Ubah payload menjadi map[string]interface{}
|
||
|
|
var payload map[string]interface{}
|
||
|
|
if err := json.Unmarshal(payloadData, &payload); err != nil {
|
||
|
|
return nil, fmt.Errorf("Failed to parse payload JSON: %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
return payload, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (_i *usersService) SendLoginOtp(name string, email string, otp string) error {
|
||
|
|
subject := "[HUMAS POLRI] Permintaan OTP"
|
||
|
|
htmlBody := fmt.Sprintf("<p>Hai %s !</p><p>Berikut kode OTP yang digunakan untuk Login.</p>", name)
|
||
|
|
htmlBody += fmt.Sprintf("<p style='padding: 10px 50px; background: #eef2f6; border-radius: 8px; max-width: 300px; text-align: center'><b>%s</b></p>", otp)
|
||
|
|
htmlBody += "<p style='padding-top: 10px;'>Kode diatas hanya berlaku selama 10 menit. Harap segera masukkan kode tersebut pada aplikasi HUMAS POLRI.</p>"
|
||
|
|
htmlBody += "<p style='padding-top: 10px; padding-bottom: 10px'>Demi menjaga kerahasiaan data kamu, mohon jangan membagikan kode OTP ke siapapun.</p>"
|
||
|
|
err := _i.Smtp.SendEmail(subject, email, name, htmlBody)
|
||
|
|
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
func (_i *usersService) SendRegistrationOtp(name string, email string, otp string) error {
|
||
|
|
subject := "[HUMAS POLRI] Permintaan OTP"
|
||
|
|
htmlBody := fmt.Sprintf("<p>Hai %s !</p><p>Berikut kode OTP yang digunakan untuk Verifikasi Registrasi.</p>", name)
|
||
|
|
htmlBody += fmt.Sprintf("<p style='padding: 10px 50px; background: #eef2f6; border-radius: 8px; max-width: 300px; text-align: center'><b>%s</b></p>", otp)
|
||
|
|
htmlBody += "<p style='padding-top: 10px;'>Kode diatas hanya berlaku selama 10 menit. Harap segera masukkan kode tersebut pada aplikasi HUMAS POLRI.</p>"
|
||
|
|
htmlBody += "<p style='padding-top: 10px; padding-bottom: 10px'>Demi menjaga kerahasiaan data kamu, mohon jangan membagikan kode OTP ke siapapun.</p>"
|
||
|
|
err := _i.Smtp.SendEmail(subject, email, name, htmlBody)
|
||
|
|
|
||
|
|
return err
|
||
|
|
}
|