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

     1  /*
     2   * Copyright (c) 2021-present unTill Pro, Ltd.
     3   * @author Denis Gribanov
     4   */
     5  
     6  package registry
     7  
     8  import (
     9  	"errors"
    10  	"net/http"
    11  	"strings"
    12  
    13  	"github.com/voedger/voedger/pkg/appdef"
    14  	"github.com/voedger/voedger/pkg/goutils/iterate"
    15  	"github.com/voedger/voedger/pkg/istructs"
    16  	"github.com/voedger/voedger/pkg/istructsmem"
    17  	"github.com/voedger/voedger/pkg/state"
    18  	"github.com/voedger/voedger/pkg/sys/authnz"
    19  	coreutils "github.com/voedger/voedger/pkg/utils"
    20  )
    21  
    22  // sys/registry, pseudoProfileWSID translated to appWSID
    23  // creation of CDoc<Login> triggers opAsyncProjectorInvokeCreateWorkspaceID
    24  func execCmdCreateLogin(asp istructs.IAppStructsProvider) istructsmem.ExecCommandClosure {
    25  	return func(args istructs.ExecCommandArgs) (err error) {
    26  		loginStr := args.ArgumentObject.AsString(authnz.Field_Login)
    27  		appName := args.ArgumentObject.AsString(authnz.Field_AppName)
    28  
    29  		subjectKind := istructs.SubjectKindType(args.ArgumentObject.AsInt32(authnz.Field_SubjectKind))
    30  		if subjectKind >= istructs.SubjectKind_FakeLast || subjectKind <= istructs.SubjectKind_null {
    31  			return errors.New("wrong subject kind")
    32  		}
    33  
    34  		appQName, err := istructs.ParseAppQName(appName)
    35  		if err != nil {
    36  			return coreutils.NewHTTPErrorf(http.StatusBadRequest, "failed to parse app qualified name", appQName.String(), ":", err)
    37  		}
    38  
    39  		as, err := asp.AppStructs(appQName)
    40  		if err != nil {
    41  			if errors.Is(err, istructs.ErrAppNotFound) {
    42  				return coreutils.NewHTTPErrorf(http.StatusBadRequest, "unknown application ", appName)
    43  			}
    44  			return err
    45  		}
    46  
    47  		// still need this check after https://github.com/voedger/voedger/issues/1311: the command is tkaen from AppWS, number of AppWS related to the login is checked here
    48  		if err = CheckAppWSID(loginStr, args.WSID, as.NumAppWorkspaces()); err != nil {
    49  			return
    50  		}
    51  
    52  		// see https://dev.untill.com/projects/#!537026
    53  		if strings.HasPrefix(loginStr, "-") || strings.HasPrefix(loginStr, ".") || strings.HasPrefix(loginStr, " ") ||
    54  			strings.HasSuffix(loginStr, "-") || strings.HasSuffix(loginStr, ".") || strings.HasSuffix(loginStr, " ") ||
    55  			strings.Contains(loginStr, "..") || !validLoginRegexp.MatchString(loginStr) {
    56  			return coreutils.NewHTTPErrorf(http.StatusBadRequest, "incorrect login format: ", loginStr)
    57  		}
    58  
    59  		cdocLoginID, err := GetCDocLoginID(args.State, args.WSID, appName, loginStr)
    60  		if err != nil {
    61  			return err
    62  		}
    63  		if cdocLoginID > 0 {
    64  			return coreutils.NewHTTPErrorf(http.StatusConflict, "login already exists")
    65  		}
    66  
    67  		wsKindInitializationData := args.ArgumentObject.AsString(authnz.Field_WSKindInitializationData)
    68  		pwdSaltedHash, err := GetPasswordSaltedHash(args.ArgumentUnloggedObject.AsString(field_Passwrd))
    69  		if err != nil {
    70  			return err
    71  		}
    72  		profileCluster := args.ArgumentObject.AsInt32(authnz.Field_ProfileCluster)
    73  
    74  		kb, err := args.State.KeyBuilder(state.Record, QNameCDocLogin)
    75  		if err != nil {
    76  			return err
    77  		}
    78  		cdocLogin, err := args.Intents.NewValue(kb)
    79  		if err != nil {
    80  			return err
    81  		}
    82  		cdocLogin.PutInt32(authnz.Field_ProfileCluster, profileCluster)
    83  		cdocLogin.PutBytes(field_PwdHash, pwdSaltedHash)
    84  		cdocLogin.PutString(authnz.Field_AppName, appName)
    85  		cdocLogin.PutInt32(authnz.Field_SubjectKind, args.ArgumentObject.AsInt32(authnz.Field_SubjectKind))
    86  		cdocLogin.PutString(authnz.Field_LoginHash, GetLoginHash(loginStr))
    87  		cdocLogin.PutRecordID(appdef.SystemField_ID, 1)
    88  		cdocLogin.PutString(authnz.Field_WSKindInitializationData, wsKindInitializationData)
    89  
    90  		return
    91  	}
    92  }
    93  
    94  // sys/registry, appWorkspace, triggered by CDoc<Login>
    95  var projectorLoginIdx = func(event istructs.IPLogEvent, s istructs.IState, intents istructs.IIntents) (err error) {
    96  	return iterate.ForEachError(event.CUDs, func(rec istructs.ICUDRow) error {
    97  		if rec.QName() != QNameCDocLogin {
    98  			return nil
    99  		}
   100  		kb, err := s.KeyBuilder(state.View, QNameViewLoginIdx)
   101  		if err != nil {
   102  			return err
   103  		}
   104  		kb.PutInt64(field_AppWSID, int64(event.Workspace()))
   105  		kb.PutString(field_AppIDLoginHash, rec.AsString(authnz.Field_AppName)+"/"+rec.AsString(authnz.Field_LoginHash))
   106  
   107  		vb, err := intents.NewValue(kb)
   108  		if err != nil {
   109  			return err
   110  		}
   111  		vb.PutInt64(field_CDocLoginID, int64(rec.AsRecordID(appdef.SystemField_ID)))
   112  		return nil
   113  	})
   114  }