2024-05-03 17:11:56 +00:00
package repo
import (
"errors"
"fmt"
"strings"
"time"
2024-05-08 14:35:04 +00:00
"xmr-remote-nodes/internal/database"
2024-05-03 17:11:56 +00:00
"github.com/alexedwards/argon2id"
)
type Admin struct {
Id int ` db:"id" `
Username string ` db:"username" `
Password string ` db:"password" `
LastactiveTs int64 ` db:"lastactive_ts" `
CreatedTs int64 ` db:"created_ts" `
}
type AdminRepo struct {
db * database . DB
}
type AdminRepository interface {
CreateAdmin ( * Admin ) ( * Admin , error )
Login ( username string , password string ) ( * Admin , error )
}
func NewAdminRepo ( db * database . DB ) AdminRepository {
return & AdminRepo { db }
}
func ( repo * AdminRepo ) CreateAdmin ( admin * Admin ) ( * Admin , error ) {
if ! validUsername ( admin . Username ) {
return nil , errors . New ( "username is not valid, must be at least 4 characters long and contain only lowercase letters and numbers" )
}
if ! strongPassword ( admin . Password ) {
return nil , errors . New ( "password is not strong enough, must be at least 8 characters long and contain at least one uppercase letter, one lowercase letter, one number and one special character" )
}
hash , err := setPassword ( admin . Password )
if err != nil {
return nil , err
}
admin . Password = hash
admin . CreatedTs = time . Now ( ) . Unix ( )
if repo . isUsernameExists ( admin . Username ) {
return nil , errors . New ( "username already exists" )
}
2024-05-19 21:22:58 +00:00
_ , err = repo . db . Exec ( `
INSERT INTO tbl_admin (
username ,
password ,
created_ts
) VALUES (
? ,
? ,
?
) ` ,
admin . Username ,
admin . Password ,
admin . CreatedTs )
2024-05-03 17:11:56 +00:00
if err != nil {
return nil , err
}
return admin , nil
}
func ( repo * AdminRepo ) Login ( username , password string ) ( * Admin , error ) {
2024-05-19 21:22:58 +00:00
row , err := repo . db . Query ( `
SELECT
id ,
username ,
password
FROM
tbl_admin
WHERE
username = ?
LIMIT 1 ` , username )
2024-05-03 17:11:56 +00:00
if err != nil {
fmt . Println ( err )
return nil , err
}
defer row . Close ( )
admin := Admin { }
if row . Next ( ) {
err = row . Scan ( & admin . Id , & admin . Username , & admin . Password )
if err != nil {
fmt . Println ( err )
return nil , err
}
} else {
return nil , errors . New ( "Invalid username or password" )
}
match , err := checkPassword ( admin . Password , password )
if err != nil {
fmt . Println ( err )
return nil , err
}
if ! match {
return nil , errors . New ( "Invalid username or password" )
}
2024-05-19 21:22:58 +00:00
_ , err = repo . db . Exec ( `
UPDATE tbl_admin
SET lastactive_ts = ?
WHERE id = ? ` , time . Now ( ) . Unix ( ) , admin . Id )
2024-05-03 17:11:56 +00:00
if err != nil {
fmt . Println ( err )
return nil , err
}
return & admin , nil
}
func ( repo * AdminRepo ) isUsernameExists ( username string ) bool {
2024-05-19 21:22:58 +00:00
row , err := repo . db . Query ( `
SELECT
id
FROM
tbl_admin
WHERE
username = ?
LIMIT 1 ` , username )
2024-05-03 17:11:56 +00:00
if err != nil {
return false
}
defer row . Close ( )
return row . Next ( )
}
func setPassword ( password string ) ( string , error ) {
hash , err := argon2id . CreateHash ( password , argon2id . DefaultParams )
if err != nil {
return "" , err
}
return hash , nil
}
func checkPassword ( hash , password string ) ( bool , error ) {
match , err := argon2id . ComparePasswordAndHash ( password , hash )
if err != nil {
return false , err
}
return match , nil
}
func strongPassword ( password string ) bool {
if len ( password ) < 8 {
return false
}
if ! strings . ContainsAny ( password , "0123456789" ) {
return false
}
if ! strings . ContainsAny ( password , "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ) {
return false
}
if ! strings . ContainsAny ( password , "abcdefghijklmnopqrstuvwxyz" ) {
return false
}
if ! strings . ContainsAny ( password , "!@#$%^&*()_+|~-=`{}[]:;<>?,./" ) {
return false
}
return true
}
// No special character and unicode for username
func validUsername ( username string ) bool {
if len ( username ) < 5 || len ( username ) > 20 {
return false
}
// reject witespace, tabs, newlines, and other special characters
if strings . ContainsAny ( username , " \t\n" ) {
return false
}
// reject unicode
if strings . ContainsAny ( username , "^\x00-\x7F" ) {
return false
}
// reject special characters
if strings . ContainsAny ( username , "!@#$%^&*()_+|~-=`{}[]:;<>?,./ " ) { // note last blank space
return false
}
if ! strings . ContainsAny ( username , "abcdefghijklmnopqrstuvwxyz0123456789" ) {
return false
}
return true
}