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 }