backend for moffas 5 based login
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

318 lines
9.9 KiB

11 months ago
package moffas
import (
"encoding/json"
"errors"
"moffas_go/helper"
"moffas_go/logger"
"strconv"
"strings"
"time"
"github.com/jmoiron/sqlx"
11 months ago
)
type GeneratedChallenge struct {
Full_nonce string
User_id int64
Salt string
Iterations int
}
type VerificationResult struct {
Server_hash string
Final_nonce string
Session_id string
Privileges map[string]interface{}
Tier string
Full_name string
Organization_data map[string]interface{}
}
func Generate_challenge(reference_id string, conn *sqlx.DB, username string, half_nonce string) (GeneratedChallenge, error) {
11 months ago
logger.Debug(reference_id, " -- start generate_challenge")
startTime := time.Now()
defer func() {
dur := time.Since(startTime)
logger.Debug(reference_id, " -- generate_challenge done in ", dur)
}()
current_time := startTime.Unix()
11 months ago
nonce, err := helper.GenerateRandomString(8)
if err != nil {
return GeneratedChallenge{}, err
}
full_nonce := half_nonce + nonce
logger.Info(reference_id, "HALF NONCE : ", half_nonce)
logger.Info(reference_id, "USERNAME : ", username)
logger.Info(reference_id, "FULL NONCE : ", full_nonce)
type DBResult struct {
User_id int64 `db:"id"`
Salt string `db:"salt"`
Iterations int `db:"iterations"`
Timestamp int64 `db:"tstamp"`
SaltedPasword string `db:"saltedpassword"`
}
query := `
SELECT
a.id,a.salt,a.saltedpassword,a.iterations,
COALESCE(b.tstamp,0) as tstamp
FROM servouser.user a
LEFT JOIN servouser.challenge_response b ON b.user_id = a.id
WHERE a.username = $1 AND a.st=1 LIMIT 1`
sql := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(query, "\t", " "), "\n", " "), " ", " ")
logger.Debug(reference_id, "SQL : ", sql)
dbresult := DBResult{}
err = conn.Get(&dbresult, sql, username)
if err != nil {
return GeneratedChallenge{}, err
}
logger.Info(reference_id, "USER ID : ", dbresult.User_id)
waittime, err := strconv.Atoi(helper.GetEnv("AUTH_WAIT_TIME", "10"))
if err != nil {
waittime = 10
}
wait_until := dbresult.Timestamp + int64(waittime)
logger.Debug(reference_id, "LAST AUTH REQUEST : ", dbresult.Timestamp)
logger.Debug(reference_id, "WAIT TIME : ", waittime)
logger.Debug(reference_id, "WAIT UNTIL : ", wait_until)
logger.Debug(reference_id, "CURRENT TIME : ", current_time)
if wait_until > current_time {
logger.Info(reference_id, "!!! WAIT TIME FOR THE USER ID HAS NOT ELAPSED")
err = errors.New("wait time has not elapsed")
return GeneratedChallenge{}, err
}
//----- CONTEKAN DOANG -----
sharedSecret := helper.GetEnv("SHARED_SECRET", "Winter is coming")
logger.Info(reference_id, "SHARED SECRET : ", sharedSecret)
logger.Info(reference_id, "SALTED PASSWORD : ", dbresult.SaltedPasword)
hash1, err := helper.HMAC_SHA256(sharedSecret, dbresult.SaltedPasword)
if err != nil {
return GeneratedChallenge{}, err
}
logger.Info(reference_id, "HASH1 : ", hash1)
calculated_client_hash, err := helper.HMAC_SHA256(full_nonce, hash1)
if err != nil {
return GeneratedChallenge{}, err
}
logger.Info(reference_id, "CALCULATED CLIENT HASH : ", calculated_client_hash)
//----- CONTEKAN DOANG -----
err = upsert_challenge(reference_id, conn, full_nonce, dbresult.User_id)
11 months ago
if err != nil {
return GeneratedChallenge{}, err
}
return GeneratedChallenge{
User_id: dbresult.User_id,
Salt: dbresult.Salt,
Iterations: dbresult.Iterations,
Full_nonce: full_nonce,
}, nil
}
func Verify_challenge(reference_id string, conn *sqlx.DB, full_nonce string, client_hash string, next_nonce string) (VerificationResult, error) {
logger.Debug(reference_id, " -- start verify_challenge")
11 months ago
startTime := time.Now()
defer func() {
dur := time.Since(startTime)
logger.Debug(reference_id, " -- verify_challenge done in ", dur)
}()
current_time := time.Now().Unix()
nonce, err := helper.GenerateRandomString(8)
if err != nil {
return VerificationResult{}, err
}
logger.Info(reference_id, "FULL NONCE : ", full_nonce)
type DBResult struct {
User_id int64 `db:"user_id"`
Timestamp int64 `db:"tstamp"`
Fullname string `db:"full_name"`
Privileges string `db:"privileges"`
Tier string `db:"tier"`
SaltedPasword string `db:"saltedpassword"`
Organization_data string `db:"organization_data"`
}
dbresult := DBResult{}
query := `
SELECT
a.user_id, a.tstamp,
b.full_name, b.saltedpassword,
c.privileges,
d.tier, d.data organization_data
FROM servouser.challenge_response a
LEFT JOIN servouser.user b ON b.id = a.user_id
LEFT JOIN servouser.role c ON c.name = b.role
LEFT JOIN servouser.organization d ON d.id = b.organization_id
WHERE a.full_nonce = $1 AND b.id IS NOT NULL AND c.privileges IS NOT NULL AND b.st = 1
LIMIT 1`
sql := strings.ReplaceAll(strings.ReplaceAll(query, "\t", " "), "\n", " ")
logger.Debug(reference_id, "SQL : ", sql)
err = conn.Get(&dbresult, sql, full_nonce)
if err != nil {
return VerificationResult{}, err
}
rep_nonce := full_nonce[0:15]
logger.Debug(reference_id, "replacement nonce : ", rep_nonce)
query = "UPDATE servouser.challenge_response SET full_nonce = $1 WHERE full_nonce = $2"
logger.Debug(reference_id, "SQL : ", query)
_, err = conn.Exec(query, rep_nonce, full_nonce)
if err != nil {
return VerificationResult{}, err
}
timeout, err := strconv.Atoi(helper.GetEnv("AUTH_TIMEOUT", "300"))
if err != nil {
timeout = 300
}
logger.Info(reference_id, "AUTH TIMEOUT : ", timeout)
expire_limit := dbresult.Timestamp + int64(timeout)
logger.Info(reference_id, "AUTH EXPIRATION TIME : ", expire_limit)
logger.Info(reference_id, "CURRENT TIME : ", current_time)
if current_time > expire_limit {
logger.Error(reference_id, "!!! CHALLENGE EXPIRED")
err = errors.New("client expired")
return VerificationResult{}, err
}
sharedSecret := helper.GetEnv("SHARED_SECRET", "Winter is coming")
logger.Info(reference_id, "SHARED SECRET : ", sharedSecret)
logger.Info(reference_id, "SALTED PASSWORD : ", dbresult.SaltedPasword)
hash1, err := helper.HMAC_SHA256(sharedSecret, dbresult.SaltedPasword)
if err != nil {
return VerificationResult{}, err
}
logger.Info(reference_id, "HASH1 : ", hash1)
calculated_client_hash, err := helper.HMAC_SHA256(full_nonce, hash1)
if err != nil {
return VerificationResult{}, err
}
logger.Info(reference_id, "CALCULATED CLIENT HASH : ", calculated_client_hash)
logger.Info(reference_id, "CLIENT HASH : ", client_hash)
if calculated_client_hash != client_hash {
logger.Warning(reference_id, "!!! CLIENT HASH DOES NOT MATCH STORED CHALLENGE RESPONSE")
err = errors.New("client hash does not match stored challenge response")
return VerificationResult{}, err
}
logger.Info(reference_id, "NEXT NONCE : ", next_nonce)
final_nonce := next_nonce + nonce
logger.Info(reference_id, "FINAL NONCE : ", final_nonce)
server_hash, err := helper.HMAC_SHA256(final_nonce, hash1)
if err != nil {
return VerificationResult{}, err
}
logger.Info(reference_id, "SERVER HASH : ", server_hash)
session_secret, err := helper.HMAC_SHA256(full_nonce+final_nonce, hash1)
if err != nil {
return VerificationResult{}, err
}
logger.Info(reference_id, "SESSION SECRET : ", session_secret)
session_id, _ := helper.GenerateRandomString(16)
logger.Info(reference_id, "SESSION ID : ", session_id)
err = upsert_session(reference_id, conn, session_id, dbresult.User_id, session_secret)
11 months ago
if err != nil {
return VerificationResult{}, err
}
privileges := make(map[string]interface{})
organization_data := make(map[string]interface{})
err = json.Unmarshal([]byte(dbresult.Privileges), &privileges)
if err != nil {
return VerificationResult{}, err
}
err = json.Unmarshal([]byte(dbresult.Organization_data), &organization_data)
if err != nil {
return VerificationResult{}, err
}
return VerificationResult{
Final_nonce: final_nonce,
Server_hash: server_hash,
Session_id: session_id,
Privileges: privileges,
Tier: dbresult.Tier,
Full_name: dbresult.Fullname,
Organization_data: organization_data,
}, nil
}
func upsert_challenge(reference_id string, conn *sqlx.DB, full_nonce string, user_id int64) error {
11 months ago
logger.Debug(reference_id, " start upsert_challenge")
startTime := time.Now()
defer func() {
dur := time.Since(startTime)
logger.Debug(reference_id, " -- upsert_challenge done in ", dur)
}()
query := "DELETE FROM servouser.challenge_response WHERE full_nonce = $1 OR user_id = $2"
sql := strings.ReplaceAll(strings.ReplaceAll(query, "\t", " "), "\n", " ")
logger.Debug(reference_id, "SQL : ", sql)
_, err := conn.Exec(query, full_nonce, user_id)
11 months ago
if err != nil {
return err
}
query = `
INSERT INTO
servouser.challenge_response (full_nonce,user_id,challenge_response,tstamp)
VALUES
($1,$2,'0123456789abcdefghijklmnopqrstuv',$3)`
sql = strings.ReplaceAll(strings.ReplaceAll(query, "\t", " "), "\n", " ")
logger.Debug(reference_id, "SQL : ", sql)
_, err = conn.Exec(sql, full_nonce, user_id, time.Now().Unix())
return err
}
func upsert_session(reference_id string, conn *sqlx.DB, session_id string, user_id int64, session_secret string) error {
logger.Debug(reference_id, " -- start upsert_session.")
11 months ago
startTime := time.Now()
defer func() {
dur := time.Since(startTime)
logger.Debug(reference_id, " -- upsert_session done in ", dur)
}()
query := "DELETE FROM servouser.session WHERE session_id = $1 OR user_id = $2"
sql := strings.ReplaceAll(strings.ReplaceAll(query, "\t", " "), "\n", " ")
logger.Debug(reference_id, "SQL : ", sql)
_, err := conn.Exec(query, session_id, user_id)
11 months ago
if err != nil {
return err
}
query = `
INSERT INTO
servouser.session (session_id,user_id,session_secret,tstamp,st,last_ms_tstamp,last_sequence)
VALUES
($1,$2,$3,$4,1,0,0)`
sql = strings.ReplaceAll(strings.ReplaceAll(query, "\t", " "), "\n", " ")
logger.Debug(reference_id, "SQL : ", sql)
_, err = conn.Exec(query, session_id, user_id, session_secret, time.Now().Unix())
return err
}