github.com/gravitational/teleport/api@v0.0.0-20240507183017-3110591cbafc/types/accountrecovery.go (about)

     1  /*
     2   * Copyright 2021 Gravitational, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package types
    18  
    19  import (
    20  	"time"
    21  
    22  	"github.com/gravitational/trace"
    23  )
    24  
    25  // NewRecoveryCodes creates a new RecoveryCodes with the given codes and created
    26  // time.
    27  func NewRecoveryCodes(codes []RecoveryCode, created time.Time, username string) (*RecoveryCodesV1, error) {
    28  	rc := &RecoveryCodesV1{
    29  		Metadata: Metadata{
    30  			Name: username,
    31  		},
    32  		Spec: RecoveryCodesSpecV1{
    33  			Codes:   codes,
    34  			Created: created,
    35  		},
    36  	}
    37  
    38  	if err := rc.CheckAndSetDefaults(); err != nil {
    39  		return nil, trace.Wrap(err)
    40  	}
    41  
    42  	return rc, nil
    43  }
    44  
    45  // CheckAndSetDefaults validates fields and populates empty fields with default values.
    46  func (t *RecoveryCodesV1) CheckAndSetDefaults() error {
    47  	t.setStaticFields()
    48  
    49  	if err := t.Metadata.CheckAndSetDefaults(); err != nil {
    50  		return trace.Wrap(err)
    51  	}
    52  
    53  	if t.Spec.Codes == nil {
    54  		return trace.BadParameter("missing Codes field")
    55  	}
    56  
    57  	if t.Spec.Created.IsZero() {
    58  		return trace.BadParameter("missing Created field")
    59  	}
    60  
    61  	return nil
    62  }
    63  
    64  func (t *RecoveryCodesV1) setStaticFields() {
    65  	t.Kind = KindRecoveryCodes
    66  	t.Version = V1
    67  }
    68  
    69  // GetCodes returns recovery codes.
    70  func (t *RecoveryCodesV1) GetCodes() []RecoveryCode {
    71  	return t.Spec.Codes
    72  }
    73  
    74  // RecoveryAttempt represents an unsuccessful attempt at recovering a user's account.
    75  type RecoveryAttempt struct {
    76  	// Time is time of the attempt.
    77  	Time time.Time `json:"time"`
    78  	// Expires defines the time when this attempt should expire.
    79  	Expires time.Time `json:"expires"`
    80  }
    81  
    82  func (a *RecoveryAttempt) Check() error {
    83  	switch {
    84  	case a.Time.IsZero():
    85  		return trace.BadParameter("missing parameter time")
    86  	case a.Expires.IsZero():
    87  		return trace.BadParameter("missing parameter expires")
    88  	}
    89  	return nil
    90  }
    91  
    92  // IsMaxFailedRecoveryAttempt determines if user reached their max failed attempts.
    93  // Attempts list is expected to come sorted from oldest to latest time.
    94  func IsMaxFailedRecoveryAttempt(maxAttempts int, attempts []*RecoveryAttempt, now time.Time) bool {
    95  	var failed int
    96  	for i := len(attempts) - 1; i >= 0; i-- {
    97  		if attempts[i].Expires.After(now) {
    98  			failed++
    99  		}
   100  		if failed >= maxAttempts {
   101  			return true
   102  		}
   103  	}
   104  	return false
   105  }