diff --git a/doc/api-extensions.md b/doc/api-extensions.md index 694140531e15..1f9d8146ac39 100644 --- a/doc/api-extensions.md +++ b/doc/api-extensions.md @@ -2557,3 +2557,10 @@ disk devices. Introduces per-project uplink IP limits for each available uplink network, adding `limits.networks.uplink_ips.ipv4.NETWORK_NAME` and `limits.networks.uplink_ips.ipv6.NETWORK_NAME` configuration keys for projects with `features.networks` enabled. These keys define the maximum value of IPs made available on a network named NETWORK_NAME to be assigned as uplink IPs for entities inside a certain project. These entities can be other networks, network forwards or load balancers. + +## `entities_with_entitlements` + +Adds `fine_grained` field to `GET /1.0/auth/identities/current` to indicate if the current identity +interacting with the LXD API is fine-grained (that is, associated permissions are managed via group membership). +Allows LXD entities to be returned with an `access_entitlements` field if the current identity is fine-grained and the +GET request to fetch the LXD entities has the `with-access-entitlements=` query parameter. diff --git a/doc/rest-api.yaml b/doc/rest-api.yaml index a7e199e360d7..9c2c63e51224 100644 --- a/doc/rest-api.yaml +++ b/doc/rest-api.yaml @@ -829,6 +829,12 @@ definitions: $ref: '#/definitions/Permission' type: array x-go-name: EffectivePermissions + fine_grained: + description: |- + FineGrained is a boolean indicating whether the identity is fine-grained, + meaning that permissions are managed via group membership. + type: boolean + x-go-name: FineGrained groups: description: Groups is the list of groups for which the identity is a member. example: diff --git a/lxd/identities.go b/lxd/identities.go index c124524a1e4b..27e34412f9a6 100644 --- a/lxd/identities.go +++ b/lxd/identities.go @@ -1126,6 +1126,7 @@ func getCurrentIdentityInfo(d *Daemon, r *http.Request) response.Response { Identity: *apiIdentity, EffectiveGroups: effectiveGroups, EffectivePermissions: effectivePermissions, + FineGrained: identity.IsFineGrainedIdentityType(apiIdentity.Type), }) } @@ -1884,7 +1885,8 @@ func updateIdentityCacheFromLocal(d *Daemon) error { return fmt.Errorf("Failed reading certificates from local database: %w", err) } - var identityCacheEntries []identity.CacheEntry + // identityCacheEntries needs to be pre-allocated. + identityCacheEntries := make([]identity.CacheEntry, 0, len(localServerCerts)) for _, dbCert := range localServerCerts { certBlock, _ := pem.Decode([]byte(dbCert.Certificate)) if certBlock == nil { diff --git a/shared/api/auth.go b/shared/api/auth.go index 487a77819fac..1d0926e4641a 100644 --- a/shared/api/auth.go +++ b/shared/api/auth.go @@ -99,6 +99,10 @@ type IdentityInfo struct { // Effective permissions is the combined and deduplicated list of permissions that the identity has by virtue of // direct membership to a LXD group, or effective membership of a LXD group via identity provider group mappings. EffectivePermissions []Permission `json:"effective_permissions" yaml:"effective_permissions"` + + // FineGrained is a boolean indicating whether the identity is fine-grained, + // meaning that permissions are managed via group membership. + FineGrained bool `json:"fine_grained" yaml:"fine_grained"` } // IdentityPut contains the editable fields of an IdentityInfo. diff --git a/shared/version/api.go b/shared/version/api.go index db46490453ba..86983fc3a2bb 100644 --- a/shared/version/api.go +++ b/shared/version/api.go @@ -432,6 +432,7 @@ var APIExtensions = []string{ "network_zones_all_projects", "instance_root_volume_attachment", "projects_limits_uplink_ips", + "entities_with_entitlements", } // APIExtensionsCount returns the number of available API extensions. diff --git a/test/suites/auth.sh b/test/suites/auth.sh index 36ce7b1c4245..5706f0894edc 100644 --- a/test/suites/auth.sh +++ b/test/suites/auth.sh @@ -147,7 +147,9 @@ groups: tls_certificate: "" effective_groups: - test-group -effective_permissions: []' +effective_permissions: [] +fine_grained: true' + [ "$(lxc auth identity info oidc:)" = "${expectedOIDCInfo}" ] expectedTLSInfo="authentication_method: tls @@ -160,7 +162,9 @@ tls_certificate: | $(awk '{printf " %s\n", $0}' "${LXD_CONF2}/client.crt") effective_groups: - test-group -effective_permissions: []" +effective_permissions: [] +fine_grained: true" + [ "$(LXD_CONF="${LXD_CONF2}" lxc auth identity info tls:)" = "${expectedTLSInfo}" ] @@ -274,6 +278,15 @@ effective_permissions: []" [ "$(LXD_CONF="${LXD_CONF4}" lxc_remote query tls:/1.0 | jq -r '.auth')" = "trusted" ] [ "$(LXD_CONF="${LXD_CONF5}" lxc_remote query tls:/1.0 | jq -r '.auth')" = "untrusted" ] + # Check that an unrestricted client certificate is not fine grained. + LXD_CONF6=$(mktemp -d -p "${TEST_DIR}" XXX) + LXD_CONF="${LXD_CONF6}" gen_cert_and_key "unrestricted" + lxdconf6_fingerprint_short="$(cert_fingerprint "${LXD_CONF6}/unrestricted.crt" | head -c12)" + lxc config trust add "${LXD_CONF6}/unrestricted.crt" + lxc config trust show "${lxdconf6_fingerprint_short}" | grep -xF "restricted: false" + [ "$(LXD_CONF="${LXD_CONF6}" CERTNAME=unrestricted my_curl -X GET "https://${LXD_ADDR}/1.0/auth/identities/current" | jq -r .metadata.fine_grained)" = "false" ] + lxc config trust remove "${lxdconf6_fingerprint_short}" + # Cleanup lxc auth group delete test-group lxc auth identity-provider-group delete test-idp-group @@ -284,6 +297,7 @@ effective_permissions: []" rm -r "${LXD_CONF3}" rm -r "${LXD_CONF4}" rm -r "${LXD_CONF5}" + rm -r "${LXD_CONF6}" lxc config unset core.remote_token_expiry lxc config unset oidc.issuer lxc config unset oidc.client.id