feat: provide option to limit (T)OTP checks (#7693)

* feat: provide option to limit (T)OTP checks

* fix requests in console

* update errors pkg

* cleanup

* cleanup

* improve naming of existing config
This commit is contained in:
Livio Spring
2024-04-10 11:14:55 +02:00
committed by GitHub
parent e3f10f7e23
commit 153df2e12f
58 changed files with 752 additions and 755 deletions

View File

@@ -1671,6 +1671,15 @@ func TestCommandSide_HumanCheckOTPSMS(t *testing.T) {
),
),
),
expectFilter(), // recheck
expectFilter(
eventFromEventPusher(
org.NewLockoutPolicyAddedEvent(ctx,
&org.NewAggregate("orgID").Aggregate,
3, 3, true,
),
),
),
expectPush(
user.NewHumanOTPSMSCheckFailedEvent(ctx,
&user.NewAggregate("user1", "org1").Aggregate,
@@ -1707,6 +1716,86 @@ func TestCommandSide_HumanCheckOTPSMS(t *testing.T) {
err: zerrors.ThrowInvalidArgument(nil, "CODE-woT0xc", "Errors.User.Code.Invalid"),
},
},
{
name: "invalid code, max attempts reached, error",
fields: fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
user.NewHumanOTPSMSAddedEvent(ctx,
&user.NewAggregate("user1", "org1").Aggregate,
),
),
eventFromEventPusherWithCreationDateNow(
user.NewHumanOTPSMSCodeAddedEvent(ctx,
&user.NewAggregate("user1", "org1").Aggregate,
&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "enc",
KeyID: "id",
Crypted: []byte("other-code"),
},
time.Hour,
&user.AuthRequestInfo{
ID: "authRequestID",
UserAgentID: "userAgentID",
BrowserInfo: &user.BrowserInfo{
UserAgent: "user-agent",
AcceptLanguage: "en",
RemoteIP: net.IP{192, 0, 2, 1},
},
},
),
),
),
expectFilter(), // recheck
expectFilter(
eventFromEventPusher(
org.NewLockoutPolicyAddedEvent(ctx,
&org.NewAggregate("orgID").Aggregate,
1, 1, true,
),
),
),
expectPush(
user.NewHumanOTPSMSCheckFailedEvent(ctx,
&user.NewAggregate("user1", "org1").Aggregate,
&user.AuthRequestInfo{
ID: "authRequestID",
UserAgentID: "userAgentID",
BrowserInfo: &user.BrowserInfo{
UserAgent: "user-agent",
AcceptLanguage: "en",
RemoteIP: net.IP{192, 0, 2, 1},
},
},
),
user.NewUserLockedEvent(ctx,
&user.NewAggregate("user1", "org1").Aggregate,
),
),
),
userEncryption: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
},
args: args{
ctx: ctx,
userID: "user1",
code: "code",
resourceOwner: "org1",
authRequest: &domain.AuthRequest{
ID: "authRequestID",
AgentID: "userAgentID",
BrowserInfo: &domain.BrowserInfo{
UserAgent: "user-agent",
AcceptLanguage: "en",
RemoteIP: net.IP{192, 0, 2, 1},
},
},
},
res: res{
err: zerrors.ThrowInvalidArgument(nil, "CODE-woT0xc", "Errors.User.Code.Invalid"),
},
},
{
name: "code ok",
fields: fields{
@@ -1739,6 +1828,7 @@ func TestCommandSide_HumanCheckOTPSMS(t *testing.T) {
),
),
),
expectFilter(), // recheck
expectPush(
user.NewHumanOTPSMSCheckSucceededEvent(ctx,
&user.NewAggregate("user1", "org1").Aggregate,
@@ -1777,6 +1867,65 @@ func TestCommandSide_HumanCheckOTPSMS(t *testing.T) {
},
},
},
{
name: "code ok, locked in the meantime",
fields: fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
user.NewHumanOTPSMSAddedEvent(ctx,
&user.NewAggregate("user1", "org1").Aggregate,
),
),
eventFromEventPusherWithCreationDateNow(
user.NewHumanOTPSMSCodeAddedEvent(ctx,
&user.NewAggregate("user1", "org1").Aggregate,
&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "enc",
KeyID: "id",
Crypted: []byte("code"),
},
time.Hour,
&user.AuthRequestInfo{
ID: "authRequestID",
UserAgentID: "userAgentID",
BrowserInfo: &user.BrowserInfo{
UserAgent: "user-agent",
AcceptLanguage: "en",
RemoteIP: net.IP{192, 0, 2, 1},
},
},
),
),
),
expectFilter( // recheck
user.NewUserLockedEvent(ctx,
&user.NewAggregate("user1", "org1").Aggregate,
),
),
),
userEncryption: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
},
args: args{
ctx: ctx,
userID: "user1",
code: "code",
resourceOwner: "org1",
authRequest: &domain.AuthRequest{
ID: "authRequestID",
AgentID: "userAgentID",
BrowserInfo: &domain.BrowserInfo{
UserAgent: "user-agent",
AcceptLanguage: "en",
RemoteIP: net.IP{192, 0, 2, 1},
},
},
},
res: res{
err: zerrors.ThrowPreconditionFailed(nil, "COMMAND-S6h4R", "Errors.User.Locked"),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@@ -2616,6 +2765,15 @@ func TestCommandSide_HumanCheckOTPEmail(t *testing.T) {
),
),
),
expectFilter(), // recheck
expectFilter(
eventFromEventPusher(
org.NewLockoutPolicyAddedEvent(ctx,
&org.NewAggregate("orgID").Aggregate,
3, 3, true,
),
),
),
expectPush(
user.NewHumanOTPEmailCheckFailedEvent(ctx,
&user.NewAggregate("user1", "org1").Aggregate,
@@ -2652,6 +2810,86 @@ func TestCommandSide_HumanCheckOTPEmail(t *testing.T) {
err: zerrors.ThrowInvalidArgument(nil, "CODE-woT0xc", "Errors.User.Code.Invalid"),
},
},
{
name: "invalid code, max attempts reached, error",
fields: fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
user.NewHumanOTPEmailAddedEvent(ctx,
&user.NewAggregate("user1", "org1").Aggregate,
),
),
eventFromEventPusherWithCreationDateNow(
user.NewHumanOTPEmailCodeAddedEvent(ctx,
&user.NewAggregate("user1", "org1").Aggregate,
&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "enc",
KeyID: "id",
Crypted: []byte("other-code"),
},
time.Hour,
&user.AuthRequestInfo{
ID: "authRequestID",
UserAgentID: "userAgentID",
BrowserInfo: &user.BrowserInfo{
UserAgent: "user-agent",
AcceptLanguage: "en",
RemoteIP: net.IP{192, 0, 2, 1},
},
},
),
),
),
expectFilter(), // recheck
expectFilter(
eventFromEventPusher(
org.NewLockoutPolicyAddedEvent(ctx,
&org.NewAggregate("orgID").Aggregate,
1, 1, true,
),
),
),
expectPush(
user.NewHumanOTPEmailCheckFailedEvent(ctx,
&user.NewAggregate("user1", "org1").Aggregate,
&user.AuthRequestInfo{
ID: "authRequestID",
UserAgentID: "userAgentID",
BrowserInfo: &user.BrowserInfo{
UserAgent: "user-agent",
AcceptLanguage: "en",
RemoteIP: net.IP{192, 0, 2, 1},
},
},
),
user.NewUserLockedEvent(ctx,
&user.NewAggregate("user1", "org1").Aggregate,
),
),
),
userEncryption: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
},
args: args{
ctx: ctx,
userID: "user1",
code: "code",
resourceOwner: "org1",
authRequest: &domain.AuthRequest{
ID: "authRequestID",
AgentID: "userAgentID",
BrowserInfo: &domain.BrowserInfo{
UserAgent: "user-agent",
AcceptLanguage: "en",
RemoteIP: net.IP{192, 0, 2, 1},
},
},
},
res: res{
err: zerrors.ThrowInvalidArgument(nil, "CODE-woT0xc", "Errors.User.Code.Invalid"),
},
},
{
name: "code ok",
fields: fields{
@@ -2684,6 +2922,7 @@ func TestCommandSide_HumanCheckOTPEmail(t *testing.T) {
),
),
),
expectFilter(), // recheck
expectPush(
user.NewHumanOTPEmailCheckSucceededEvent(ctx,
&user.NewAggregate("user1", "org1").Aggregate,
@@ -2722,6 +2961,65 @@ func TestCommandSide_HumanCheckOTPEmail(t *testing.T) {
},
},
},
{
name: "code ok, locked in the meantime",
fields: fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
user.NewHumanOTPEmailAddedEvent(ctx,
&user.NewAggregate("user1", "org1").Aggregate,
),
),
eventFromEventPusherWithCreationDateNow(
user.NewHumanOTPEmailCodeAddedEvent(ctx,
&user.NewAggregate("user1", "org1").Aggregate,
&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "enc",
KeyID: "id",
Crypted: []byte("code"),
},
time.Hour,
&user.AuthRequestInfo{
ID: "authRequestID",
UserAgentID: "userAgentID",
BrowserInfo: &user.BrowserInfo{
UserAgent: "user-agent",
AcceptLanguage: "en",
RemoteIP: net.IP{192, 0, 2, 1},
},
},
),
),
),
expectFilter( // recheck
user.NewUserLockedEvent(ctx,
&user.NewAggregate("user1", "org1").Aggregate,
),
),
),
userEncryption: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
},
args: args{
ctx: ctx,
userID: "user1",
code: "code",
resourceOwner: "org1",
authRequest: &domain.AuthRequest{
ID: "authRequestID",
AgentID: "userAgentID",
BrowserInfo: &domain.BrowserInfo{
UserAgent: "user-agent",
AcceptLanguage: "en",
RemoteIP: net.IP{192, 0, 2, 1},
},
},
},
res: res{
err: zerrors.ThrowPreconditionFailed(nil, "COMMAND-S6h4R", "Errors.User.Locked"),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {