package entities import ( "git.infra.nkode.tech/dkelly/nkode-core/config" "git.infra.nkode.tech/dkelly/nkode-core/security" "github.com/DonovanKelly/sugar-n-spice/all" "github.com/DonovanKelly/sugar-n-spice/set" "github.com/google/uuid" "log" "sort" ) type UserSignSession struct { Id SessionId CustomerId CustomerId LoginUserInterface UserInterface Kp KeypadDimension SetIdxInterface IdxInterface ConfirmIdxInterface IdxInterface SetKeySelection KeySelection UserEmail UserEmail Reset bool Expire int Colors []RGBColor } func NewSignupResetSession(userEmail UserEmail, kp KeypadDimension, customerId CustomerId, svgInterface SvgIdInterface, reset bool) (*UserSignSession, error) { loginInterface, err := NewUserInterface(&kp, svgInterface) if err != nil { return nil, err } signupInterface, colors, err := signupInterface(*loginInterface, kp) if err != nil { return nil, err } session := UserSignSession{ Id: SessionId(uuid.New()), CustomerId: customerId, LoginUserInterface: *loginInterface, SetIdxInterface: signupInterface.IdxInterface, ConfirmIdxInterface: nil, SetKeySelection: nil, UserEmail: userEmail, Kp: kp, Reset: reset, Colors: colors, } return &session, nil } func (s *UserSignSession) DeducePasscode(confirmKeyEntry KeySelection) ([]int, error) { validEntry := all.All[int](confirmKeyEntry, func(i int) bool { return 0 <= i && i < s.Kp.NumbOfKeys }) if !validEntry { log.Printf("Invalid Key entry. One or more key index: %#v, not in range 0-%d", confirmKeyEntry, s.Kp.NumbOfKeys) return nil, config.ErrKeyIndexOutOfRange } if s.SetIdxInterface == nil { log.Print("signup session set interface is nil") return nil, config.ErrIncompleteUserSignupSession } if s.ConfirmIdxInterface == nil { log.Print("signup session confirm interface is nil") return nil, config.ErrIncompleteUserSignupSession } if s.SetKeySelection == nil { log.Print("signup session set key entry is nil") return nil, config.ErrIncompleteUserSignupSession } if s.UserEmail == "" { log.Print("signup session username is nil") return nil, config.ErrIncompleteUserSignupSession } if len(confirmKeyEntry) != len(s.SetKeySelection) { log.Printf("confirm and set key entry length mismatch %d != %d", len(confirmKeyEntry), len(s.SetKeySelection)) return nil, config.ErrSetConfirmSignupMismatch } passcodeLen := len(confirmKeyEntry) setKeyVals, err := s.getSelectedKeyVals(s.SetKeySelection, s.SetIdxInterface) if err != nil { return nil, err } confirmKeyVals, err := s.getSelectedKeyVals(confirmKeyEntry, s.ConfirmIdxInterface) passcode := make([]int, passcodeLen) for idx := 0; idx < passcodeLen; idx++ { setKey := set.NewSetFromSlice[int](setKeyVals[idx]) confirmKey := set.NewSetFromSlice[int](confirmKeyVals[idx]) intersection := setKey.Intersect(confirmKey) if intersection.Size() < 1 { log.Printf("set and confirm do not intersect at index %d", idx) return nil, config.ErrSetConfirmSignupMismatch } if intersection.Size() > 1 { log.Printf("set and confirm intersect at more than one point at index %d", idx) return nil, config.ErrSetConfirmSignupMismatch } intersectionSlice := intersection.ToSlice() passcode[idx] = intersectionSlice[0] } return passcode, nil } func (s *UserSignSession) SetUserNKode(keySelection KeySelection) (IdxInterface, error) { validKeySelection := all.All[int](keySelection, func(i int) bool { return 0 <= i && i < s.Kp.NumbOfKeys }) if !validKeySelection { log.Printf("one or key selection is out of range 0-%d", s.Kp.NumbOfKeys-1) return nil, config.ErrKeyIndexOutOfRange } s.SetKeySelection = keySelection setKp := s.SignupKeypad() setInterface := UserInterface{IdxInterface: s.SetIdxInterface, Kp: &setKp} err := setInterface.DisperseInterface() if err != nil { return nil, err } s.ConfirmIdxInterface = setInterface.IdxInterface return s.ConfirmIdxInterface, nil } func (s *UserSignSession) getSelectedKeyVals(keySelections KeySelection, userInterface []int) ([][]int, error) { signupKp := s.SignupKeypad() keypadInterface, err := security.ListToMatrix(userInterface, signupKp.AttrsPerKey) if err != nil { return nil, err } keyVals := make([][]int, len(keySelections)) for idx, keyIdx := range keySelections { keyVals[idx] = keypadInterface[keyIdx] } return keyVals, nil } func signupInterface(baseUserInterface UserInterface, kp KeypadDimension) (*UserInterface, []RGBColor, error) { // This method randomly drops sets from the base user interface so it is a square and dispersable matrix if kp.IsDispersable() { return nil, nil, config.ErrKeypadIsNotDispersible } err := baseUserInterface.RandomShuffle() if err != nil { return nil, nil, err } // attributes are arranged by key interfaceMatrix interfaceMatrix, err := baseUserInterface.InterfaceMatrix() if err != nil { return nil, nil, err } // attributes are arranged by set attrSetView, err := security.MatrixTranspose(interfaceMatrix) if err != nil { return nil, nil, err } setIdxs := security.IdentityArray(kp.AttrsPerKey) if err := security.FisherYatesShuffle[int](&setIdxs); err != nil { return nil, nil, err } setIdxs = setIdxs[:kp.NumbOfKeys] sort.Ints(setIdxs) selectedSets := make([][]int, kp.NumbOfKeys) selectedColors := make([]RGBColor, kp.NumbOfKeys) for idx, setIdx := range setIdxs { selectedSets[idx] = attrSetView[setIdx] selectedColors[idx] = SetColors[setIdx] } // convert set view back into key view selectedSets, err = security.MatrixTranspose(selectedSets) if err != nil { return nil, nil, err } signupUserInterface := UserInterface{ IdxInterface: security.MatrixToList(selectedSets), Kp: &KeypadDimension{ AttrsPerKey: kp.NumbOfKeys, NumbOfKeys: kp.NumbOfKeys, }, } return &signupUserInterface, selectedColors, nil } func (s *UserSignSession) SignupKeypad() KeypadDimension { return KeypadDimension{ AttrsPerKey: s.Kp.NumbOfKeys, NumbOfKeys: s.Kp.NumbOfKeys, } }