731 lines
19 KiB
Go
731 lines
19 KiB
Go
package controller
|
|
|
|
import (
|
|
"context"
|
|
"crypto/md5"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
models "kemendagri/sipd/services/sipd_auth/model"
|
|
"kemendagri/sipd/services/sipd_auth/model/form"
|
|
"kemendagri/sipd/services/sipd_auth/utils"
|
|
"net/http"
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
|
|
// "github.com/dchest/captcha"
|
|
"github.com/go-playground/validator/v10"
|
|
"github.com/jackc/pgx/v5"
|
|
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
"golang.org/x/crypto/bcrypt"
|
|
)
|
|
|
|
type AuthController struct {
|
|
contextTimeout time.Duration
|
|
pgxConn *pgxpool.Pool
|
|
pgxConnPegawai *pgxpool.Pool
|
|
pgxConnMstData *pgxpool.Pool
|
|
dbConnMapsAnggaran map[string]*pgxpool.Pool
|
|
jwtManager *utils.JWTManager
|
|
Validate *validator.Validate
|
|
}
|
|
|
|
func NewAuthController(
|
|
conn, connPegawai,
|
|
connMstData *pgxpool.Pool,
|
|
mapsDbAnggaran map[string]*pgxpool.Pool,
|
|
timeout time.Duration,
|
|
jm *utils.JWTManager,
|
|
vld *validator.Validate,
|
|
) (controller *AuthController) {
|
|
controller = &AuthController{
|
|
pgxConn: conn,
|
|
pgxConnMstData: connMstData,
|
|
pgxConnPegawai: connPegawai,
|
|
dbConnMapsAnggaran: mapsDbAnggaran,
|
|
contextTimeout: timeout,
|
|
jwtManager: jm,
|
|
Validate: vld,
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func detectHashType(s string) string {
|
|
// Cek bcrypt (biasanya panjang 60 dan prefix $2a$, $2b$, $2y$)
|
|
if len(s) == 60 && (s[:4] == "$2a$" || s[:4] == "$2b$" || s[:4] == "$2y$") {
|
|
return "bcrypt"
|
|
}
|
|
|
|
// Cek md5 (panjang 32 dan hex)
|
|
matchMD5, _ := regexp.MatchString("^[a-fA-F0-9]{32}$", s)
|
|
if matchMD5 {
|
|
return "md5"
|
|
}
|
|
|
|
return "unknown"
|
|
}
|
|
|
|
func (ac *AuthController) PreLogin(f form.PreLoginForm) (r []models.PreLoginModel, err error) {
|
|
r = make([]models.PreLoginModel, 0)
|
|
|
|
// validate captcha
|
|
// if !captcha.VerifyString(f.CaptchaId, f.CaptchaSolution) {
|
|
// err = utils.RequestError{
|
|
// Code: http.StatusUnprocessableEntity,
|
|
// Message: "invalid captcha",
|
|
// }
|
|
// return
|
|
// }
|
|
|
|
// Validate form input
|
|
err = ac.Validate.Struct(f)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
var hashed bool
|
|
var idUser, loginAttp, nextLogin int
|
|
var nipUser, namaUser, passUser string
|
|
q := `SELECT id_user, nip_user, nama_user, pass_user, hashed, login_attempt, next_login
|
|
FROM "sipd_user"
|
|
WHERE nip_user = $1`
|
|
err = ac.pgxConn.QueryRow(context.Background(), q, f.Username).Scan(
|
|
&idUser,
|
|
&nipUser,
|
|
&namaUser,
|
|
&passUser,
|
|
&hashed,
|
|
&loginAttp,
|
|
&nextLogin,
|
|
)
|
|
if err != nil {
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
err = utils.LoginError{
|
|
NextLogin: nextLogin,
|
|
Attempt: loginAttp,
|
|
Message: "invalid username or password",
|
|
}
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
tNow := time.Now()
|
|
|
|
if int64(nextLogin) > tNow.Unix() {
|
|
err = utils.LoginError{
|
|
NextLogin: nextLogin,
|
|
Attempt: loginAttp,
|
|
Message: "Login dibatasi",
|
|
}
|
|
return
|
|
}
|
|
|
|
switch detectHashType(passUser) {
|
|
case "bcrypt":
|
|
if err = bcrypt.CompareHashAndPassword([]byte(passUser), []byte(f.Password)); err != nil {
|
|
err = utils.LoginError{
|
|
NextLogin: nextLogin,
|
|
Attempt: loginAttp,
|
|
Message: "invalid username or password ----",
|
|
}
|
|
return
|
|
}
|
|
case "md5":
|
|
if ac.validatePassword(f.Password, passUser) == false {
|
|
err = utils.LoginError{
|
|
NextLogin: nextLogin,
|
|
Attempt: loginAttp,
|
|
Message: "invalid username or password",
|
|
}
|
|
return
|
|
}
|
|
default:
|
|
err = utils.LoginError{
|
|
NextLogin: nextLogin,
|
|
Attempt: loginAttp,
|
|
Message: "invalid username or password (*)",
|
|
}
|
|
return
|
|
}
|
|
|
|
// ambil list data pegawai user ini di service pegawai
|
|
type pegawaiJabatanModel struct {
|
|
IdPegawai int64 `json:"id_pegawai" xml:"id_pegawai" example:"1"`
|
|
IdUser int64 `json:"id_user" xml:"id_user" example:"1"` // ID user
|
|
Nip string `json:"nip_user" xml:"nip_user" example:"196408081992011001"`
|
|
Nama string `json:"nama_user" xml:"nama_user" example:"John Doe"`
|
|
IdDaerah int64 `json:"id_daerah" xml:"id_daerah" example:"229"`
|
|
NamaDaerah string `json:"nama_daerah" xml:"nama_daerah" example:"Kota Bandar Lampung"`
|
|
IdUnikSkpd string `json:"id_unik_skpd" xml:"id_unik_skpd"`
|
|
IdSkpdLama int64 `json:"id_skpd_lama" xml:"id_skpd_lama"`
|
|
KodeSkpd string `json:"kode_skpd" xml:"kode_skpd"`
|
|
NamaSkpd string `json:"nama_skpd" xml:"nama_skpd"`
|
|
IdRole int `json:"id_role" xml:"id_role" example:"1"`
|
|
NamaRole string `json:"nama_role" xml:"nama_role" exampe:"BENDAHARA UMUM DAERAH"`
|
|
}
|
|
var rows pgx.Rows
|
|
q = `SELECT
|
|
p.id,
|
|
p.id_daerah,
|
|
p.id_skpd,
|
|
p.id_role,
|
|
p.id_user,
|
|
pr.nama_role
|
|
FROM sipd_pegawai p
|
|
LEFT JOIN sipd_roles pr on p.id_role = pr.id_role
|
|
WHERE
|
|
p.id_user=$1
|
|
AND p.tahun_pegawai=$2
|
|
and p.locked=false
|
|
and p.deleted=false ORDER BY p.id_skpd,p.id_role`
|
|
rows, err = ac.pgxConnPegawai.Query(context.Background(), q, idUser, f.Tahun)
|
|
if err != nil {
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
err = utils.LoginError{
|
|
NextLogin: nextLogin,
|
|
Attempt: loginAttp,
|
|
Message: "invalid username or password",
|
|
}
|
|
} else {
|
|
err = utils.RequestError{
|
|
Code: http.StatusInternalServerError,
|
|
Message: "gagal mengambil informasi pegawai. - " + err.Error(),
|
|
}
|
|
}
|
|
return r, err
|
|
}
|
|
defer rows.Close()
|
|
for rows.Next() {
|
|
mPeg := pegawaiJabatanModel{}
|
|
err = rows.Scan(
|
|
&mPeg.IdPegawai,
|
|
&mPeg.IdDaerah,
|
|
&mPeg.IdSkpdLama,
|
|
&mPeg.IdRole,
|
|
&mPeg.IdUser,
|
|
&mPeg.NamaRole,
|
|
)
|
|
if err != nil {
|
|
err = utils.RequestError{
|
|
Code: http.StatusInternalServerError,
|
|
Message: "gagal scan informasi pegawai. - " + err.Error(),
|
|
}
|
|
return r, err
|
|
}
|
|
|
|
var kodeDdn string
|
|
q = `select kode_ddn, nama_daerah from public.mst_daerah where id_daerah=$1`
|
|
err = ac.pgxConnMstData.QueryRow(context.Background(), q, mPeg.IdDaerah).Scan(&kodeDdn, &mPeg.NamaDaerah)
|
|
if err != nil {
|
|
err = utils.RequestError{
|
|
Code: http.StatusInternalServerError,
|
|
Message: "gagal mengambil informasi daerah. - " + err.Error(),
|
|
}
|
|
return r, err
|
|
}
|
|
|
|
if mPeg.IdSkpdLama != 0 {
|
|
// ambil data skpd
|
|
var dbMapCode string
|
|
dbMapCodeDraft := fmt.Sprintf("%s_%d", kodeDdn, f.Tahun)
|
|
_, connExists := ac.dbConnMapsAnggaran[dbMapCodeDraft]
|
|
if connExists {
|
|
dbMapCode = dbMapCodeDraft
|
|
} else {
|
|
dbMapCode = fmt.Sprintf("%s_%d", strings.Split(dbMapCodeDraft, ".")[0], f.Tahun)
|
|
}
|
|
fmt.Println("ini id_skpd_lama = ", mPeg.IdSkpdLama)
|
|
q = `select id_unik_skpd, kode_skpd, nama_skpd from public.mst_skpd where id_skpd_lama=$1`
|
|
err = ac.dbConnMapsAnggaran[dbMapCode].QueryRow(context.Background(), q, mPeg.IdSkpdLama).Scan(&mPeg.IdUnikSkpd, &mPeg.KodeSkpd, &mPeg.NamaSkpd)
|
|
if err != nil {
|
|
err = utils.RequestError{
|
|
Code: http.StatusInternalServerError,
|
|
Message: "gagal mengambil informasi skpd. - " + err.Error(),
|
|
}
|
|
return r, err
|
|
}
|
|
} else {
|
|
mPeg.IdUnikSkpd = "00000000-0000-0000-0000-000000000000"
|
|
mPeg.KodeSkpd = "0.00.0.00.0.00.00.0000"
|
|
mPeg.NamaSkpd = "TIDAK ADA SKPD"
|
|
}
|
|
|
|
m := models.PreLoginModel{
|
|
IdPegawai: mPeg.IdPegawai,
|
|
IdUser: mPeg.IdUser,
|
|
Nip: nipUser,
|
|
Nama: namaUser,
|
|
IdDaerah: mPeg.IdDaerah,
|
|
NamaDaerah: mPeg.NamaDaerah,
|
|
IdSkpdLama: mPeg.IdSkpdLama,
|
|
IdUnikSkpd: mPeg.IdUnikSkpd,
|
|
KodeSkpd: mPeg.KodeSkpd,
|
|
NamaSkpd: mPeg.NamaSkpd,
|
|
IdRole: mPeg.IdRole,
|
|
NamaRole: mPeg.NamaRole,
|
|
}
|
|
|
|
r = append(r, m)
|
|
}
|
|
if rows.Err() != nil {
|
|
rows.Close()
|
|
err = utils.RequestError{
|
|
Code: http.StatusInternalServerError,
|
|
Message: "gagal mengambil informasi pegawai (rows). - " + rows.Err().Error(),
|
|
}
|
|
return r, err
|
|
}
|
|
rows.Close()
|
|
|
|
return
|
|
}
|
|
|
|
func (ac *AuthController) Login(f form.LoginForm) (token, refreshToken string, IsDefaultPassword bool, err error) {
|
|
var q string
|
|
|
|
user := models.User{
|
|
IdPegawai: f.IdPegawai,
|
|
}
|
|
|
|
// Validate form input
|
|
err = ac.Validate.Struct(f)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
var tahunPegawai int
|
|
q = `select id_user, id_skpd, id_daerah, id_role, tahun_pegawai from public.sipd_pegawai where id=$1 and locked=false and deleted=false`
|
|
err = ac.pgxConnPegawai.QueryRow(context.Background(), q, f.IdPegawai).
|
|
Scan(&user.IdUser, &user.IdSkpd, &user.IdDaerah, &user.IdRole, &tahunPegawai)
|
|
if err != nil {
|
|
err = utils.RequestError{
|
|
Code: http.StatusInternalServerError,
|
|
Message: "gagal mengambil data. - " + err.Error(),
|
|
}
|
|
return
|
|
}
|
|
var passUser string
|
|
q = `SELECT pass_user FROM "sipd_user" WHERE id_user = $1 and deleted_by=0`
|
|
err = ac.pgxConn.QueryRow(context.Background(), q, user.IdUser).Scan(&passUser)
|
|
if err != nil {
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
err = utils.RequestError{
|
|
Code: http.StatusUnauthorized,
|
|
Message: "invalid username or password-",
|
|
}
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
switch detectHashType(passUser) {
|
|
case "bcrypt":
|
|
if err = bcrypt.CompareHashAndPassword([]byte(passUser), []byte(f.Password)); err != nil {
|
|
err = utils.RequestError{
|
|
Code: http.StatusUnauthorized,
|
|
Message: "invalid username or password" + err.Error(),
|
|
}
|
|
return
|
|
}
|
|
case "md5":
|
|
if ac.validatePassword(f.Password, passUser) == false {
|
|
err = utils.RequestError{
|
|
Code: http.StatusUnauthorized,
|
|
Message: "invalid username or password",
|
|
}
|
|
return
|
|
}
|
|
default:
|
|
err = utils.RequestError{
|
|
Code: http.StatusUnauthorized,
|
|
Message: "invalid username or password" + err.Error(),
|
|
}
|
|
return
|
|
}
|
|
|
|
// ambil kode wilayah user
|
|
q = `SELECT nama_daerah, kode_ddn FROM public.mst_daerah where id_daerah=$1 and deleted_by=0`
|
|
err = ac.pgxConnMstData.QueryRow(context.Background(), q, user.IdDaerah).
|
|
Scan(&user.SubDomainDaerah, &user.KodeDdn)
|
|
if err != nil {
|
|
err = utils.RequestError{
|
|
Code: http.StatusInternalServerError,
|
|
Message: "Data Pemerintah Daerah Tidak Tersedia. - " + err.Error(),
|
|
}
|
|
return
|
|
}
|
|
user.KodeProvinsi = strings.Split(user.KodeDdn, ".")[0]
|
|
|
|
if len(user.KodeDdn) == 2 {
|
|
user.KodeDdn += ".00"
|
|
}
|
|
|
|
token, refreshToken, _, err = ac.jwtManager.Generate(ac.pgxConn, user, tahunPegawai, f.IdPegawai)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (ac *AuthController) Register(f *form.SignupForm) (response bool, err error) {
|
|
// Validate form input
|
|
err = ac.Validate.Struct(f)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
//currentTime := time.Now()
|
|
|
|
// validasi username
|
|
var usernameExists bool
|
|
qStr := `SELECT EXISTS(SELECT 1 FROM "user" WHERE "nip_user"=$1 and deleted_by=0)`
|
|
err = ac.pgxConn.QueryRow(context.Background(), qStr, f.Username).Scan(&usernameExists)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if usernameExists {
|
|
err = utils.RequestError{
|
|
Fields: []utils.DataValidationError{{Field: "username", Message: "username already taken"}},
|
|
}
|
|
return
|
|
}
|
|
|
|
// validasi nip
|
|
/*var nipExists bool
|
|
qStr = `SELECT EXISTS(SELECT 1 FROM "user" WHERE "nip"=$1)`
|
|
err = ac.pgxConn.QueryRow(context.Background(), qStr, f.Nip).Scan(&nipExists)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if nipExists {
|
|
err = utils.RequestError{
|
|
Fields: []utils.DataValidationError{{Field: "nip", Message: "nip already taken"}},
|
|
}
|
|
return
|
|
}*/
|
|
|
|
var maxIdUser int64
|
|
// jika id_user tidak ditemukan maka otomatis akan menjadi 0
|
|
qStr = `SELECT COALESCE(MAX(id_user),0) AS last_user_id FROM "user" WHERE "id_daerah"=$1`
|
|
err = ac.pgxConn.QueryRow(context.Background(), qStr, f.IdDaerah).Scan(&maxIdUser)
|
|
|
|
/*strQuery := `INSERT INTO "user" (
|
|
id_user,
|
|
id_daerah,
|
|
nip,
|
|
nama_user,
|
|
jabatan,
|
|
id_profil,
|
|
login_name,
|
|
login_passwd,
|
|
id_level,
|
|
akses_user,
|
|
is_locked,
|
|
is_deleted,
|
|
created_at,
|
|
updated_at,
|
|
is_login,
|
|
last_login,
|
|
last_ip_login,
|
|
nama_bidang,
|
|
id_unik,
|
|
token_login)
|
|
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20)`
|
|
_, err = ac.pgxConn.Exec(context.Background(), strQuery,
|
|
maxIdUser+1,
|
|
f.IdDaerah,
|
|
f.Nip,
|
|
f.NamaUser,
|
|
"-",
|
|
0,
|
|
f.Username,
|
|
ac.encodePasswd(f.Password),
|
|
2,
|
|
"1|1|1|1|1|1|1|1|1|1|1|1|1",
|
|
0,
|
|
0,
|
|
currentTime.Format("2006-01-02 15:04:05"),
|
|
currentTime.Format("2006-01-02 15:04:05"),
|
|
0,
|
|
currentTime.Format("2006-01-02 15:04:05"),
|
|
"116.206.43.97:61968, 116.206.43.97",
|
|
f.NamaBidang,
|
|
"37fa8cde-849d-4801-99ac-9067b6b4d5ac",
|
|
"61dd0d091b2c5-1641876745")
|
|
if err != nil {
|
|
return
|
|
}*/
|
|
|
|
response = true
|
|
return
|
|
}
|
|
|
|
func (ac *AuthController) RefreshToken(pl form.RefreshTokenForm) (r models.ResponseLogin, err error) {
|
|
var user models.User
|
|
|
|
err = ac.Validate.Struct(pl)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
claims, err := ac.jwtManager.Verify(pl.Token)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
user.IdUser = claims.IdUser
|
|
user.IdDaerah = claims.IdDaerah
|
|
user.IdSkpd = claims.IdSkpd
|
|
user.IdRole = claims.IdRole
|
|
user.IdPegawai = claims.IdPegawai
|
|
|
|
/*subArr := strings.Split(claims.Subject, ".")
|
|
if len(subArr) != 2 {
|
|
err = utils.RequestError{
|
|
Code: http.StatusUnprocessableEntity,
|
|
Message: "Akun tidak valid",
|
|
}
|
|
return
|
|
}
|
|
// log.Println(subArr)
|
|
|
|
userId, err := strconv.ParseUint(subArr[0], 10, 64)
|
|
if err != nil {
|
|
err = utils.RequestError{
|
|
Code: http.StatusUnprocessableEntity,
|
|
Message: "Akun tidak valid",
|
|
}
|
|
return
|
|
}
|
|
|
|
userIdDaerah, err := strconv.ParseUint(subArr[1], 10, 64)
|
|
if err != nil {
|
|
err = utils.RequestError{
|
|
Code: http.StatusUnprocessableEntity,
|
|
Message: "Akun tidak valid",
|
|
}
|
|
return
|
|
}*/
|
|
|
|
var idUser, idDaerah uint64
|
|
var passwdUser string
|
|
q := `SELECT id_user, id_daerah, pass_user FROM "user" WHERE id_user = $1 and deleted_by=0`
|
|
err = ac.pgxConn.QueryRow(context.Background(), q, claims.IdUser).Scan(&idUser, &idDaerah, &passwdUser)
|
|
if err != nil {
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
err = utils.RequestError{
|
|
Code: http.StatusUnprocessableEntity,
|
|
Message: "Akun tidak valid",
|
|
}
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
r.Token, r.RefreshToken, _, err = ac.jwtManager.Generate(ac.pgxConn, user, claims.Tahun, claims.IdPegawai)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (c *AuthController) AmankanKataSandi(pl form.ChangePasswordFormPublik) error {
|
|
var err error
|
|
var q string
|
|
|
|
/*log.Println("idUser: ", userId)
|
|
log.Println("idDaerah: ", idDaerah)
|
|
log.Printf("%#v\\n", pl)*/
|
|
|
|
var hashed bool
|
|
var idUser, idDaerah, loginAttp, nextLogin int
|
|
var passUser string
|
|
q = `SELECT id_user, id_daerah, pass_user, hashed, login_attempt, next_login
|
|
FROM "user"
|
|
WHERE nip_user = $1 and deleted_by = 0`
|
|
err = c.pgxConn.QueryRow(context.Background(), q, pl.Username).Scan(
|
|
&idUser,
|
|
&idDaerah,
|
|
&passUser,
|
|
&hashed,
|
|
&loginAttp,
|
|
&nextLogin,
|
|
)
|
|
if err != nil {
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
err = utils.LoginError{
|
|
NextLogin: nextLogin,
|
|
Attempt: loginAttp,
|
|
Message: "invalid username or password",
|
|
}
|
|
return err
|
|
}
|
|
return err
|
|
}
|
|
|
|
tNow := time.Now()
|
|
|
|
if int64(nextLogin) > tNow.Unix() {
|
|
err = utils.LoginError{
|
|
NextLogin: nextLogin,
|
|
Attempt: loginAttp,
|
|
Message: "Login dibatasi",
|
|
}
|
|
return err
|
|
}
|
|
if pl.NewPassword != pl.NewPasswordRepeat {
|
|
err = utils.LoginError{
|
|
NextLogin: nextLogin,
|
|
Attempt: loginAttp,
|
|
Message: "Password tidak sama dengan konfirmasi password",
|
|
}
|
|
return err
|
|
}
|
|
|
|
if hashed {
|
|
// validasi password terhadap hash
|
|
if err = bcrypt.CompareHashAndPassword([]byte(passUser), []byte(pl.OldPassword)); err != nil {
|
|
/*errAtp := ac.wrongLoginCounter(user.IdUser, user.IdDaerah, tNow)
|
|
if errAtp != nil {
|
|
err = utils.RequestError{Code: http.StatusBadRequest, Message: errAtp.Error()}
|
|
return err
|
|
}*/
|
|
|
|
err = utils.LoginError{
|
|
NextLogin: nextLogin,
|
|
Attempt: loginAttp,
|
|
Message: "invalid username or password",
|
|
}
|
|
return err
|
|
}
|
|
} else {
|
|
if c.validatePassword(pl.OldPassword, passUser) == false {
|
|
/*errAtp := ac.wrongLoginCounter(user.IdUser, user.IdDaerah, tNow)
|
|
if errAtp != nil {
|
|
err = utils.RequestError{Code: http.StatusBadRequest, Message: errAtp.Error()}
|
|
return err
|
|
}*/
|
|
|
|
err = utils.LoginError{
|
|
NextLogin: nextLogin,
|
|
Attempt: loginAttp,
|
|
Message: "invalid username or password",
|
|
}
|
|
return err
|
|
}
|
|
}
|
|
// log.Println("passwordHash: ", passwordHash)
|
|
|
|
if passUser == "" {
|
|
err = utils.RequestError{
|
|
Code: http.StatusUnprocessableEntity,
|
|
Message: "Salah satu field tidak valid",
|
|
Fields: []utils.DataValidationError{{Field: "old_password", Message: ""}},
|
|
}
|
|
return err
|
|
}
|
|
|
|
// validate oldPassword
|
|
err = bcrypt.CompareHashAndPassword([]byte(passUser), []byte(pl.OldPassword))
|
|
if err != nil {
|
|
err = utils.LoginError{
|
|
NextLogin: nextLogin,
|
|
Attempt: loginAttp,
|
|
Message: "invalid username or password",
|
|
}
|
|
return err
|
|
}
|
|
|
|
// validate password
|
|
if !c.isValidPassword(pl.NewPassword) {
|
|
err = utils.LoginError{
|
|
NextLogin: nextLogin,
|
|
Attempt: loginAttp,
|
|
Message: "kata sandi baru tidak valid. minimal 8 karakter mengandung angka, huruf besar, huruf kecil dan karakter spesial.",
|
|
}
|
|
return err
|
|
}
|
|
|
|
// generate new password hash
|
|
var bytesPwd []byte
|
|
bytesPwd, err = bcrypt.GenerateFromPassword([]byte(pl.NewPassword), 14)
|
|
if err != nil {
|
|
err = utils.RequestError{
|
|
Code: http.StatusInternalServerError,
|
|
Message: "gagal enkripsi. - " + err.Error(),
|
|
}
|
|
return err
|
|
}
|
|
// log.Println("bytesPwd: ", bytesPwd)
|
|
|
|
// update to new password
|
|
q = `UPDATE "user" SET "pass_user"=$1 WHERE id_user=$2 AND id_daerah=$3`
|
|
_, err = c.pgxConn.Exec(context.Background(), q, bytesPwd, idUser, idDaerah)
|
|
if err != nil {
|
|
err = utils.RequestError{
|
|
Code: http.StatusInternalServerError,
|
|
Message: "gagal set new password. - " + err.Error(),
|
|
}
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (ac *AuthController) validatePassword(content, encrypted string) bool {
|
|
return strings.EqualFold(ac.encodePasswd(content), encrypted)
|
|
}
|
|
|
|
func (ac *AuthController) encodePasswd(data string) string {
|
|
h := md5.New()
|
|
h.Write([]byte(data))
|
|
return hex.EncodeToString(h.Sum(nil))
|
|
}
|
|
|
|
func (ac *AuthController) generatePasswordHash(password string) (string, error) {
|
|
bytesPwd, err := bcrypt.GenerateFromPassword([]byte(password), 14)
|
|
return string(bytesPwd), err
|
|
}
|
|
|
|
func (ac *AuthController) wrongLoginCounter(idUser, idDaerah int64, t time.Time) error {
|
|
var loginAtp uint32
|
|
q := `UPDATE "user" SET "login_attempt"="user"."login_attempt"-1 WHERE "id_user"=$1 AND "id_daerah"=$2 RETURNING "login_attempt"`
|
|
err := ac.pgxConn.QueryRow(context.Background(), q, idUser, idDaerah).Scan(&loginAtp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if loginAtp <= 0 {
|
|
q = `UPDATE "user" SET "next_login"=$1 WHERE "id_user"=$2 AND "id_daerah"=$3 RETURNING "login_attempt"`
|
|
_, err = ac.pgxConn.Exec(context.Background(), q, t.Add(time.Minute*5).Unix(), idUser, idDaerah)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (ac *AuthController) isValidPassword(password string) bool {
|
|
if len(password) < 8 {
|
|
return false
|
|
}
|
|
|
|
reLower := regexp.MustCompile(`[a-z]`)
|
|
reUpper := regexp.MustCompile(`[A-Z]`)
|
|
reDigit := regexp.MustCompile(`\d`)
|
|
reSpecial := regexp.MustCompile(`[@$!%*?&]`)
|
|
|
|
return reLower.MatchString(password) &&
|
|
reUpper.MatchString(password) &&
|
|
reDigit.MatchString(password) &&
|
|
reSpecial.MatchString(password)
|
|
}
|