forked from baron/baron-sso
RP 테넌트 접근 정책 변경 시 기존 consent 자동 폐기
This commit is contained in:
@@ -1662,6 +1662,167 @@ func TestUpdateClient_HeadlessLoginIgnoresExistingTopLevelJWKS(t *testing.T) {
|
||||
assert.False(t, hasRequestObjectAlg)
|
||||
}
|
||||
|
||||
func TestUpdateClient_RevokesExistingConsentsWhenTenantPolicyChanges(t *testing.T) {
|
||||
var revokedSubjects []string
|
||||
|
||||
transport := roundTripFunc(func(r *http.Request) (*http.Response, error) {
|
||||
if r.Method == http.MethodGet && r.URL.Path == "/clients/client-1" {
|
||||
return httpJSONAny(r, http.StatusOK, domain.HydraClient{
|
||||
ClientID: "client-1",
|
||||
ClientName: "Tenant Guarded App",
|
||||
RedirectURIs: []string{"https://rp.example.com/callback"},
|
||||
GrantTypes: []string{"authorization_code", "refresh_token"},
|
||||
ResponseTypes: []string{"code"},
|
||||
Scope: "openid tenant profile email",
|
||||
TokenEndpointAuthMethod: "none",
|
||||
Metadata: map[string]interface{}{
|
||||
"tenant_access_restricted": true,
|
||||
"allowed_tenants": []string{"tenant-a"},
|
||||
},
|
||||
}), nil
|
||||
}
|
||||
if r.Method == http.MethodPut && r.URL.Path == "/clients/client-1" {
|
||||
var updated domain.HydraClient
|
||||
body, err := io.ReadAll(r.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, json.Unmarshal(body, &updated))
|
||||
return httpJSONAny(r, http.StatusOK, map[string]any{
|
||||
"client_id": updated.ClientID,
|
||||
"client_name": updated.ClientName,
|
||||
"redirect_uris": updated.RedirectURIs,
|
||||
"grant_types": updated.GrantTypes,
|
||||
"response_types": updated.ResponseTypes,
|
||||
"scope": updated.Scope,
|
||||
"token_endpoint_auth_method": updated.TokenEndpointAuthMethod,
|
||||
"metadata": updated.Metadata,
|
||||
}), nil
|
||||
}
|
||||
if r.Method == http.MethodDelete && r.URL.Path == "/oauth2/auth/sessions/consent" {
|
||||
revokedSubjects = append(revokedSubjects, r.URL.Query().Get("subject"))
|
||||
assert.Equal(t, "client-1", r.URL.Query().Get("client"))
|
||||
return httpResponse(r, http.StatusNoContent, ""), nil
|
||||
}
|
||||
return httpJSONAny(r, http.StatusNotFound, nil), nil
|
||||
})
|
||||
|
||||
consentRepo := &mockConsentRepo{
|
||||
consents: []domain.ClientConsent{
|
||||
{ClientID: "client-1", Subject: "user-1"},
|
||||
{ClientID: "client-1", Subject: "user-2"},
|
||||
{ClientID: "other-client", Subject: "user-3"},
|
||||
},
|
||||
}
|
||||
|
||||
h := &DevHandler{
|
||||
Hydra: &service.HydraAdminService{
|
||||
AdminURL: "http://hydra.test",
|
||||
PublicURL: "http://hydra.public",
|
||||
HTTPClient: &http.Client{Transport: transport},
|
||||
},
|
||||
ConsentRepo: consentRepo,
|
||||
Keto: new(devMockKetoService),
|
||||
}
|
||||
|
||||
app := fiber.New()
|
||||
app.Use(func(c *fiber.Ctx) error {
|
||||
c.Locals("user_profile", &domain.UserProfileResponse{ID: "test-user", Role: domain.RoleSuperAdmin})
|
||||
return c.Next()
|
||||
})
|
||||
app.Put("/api/v1/dev/clients/:id", h.UpdateClient)
|
||||
|
||||
body, _ := json.Marshal(map[string]any{
|
||||
"metadata": map[string]any{
|
||||
"tenant_access_restricted": true,
|
||||
"allowed_tenants": []string{"tenant-b"},
|
||||
},
|
||||
})
|
||||
req := httptest.NewRequest(http.MethodPut, "/api/v1/dev/clients/client-1", bytes.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, _ := app.Test(req, -1)
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
assert.ElementsMatch(t, []string{"user-1", "user-2"}, revokedSubjects)
|
||||
assert.Len(t, consentRepo.consents, 1)
|
||||
assert.Equal(t, "other-client", consentRepo.consents[0].ClientID)
|
||||
}
|
||||
|
||||
func TestUpdateClient_DoesNotRevokeConsentsWhenTenantPolicyUnchanged(t *testing.T) {
|
||||
revoked := false
|
||||
|
||||
transport := roundTripFunc(func(r *http.Request) (*http.Response, error) {
|
||||
if r.Method == http.MethodGet && r.URL.Path == "/clients/client-1" {
|
||||
return httpJSONAny(r, http.StatusOK, domain.HydraClient{
|
||||
ClientID: "client-1",
|
||||
ClientName: "Tenant Guarded App",
|
||||
RedirectURIs: []string{"https://rp.example.com/callback"},
|
||||
GrantTypes: []string{"authorization_code", "refresh_token"},
|
||||
ResponseTypes: []string{"code"},
|
||||
Scope: "openid tenant profile email",
|
||||
TokenEndpointAuthMethod: "none",
|
||||
Metadata: map[string]interface{}{
|
||||
"tenant_access_restricted": true,
|
||||
"allowed_tenants": []string{"tenant-a"},
|
||||
},
|
||||
}), nil
|
||||
}
|
||||
if r.Method == http.MethodPut && r.URL.Path == "/clients/client-1" {
|
||||
var updated domain.HydraClient
|
||||
body, err := io.ReadAll(r.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, json.Unmarshal(body, &updated))
|
||||
return httpJSONAny(r, http.StatusOK, map[string]any{
|
||||
"client_id": updated.ClientID,
|
||||
"client_name": updated.ClientName,
|
||||
"redirect_uris": updated.RedirectURIs,
|
||||
"grant_types": updated.GrantTypes,
|
||||
"response_types": updated.ResponseTypes,
|
||||
"scope": updated.Scope,
|
||||
"token_endpoint_auth_method": updated.TokenEndpointAuthMethod,
|
||||
"metadata": updated.Metadata,
|
||||
}), nil
|
||||
}
|
||||
if r.Method == http.MethodDelete && r.URL.Path == "/oauth2/auth/sessions/consent" {
|
||||
revoked = true
|
||||
return httpResponse(r, http.StatusNoContent, ""), nil
|
||||
}
|
||||
return httpJSONAny(r, http.StatusNotFound, nil), nil
|
||||
})
|
||||
|
||||
consentRepo := &mockConsentRepo{
|
||||
consents: []domain.ClientConsent{
|
||||
{ClientID: "client-1", Subject: "user-1"},
|
||||
},
|
||||
}
|
||||
|
||||
h := &DevHandler{
|
||||
Hydra: &service.HydraAdminService{
|
||||
AdminURL: "http://hydra.test",
|
||||
PublicURL: "http://hydra.public",
|
||||
HTTPClient: &http.Client{Transport: transport},
|
||||
},
|
||||
ConsentRepo: consentRepo,
|
||||
Keto: new(devMockKetoService),
|
||||
}
|
||||
|
||||
app := fiber.New()
|
||||
app.Use(func(c *fiber.Ctx) error {
|
||||
c.Locals("user_profile", &domain.UserProfileResponse{ID: "test-user", Role: domain.RoleSuperAdmin})
|
||||
return c.Next()
|
||||
})
|
||||
app.Put("/api/v1/dev/clients/:id", h.UpdateClient)
|
||||
|
||||
body, _ := json.Marshal(map[string]any{
|
||||
"name": "Renamed App",
|
||||
})
|
||||
req := httptest.NewRequest(http.MethodPut, "/api/v1/dev/clients/client-1", bytes.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, _ := app.Test(req, -1)
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
assert.False(t, revoked)
|
||||
assert.Len(t, consentRepo.consents, 1)
|
||||
}
|
||||
|
||||
func TestRefreshHeadlessJWKSCache_ReturnsUpdatedCacheState(t *testing.T) {
|
||||
privateKey, jwks := mustHeadlessRSAJWK(t)
|
||||
_ = privateKey
|
||||
|
||||
Reference in New Issue
Block a user