Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 2 additions & 8 deletions api/proto/teleport/legacy/types/types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -3276,14 +3276,8 @@ message LoginStatus {
(gogoproto.nullable) = false,
(gogoproto.jsontag) = "lock_expires,omitempty"
];
// RecoveryAttemptLockExpires contains the time when this lock will expire
// from reaching MaxAccountRecoveryAttempts. This field is used to determine
// if a user got locked from recovery attempts.
google.protobuf.Timestamp RecoveryAttemptLockExpires = 5 [
(gogoproto.stdtime) = true,
(gogoproto.nullable) = false,
(gogoproto.jsontag) = "recovery_attempt_lock_expires,omitempty"
];
reserved 5; // removed "google.protobuf.Timestamp RecoveryAttemptLockExpires" after lockout was removed
reserved "RecoveryAttemptLockExpires";
Comment on lines +3279 to +3280
Copy link
Copy Markdown
Contributor

@codingllama codingllama Jan 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The removal of this field is making UpdateAndSwapUser fail.

If the stored user had the "recovery_attempt_lock_expires" field set, then the comparison always fails. That is because when we read and unmarshal the user the unknown field gets dropped, but the actual CompareAndSwap operation compares using the storage blobs instead.

Reverting the deletion, but keeping the field deprecated should serve as an immediate fix. Arguably, a better fix is to make sure CompareAndSwap compare apples-to-apples: either user vs user, or blob vs blob, but never user vs blob.

@jentfoo @rosstimothy

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just submitted a PR to bring the field back: #37618

Thank you for finding this issue @codingllama!

}

// CreatedBy holds information about the person or agent who created the user
Expand Down
3,368 changes: 1,660 additions & 1,708 deletions api/types/types.pb.go

Large diffs are not rendered by default.

9 changes: 0 additions & 9 deletions api/types/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,6 @@ type User interface {
GetStatus() LoginStatus
// SetLocked sets login status to locked
SetLocked(until time.Time, reason string)
// SetRecoveryAttemptLockExpires sets the lock expiry time for both recovery and login attempt.
SetRecoveryAttemptLockExpires(until time.Time, reason string)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: it wouldn't hurt to do separate PRs to v14 and v13 which mark this method as deprecated. This way if anyone is using this part of Teleport's API it will start to get flagged for them.

// ResetLocks resets lock related fields to empty values.
ResetLocks()
// SetRoles sets user roles
Expand Down Expand Up @@ -527,18 +525,11 @@ func (u *UserV2) SetLocked(until time.Time, reason string) {
u.Spec.Status.LockedTime = time.Now().UTC()
}

// SetRecoveryAttemptLockExpires sets the lock expiry time for both recovery and login attempt.
func (u *UserV2) SetRecoveryAttemptLockExpires(until time.Time, reason string) {
u.Spec.Status.RecoveryAttemptLockExpires = until
u.SetLocked(until, reason)
}

// ResetLocks resets lock related fields to empty values.
func (u *UserV2) ResetLocks() {
u.Spec.Status.IsLocked = false
u.Spec.Status.LockedMessage = ""
u.Spec.Status.LockExpires = time.Time{}
u.Spec.Status.RecoveryAttemptLockExpires = time.Time{}
}

// DeepCopy creates a clone of this user value.
Expand Down
1 change: 0 additions & 1 deletion docs/pages/access-controls/sso.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ spec:
is_locked: false
lock_expires: "0001-01-01T00:00:00Z"
locked_time: "0001-01-01T00:00:00Z"
recovery_attempt_lock_expires: "0001-01-01T00:00:00Z"
traits:
github_teams:
- my-team
Expand Down
3 changes: 1 addition & 2 deletions e2e/config/state.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ spec:
is_locked: false
lock_expires: "0001-01-01T00:00:00Z"
locked_time: "0001-01-01T00:00:00Z"
recovery_attempt_lock_expires: "0001-01-01T00:00:00Z"
traits:
aws_role_arns: null
azure_identities: null
Expand All @@ -30,4 +29,4 @@ spec:
logins:
- root
windows_logins: null
version: v2
version: v2
Original file line number Diff line number Diff line change
Expand Up @@ -3081,14 +3081,8 @@ message LoginStatus {
(gogoproto.nullable) = false,
(gogoproto.jsontag) = "lock_expires,omitempty"
];
// RecoveryAttemptLockExpires contains the time when this lock will expire
// from reaching MaxAccountRecoveryAttempts. This field is used to determine
// if a user got locked from recovery attempts.
google.protobuf.Timestamp RecoveryAttemptLockExpires = 5 [
(gogoproto.stdtime) = true,
(gogoproto.nullable) = false,
(gogoproto.jsontag) = "recovery_attempt_lock_expires,omitempty"
];
reserved 5; // removed "google.protobuf.Timestamp RecoveryAttemptLockExpires" after lockout was removed
reserved "RecoveryAttemptLockExpires";
}

// CreatedBy holds information about the person or agent who created the user
Expand Down
Loading