github.com/voedger/voedger@v0.0.0-20240520144910-273e84102129/pkg/registry/utils.go (about)

     1  /*
     2   * Copyright (c) 2021-present unTill Pro, Ltd.
     3   * @author Denis Gribanov
     4   */
     5  
     6  package registry
     7  
     8  import (
     9  	"crypto/sha256"
    10  	"errors"
    11  	"fmt"
    12  	"net/http"
    13  
    14  	"golang.org/x/crypto/bcrypt"
    15  
    16  	"github.com/voedger/voedger/pkg/appdef"
    17  	"github.com/voedger/voedger/pkg/istructs"
    18  	"github.com/voedger/voedger/pkg/state"
    19  	coreutils "github.com/voedger/voedger/pkg/utils"
    20  )
    21  
    22  func CheckAppWSID(login string, urlWSID istructs.WSID, numAppWorkspaces istructs.NumAppWorkspaces) error {
    23  	crc16 := coreutils.CRC16([]byte(login))
    24  	appWSID := istructs.WSID(crc16%uint16(numAppWorkspaces)) + istructs.FirstBaseAppWSID
    25  	expectedAppWSID := istructs.NewWSID(urlWSID.ClusterID(), appWSID)
    26  	if expectedAppWSID != urlWSID {
    27  		return coreutils.NewHTTPErrorf(http.StatusForbidden, "wrong AppWSID: ", expectedAppWSID, " expected, ", urlWSID, " got")
    28  	}
    29  	return nil
    30  }
    31  
    32  // istructs.NullRecordID means not found
    33  func GetCDocLoginID(st istructs.IState, appWSID istructs.WSID, appName string, login string) (cdocLoginID istructs.RecordID, err error) {
    34  	kb, err := st.KeyBuilder(state.View, QNameViewLoginIdx)
    35  	if err != nil {
    36  		return istructs.NullRecordID, err
    37  	}
    38  	loginHash := GetLoginHash(login)
    39  	kb.PutInt64(field_AppWSID, int64(appWSID))
    40  	kb.PutString(field_AppIDLoginHash, appName+"/"+loginHash)
    41  	loginIdx, ok, err := st.CanExist(kb)
    42  	if err != nil {
    43  		return istructs.NullRecordID, err
    44  	}
    45  	if !ok {
    46  		return istructs.NullRecordID, nil
    47  	}
    48  	return loginIdx.AsRecordID(field_CDocLoginID), nil
    49  
    50  }
    51  
    52  func GetCDocLogin(login string, st istructs.IState, appWSID istructs.WSID, appName string) (cdocLogin istructs.IStateValue, doesLoginExist bool, err error) {
    53  	cdocLoginID, err := GetCDocLoginID(st, appWSID, appName, login)
    54  	doesLoginExist = true
    55  	if err != nil {
    56  		return nil, doesLoginExist, err
    57  	}
    58  	if cdocLoginID == istructs.NullRecordID {
    59  		doesLoginExist = false
    60  		return nil, doesLoginExist, err
    61  	}
    62  
    63  	kb, err := st.KeyBuilder(state.Record, QNameCDocLogin)
    64  	if err != nil {
    65  		return nil, doesLoginExist, err
    66  	}
    67  	kb.PutRecordID(state.Field_ID, cdocLoginID)
    68  	cdocLogin, err = st.MustExist(kb)
    69  	return
    70  }
    71  
    72  func GetLoginHash(login string) string {
    73  	return fmt.Sprintf("%x", sha256.Sum256([]byte(login)))
    74  }
    75  
    76  func ChangePassword(login string, st istructs.IState, intents istructs.IIntents, wsid istructs.WSID, appName string, newPwd string) error {
    77  	cdocLogin, doesLoginExist, err := GetCDocLogin(login, st, wsid, appName)
    78  	if err != nil {
    79  		return err
    80  	}
    81  
    82  	if !doesLoginExist {
    83  		return errLoginDoesNotExist(login)
    84  	}
    85  
    86  	return ChangePasswordCDocLogin(cdocLogin, newPwd, intents, st)
    87  }
    88  
    89  func errLoginDoesNotExist(login string) error {
    90  	return coreutils.NewHTTPErrorf(http.StatusUnauthorized, fmt.Errorf("login %s does not exist", login))
    91  }
    92  
    93  func ChangePasswordCDocLogin(cdocLogin istructs.IStateValue, newPwd string, intents istructs.IIntents, st istructs.IState) error {
    94  	kb, err := st.KeyBuilder(state.Record, appdef.NullQName)
    95  	if err != nil {
    96  		return err
    97  	}
    98  	loginUpdater, err := intents.UpdateValue(kb, cdocLogin)
    99  	if err != nil {
   100  		return err
   101  	}
   102  	newPwdSaltedHash, err := GetPasswordSaltedHash(newPwd)
   103  	if err != nil {
   104  		return err
   105  	}
   106  	loginUpdater.PutBytes(field_PwdHash, newPwdSaltedHash)
   107  	return nil
   108  }
   109  
   110  func GetPasswordSaltedHash(pwd string) (pwdSaltedHash []byte, err error) {
   111  	if pwdSaltedHash, err = bcrypt.GenerateFromPassword([]byte(pwd), bcrypt.MinCost); err != nil {
   112  		err = fmt.Errorf("password salting & hashing failed: %w", err)
   113  	}
   114  	return
   115  }
   116  
   117  func CheckPassword(cdocLogin istructs.IStateValue, pwd string) (isPasswordOK bool, err error) {
   118  	isPasswordOK = true
   119  	if err := bcrypt.CompareHashAndPassword(cdocLogin.AsBytes(field_PwdHash), []byte(pwd)); err != nil {
   120  		if errors.Is(err, bcrypt.ErrMismatchedHashAndPassword) {
   121  			isPasswordOK = false
   122  			return isPasswordOK, nil
   123  		}
   124  		return isPasswordOK, fmt.Errorf("failed to authenticate: %w", err)
   125  	}
   126  	return isPasswordOK, err
   127  }