mirror of
https://github.com/QuantumNous/new-api.git
synced 2026-10-30 11:06:56 +00:00
CreateUser accepted any integer role below the operator's own, so an admin could create a user with role 5 or -1, and a root user's request for role 99 went on to step-up verification before failing. Only the guest, common, admin and root roles are accepted now; any other value fails with an invalid-parameter error before verification and nothing is written.
390 lines
20 KiB
Go
390 lines
20 KiB
Go
package controller
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/QuantumNous/new-api/common"
|
|
"github.com/QuantumNous/new-api/model"
|
|
"github.com/QuantumNous/new-api/service"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// setupAdminUserTest promotes the enrolled operator to root and creates a
|
|
// managed common user for the administrative endpoints to act on.
|
|
func setupAdminUserTest(t *testing.T) (*model.User, service.AuthIdentity, *model.User) {
|
|
t.Helper()
|
|
operator, identity := setupSecurityEnrollmentTest(t)
|
|
require.NoError(t, model.DB.Model(operator).Update("role", common.RoleRootUser).Error)
|
|
require.NoError(t, model.PublishUserAuthCache(operator.Id))
|
|
password, err := common.Password2Hash("managed-password")
|
|
require.NoError(t, err)
|
|
target := &model.User{Username: "managed-user", Password: password, Role: common.RoleCommonUser, Status: common.UserStatusEnabled, Group: "default", AuthVersion: 1, AffCode: "managed-aff", GitHubId: "managed-github"}
|
|
require.NoError(t, model.DB.Create(target).Error)
|
|
return operator, identity, target
|
|
}
|
|
|
|
// adminUserRequest drives an admin user-management handler with the operator
|
|
// context that AdminAuth would have populated for a dashboard session.
|
|
func adminUserRequest(method, path, body, proof string, identity service.AuthIdentity, role int, params gin.Params, handler gin.HandlerFunc) *httptest.ResponseRecorder {
|
|
response := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(response)
|
|
c.Request = httptest.NewRequest(method, path, strings.NewReader(body))
|
|
c.Request.Header.Set("Content-Type", "application/json")
|
|
if proof != "" {
|
|
c.Request.Header.Set("X-Security-Proof", proof)
|
|
}
|
|
c.Params = params
|
|
c.Set("id", identity.UserID)
|
|
c.Set("username", "enrollment-user")
|
|
c.Set("role", role)
|
|
c.Set("session_id", identity.SessionID)
|
|
c.Set("auth_version", identity.UserAuthVersion)
|
|
c.Set("session_version", identity.SessionVersion)
|
|
handler(c)
|
|
return response
|
|
}
|
|
|
|
func TestAdminUserRiskOperationsRequireProofBeforeMutation(t *testing.T) {
|
|
for _, test := range []struct {
|
|
name, method, path string
|
|
body func(target *model.User) string
|
|
params func(target *model.User) gin.Params
|
|
handler gin.HandlerFunc
|
|
unchanged func(t *testing.T, target *model.User)
|
|
}{
|
|
{
|
|
name: "hard delete", method: http.MethodDelete, path: "/api/user/:id", handler: DeleteUser,
|
|
params: func(target *model.User) gin.Params { return gin.Params{{Key: "id", Value: fmt.Sprint(target.Id)}} },
|
|
unchanged: func(t *testing.T, target *model.User) {
|
|
_, err := model.GetUserById(target.Id, false)
|
|
assert.NoError(t, err)
|
|
},
|
|
},
|
|
{
|
|
name: "manage disable", method: http.MethodPost, path: "/api/user/manage", handler: ManageUser,
|
|
body: func(target *model.User) string { return fmt.Sprintf(`{"id":%d,"action":"disable"}`, target.Id) },
|
|
unchanged: func(t *testing.T, target *model.User) {
|
|
stored, err := model.GetUserById(target.Id, false)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, common.UserStatusEnabled, stored.Status)
|
|
},
|
|
},
|
|
{
|
|
name: "manage promote", method: http.MethodPost, path: "/api/user/manage", handler: ManageUser,
|
|
body: func(target *model.User) string { return fmt.Sprintf(`{"id":%d,"action":"promote"}`, target.Id) },
|
|
unchanged: func(t *testing.T, target *model.User) {
|
|
stored, err := model.GetUserById(target.Id, false)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, common.RoleCommonUser, stored.Role)
|
|
},
|
|
},
|
|
{
|
|
name: "manage delete", method: http.MethodPost, path: "/api/user/manage", handler: ManageUser,
|
|
body: func(target *model.User) string { return fmt.Sprintf(`{"id":%d,"action":"delete"}`, target.Id) },
|
|
unchanged: func(t *testing.T, target *model.User) {
|
|
_, err := model.GetUserById(target.Id, false)
|
|
assert.NoError(t, err)
|
|
},
|
|
},
|
|
{
|
|
name: "password reset", method: http.MethodPut, path: "/api/user/", handler: UpdateUser,
|
|
body: func(target *model.User) string {
|
|
return fmt.Sprintf(`{"id":%d,"username":"managed-user","display_name":"Managed","password":"replacement-pass"}`, target.Id)
|
|
},
|
|
unchanged: func(t *testing.T, target *model.User) {
|
|
stored, err := model.GetUserById(target.Id, true)
|
|
require.NoError(t, err)
|
|
assert.True(t, common.ValidatePasswordAndHash("managed-password", stored.Password))
|
|
},
|
|
},
|
|
{
|
|
name: "admin permission matrix", method: http.MethodPut, path: "/api/user/", handler: UpdateUser,
|
|
body: func(target *model.User) string {
|
|
return fmt.Sprintf(`{"id":%d,"username":"managed-user","display_name":"Managed","admin_permissions":{}}`, target.Id)
|
|
},
|
|
unchanged: func(t *testing.T, target *model.User) {
|
|
stored, err := model.GetUserById(target.Id, false)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "", stored.DisplayName)
|
|
},
|
|
},
|
|
{
|
|
name: "create administrator", method: http.MethodPost, path: "/api/user/", handler: CreateUser,
|
|
body: func(*model.User) string { return `{"username":"new-admin","password":"admin-password-1","role":10}` },
|
|
unchanged: func(t *testing.T, _ *model.User) {
|
|
var count int64
|
|
require.NoError(t, model.DB.Model(&model.User{}).Where("username = ?", "new-admin").Count(&count).Error)
|
|
assert.Zero(t, count)
|
|
},
|
|
},
|
|
{
|
|
name: "passkey reset", method: http.MethodDelete, path: "/api/user/:id/reset_passkey", handler: AdminResetPasskey,
|
|
params: func(target *model.User) gin.Params { return gin.Params{{Key: "id", Value: fmt.Sprint(target.Id)}} },
|
|
unchanged: func(t *testing.T, target *model.User) {
|
|
_, err := model.GetPasskeyByUserID(target.Id)
|
|
assert.NoError(t, err)
|
|
},
|
|
},
|
|
{
|
|
name: "two-factor disable", method: http.MethodDelete, path: "/api/user/:id/2fa", handler: AdminDisable2FA,
|
|
params: func(target *model.User) gin.Params { return gin.Params{{Key: "id", Value: fmt.Sprint(target.Id)}} },
|
|
unchanged: func(t *testing.T, target *model.User) {
|
|
twoFA, err := model.GetTwoFAByUserId(target.Id)
|
|
require.NoError(t, err)
|
|
assert.True(t, twoFA.IsEnabled)
|
|
},
|
|
},
|
|
{
|
|
name: "built-in binding clear", method: http.MethodDelete, path: "/api/user/:id/bindings/:binding_type", handler: AdminClearUserBinding,
|
|
params: func(target *model.User) gin.Params {
|
|
return gin.Params{{Key: "id", Value: fmt.Sprint(target.Id)}, {Key: "binding_type", Value: "github"}}
|
|
},
|
|
unchanged: func(t *testing.T, target *model.User) {
|
|
stored, err := model.GetUserById(target.Id, false)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "managed-github", stored.GitHubId)
|
|
},
|
|
},
|
|
{
|
|
name: "custom oauth unbind", method: http.MethodDelete, path: "/api/user/:id/oauth/bindings/:provider_id", handler: UnbindCustomOAuthByAdmin,
|
|
params: func(target *model.User) gin.Params {
|
|
return gin.Params{{Key: "id", Value: fmt.Sprint(target.Id)}, {Key: "provider_id", Value: "7"}}
|
|
},
|
|
unchanged: func(t *testing.T, target *model.User) {
|
|
bindings, err := model.GetUserOAuthBindingsByUserId(target.Id)
|
|
require.NoError(t, err)
|
|
assert.Len(t, bindings, 1)
|
|
},
|
|
},
|
|
} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
_, identity, target := setupAdminUserTest(t)
|
|
require.NoError(t, model.DB.Create(&model.PasskeyCredential{UserID: target.Id, CredentialID: "managed-passkey", PublicKey: "public-key"}).Error)
|
|
require.NoError(t, model.DB.Create(&model.TwoFA{UserId: target.Id, Secret: "JBSWY3DPEHPK3PXP", IsEnabled: true}).Error)
|
|
require.NoError(t, model.DB.Create(&model.UserOAuthBinding{UserId: target.Id, ProviderId: 7, ProviderUserId: "provider-user"}).Error)
|
|
var body string
|
|
if test.body != nil {
|
|
body = test.body(target)
|
|
}
|
|
var params gin.Params
|
|
if test.params != nil {
|
|
params = test.params(target)
|
|
}
|
|
response := adminUserRequest(test.method, test.path, body, "", identity, common.RoleRootUser, params, test.handler)
|
|
var result securityEnrollmentResponse
|
|
require.NoError(t, common.Unmarshal(response.Body.Bytes(), &result))
|
|
assert.Equal(t, http.StatusForbidden, response.Code)
|
|
assert.Equal(t, "SECURITY_PROOF_REQUIRED", result.Code)
|
|
test.unchanged(t, target)
|
|
var flows int64
|
|
require.NoError(t, model.DB.Model(&model.AuthFlow{}).Count(&flows).Error)
|
|
assert.Zero(t, flows)
|
|
})
|
|
}
|
|
}
|
|
|
|
// An administrator (not root) exercises the routine paths without touching the
|
|
// Casbin policy store, which the security fixture does not provision.
|
|
func TestAdminUserRoutineEditsDoNotRequireProof(t *testing.T) {
|
|
_, identity, target := setupAdminUserTest(t)
|
|
response := adminUserRequest(http.MethodPut, "/api/user/", fmt.Sprintf(`{"id":%d,"username":"managed-user","display_name":"Renamed","group":"default"}`, target.Id), "", identity, common.RoleAdminUser, nil, UpdateUser)
|
|
var result securityEnrollmentResponse
|
|
require.NoError(t, common.Unmarshal(response.Body.Bytes(), &result))
|
|
require.True(t, result.Success, response.Body.String())
|
|
stored, err := model.GetUserById(target.Id, true)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "Renamed", stored.DisplayName)
|
|
assert.True(t, common.ValidatePasswordAndHash("managed-password", stored.Password))
|
|
|
|
response = adminUserRequest(http.MethodPost, "/api/user/", `{"username":"new-member","password":"member-password-1","role":1}`, "", identity, common.RoleAdminUser, nil, CreateUser)
|
|
require.NoError(t, common.Unmarshal(response.Body.Bytes(), &result))
|
|
require.True(t, result.Success, response.Body.String())
|
|
var count int64
|
|
require.NoError(t, model.DB.Model(&model.User{}).Where("username = ?", "new-member").Count(&count).Error)
|
|
assert.EqualValues(t, 1, count)
|
|
}
|
|
|
|
func TestCreateUserRejectsNonStandardRole(t *testing.T) {
|
|
_, identity, _ := setupAdminUserTest(t)
|
|
for i, test := range []struct {
|
|
name string
|
|
role int
|
|
operatorRole int
|
|
}{
|
|
{"between common and admin", 5, common.RoleAdminUser},
|
|
{"negative", -1, common.RoleAdminUser},
|
|
{"between admin and root", 99, common.RoleRootUser},
|
|
} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
username := fmt.Sprintf("odd-role-%d", i)
|
|
body := fmt.Sprintf(`{"username":%q,"password":"member-password-1","role":%d}`, username, test.role)
|
|
response := adminUserRequest(http.MethodPost, "/api/user/", body, "", identity, test.operatorRole, nil, CreateUser)
|
|
var result securityEnrollmentResponse
|
|
require.NoError(t, common.Unmarshal(response.Body.Bytes(), &result))
|
|
assert.Equal(t, http.StatusOK, response.Code)
|
|
assert.False(t, result.Success)
|
|
assert.Empty(t, result.Code)
|
|
var count int64
|
|
require.NoError(t, model.DB.Model(&model.User{}).Where("username = ?", username).Count(&count).Error)
|
|
assert.Zero(t, count)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAdminUserProofIsBoundToTargetAndActionAndConsumedOnce(t *testing.T) {
|
|
_, identity, target := setupAdminUserTest(t)
|
|
other := &model.User{Username: "other-user", Password: target.Password, Role: common.RoleCommonUser, Status: common.UserStatusEnabled, Group: "default", AuthVersion: 1, AffCode: "other-aff"}
|
|
require.NoError(t, model.DB.Create(other).Error)
|
|
disableTarget := service.VerificationOperation{Scope: service.VerificationScopeAdminUserManage, Context: []byte(fmt.Sprintf(`{"user_id":%d,"action":"disable"}`, target.Id))}
|
|
proof := issueSecurityEnrollmentProof(t, identity, disableTarget, service.VerificationMethodPassword)
|
|
|
|
for _, test := range []struct {
|
|
name, body, code string
|
|
params gin.Params
|
|
handler gin.HandlerFunc
|
|
}{
|
|
{"different action", fmt.Sprintf(`{"id":%d,"action":"enable"}`, target.Id), "SECURITY_PROOF_CONTEXT_MISMATCH", nil, ManageUser},
|
|
{"different user", fmt.Sprintf(`{"id":%d,"action":"disable"}`, other.Id), "SECURITY_PROOF_CONTEXT_MISMATCH", nil, ManageUser},
|
|
{"different scope", "", "SECURITY_PROOF_SCOPE_MISMATCH", gin.Params{{Key: "id", Value: fmt.Sprint(target.Id)}}, DeleteUser},
|
|
} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
response := adminUserRequest(http.MethodPost, "/api/user/manage", test.body, proof, identity, common.RoleRootUser, test.params, test.handler)
|
|
var result securityEnrollmentResponse
|
|
require.NoError(t, common.Unmarshal(response.Body.Bytes(), &result))
|
|
assert.Equal(t, http.StatusForbidden, response.Code)
|
|
assert.Equal(t, test.code, result.Code)
|
|
})
|
|
}
|
|
for _, id := range []int{target.Id, other.Id} {
|
|
stored, err := model.GetUserById(id, false)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, common.UserStatusEnabled, stored.Status)
|
|
}
|
|
|
|
response := adminUserRequest(http.MethodPost, "/api/user/manage", fmt.Sprintf(`{"id":%d,"action":"disable"}`, target.Id), proof, identity, common.RoleRootUser, nil, ManageUser)
|
|
var result securityEnrollmentResponse
|
|
require.NoError(t, common.Unmarshal(response.Body.Bytes(), &result))
|
|
require.True(t, result.Success, response.Body.String())
|
|
stored, err := model.GetUserById(target.Id, false)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, common.UserStatusDisabled, stored.Status)
|
|
var audit model.AuditLog
|
|
require.NoError(t, model.LOG_DB.Where("action = ?", "user.manage").Last(&audit).Error)
|
|
require.NotNil(t, audit.Other.Op)
|
|
verificationMethod, err := common.Marshal(audit.Other.Op.Params["verification_method"])
|
|
require.NoError(t, err)
|
|
assert.JSONEq(t, `"password"`, string(verificationMethod))
|
|
targetUserID, err := common.Marshal(audit.Other.Op.Params["target_user_id"])
|
|
require.NoError(t, err)
|
|
assert.JSONEq(t, fmt.Sprint(target.Id), string(targetUserID))
|
|
auditJSON, err := common.Marshal(audit)
|
|
require.NoError(t, err)
|
|
assert.NotContains(t, string(auditJSON), proof)
|
|
|
|
response = adminUserRequest(http.MethodPost, "/api/user/manage", fmt.Sprintf(`{"id":%d,"action":"disable"}`, target.Id), proof, identity, common.RoleRootUser, nil, ManageUser)
|
|
require.NoError(t, common.Unmarshal(response.Body.Bytes(), &result))
|
|
assert.Equal(t, http.StatusForbidden, response.Code)
|
|
assert.Equal(t, "SECURITY_PROOF_CONSUMED", result.Code)
|
|
}
|
|
|
|
func TestAdminUserBindingProofDistinguishesBuiltInAndCustomBindings(t *testing.T) {
|
|
_, identity, target := setupAdminUserTest(t)
|
|
require.NoError(t, model.DB.Create(&model.UserOAuthBinding{UserId: target.Id, ProviderId: 7, ProviderUserId: "provider-user"}).Error)
|
|
custom := service.VerificationOperation{Scope: service.VerificationScopeAdminUserBindingClear, Context: []byte(fmt.Sprintf(`{"user_id":%d,"provider_id":7}`, target.Id))}
|
|
proof := issueSecurityEnrollmentProof(t, identity, custom, service.VerificationMethodPassword)
|
|
params := gin.Params{{Key: "id", Value: fmt.Sprint(target.Id)}, {Key: "binding_type", Value: "github"}}
|
|
response := adminUserRequest(http.MethodDelete, "/api/user/:id/bindings/:binding_type", "", proof, identity, common.RoleRootUser, params, AdminClearUserBinding)
|
|
var result securityEnrollmentResponse
|
|
require.NoError(t, common.Unmarshal(response.Body.Bytes(), &result))
|
|
assert.Equal(t, "SECURITY_PROOF_CONTEXT_MISMATCH", result.Code)
|
|
stored, err := model.GetUserById(target.Id, false)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "managed-github", stored.GitHubId)
|
|
|
|
params = gin.Params{{Key: "id", Value: fmt.Sprint(target.Id)}, {Key: "provider_id", Value: "7"}}
|
|
response = adminUserRequest(http.MethodDelete, "/api/user/:id/oauth/bindings/:provider_id", "", proof, identity, common.RoleRootUser, params, UnbindCustomOAuthByAdmin)
|
|
require.NoError(t, common.Unmarshal(response.Body.Bytes(), &result))
|
|
require.True(t, result.Success, response.Body.String())
|
|
bindings, err := model.GetUserOAuthBindingsByUserId(target.Id)
|
|
require.NoError(t, err)
|
|
assert.Empty(t, bindings)
|
|
var audit model.AuditLog
|
|
require.NoError(t, model.LOG_DB.Where("action = ?", "user.binding_clear").Last(&audit).Error)
|
|
require.NotNil(t, audit.Other.Op)
|
|
providerID, err := common.Marshal(audit.Other.Op.Params["provider_id"])
|
|
require.NoError(t, err)
|
|
assert.JSONEq(t, "7", string(providerID))
|
|
}
|
|
|
|
func TestAdminUserVerificationPolicy(t *testing.T) {
|
|
t.Run("requires an administrator", func(t *testing.T) {
|
|
_, identity := setupSecurityEnrollmentTest(t)
|
|
_, err := service.GetVerificationRequirements(identity, service.VerificationScopeAdminUserDelete)
|
|
assert.ErrorIs(t, err, service.ErrVerificationForbidden)
|
|
})
|
|
t.Run("falls back to the password without a second factor", func(t *testing.T) {
|
|
_, identity, _ := setupAdminUserTest(t)
|
|
requirements, err := service.GetVerificationRequirements(identity, service.VerificationScopeAdminUserDelete)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, []service.VerificationMethodOption{{Method: service.VerificationMethodPassword, Available: true}}, requirements.Methods)
|
|
})
|
|
t.Run("prefers an enrolled second factor", func(t *testing.T) {
|
|
operator, identity, _ := setupAdminUserTest(t)
|
|
require.NoError(t, model.DB.Create(&model.TwoFA{UserId: operator.Id, Secret: "JBSWY3DPEHPK3PXP", IsEnabled: true}).Error)
|
|
requirements, err := service.GetVerificationRequirements(identity, service.VerificationScopeAdminUserManage)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, []service.VerificationMethodOption{{Method: service.VerificationMethodTwoFA, Available: true}}, requirements.Methods)
|
|
})
|
|
t.Run("a legacy token cannot present a proof", func(t *testing.T) {
|
|
operator, identity, target := setupAdminUserTest(t)
|
|
require.NoError(t, model.DB.Model(operator).Update("access_token", "legacy-admin-token").Error)
|
|
proof := issueSecurityEnrollmentProof(t, identity, service.VerificationOperation{Scope: service.VerificationScopeAdminUserDelete, Context: []byte(fmt.Sprintf(`{"user_id":%d}`, target.Id))}, service.VerificationMethodPassword)
|
|
response := accessTokenRequest(newAccessTokenTestRouter(), http.MethodDelete, fmt.Sprintf("/api/user/%d", target.Id), "legacy-admin-token", proof, "")
|
|
assert.Equal(t, http.StatusForbidden, response.Code)
|
|
assert.Equal(t, "SECURITY_PROOF_INVALID", decodeSecurityEnrollmentResponse(t, response).Code)
|
|
_, err := model.GetUserById(target.Id, false)
|
|
assert.NoError(t, err)
|
|
})
|
|
t.Run("a user:write token completes the deletion on its own proof", func(t *testing.T) {
|
|
operator, _, target := setupAdminUserTest(t)
|
|
require.NoError(t, model.DB.AutoMigrate(&model.ExternalIdentityClaim{}, &model.Token{}))
|
|
raw, _ := createScopedAccessToken(t, operator.Id, 0, "user:write")
|
|
router := newAccessTokenTestRouter()
|
|
response := accessTokenRequest(router, http.MethodPost, "/api/verify", raw, "", fmt.Sprintf(`{"scope":"admin.user.delete","method":"password","password":"enrollment-password","context":{"user_id":%d}}`, target.Id))
|
|
body := decodeSecurityEnrollmentResponse(t, response)
|
|
require.True(t, body.Success, body.Message)
|
|
var proof service.SecurityProof
|
|
require.NoError(t, common.Unmarshal(body.Data, &proof))
|
|
|
|
response = accessTokenRequest(router, http.MethodDelete, fmt.Sprintf("/api/user/%d", target.Id), raw, proof.ProofToken, "")
|
|
body = decodeSecurityEnrollmentResponse(t, response)
|
|
require.True(t, body.Success, body.Message)
|
|
_, err := model.GetUserById(target.Id, true)
|
|
assert.Error(t, err)
|
|
var audit model.AuditLog
|
|
require.NoError(t, model.LOG_DB.Where("action = ?", "user.delete").Last(&audit).Error)
|
|
assert.Equal(t, "access_token", audit.AuthMethod)
|
|
auditJSON, err := common.Marshal(audit)
|
|
require.NoError(t, err)
|
|
for _, secret := range []string{raw, proof.ProofToken, "enrollment-password"} {
|
|
assert.NotContains(t, string(auditJSON), secret)
|
|
}
|
|
})
|
|
t.Run("a user:read token cannot obtain a proof", func(t *testing.T) {
|
|
operator, _, target := setupAdminUserTest(t)
|
|
raw, _ := createScopedAccessToken(t, operator.Id, 0, "user:read")
|
|
response := accessTokenRequest(newAccessTokenTestRouter(), http.MethodPost, "/api/verify", raw, "", fmt.Sprintf(`{"scope":"admin.user.delete","method":"password","password":"enrollment-password","context":{"user_id":%d}}`, target.Id))
|
|
assert.Equal(t, http.StatusForbidden, response.Code)
|
|
assert.Equal(t, "SECURITY_ACTION_FORBIDDEN", decodeSecurityEnrollmentResponse(t, response).Code)
|
|
var flows int64
|
|
require.NoError(t, model.DB.Model(&model.AuthFlow{}).Count(&flows).Error)
|
|
assert.Zero(t, flows)
|
|
})
|
|
}
|