Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: have the /admin/identities list call return all fields #3343

Merged
merged 1 commit into from
Feb 20, 2024
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
32 changes: 30 additions & 2 deletions identity/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,15 @@
// in: query
CredentialsIdentifierSimilar string `json:"preview_credentials_identifier_similar"`

// Include Credentials in Response
//
// Include any credential, for example `password` or `oidc`, in the response. When set to `oidc`, This will return
// the initial OAuth 2.0 Access Token, OAuth 2.0 Refresh Token and the OpenID Connect ID Token if available.
//
// required: false
// in: query
DeclassifyCredentials []string `json:"include_credential"`

crdbx.ConsistencyRequestParameters
}

Expand All @@ -183,6 +192,18 @@
// 200: listIdentities
// default: errorGeneric
func (h *Handler) list(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
includeCredentials := r.URL.Query()["include_credential"]
var declassify []CredentialsType
for _, v := range includeCredentials {
tc, ok := ParseCredentialsType(v)
if ok {
declassify = append(declassify, tc)
} else {
h.r.Writer().WriteError(w, r, errors.WithStack(herodot.ErrBadRequest.WithReasonf("Invalid value `%s` for parameter `include_credential`.", declassify)))
return
}
}

var (
err error
params = ListIdentityParameters{
Expand All @@ -191,13 +212,14 @@
CredentialsIdentifier: r.URL.Query().Get("credentials_identifier"),
CredentialsIdentifierSimilar: r.URL.Query().Get("preview_credentials_identifier_similar"),
ConsistencyLevel: crdbx.ConsistencyLevelFromRequest(r),
DeclassifyCredentials: declassify,
}
)
if params.CredentialsIdentifier != "" && params.CredentialsIdentifierSimilar != "" {
h.r.Writer().WriteError(w, r, herodot.ErrBadRequest.WithReason("Cannot pass both credentials_identifier and preview_credentials_identifier_similar."))
return
}
if params.CredentialsIdentifier != "" || params.CredentialsIdentifierSimilar != "" {
if params.CredentialsIdentifier != "" || params.CredentialsIdentifierSimilar != "" || len(params.DeclassifyCredentials) > 0 {
params.Expand = ExpandEverything
}
params.KeySetPagination, params.PagePagination, err = x.ParseKeysetOrPagePagination(r)
Expand Down Expand Up @@ -231,7 +253,13 @@
// Identities using the marshaler for including metadata_admin
isam := make([]WithCredentialsMetadataAndAdminMetadataInJSON, len(is))
for i, identity := range is {
isam[i] = WithCredentialsMetadataAndAdminMetadataInJSON(identity)
emit, err := identity.WithDeclassifiedCredentials(r.Context(), h.r, params.DeclassifyCredentials)
if err != nil {
h.r.Writer().WriteError(w, r, err)
return

Check warning on line 259 in identity/handler.go

View check run for this annotation

Codecov / codecov/patch

identity/handler.go#L258-L259

Added lines #L258 - L259 were not covered by tests
}

isam[i] = WithCredentialsMetadataAndAdminMetadataInJSON(*emit)
}

h.r.Writer().Write(w, r, isam)
Expand Down
23 changes: 22 additions & 1 deletion identity/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1302,7 +1302,7 @@ func TestHandler(t *testing.T) {
})

t.Run("case=should list all identities", func(t *testing.T) {
for name, ts := range map[string]*httptest.Server{"public": publicTS, "admin": adminTS} {
for name, ts := range map[string]*httptest.Server{"admin": adminTS} {
t.Run("endpoint="+name, func(t *testing.T) {
res := get(t, ts, "/identities", http.StatusOK)
assert.False(t, res.Get("0.credentials").Exists(), "credentials config should be omitted: %s", res.Raw)
Expand All @@ -1313,6 +1313,27 @@ func TestHandler(t *testing.T) {
}
})

t.Run("case=should list all identities with credentials", func(t *testing.T) {
for name, ts := range map[string]*httptest.Server{"admin": adminTS} {
t.Run("endpoint="+name, func(t *testing.T) {
res := get(t, ts, "/identities?include_credential=totp", http.StatusOK)
assert.True(t, res.Get("0.credentials").Exists(), "credentials config should be included: %s", res.Raw)
assert.True(t, res.Get("0.metadata_public").Exists(), "metadata_public config should be included: %s", res.Raw)
assert.True(t, res.Get("0.metadata_admin").Exists(), "metadata_admin config should be included: %s", res.Raw)
assert.EqualValues(t, "baz", res.Get(`#(traits.bar=="baz").traits.bar`).String(), "%s", res.Raw)
})
}
})

t.Run("case=should not be able to list all identities with credentials due to wrong credentials type", func(t *testing.T) {
for name, ts := range map[string]*httptest.Server{"admin": adminTS} {
t.Run("endpoint="+name, func(t *testing.T) {
res := get(t, ts, "/identities?include_credential=XYZ", http.StatusBadRequest)
assert.Contains(t, res.Get("error.message").String(), "The request was malformed or contained invalid parameters", "%s", res.Raw)
})
}
})

t.Run("case=should list all identities with eventual consistency", func(t *testing.T) {
for name, ts := range map[string]*httptest.Server{"public": publicTS, "admin": adminTS} {
t.Run("endpoint="+name, func(t *testing.T) {
Expand Down
1 change: 1 addition & 0 deletions identity/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type (
IdsFilter []string
CredentialsIdentifier string
CredentialsIdentifierSimilar string
DeclassifyCredentials []CredentialsType
KeySetPagination []keysetpagination.Option
// DEPRECATED
PagePagination *x.Page
Expand Down
16 changes: 16 additions & 0 deletions internal/client-go/api_identity.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions internal/httpclient/api_identity.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions spec/api.json
Original file line number Diff line number Diff line change
Expand Up @@ -3628,6 +3628,17 @@
"schema": {
"type": "string"
}
},
{
"description": "Include Credentials in Response\n\nInclude any credential, for example `password` or `oidc`, in the response. When set to `oidc`, This will return\nthe initial OAuth 2.0 Access Token, OAuth 2.0 Refresh Token and the OpenID Connect ID Token if available.",
"in": "query",
"name": "include_credential",
"schema": {
"items": {
"type": "string"
},
"type": "array"
}
}
],
"responses": {
Expand Down
9 changes: 9 additions & 0 deletions spec/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,15 @@
"description": "This is an EXPERIMENTAL parameter that WILL CHANGE. Do NOT rely on consistent, deterministic behavior.\nTHIS PARAMETER WILL BE REMOVED IN AN UPCOMING RELEASE WITHOUT ANY MIGRATION PATH.\n\nCredentialsIdentifierSimilar is the (partial) identifier (username, email) of the credentials to look up using similarity search.\nOnly one of CredentialsIdentifier and CredentialsIdentifierSimilar can be used.",
"name": "preview_credentials_identifier_similar",
"in": "query"
},
{
"type": "array",
"items": {
"type": "string"
},
"description": "Include Credentials in Response\n\nInclude any credential, for example `password` or `oidc`, in the response. When set to `oidc`, This will return\nthe initial OAuth 2.0 Access Token, OAuth 2.0 Refresh Token and the OpenID Connect ID Token if available.",
"name": "include_credential",
"in": "query"
}
],
"responses": {
Expand Down
Loading