diff --git a/apis/supervisor/idp/v1alpha1/types_activedirectoryidentityprovider.go.tmpl b/apis/supervisor/idp/v1alpha1/types_activedirectoryidentityprovider.go.tmpl index d4e481b9..18626629 100644 --- a/apis/supervisor/idp/v1alpha1/types_activedirectoryidentityprovider.go.tmpl +++ b/apis/supervisor/idp/v1alpha1/types_activedirectoryidentityprovider.go.tmpl @@ -1,4 +1,4 @@ -// Copyright 2021 the Pinniped contributors. All Rights Reserved. +// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 @@ -131,6 +131,31 @@ type ActiveDirectoryIdentityProviderGroupSearch struct { // the result of the group search. // +optional Attributes ActiveDirectoryIdentityProviderGroupSearchAttributes `json:"attributes,omitempty"` + + // The user's group membership is refreshed as they interact with the supervisor + // to obtain new credentials (as their old credentials expire). This allows group + // membership changes to be quickly reflected into Kubernetes clusters. Since + // group membership is often used to bind authorization policies, it is important + // to keep the groups observed in Kubernetes clusters in-sync with the identity + // provider. + // + // In some environments, frequent group membership queries may result in a + // significant performance impact on the identity provider and/or the supervisor. + // The best approach to handle performance impacts is to tweak the group query + // to be more performant, for example by disabling nested group search or by + // using a more targeted group search base. + // + // If the group search query cannot be made performant and you are willing to + // have group memberships remain static for approximately a day, then set + // skipGroupRefresh to true. This is an insecure configuration as authorization + // policies that are bound to group membership will not notice if a user has + // been removed from a particular group until their next login. + // + // This is an experimental feature that may be removed or significantly altered + // in the future. Consumers of this configuration should carefully read all + // release notes before upgrading to ensure that the meaning of this field has + // not changed. + SkipGroupRefresh bool `json:"skipGroupRefresh,omitempty"` } // Spec for configuring an ActiveDirectory identity provider. diff --git a/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go.tmpl b/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go.tmpl index 74570a92..09cb843d 100644 --- a/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go.tmpl +++ b/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go.tmpl @@ -1,4 +1,4 @@ -// Copyright 2021 the Pinniped contributors. All Rights Reserved. +// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 @@ -119,6 +119,31 @@ type LDAPIdentityProviderGroupSearch struct { // the result of the group search. // +optional Attributes LDAPIdentityProviderGroupSearchAttributes `json:"attributes,omitempty"` + + // The user's group membership is refreshed as they interact with the supervisor + // to obtain new credentials (as their old credentials expire). This allows group + // membership changes to be quickly reflected into Kubernetes clusters. Since + // group membership is often used to bind authorization policies, it is important + // to keep the groups observed in Kubernetes clusters in-sync with the identity + // provider. + // + // In some environments, frequent group membership queries may result in a + // significant performance impact on the identity provider and/or the supervisor. + // The best approach to handle performance impacts is to tweak the group query + // to be more performant, for example by disabling nested group search or by + // using a more targeted group search base. + // + // If the group search query cannot be made performant and you are willing to + // have group memberships remain static for approximately a day, then set + // skipGroupRefresh to true. This is an insecure configuration as authorization + // policies that are bound to group membership will not notice if a user has + // been removed from a particular group until their next login. + // + // This is an experimental feature that may be removed or significantly altered + // in the future. Consumers of this configuration should carefully read all + // release notes before upgrading to ensure that the meaning of this field has + // not changed. + SkipGroupRefresh bool `json:"skipGroupRefresh,omitempty"` } // Spec for configuring an LDAP identity provider. diff --git a/deploy/supervisor/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/deploy/supervisor/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 9929b2fc..0ac278fe 100644 --- a/deploy/supervisor/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/deploy/supervisor/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -119,6 +119,30 @@ spec: search can be slow for some Active Directory servers. To disable it, you can set the filter to "(&(objectClass=group)(member={})" type: string + skipGroupRefresh: + description: "The user's group membership is refreshed as they + interact with the supervisor to obtain new credentials (as their + old credentials expire). This allows group membership changes + to be quickly reflected into Kubernetes clusters. Since group + membership is often used to bind authorization policies, it + is important to keep the groups observed in Kubernetes clusters + in-sync with the identity provider. \n In some environments, + frequent group membership queries may result in a significant + performance impact on the identity provider and/or the supervisor. + The best approach to handle performance impacts is to tweak + the group query to be more performant, for example by disabling + nested group search or by using a more targeted group search + base. \n If the group search query cannot be made performant + and you are willing to have group memberships remain static + for approximately a day, then set skipGroupRefresh to true. + \ This is an insecure configuration as authorization policies + that are bound to group membership will not notice if a user + has been removed from a particular group until their next login. + \n This is an experimental feature that may be removed or significantly + altered in the future. Consumers of this configuration should + carefully read all release notes before upgrading to ensure + that the meaning of this field has not changed." + type: boolean type: object host: description: 'Host is the hostname of this Active Directory identity diff --git a/deploy/supervisor/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/deploy/supervisor/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index ed9780b5..33f1c954 100644 --- a/deploy/supervisor/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/deploy/supervisor/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -111,6 +111,30 @@ spec: an entry, so "dn={}" cannot be used. Optional. When not specified, the default will act as if the Filter were specified as "member={}". type: string + skipGroupRefresh: + description: "The user's group membership is refreshed as they + interact with the supervisor to obtain new credentials (as their + old credentials expire). This allows group membership changes + to be quickly reflected into Kubernetes clusters. Since group + membership is often used to bind authorization policies, it + is important to keep the groups observed in Kubernetes clusters + in-sync with the identity provider. \n In some environments, + frequent group membership queries may result in a significant + performance impact on the identity provider and/or the supervisor. + The best approach to handle performance impacts is to tweak + the group query to be more performant, for example by disabling + nested group search or by using a more targeted group search + base. \n If the group search query cannot be made performant + and you are willing to have group memberships remain static + for approximately a day, then set skipGroupRefresh to true. + \ This is an insecure configuration as authorization policies + that are bound to group membership will not notice if a user + has been removed from a particular group until their next login. + \n This is an experimental feature that may be removed or significantly + altered in the future. Consumers of this configuration should + carefully read all release notes before upgrading to ensure + that the meaning of this field has not changed." + type: boolean type: object host: description: 'Host is the hostname of this LDAP identity provider, diff --git a/generated/1.17/README.adoc b/generated/1.17/README.adoc index d18e7610..bc731591 100644 --- a/generated/1.17/README.adoc +++ b/generated/1.17/README.adoc @@ -801,6 +801,10 @@ ActiveDirectoryIdentityProvider describes the configuration of an upstream Micro | *`base`* __string__ | Base is the dn (distinguished name) that should be used as the search base when searching for groups. E.g. "ou=groups,dc=example,dc=com". Optional, when not specified it will be based on the result of a query for the defaultNamingContext (see https://docs.microsoft.com/en-us/windows/win32/adschema/rootdse). The default behavior searches your entire domain for groups. It may make sense to specify a subtree as a search base if you wish to exclude some groups for security reasons or to make searches faster. | *`filter`* __string__ | Filter is the ActiveDirectory search filter which should be applied when searching for groups for a user. The pattern "{}" must occur in the filter at least once and will be dynamically replaced by the dn (distinguished name) of the user entry found as a result of the user search. E.g. "member={}" or "&(objectClass=groupOfNames)(member={})". For more information about ActiveDirectory filters, see https://ldap.com/ldap-filters. Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used. Optional. When not specified, the default will act as if the filter were specified as "(&(objectClass=group)(member:1.2.840.113556.1.4.1941:={})". This searches nested groups by default. Note that nested group search can be slow for some Active Directory servers. To disable it, you can set the filter to "(&(objectClass=group)(member={})" | *`attributes`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-activedirectoryidentityprovidergroupsearchattributes[$$ActiveDirectoryIdentityProviderGroupSearchAttributes$$]__ | Attributes specifies how the group's information should be read from each ActiveDirectory entry which was found as the result of the group search. +| *`skipGroupRefresh`* __boolean__ | The user's group membership is refreshed as they interact with the supervisor to obtain new credentials (as their old credentials expire). This allows group membership changes to be quickly reflected into Kubernetes clusters. Since group membership is often used to bind authorization policies, it is important to keep the groups observed in Kubernetes clusters in-sync with the identity provider. + In some environments, frequent group membership queries may result in a significant performance impact on the identity provider and/or the supervisor. The best approach to handle performance impacts is to tweak the group query to be more performant, for example by disabling nested group search or by using a more targeted group search base. + If the group search query cannot be made performant and you are willing to have group memberships remain static for approximately a day, then set skipGroupRefresh to true. This is an insecure configuration as authorization policies that are bound to group membership will not notice if a user has been removed from a particular group until their next login. + This is an experimental feature that may be removed or significantly altered in the future. Consumers of this configuration should carefully read all release notes before upgrading to ensure that the meaning of this field has not changed. |=== @@ -988,6 +992,10 @@ LDAPIdentityProvider describes the configuration of an upstream Lightweight Dire | *`base`* __string__ | Base is the dn (distinguished name) that should be used as the search base when searching for groups. E.g. "ou=groups,dc=example,dc=com". When not specified, no group search will be performed and authenticated users will not belong to any groups from the LDAP provider. Also, when not specified, the values of Filter and Attributes are ignored. | *`filter`* __string__ | Filter is the LDAP search filter which should be applied when searching for groups for a user. The pattern "{}" must occur in the filter at least once and will be dynamically replaced by the dn (distinguished name) of the user entry found as a result of the user search. E.g. "member={}" or "&(objectClass=groupOfNames)(member={})". For more information about LDAP filters, see https://ldap.com/ldap-filters. Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used. Optional. When not specified, the default will act as if the Filter were specified as "member={}". | *`attributes`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-ldapidentityprovidergroupsearchattributes[$$LDAPIdentityProviderGroupSearchAttributes$$]__ | Attributes specifies how the group's information should be read from each LDAP entry which was found as the result of the group search. +| *`skipGroupRefresh`* __boolean__ | The user's group membership is refreshed as they interact with the supervisor to obtain new credentials (as their old credentials expire). This allows group membership changes to be quickly reflected into Kubernetes clusters. Since group membership is often used to bind authorization policies, it is important to keep the groups observed in Kubernetes clusters in-sync with the identity provider. + In some environments, frequent group membership queries may result in a significant performance impact on the identity provider and/or the supervisor. The best approach to handle performance impacts is to tweak the group query to be more performant, for example by disabling nested group search or by using a more targeted group search base. + If the group search query cannot be made performant and you are willing to have group memberships remain static for approximately a day, then set skipGroupRefresh to true. This is an insecure configuration as authorization policies that are bound to group membership will not notice if a user has been removed from a particular group until their next login. + This is an experimental feature that may be removed or significantly altered in the future. Consumers of this configuration should carefully read all release notes before upgrading to ensure that the meaning of this field has not changed. |=== diff --git a/generated/1.17/apis/supervisor/idp/v1alpha1/types_activedirectoryidentityprovider.go b/generated/1.17/apis/supervisor/idp/v1alpha1/types_activedirectoryidentityprovider.go index d4e481b9..18626629 100644 --- a/generated/1.17/apis/supervisor/idp/v1alpha1/types_activedirectoryidentityprovider.go +++ b/generated/1.17/apis/supervisor/idp/v1alpha1/types_activedirectoryidentityprovider.go @@ -1,4 +1,4 @@ -// Copyright 2021 the Pinniped contributors. All Rights Reserved. +// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 @@ -131,6 +131,31 @@ type ActiveDirectoryIdentityProviderGroupSearch struct { // the result of the group search. // +optional Attributes ActiveDirectoryIdentityProviderGroupSearchAttributes `json:"attributes,omitempty"` + + // The user's group membership is refreshed as they interact with the supervisor + // to obtain new credentials (as their old credentials expire). This allows group + // membership changes to be quickly reflected into Kubernetes clusters. Since + // group membership is often used to bind authorization policies, it is important + // to keep the groups observed in Kubernetes clusters in-sync with the identity + // provider. + // + // In some environments, frequent group membership queries may result in a + // significant performance impact on the identity provider and/or the supervisor. + // The best approach to handle performance impacts is to tweak the group query + // to be more performant, for example by disabling nested group search or by + // using a more targeted group search base. + // + // If the group search query cannot be made performant and you are willing to + // have group memberships remain static for approximately a day, then set + // skipGroupRefresh to true. This is an insecure configuration as authorization + // policies that are bound to group membership will not notice if a user has + // been removed from a particular group until their next login. + // + // This is an experimental feature that may be removed or significantly altered + // in the future. Consumers of this configuration should carefully read all + // release notes before upgrading to ensure that the meaning of this field has + // not changed. + SkipGroupRefresh bool `json:"skipGroupRefresh,omitempty"` } // Spec for configuring an ActiveDirectory identity provider. diff --git a/generated/1.17/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go b/generated/1.17/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go index 74570a92..09cb843d 100644 --- a/generated/1.17/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go +++ b/generated/1.17/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go @@ -1,4 +1,4 @@ -// Copyright 2021 the Pinniped contributors. All Rights Reserved. +// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 @@ -119,6 +119,31 @@ type LDAPIdentityProviderGroupSearch struct { // the result of the group search. // +optional Attributes LDAPIdentityProviderGroupSearchAttributes `json:"attributes,omitempty"` + + // The user's group membership is refreshed as they interact with the supervisor + // to obtain new credentials (as their old credentials expire). This allows group + // membership changes to be quickly reflected into Kubernetes clusters. Since + // group membership is often used to bind authorization policies, it is important + // to keep the groups observed in Kubernetes clusters in-sync with the identity + // provider. + // + // In some environments, frequent group membership queries may result in a + // significant performance impact on the identity provider and/or the supervisor. + // The best approach to handle performance impacts is to tweak the group query + // to be more performant, for example by disabling nested group search or by + // using a more targeted group search base. + // + // If the group search query cannot be made performant and you are willing to + // have group memberships remain static for approximately a day, then set + // skipGroupRefresh to true. This is an insecure configuration as authorization + // policies that are bound to group membership will not notice if a user has + // been removed from a particular group until their next login. + // + // This is an experimental feature that may be removed or significantly altered + // in the future. Consumers of this configuration should carefully read all + // release notes before upgrading to ensure that the meaning of this field has + // not changed. + SkipGroupRefresh bool `json:"skipGroupRefresh,omitempty"` } // Spec for configuring an LDAP identity provider. diff --git a/generated/1.17/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.17/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 9929b2fc..0ac278fe 100644 --- a/generated/1.17/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.17/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -119,6 +119,30 @@ spec: search can be slow for some Active Directory servers. To disable it, you can set the filter to "(&(objectClass=group)(member={})" type: string + skipGroupRefresh: + description: "The user's group membership is refreshed as they + interact with the supervisor to obtain new credentials (as their + old credentials expire). This allows group membership changes + to be quickly reflected into Kubernetes clusters. Since group + membership is often used to bind authorization policies, it + is important to keep the groups observed in Kubernetes clusters + in-sync with the identity provider. \n In some environments, + frequent group membership queries may result in a significant + performance impact on the identity provider and/or the supervisor. + The best approach to handle performance impacts is to tweak + the group query to be more performant, for example by disabling + nested group search or by using a more targeted group search + base. \n If the group search query cannot be made performant + and you are willing to have group memberships remain static + for approximately a day, then set skipGroupRefresh to true. + \ This is an insecure configuration as authorization policies + that are bound to group membership will not notice if a user + has been removed from a particular group until their next login. + \n This is an experimental feature that may be removed or significantly + altered in the future. Consumers of this configuration should + carefully read all release notes before upgrading to ensure + that the meaning of this field has not changed." + type: boolean type: object host: description: 'Host is the hostname of this Active Directory identity diff --git a/generated/1.17/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.17/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index ed9780b5..33f1c954 100644 --- a/generated/1.17/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.17/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -111,6 +111,30 @@ spec: an entry, so "dn={}" cannot be used. Optional. When not specified, the default will act as if the Filter were specified as "member={}". type: string + skipGroupRefresh: + description: "The user's group membership is refreshed as they + interact with the supervisor to obtain new credentials (as their + old credentials expire). This allows group membership changes + to be quickly reflected into Kubernetes clusters. Since group + membership is often used to bind authorization policies, it + is important to keep the groups observed in Kubernetes clusters + in-sync with the identity provider. \n In some environments, + frequent group membership queries may result in a significant + performance impact on the identity provider and/or the supervisor. + The best approach to handle performance impacts is to tweak + the group query to be more performant, for example by disabling + nested group search or by using a more targeted group search + base. \n If the group search query cannot be made performant + and you are willing to have group memberships remain static + for approximately a day, then set skipGroupRefresh to true. + \ This is an insecure configuration as authorization policies + that are bound to group membership will not notice if a user + has been removed from a particular group until their next login. + \n This is an experimental feature that may be removed or significantly + altered in the future. Consumers of this configuration should + carefully read all release notes before upgrading to ensure + that the meaning of this field has not changed." + type: boolean type: object host: description: 'Host is the hostname of this LDAP identity provider, diff --git a/generated/1.18/README.adoc b/generated/1.18/README.adoc index 417b57ac..037416d6 100644 --- a/generated/1.18/README.adoc +++ b/generated/1.18/README.adoc @@ -801,6 +801,10 @@ ActiveDirectoryIdentityProvider describes the configuration of an upstream Micro | *`base`* __string__ | Base is the dn (distinguished name) that should be used as the search base when searching for groups. E.g. "ou=groups,dc=example,dc=com". Optional, when not specified it will be based on the result of a query for the defaultNamingContext (see https://docs.microsoft.com/en-us/windows/win32/adschema/rootdse). The default behavior searches your entire domain for groups. It may make sense to specify a subtree as a search base if you wish to exclude some groups for security reasons or to make searches faster. | *`filter`* __string__ | Filter is the ActiveDirectory search filter which should be applied when searching for groups for a user. The pattern "{}" must occur in the filter at least once and will be dynamically replaced by the dn (distinguished name) of the user entry found as a result of the user search. E.g. "member={}" or "&(objectClass=groupOfNames)(member={})". For more information about ActiveDirectory filters, see https://ldap.com/ldap-filters. Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used. Optional. When not specified, the default will act as if the filter were specified as "(&(objectClass=group)(member:1.2.840.113556.1.4.1941:={})". This searches nested groups by default. Note that nested group search can be slow for some Active Directory servers. To disable it, you can set the filter to "(&(objectClass=group)(member={})" | *`attributes`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-activedirectoryidentityprovidergroupsearchattributes[$$ActiveDirectoryIdentityProviderGroupSearchAttributes$$]__ | Attributes specifies how the group's information should be read from each ActiveDirectory entry which was found as the result of the group search. +| *`skipGroupRefresh`* __boolean__ | The user's group membership is refreshed as they interact with the supervisor to obtain new credentials (as their old credentials expire). This allows group membership changes to be quickly reflected into Kubernetes clusters. Since group membership is often used to bind authorization policies, it is important to keep the groups observed in Kubernetes clusters in-sync with the identity provider. + In some environments, frequent group membership queries may result in a significant performance impact on the identity provider and/or the supervisor. The best approach to handle performance impacts is to tweak the group query to be more performant, for example by disabling nested group search or by using a more targeted group search base. + If the group search query cannot be made performant and you are willing to have group memberships remain static for approximately a day, then set skipGroupRefresh to true. This is an insecure configuration as authorization policies that are bound to group membership will not notice if a user has been removed from a particular group until their next login. + This is an experimental feature that may be removed or significantly altered in the future. Consumers of this configuration should carefully read all release notes before upgrading to ensure that the meaning of this field has not changed. |=== @@ -988,6 +992,10 @@ LDAPIdentityProvider describes the configuration of an upstream Lightweight Dire | *`base`* __string__ | Base is the dn (distinguished name) that should be used as the search base when searching for groups. E.g. "ou=groups,dc=example,dc=com". When not specified, no group search will be performed and authenticated users will not belong to any groups from the LDAP provider. Also, when not specified, the values of Filter and Attributes are ignored. | *`filter`* __string__ | Filter is the LDAP search filter which should be applied when searching for groups for a user. The pattern "{}" must occur in the filter at least once and will be dynamically replaced by the dn (distinguished name) of the user entry found as a result of the user search. E.g. "member={}" or "&(objectClass=groupOfNames)(member={})". For more information about LDAP filters, see https://ldap.com/ldap-filters. Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used. Optional. When not specified, the default will act as if the Filter were specified as "member={}". | *`attributes`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-ldapidentityprovidergroupsearchattributes[$$LDAPIdentityProviderGroupSearchAttributes$$]__ | Attributes specifies how the group's information should be read from each LDAP entry which was found as the result of the group search. +| *`skipGroupRefresh`* __boolean__ | The user's group membership is refreshed as they interact with the supervisor to obtain new credentials (as their old credentials expire). This allows group membership changes to be quickly reflected into Kubernetes clusters. Since group membership is often used to bind authorization policies, it is important to keep the groups observed in Kubernetes clusters in-sync with the identity provider. + In some environments, frequent group membership queries may result in a significant performance impact on the identity provider and/or the supervisor. The best approach to handle performance impacts is to tweak the group query to be more performant, for example by disabling nested group search or by using a more targeted group search base. + If the group search query cannot be made performant and you are willing to have group memberships remain static for approximately a day, then set skipGroupRefresh to true. This is an insecure configuration as authorization policies that are bound to group membership will not notice if a user has been removed from a particular group until their next login. + This is an experimental feature that may be removed or significantly altered in the future. Consumers of this configuration should carefully read all release notes before upgrading to ensure that the meaning of this field has not changed. |=== diff --git a/generated/1.18/apis/supervisor/idp/v1alpha1/types_activedirectoryidentityprovider.go b/generated/1.18/apis/supervisor/idp/v1alpha1/types_activedirectoryidentityprovider.go index d4e481b9..18626629 100644 --- a/generated/1.18/apis/supervisor/idp/v1alpha1/types_activedirectoryidentityprovider.go +++ b/generated/1.18/apis/supervisor/idp/v1alpha1/types_activedirectoryidentityprovider.go @@ -1,4 +1,4 @@ -// Copyright 2021 the Pinniped contributors. All Rights Reserved. +// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 @@ -131,6 +131,31 @@ type ActiveDirectoryIdentityProviderGroupSearch struct { // the result of the group search. // +optional Attributes ActiveDirectoryIdentityProviderGroupSearchAttributes `json:"attributes,omitempty"` + + // The user's group membership is refreshed as they interact with the supervisor + // to obtain new credentials (as their old credentials expire). This allows group + // membership changes to be quickly reflected into Kubernetes clusters. Since + // group membership is often used to bind authorization policies, it is important + // to keep the groups observed in Kubernetes clusters in-sync with the identity + // provider. + // + // In some environments, frequent group membership queries may result in a + // significant performance impact on the identity provider and/or the supervisor. + // The best approach to handle performance impacts is to tweak the group query + // to be more performant, for example by disabling nested group search or by + // using a more targeted group search base. + // + // If the group search query cannot be made performant and you are willing to + // have group memberships remain static for approximately a day, then set + // skipGroupRefresh to true. This is an insecure configuration as authorization + // policies that are bound to group membership will not notice if a user has + // been removed from a particular group until their next login. + // + // This is an experimental feature that may be removed or significantly altered + // in the future. Consumers of this configuration should carefully read all + // release notes before upgrading to ensure that the meaning of this field has + // not changed. + SkipGroupRefresh bool `json:"skipGroupRefresh,omitempty"` } // Spec for configuring an ActiveDirectory identity provider. diff --git a/generated/1.18/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go b/generated/1.18/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go index 74570a92..09cb843d 100644 --- a/generated/1.18/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go +++ b/generated/1.18/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go @@ -1,4 +1,4 @@ -// Copyright 2021 the Pinniped contributors. All Rights Reserved. +// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 @@ -119,6 +119,31 @@ type LDAPIdentityProviderGroupSearch struct { // the result of the group search. // +optional Attributes LDAPIdentityProviderGroupSearchAttributes `json:"attributes,omitempty"` + + // The user's group membership is refreshed as they interact with the supervisor + // to obtain new credentials (as their old credentials expire). This allows group + // membership changes to be quickly reflected into Kubernetes clusters. Since + // group membership is often used to bind authorization policies, it is important + // to keep the groups observed in Kubernetes clusters in-sync with the identity + // provider. + // + // In some environments, frequent group membership queries may result in a + // significant performance impact on the identity provider and/or the supervisor. + // The best approach to handle performance impacts is to tweak the group query + // to be more performant, for example by disabling nested group search or by + // using a more targeted group search base. + // + // If the group search query cannot be made performant and you are willing to + // have group memberships remain static for approximately a day, then set + // skipGroupRefresh to true. This is an insecure configuration as authorization + // policies that are bound to group membership will not notice if a user has + // been removed from a particular group until their next login. + // + // This is an experimental feature that may be removed or significantly altered + // in the future. Consumers of this configuration should carefully read all + // release notes before upgrading to ensure that the meaning of this field has + // not changed. + SkipGroupRefresh bool `json:"skipGroupRefresh,omitempty"` } // Spec for configuring an LDAP identity provider. diff --git a/generated/1.18/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.18/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 9929b2fc..0ac278fe 100644 --- a/generated/1.18/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.18/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -119,6 +119,30 @@ spec: search can be slow for some Active Directory servers. To disable it, you can set the filter to "(&(objectClass=group)(member={})" type: string + skipGroupRefresh: + description: "The user's group membership is refreshed as they + interact with the supervisor to obtain new credentials (as their + old credentials expire). This allows group membership changes + to be quickly reflected into Kubernetes clusters. Since group + membership is often used to bind authorization policies, it + is important to keep the groups observed in Kubernetes clusters + in-sync with the identity provider. \n In some environments, + frequent group membership queries may result in a significant + performance impact on the identity provider and/or the supervisor. + The best approach to handle performance impacts is to tweak + the group query to be more performant, for example by disabling + nested group search or by using a more targeted group search + base. \n If the group search query cannot be made performant + and you are willing to have group memberships remain static + for approximately a day, then set skipGroupRefresh to true. + \ This is an insecure configuration as authorization policies + that are bound to group membership will not notice if a user + has been removed from a particular group until their next login. + \n This is an experimental feature that may be removed or significantly + altered in the future. Consumers of this configuration should + carefully read all release notes before upgrading to ensure + that the meaning of this field has not changed." + type: boolean type: object host: description: 'Host is the hostname of this Active Directory identity diff --git a/generated/1.18/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.18/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index ed9780b5..33f1c954 100644 --- a/generated/1.18/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.18/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -111,6 +111,30 @@ spec: an entry, so "dn={}" cannot be used. Optional. When not specified, the default will act as if the Filter were specified as "member={}". type: string + skipGroupRefresh: + description: "The user's group membership is refreshed as they + interact with the supervisor to obtain new credentials (as their + old credentials expire). This allows group membership changes + to be quickly reflected into Kubernetes clusters. Since group + membership is often used to bind authorization policies, it + is important to keep the groups observed in Kubernetes clusters + in-sync with the identity provider. \n In some environments, + frequent group membership queries may result in a significant + performance impact on the identity provider and/or the supervisor. + The best approach to handle performance impacts is to tweak + the group query to be more performant, for example by disabling + nested group search or by using a more targeted group search + base. \n If the group search query cannot be made performant + and you are willing to have group memberships remain static + for approximately a day, then set skipGroupRefresh to true. + \ This is an insecure configuration as authorization policies + that are bound to group membership will not notice if a user + has been removed from a particular group until their next login. + \n This is an experimental feature that may be removed or significantly + altered in the future. Consumers of this configuration should + carefully read all release notes before upgrading to ensure + that the meaning of this field has not changed." + type: boolean type: object host: description: 'Host is the hostname of this LDAP identity provider, diff --git a/generated/1.19/README.adoc b/generated/1.19/README.adoc index 0317faed..825527c2 100644 --- a/generated/1.19/README.adoc +++ b/generated/1.19/README.adoc @@ -801,6 +801,10 @@ ActiveDirectoryIdentityProvider describes the configuration of an upstream Micro | *`base`* __string__ | Base is the dn (distinguished name) that should be used as the search base when searching for groups. E.g. "ou=groups,dc=example,dc=com". Optional, when not specified it will be based on the result of a query for the defaultNamingContext (see https://docs.microsoft.com/en-us/windows/win32/adschema/rootdse). The default behavior searches your entire domain for groups. It may make sense to specify a subtree as a search base if you wish to exclude some groups for security reasons or to make searches faster. | *`filter`* __string__ | Filter is the ActiveDirectory search filter which should be applied when searching for groups for a user. The pattern "{}" must occur in the filter at least once and will be dynamically replaced by the dn (distinguished name) of the user entry found as a result of the user search. E.g. "member={}" or "&(objectClass=groupOfNames)(member={})". For more information about ActiveDirectory filters, see https://ldap.com/ldap-filters. Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used. Optional. When not specified, the default will act as if the filter were specified as "(&(objectClass=group)(member:1.2.840.113556.1.4.1941:={})". This searches nested groups by default. Note that nested group search can be slow for some Active Directory servers. To disable it, you can set the filter to "(&(objectClass=group)(member={})" | *`attributes`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-activedirectoryidentityprovidergroupsearchattributes[$$ActiveDirectoryIdentityProviderGroupSearchAttributes$$]__ | Attributes specifies how the group's information should be read from each ActiveDirectory entry which was found as the result of the group search. +| *`skipGroupRefresh`* __boolean__ | The user's group membership is refreshed as they interact with the supervisor to obtain new credentials (as their old credentials expire). This allows group membership changes to be quickly reflected into Kubernetes clusters. Since group membership is often used to bind authorization policies, it is important to keep the groups observed in Kubernetes clusters in-sync with the identity provider. + In some environments, frequent group membership queries may result in a significant performance impact on the identity provider and/or the supervisor. The best approach to handle performance impacts is to tweak the group query to be more performant, for example by disabling nested group search or by using a more targeted group search base. + If the group search query cannot be made performant and you are willing to have group memberships remain static for approximately a day, then set skipGroupRefresh to true. This is an insecure configuration as authorization policies that are bound to group membership will not notice if a user has been removed from a particular group until their next login. + This is an experimental feature that may be removed or significantly altered in the future. Consumers of this configuration should carefully read all release notes before upgrading to ensure that the meaning of this field has not changed. |=== @@ -988,6 +992,10 @@ LDAPIdentityProvider describes the configuration of an upstream Lightweight Dire | *`base`* __string__ | Base is the dn (distinguished name) that should be used as the search base when searching for groups. E.g. "ou=groups,dc=example,dc=com". When not specified, no group search will be performed and authenticated users will not belong to any groups from the LDAP provider. Also, when not specified, the values of Filter and Attributes are ignored. | *`filter`* __string__ | Filter is the LDAP search filter which should be applied when searching for groups for a user. The pattern "{}" must occur in the filter at least once and will be dynamically replaced by the dn (distinguished name) of the user entry found as a result of the user search. E.g. "member={}" or "&(objectClass=groupOfNames)(member={})". For more information about LDAP filters, see https://ldap.com/ldap-filters. Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used. Optional. When not specified, the default will act as if the Filter were specified as "member={}". | *`attributes`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-ldapidentityprovidergroupsearchattributes[$$LDAPIdentityProviderGroupSearchAttributes$$]__ | Attributes specifies how the group's information should be read from each LDAP entry which was found as the result of the group search. +| *`skipGroupRefresh`* __boolean__ | The user's group membership is refreshed as they interact with the supervisor to obtain new credentials (as their old credentials expire). This allows group membership changes to be quickly reflected into Kubernetes clusters. Since group membership is often used to bind authorization policies, it is important to keep the groups observed in Kubernetes clusters in-sync with the identity provider. + In some environments, frequent group membership queries may result in a significant performance impact on the identity provider and/or the supervisor. The best approach to handle performance impacts is to tweak the group query to be more performant, for example by disabling nested group search or by using a more targeted group search base. + If the group search query cannot be made performant and you are willing to have group memberships remain static for approximately a day, then set skipGroupRefresh to true. This is an insecure configuration as authorization policies that are bound to group membership will not notice if a user has been removed from a particular group until their next login. + This is an experimental feature that may be removed or significantly altered in the future. Consumers of this configuration should carefully read all release notes before upgrading to ensure that the meaning of this field has not changed. |=== diff --git a/generated/1.19/apis/supervisor/idp/v1alpha1/types_activedirectoryidentityprovider.go b/generated/1.19/apis/supervisor/idp/v1alpha1/types_activedirectoryidentityprovider.go index d4e481b9..18626629 100644 --- a/generated/1.19/apis/supervisor/idp/v1alpha1/types_activedirectoryidentityprovider.go +++ b/generated/1.19/apis/supervisor/idp/v1alpha1/types_activedirectoryidentityprovider.go @@ -1,4 +1,4 @@ -// Copyright 2021 the Pinniped contributors. All Rights Reserved. +// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 @@ -131,6 +131,31 @@ type ActiveDirectoryIdentityProviderGroupSearch struct { // the result of the group search. // +optional Attributes ActiveDirectoryIdentityProviderGroupSearchAttributes `json:"attributes,omitempty"` + + // The user's group membership is refreshed as they interact with the supervisor + // to obtain new credentials (as their old credentials expire). This allows group + // membership changes to be quickly reflected into Kubernetes clusters. Since + // group membership is often used to bind authorization policies, it is important + // to keep the groups observed in Kubernetes clusters in-sync with the identity + // provider. + // + // In some environments, frequent group membership queries may result in a + // significant performance impact on the identity provider and/or the supervisor. + // The best approach to handle performance impacts is to tweak the group query + // to be more performant, for example by disabling nested group search or by + // using a more targeted group search base. + // + // If the group search query cannot be made performant and you are willing to + // have group memberships remain static for approximately a day, then set + // skipGroupRefresh to true. This is an insecure configuration as authorization + // policies that are bound to group membership will not notice if a user has + // been removed from a particular group until their next login. + // + // This is an experimental feature that may be removed or significantly altered + // in the future. Consumers of this configuration should carefully read all + // release notes before upgrading to ensure that the meaning of this field has + // not changed. + SkipGroupRefresh bool `json:"skipGroupRefresh,omitempty"` } // Spec for configuring an ActiveDirectory identity provider. diff --git a/generated/1.19/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go b/generated/1.19/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go index 74570a92..09cb843d 100644 --- a/generated/1.19/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go +++ b/generated/1.19/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go @@ -1,4 +1,4 @@ -// Copyright 2021 the Pinniped contributors. All Rights Reserved. +// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 @@ -119,6 +119,31 @@ type LDAPIdentityProviderGroupSearch struct { // the result of the group search. // +optional Attributes LDAPIdentityProviderGroupSearchAttributes `json:"attributes,omitempty"` + + // The user's group membership is refreshed as they interact with the supervisor + // to obtain new credentials (as their old credentials expire). This allows group + // membership changes to be quickly reflected into Kubernetes clusters. Since + // group membership is often used to bind authorization policies, it is important + // to keep the groups observed in Kubernetes clusters in-sync with the identity + // provider. + // + // In some environments, frequent group membership queries may result in a + // significant performance impact on the identity provider and/or the supervisor. + // The best approach to handle performance impacts is to tweak the group query + // to be more performant, for example by disabling nested group search or by + // using a more targeted group search base. + // + // If the group search query cannot be made performant and you are willing to + // have group memberships remain static for approximately a day, then set + // skipGroupRefresh to true. This is an insecure configuration as authorization + // policies that are bound to group membership will not notice if a user has + // been removed from a particular group until their next login. + // + // This is an experimental feature that may be removed or significantly altered + // in the future. Consumers of this configuration should carefully read all + // release notes before upgrading to ensure that the meaning of this field has + // not changed. + SkipGroupRefresh bool `json:"skipGroupRefresh,omitempty"` } // Spec for configuring an LDAP identity provider. diff --git a/generated/1.19/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.19/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 9929b2fc..0ac278fe 100644 --- a/generated/1.19/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.19/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -119,6 +119,30 @@ spec: search can be slow for some Active Directory servers. To disable it, you can set the filter to "(&(objectClass=group)(member={})" type: string + skipGroupRefresh: + description: "The user's group membership is refreshed as they + interact with the supervisor to obtain new credentials (as their + old credentials expire). This allows group membership changes + to be quickly reflected into Kubernetes clusters. Since group + membership is often used to bind authorization policies, it + is important to keep the groups observed in Kubernetes clusters + in-sync with the identity provider. \n In some environments, + frequent group membership queries may result in a significant + performance impact on the identity provider and/or the supervisor. + The best approach to handle performance impacts is to tweak + the group query to be more performant, for example by disabling + nested group search or by using a more targeted group search + base. \n If the group search query cannot be made performant + and you are willing to have group memberships remain static + for approximately a day, then set skipGroupRefresh to true. + \ This is an insecure configuration as authorization policies + that are bound to group membership will not notice if a user + has been removed from a particular group until their next login. + \n This is an experimental feature that may be removed or significantly + altered in the future. Consumers of this configuration should + carefully read all release notes before upgrading to ensure + that the meaning of this field has not changed." + type: boolean type: object host: description: 'Host is the hostname of this Active Directory identity diff --git a/generated/1.19/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.19/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index ed9780b5..33f1c954 100644 --- a/generated/1.19/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.19/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -111,6 +111,30 @@ spec: an entry, so "dn={}" cannot be used. Optional. When not specified, the default will act as if the Filter were specified as "member={}". type: string + skipGroupRefresh: + description: "The user's group membership is refreshed as they + interact with the supervisor to obtain new credentials (as their + old credentials expire). This allows group membership changes + to be quickly reflected into Kubernetes clusters. Since group + membership is often used to bind authorization policies, it + is important to keep the groups observed in Kubernetes clusters + in-sync with the identity provider. \n In some environments, + frequent group membership queries may result in a significant + performance impact on the identity provider and/or the supervisor. + The best approach to handle performance impacts is to tweak + the group query to be more performant, for example by disabling + nested group search or by using a more targeted group search + base. \n If the group search query cannot be made performant + and you are willing to have group memberships remain static + for approximately a day, then set skipGroupRefresh to true. + \ This is an insecure configuration as authorization policies + that are bound to group membership will not notice if a user + has been removed from a particular group until their next login. + \n This is an experimental feature that may be removed or significantly + altered in the future. Consumers of this configuration should + carefully read all release notes before upgrading to ensure + that the meaning of this field has not changed." + type: boolean type: object host: description: 'Host is the hostname of this LDAP identity provider, diff --git a/generated/1.20/README.adoc b/generated/1.20/README.adoc index faf7ad54..80fbbce1 100644 --- a/generated/1.20/README.adoc +++ b/generated/1.20/README.adoc @@ -801,6 +801,10 @@ ActiveDirectoryIdentityProvider describes the configuration of an upstream Micro | *`base`* __string__ | Base is the dn (distinguished name) that should be used as the search base when searching for groups. E.g. "ou=groups,dc=example,dc=com". Optional, when not specified it will be based on the result of a query for the defaultNamingContext (see https://docs.microsoft.com/en-us/windows/win32/adschema/rootdse). The default behavior searches your entire domain for groups. It may make sense to specify a subtree as a search base if you wish to exclude some groups for security reasons or to make searches faster. | *`filter`* __string__ | Filter is the ActiveDirectory search filter which should be applied when searching for groups for a user. The pattern "{}" must occur in the filter at least once and will be dynamically replaced by the dn (distinguished name) of the user entry found as a result of the user search. E.g. "member={}" or "&(objectClass=groupOfNames)(member={})". For more information about ActiveDirectory filters, see https://ldap.com/ldap-filters. Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used. Optional. When not specified, the default will act as if the filter were specified as "(&(objectClass=group)(member:1.2.840.113556.1.4.1941:={})". This searches nested groups by default. Note that nested group search can be slow for some Active Directory servers. To disable it, you can set the filter to "(&(objectClass=group)(member={})" | *`attributes`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-supervisor-idp-v1alpha1-activedirectoryidentityprovidergroupsearchattributes[$$ActiveDirectoryIdentityProviderGroupSearchAttributes$$]__ | Attributes specifies how the group's information should be read from each ActiveDirectory entry which was found as the result of the group search. +| *`skipGroupRefresh`* __boolean__ | The user's group membership is refreshed as they interact with the supervisor to obtain new credentials (as their old credentials expire). This allows group membership changes to be quickly reflected into Kubernetes clusters. Since group membership is often used to bind authorization policies, it is important to keep the groups observed in Kubernetes clusters in-sync with the identity provider. + In some environments, frequent group membership queries may result in a significant performance impact on the identity provider and/or the supervisor. The best approach to handle performance impacts is to tweak the group query to be more performant, for example by disabling nested group search or by using a more targeted group search base. + If the group search query cannot be made performant and you are willing to have group memberships remain static for approximately a day, then set skipGroupRefresh to true. This is an insecure configuration as authorization policies that are bound to group membership will not notice if a user has been removed from a particular group until their next login. + This is an experimental feature that may be removed or significantly altered in the future. Consumers of this configuration should carefully read all release notes before upgrading to ensure that the meaning of this field has not changed. |=== @@ -988,6 +992,10 @@ LDAPIdentityProvider describes the configuration of an upstream Lightweight Dire | *`base`* __string__ | Base is the dn (distinguished name) that should be used as the search base when searching for groups. E.g. "ou=groups,dc=example,dc=com". When not specified, no group search will be performed and authenticated users will not belong to any groups from the LDAP provider. Also, when not specified, the values of Filter and Attributes are ignored. | *`filter`* __string__ | Filter is the LDAP search filter which should be applied when searching for groups for a user. The pattern "{}" must occur in the filter at least once and will be dynamically replaced by the dn (distinguished name) of the user entry found as a result of the user search. E.g. "member={}" or "&(objectClass=groupOfNames)(member={})". For more information about LDAP filters, see https://ldap.com/ldap-filters. Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used. Optional. When not specified, the default will act as if the Filter were specified as "member={}". | *`attributes`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-supervisor-idp-v1alpha1-ldapidentityprovidergroupsearchattributes[$$LDAPIdentityProviderGroupSearchAttributes$$]__ | Attributes specifies how the group's information should be read from each LDAP entry which was found as the result of the group search. +| *`skipGroupRefresh`* __boolean__ | The user's group membership is refreshed as they interact with the supervisor to obtain new credentials (as their old credentials expire). This allows group membership changes to be quickly reflected into Kubernetes clusters. Since group membership is often used to bind authorization policies, it is important to keep the groups observed in Kubernetes clusters in-sync with the identity provider. + In some environments, frequent group membership queries may result in a significant performance impact on the identity provider and/or the supervisor. The best approach to handle performance impacts is to tweak the group query to be more performant, for example by disabling nested group search or by using a more targeted group search base. + If the group search query cannot be made performant and you are willing to have group memberships remain static for approximately a day, then set skipGroupRefresh to true. This is an insecure configuration as authorization policies that are bound to group membership will not notice if a user has been removed from a particular group until their next login. + This is an experimental feature that may be removed or significantly altered in the future. Consumers of this configuration should carefully read all release notes before upgrading to ensure that the meaning of this field has not changed. |=== diff --git a/generated/1.20/apis/supervisor/idp/v1alpha1/types_activedirectoryidentityprovider.go b/generated/1.20/apis/supervisor/idp/v1alpha1/types_activedirectoryidentityprovider.go index d4e481b9..18626629 100644 --- a/generated/1.20/apis/supervisor/idp/v1alpha1/types_activedirectoryidentityprovider.go +++ b/generated/1.20/apis/supervisor/idp/v1alpha1/types_activedirectoryidentityprovider.go @@ -1,4 +1,4 @@ -// Copyright 2021 the Pinniped contributors. All Rights Reserved. +// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 @@ -131,6 +131,31 @@ type ActiveDirectoryIdentityProviderGroupSearch struct { // the result of the group search. // +optional Attributes ActiveDirectoryIdentityProviderGroupSearchAttributes `json:"attributes,omitempty"` + + // The user's group membership is refreshed as they interact with the supervisor + // to obtain new credentials (as their old credentials expire). This allows group + // membership changes to be quickly reflected into Kubernetes clusters. Since + // group membership is often used to bind authorization policies, it is important + // to keep the groups observed in Kubernetes clusters in-sync with the identity + // provider. + // + // In some environments, frequent group membership queries may result in a + // significant performance impact on the identity provider and/or the supervisor. + // The best approach to handle performance impacts is to tweak the group query + // to be more performant, for example by disabling nested group search or by + // using a more targeted group search base. + // + // If the group search query cannot be made performant and you are willing to + // have group memberships remain static for approximately a day, then set + // skipGroupRefresh to true. This is an insecure configuration as authorization + // policies that are bound to group membership will not notice if a user has + // been removed from a particular group until their next login. + // + // This is an experimental feature that may be removed or significantly altered + // in the future. Consumers of this configuration should carefully read all + // release notes before upgrading to ensure that the meaning of this field has + // not changed. + SkipGroupRefresh bool `json:"skipGroupRefresh,omitempty"` } // Spec for configuring an ActiveDirectory identity provider. diff --git a/generated/1.20/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go b/generated/1.20/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go index 74570a92..09cb843d 100644 --- a/generated/1.20/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go +++ b/generated/1.20/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go @@ -1,4 +1,4 @@ -// Copyright 2021 the Pinniped contributors. All Rights Reserved. +// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 @@ -119,6 +119,31 @@ type LDAPIdentityProviderGroupSearch struct { // the result of the group search. // +optional Attributes LDAPIdentityProviderGroupSearchAttributes `json:"attributes,omitempty"` + + // The user's group membership is refreshed as they interact with the supervisor + // to obtain new credentials (as their old credentials expire). This allows group + // membership changes to be quickly reflected into Kubernetes clusters. Since + // group membership is often used to bind authorization policies, it is important + // to keep the groups observed in Kubernetes clusters in-sync with the identity + // provider. + // + // In some environments, frequent group membership queries may result in a + // significant performance impact on the identity provider and/or the supervisor. + // The best approach to handle performance impacts is to tweak the group query + // to be more performant, for example by disabling nested group search or by + // using a more targeted group search base. + // + // If the group search query cannot be made performant and you are willing to + // have group memberships remain static for approximately a day, then set + // skipGroupRefresh to true. This is an insecure configuration as authorization + // policies that are bound to group membership will not notice if a user has + // been removed from a particular group until their next login. + // + // This is an experimental feature that may be removed or significantly altered + // in the future. Consumers of this configuration should carefully read all + // release notes before upgrading to ensure that the meaning of this field has + // not changed. + SkipGroupRefresh bool `json:"skipGroupRefresh,omitempty"` } // Spec for configuring an LDAP identity provider. diff --git a/generated/1.20/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml b/generated/1.20/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml index 9929b2fc..0ac278fe 100644 --- a/generated/1.20/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml +++ b/generated/1.20/crds/idp.supervisor.pinniped.dev_activedirectoryidentityproviders.yaml @@ -119,6 +119,30 @@ spec: search can be slow for some Active Directory servers. To disable it, you can set the filter to "(&(objectClass=group)(member={})" type: string + skipGroupRefresh: + description: "The user's group membership is refreshed as they + interact with the supervisor to obtain new credentials (as their + old credentials expire). This allows group membership changes + to be quickly reflected into Kubernetes clusters. Since group + membership is often used to bind authorization policies, it + is important to keep the groups observed in Kubernetes clusters + in-sync with the identity provider. \n In some environments, + frequent group membership queries may result in a significant + performance impact on the identity provider and/or the supervisor. + The best approach to handle performance impacts is to tweak + the group query to be more performant, for example by disabling + nested group search or by using a more targeted group search + base. \n If the group search query cannot be made performant + and you are willing to have group memberships remain static + for approximately a day, then set skipGroupRefresh to true. + \ This is an insecure configuration as authorization policies + that are bound to group membership will not notice if a user + has been removed from a particular group until their next login. + \n This is an experimental feature that may be removed or significantly + altered in the future. Consumers of this configuration should + carefully read all release notes before upgrading to ensure + that the meaning of this field has not changed." + type: boolean type: object host: description: 'Host is the hostname of this Active Directory identity diff --git a/generated/1.20/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.20/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index ed9780b5..33f1c954 100644 --- a/generated/1.20/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.20/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -111,6 +111,30 @@ spec: an entry, so "dn={}" cannot be used. Optional. When not specified, the default will act as if the Filter were specified as "member={}". type: string + skipGroupRefresh: + description: "The user's group membership is refreshed as they + interact with the supervisor to obtain new credentials (as their + old credentials expire). This allows group membership changes + to be quickly reflected into Kubernetes clusters. Since group + membership is often used to bind authorization policies, it + is important to keep the groups observed in Kubernetes clusters + in-sync with the identity provider. \n In some environments, + frequent group membership queries may result in a significant + performance impact on the identity provider and/or the supervisor. + The best approach to handle performance impacts is to tweak + the group query to be more performant, for example by disabling + nested group search or by using a more targeted group search + base. \n If the group search query cannot be made performant + and you are willing to have group memberships remain static + for approximately a day, then set skipGroupRefresh to true. + \ This is an insecure configuration as authorization policies + that are bound to group membership will not notice if a user + has been removed from a particular group until their next login. + \n This is an experimental feature that may be removed or significantly + altered in the future. Consumers of this configuration should + carefully read all release notes before upgrading to ensure + that the meaning of this field has not changed." + type: boolean type: object host: description: 'Host is the hostname of this LDAP identity provider, diff --git a/generated/latest/apis/supervisor/idp/v1alpha1/types_activedirectoryidentityprovider.go b/generated/latest/apis/supervisor/idp/v1alpha1/types_activedirectoryidentityprovider.go index d4e481b9..18626629 100644 --- a/generated/latest/apis/supervisor/idp/v1alpha1/types_activedirectoryidentityprovider.go +++ b/generated/latest/apis/supervisor/idp/v1alpha1/types_activedirectoryidentityprovider.go @@ -1,4 +1,4 @@ -// Copyright 2021 the Pinniped contributors. All Rights Reserved. +// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 @@ -131,6 +131,31 @@ type ActiveDirectoryIdentityProviderGroupSearch struct { // the result of the group search. // +optional Attributes ActiveDirectoryIdentityProviderGroupSearchAttributes `json:"attributes,omitempty"` + + // The user's group membership is refreshed as they interact with the supervisor + // to obtain new credentials (as their old credentials expire). This allows group + // membership changes to be quickly reflected into Kubernetes clusters. Since + // group membership is often used to bind authorization policies, it is important + // to keep the groups observed in Kubernetes clusters in-sync with the identity + // provider. + // + // In some environments, frequent group membership queries may result in a + // significant performance impact on the identity provider and/or the supervisor. + // The best approach to handle performance impacts is to tweak the group query + // to be more performant, for example by disabling nested group search or by + // using a more targeted group search base. + // + // If the group search query cannot be made performant and you are willing to + // have group memberships remain static for approximately a day, then set + // skipGroupRefresh to true. This is an insecure configuration as authorization + // policies that are bound to group membership will not notice if a user has + // been removed from a particular group until their next login. + // + // This is an experimental feature that may be removed or significantly altered + // in the future. Consumers of this configuration should carefully read all + // release notes before upgrading to ensure that the meaning of this field has + // not changed. + SkipGroupRefresh bool `json:"skipGroupRefresh,omitempty"` } // Spec for configuring an ActiveDirectory identity provider. diff --git a/generated/latest/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go b/generated/latest/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go index 74570a92..09cb843d 100644 --- a/generated/latest/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go +++ b/generated/latest/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go @@ -1,4 +1,4 @@ -// Copyright 2021 the Pinniped contributors. All Rights Reserved. +// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package v1alpha1 @@ -119,6 +119,31 @@ type LDAPIdentityProviderGroupSearch struct { // the result of the group search. // +optional Attributes LDAPIdentityProviderGroupSearchAttributes `json:"attributes,omitempty"` + + // The user's group membership is refreshed as they interact with the supervisor + // to obtain new credentials (as their old credentials expire). This allows group + // membership changes to be quickly reflected into Kubernetes clusters. Since + // group membership is often used to bind authorization policies, it is important + // to keep the groups observed in Kubernetes clusters in-sync with the identity + // provider. + // + // In some environments, frequent group membership queries may result in a + // significant performance impact on the identity provider and/or the supervisor. + // The best approach to handle performance impacts is to tweak the group query + // to be more performant, for example by disabling nested group search or by + // using a more targeted group search base. + // + // If the group search query cannot be made performant and you are willing to + // have group memberships remain static for approximately a day, then set + // skipGroupRefresh to true. This is an insecure configuration as authorization + // policies that are bound to group membership will not notice if a user has + // been removed from a particular group until their next login. + // + // This is an experimental feature that may be removed or significantly altered + // in the future. Consumers of this configuration should carefully read all + // release notes before upgrading to ensure that the meaning of this field has + // not changed. + SkipGroupRefresh bool `json:"skipGroupRefresh,omitempty"` } // Spec for configuring an LDAP identity provider. diff --git a/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher.go b/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher.go index 33d1557a..4fb699b7 100644 --- a/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher.go +++ b/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher.go @@ -333,6 +333,7 @@ func (c *activeDirectoryWatcherController) validateUpstream(ctx context.Context, Base: spec.GroupSearch.Base, Filter: adUpstreamImpl.Spec().GroupSearch().Filter(), GroupNameAttribute: adUpstreamImpl.Spec().GroupSearch().GroupNameAttribute(), + SkipGroupRefresh: spec.GroupSearch.SkipGroupRefresh, }, Dialer: c.ldapDialer, UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){ diff --git a/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go b/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go index 7777bfec..ce69b4af 100644 --- a/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/activedirectoryupstreamwatcher/active_directory_upstream_watcher_test.go @@ -192,6 +192,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { Attributes: v1alpha1.ActiveDirectoryIdentityProviderGroupSearchAttributes{ GroupName: testGroupNameAttrName, }, + SkipGroupRefresh: false, }, }, } @@ -1907,6 +1908,75 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) { SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInRootDSECondition(0))), }}, }, + { + name: "skipping group refresh is valid", + inputUpstreams: []runtime.Object{editedValidUpstream(func(upstream *v1alpha1.ActiveDirectoryIdentityProvider) { + upstream.Spec.GroupSearch.SkipGroupRefresh = true + })}, + inputSecrets: []runtime.Object{validBindUserSecret("4242")}, + setupMocks: func(conn *mockldapconn.MockConn) { + // Should perform a test dial and bind. + conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1) + conn.EXPECT().Close().Times(1) + }, + wantResultingCache: []*upstreamldap.ProviderConfig{ + { + Name: testName, + ResourceUID: testResourceUID, + Host: testHost, + ConnectionProtocol: upstreamldap.TLS, + CABundle: testCABundle, + BindUsername: testBindUsername, + BindPassword: testBindPassword, + UserSearch: upstreamldap.UserSearchConfig{ + Base: testUserSearchBase, + Filter: testUserSearchFilter, + UsernameAttribute: testUsernameAttrName, + UIDAttribute: testUIDAttrName, + }, + GroupSearch: upstreamldap.GroupSearchConfig{ + Base: testGroupSearchBase, + Filter: testGroupSearchFilter, + GroupNameAttribute: testGroupNameAttrName, + SkipGroupRefresh: true, + }, + UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": microsoftUUIDFromBinaryAttr("objectGUID")}, + RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{ + "pwdLastSet": upstreamldap.AttributeUnchangedSinceLogin("pwdLastSet"), + "userAccountControl": validUserAccountControl, + "msDS-User-Account-Control-Computed": validComputedUserAccountControl, + }, + }, + }, + wantResultingUpstreams: []v1alpha1.ActiveDirectoryIdentityProvider{{ + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName, Generation: 1234, UID: testResourceUID}, + Status: v1alpha1.ActiveDirectoryIdentityProviderStatus{ + Phase: "Ready", + Conditions: []v1alpha1.Condition{ + bindSecretValidTrueCondition(1234), + activeDirectoryConnectionValidTrueCondition(1234, "4242"), + searchBaseFoundInConfigCondition(1234), + { + Type: "TLSConfigurationValid", + Status: "True", + LastTransitionTime: now, + Reason: "Success", + Message: "loaded TLS configuration", + ObservedGeneration: 1234, + }, + }, + }, + }}, + wantValidatedSettings: map[string]upstreamwatchers.ValidatedSettings{testName: { + BindSecretResourceVersion: "4242", + LDAPConnectionProtocol: upstreamldap.TLS, + UserSearchBase: testUserSearchBase, + GroupSearchBase: testGroupSearchBase, + IDPSpecGeneration: 1234, + ConnectionValidCondition: condPtr(activeDirectoryConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), + SearchBaseFoundCondition: condPtr(withoutTime(searchBaseFoundInConfigCondition(0))), + }}, + }, } for _, tt := range tests { diff --git a/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher.go b/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher.go index 82a9fdec..fd57cc28 100644 --- a/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher.go +++ b/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher.go @@ -239,6 +239,7 @@ func (c *ldapWatcherController) validateUpstream(ctx context.Context, upstream * Base: spec.GroupSearch.Base, Filter: spec.GroupSearch.Filter, GroupNameAttribute: spec.GroupSearch.Attributes.GroupName, + SkipGroupRefresh: spec.GroupSearch.SkipGroupRefresh, }, Dialer: c.ldapDialer, } diff --git a/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go b/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go index d7f82edf..0f57bc2f 100644 --- a/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go +++ b/internal/controller/supervisorconfig/ldapupstreamwatcher/ldap_upstream_watcher_test.go @@ -196,6 +196,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { Attributes: v1alpha1.LDAPIdentityProviderGroupSearchAttributes{ GroupName: testGroupNameAttrName, }, + SkipGroupRefresh: false, }, }, } @@ -1053,6 +1054,67 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) { IDPSpecGeneration: 1234, ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), }}}, + { + name: "skipping group refresh is valid", + inputUpstreams: []runtime.Object{editedValidUpstream(func(upstream *v1alpha1.LDAPIdentityProvider) { + upstream.Spec.GroupSearch.SkipGroupRefresh = true + })}, + inputSecrets: []runtime.Object{validBindUserSecret("4242")}, + setupMocks: func(conn *mockldapconn.MockConn) { + // Should perform a test dial and bind. + conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1) + conn.EXPECT().Close().Times(1) + }, + wantResultingCache: []*upstreamldap.ProviderConfig{ + { + Name: testName, + ResourceUID: testResourceUID, + Host: testHost, + ConnectionProtocol: upstreamldap.TLS, + CABundle: testCABundle, + BindUsername: testBindUsername, + BindPassword: testBindPassword, + UserSearch: upstreamldap.UserSearchConfig{ + Base: testUserSearchBase, + Filter: testUserSearchFilter, + UsernameAttribute: testUsernameAttrName, + UIDAttribute: testUIDAttrName, + }, + GroupSearch: upstreamldap.GroupSearchConfig{ + Base: testGroupSearchBase, + Filter: testGroupSearchFilter, + GroupNameAttribute: testGroupNameAttrName, + SkipGroupRefresh: true, + }, + }, + }, + wantResultingUpstreams: []v1alpha1.LDAPIdentityProvider{{ + ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testName, Generation: 1234, UID: testResourceUID}, + Status: v1alpha1.LDAPIdentityProviderStatus{ + Phase: "Ready", + Conditions: []v1alpha1.Condition{ + bindSecretValidTrueCondition(1234), + ldapConnectionValidTrueCondition(1234, "4242"), + { + Type: "TLSConfigurationValid", + Status: "True", + LastTransitionTime: now, + Reason: "Success", + Message: "loaded TLS configuration", + ObservedGeneration: 1234, + }, + }, + }, + }}, + wantValidatedSettings: map[string]upstreamwatchers.ValidatedSettings{testName: { + BindSecretResourceVersion: "4242", + LDAPConnectionProtocol: upstreamldap.TLS, + UserSearchBase: testUserSearchBase, + GroupSearchBase: testGroupSearchBase, + IDPSpecGeneration: 1234, + ConnectionValidCondition: condPtr(ldapConnectionValidTrueConditionWithoutTimeOrGeneration("4242")), + }}, + }, } for _, tt := range tests { diff --git a/internal/oidc/provider/dynamic_upstream_idp_provider.go b/internal/oidc/provider/dynamic_upstream_idp_provider.go index 6f0ced3d..79771943 100644 --- a/internal/oidc/provider/dynamic_upstream_idp_provider.go +++ b/internal/oidc/provider/dynamic_upstream_idp_provider.go @@ -115,6 +115,7 @@ type StoredRefreshAttributes struct { Username string Subject string DN string + Groups []string AdditionalAttributes map[string]string } diff --git a/internal/oidc/token/token_handler.go b/internal/oidc/token/token_handler.go index eab5ed11..55e73352 100644 --- a/internal/oidc/token/token_handler.go +++ b/internal/oidc/token/token_handler.go @@ -275,6 +275,10 @@ func upstreamLDAPRefresh(ctx context.Context, providerCache oidc.UpstreamIdentit return err } subject := session.Fosite.Claims.Subject + oldGroups, err := getDownstreamGroupsFromPinnipedSession(session) + if err != nil { + return err + } s := session.Custom @@ -305,6 +309,7 @@ func upstreamLDAPRefresh(ctx context.Context, providerCache oidc.UpstreamIdentit Username: username, Subject: subject, DN: dn, + Groups: oldGroups, AdditionalAttributes: additionalAttributes, }) if err != nil { @@ -353,7 +358,7 @@ func getDownstreamUsernameFromPinnipedSession(session *psession.PinnipedSession) if extra == nil { return "", errorsx.WithStack(errMissingUpstreamSessionInternalError) } - downstreamUsernameInterface := extra["username"] + downstreamUsernameInterface := extra[oidc.DownstreamUsernameClaim] if downstreamUsernameInterface == nil { return "", errorsx.WithStack(errMissingUpstreamSessionInternalError) } @@ -363,3 +368,28 @@ func getDownstreamUsernameFromPinnipedSession(session *psession.PinnipedSession) } return downstreamUsername, nil } + +func getDownstreamGroupsFromPinnipedSession(session *psession.PinnipedSession) ([]string, error) { + extra := session.Fosite.Claims.Extra + if extra == nil { + return nil, errorsx.WithStack(errMissingUpstreamSessionInternalError) + } + downstreamGroupsInterface := extra[oidc.DownstreamGroupsClaim] + if downstreamGroupsInterface == nil { + return nil, errorsx.WithStack(errMissingUpstreamSessionInternalError) + } + downstreamGroupsInterfaceList, ok := downstreamGroupsInterface.([]interface{}) + if !ok { + return nil, errorsx.WithStack(errMissingUpstreamSessionInternalError) + } + + downstreamGroups := make([]string, 0, len(downstreamGroupsInterfaceList)) + for _, downstreamGroupInterface := range downstreamGroupsInterfaceList { + downstreamGroup, ok := downstreamGroupInterface.(string) + if !ok || len(downstreamGroup) == 0 { + return nil, errorsx.WithStack(errMissingUpstreamSessionInternalError) + } + downstreamGroups = append(downstreamGroups, downstreamGroup) + } + return downstreamGroups, nil +} diff --git a/internal/upstreamldap/upstreamldap.go b/internal/upstreamldap/upstreamldap.go index 6ea38b6b..136fc023 100644 --- a/internal/upstreamldap/upstreamldap.go +++ b/internal/upstreamldap/upstreamldap.go @@ -150,6 +150,11 @@ type GroupSearchConfig struct { // GroupNameAttribute is the attribute in the LDAP group entry from which the group name should be // retrieved. Empty means to use 'cn'. GroupNameAttribute string + + // SkipGroupRefresh skips the group refresh operation that occurs with each refresh + // (every 5 minutes). This can be done if group search is very slow or resource intensive for the LDAP + // server. + SkipGroupRefresh bool } type Provider struct { @@ -230,6 +235,10 @@ func (p *Provider) PerformRefresh(ctx context.Context, storedRefreshAttributes p } } + if p.c.GroupSearch.SkipGroupRefresh { + return storedRefreshAttributes.Groups, nil + } + mappedGroupNames, err := p.searchGroupsForUserDN(conn, userDN) if err != nil { return nil, err diff --git a/internal/upstreamldap/upstreamldap_test.go b/internal/upstreamldap/upstreamldap_test.go index 8b899568..bb837339 100644 --- a/internal/upstreamldap/upstreamldap_test.go +++ b/internal/upstreamldap/upstreamldap_test.go @@ -1298,6 +1298,37 @@ func TestUpstreamRefresh(t *testing.T) { }, wantGroups: []string{}, }, + { + name: "happy path where group search is configured but skipGroupRefresh is set", + providerConfig: &ProviderConfig{ + Name: "some-provider-name", + Host: testHost, + CABundle: nil, // this field is only used by the production dialer, which is replaced by a mock for this test + ConnectionProtocol: TLS, + BindUsername: testBindUsername, + BindPassword: testBindPassword, + UserSearch: UserSearchConfig{ + Base: testUserSearchBase, + UIDAttribute: testUserSearchUIDAttribute, + UsernameAttribute: testUserSearchUsernameAttribute, + }, + GroupSearch: GroupSearchConfig{ + Base: testGroupSearchBase, + Filter: testGroupSearchFilter, + GroupNameAttribute: testGroupSearchGroupNameAttribute, + SkipGroupRefresh: true, + }, + RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.StoredRefreshAttributes) error{ + pwdLastSetAttribute: AttributeUnchangedSinceLogin(pwdLastSetAttribute), + }, + }, + setupMocks: func(conn *mockldapconn.MockConn) { + conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1) + conn.EXPECT().Search(expectedUserSearch).Return(happyPathUserSearchResult, nil).Times(1) // note that group search is not expected + conn.EXPECT().Close().Times(1) + }, + wantGroups: nil, // do not update groups + }, { name: "error where dial fails", providerConfig: providerConfig, diff --git a/test/integration/supervisor_login_test.go b/test/integration/supervisor_login_test.go index d109d85d..5c9f7c75 100644 --- a/test/integration/supervisor_login_test.go +++ b/test/integration/supervisor_login_test.go @@ -51,25 +51,24 @@ func TestSupervisorLogin_Browser(t *testing.T) { env := testlib.IntegrationEnv(t) tests := []struct { - name string - maybeSkip func(t *testing.T) - createTestUser func(t *testing.T) (string, string) - deleteTestUser func(t *testing.T, username string) - requestAuthorization func(t *testing.T, downstreamAuthorizeURL, downstreamCallbackURL, username, password string, httpClient *http.Client) - createIDP func(t *testing.T) string - wantDownstreamIDTokenSubjectToMatch string - wantDownstreamIDTokenUsernameToMatch func(username string) string - wantDownstreamIDTokenGroups []string - wantDownstreamIDTokenGroupsAfterRefresh []string - wantErrorDescription string - wantErrorType string + name string + maybeSkip func(t *testing.T) + createTestUser func(t *testing.T) (string, string) + deleteTestUser func(t *testing.T, username string) + requestAuthorization func(t *testing.T, downstreamAuthorizeURL, downstreamCallbackURL, username, password string, httpClient *http.Client) + createIDP func(t *testing.T) string + wantDownstreamIDTokenSubjectToMatch string + wantDownstreamIDTokenUsernameToMatch func(username string) string + wantDownstreamIDTokenGroups []string + wantErrorDescription string + wantErrorType string // Either revoke the user's session on the upstream provider, or manipulate the user's session // data in such a way that it should cause the next upstream refresh attempt to fail. breakRefreshSessionData func(t *testing.T, sessionData *psession.PinnipedSession, idpName, username string) // Edit the refresh session data between the initial login and the refresh, which is expected to // succeed. - editRefreshSessionDataWithoutBreaking func(t *testing.T, sessionData *psession.PinnipedSession, idpName, username string) + editRefreshSessionDataWithoutBreaking func(t *testing.T, sessionData *psession.PinnipedSession, idpName, username string) []string }{ { name: "oidc with default username and groups claim settings", @@ -132,13 +131,14 @@ func TestSupervisorLogin_Browser(t *testing.T) { wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+", wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Username) + "$" }, wantDownstreamIDTokenGroups: env.SupervisorUpstreamOIDC.ExpectedGroups, - editRefreshSessionDataWithoutBreaking: func(t *testing.T, sessionData *psession.PinnipedSession, _, _ string) { + editRefreshSessionDataWithoutBreaking: func(t *testing.T, sessionData *psession.PinnipedSession, _, _ string) []string { // even if we update this group to the wrong thing, we expect that it will return to the correct // value after we refresh. // However if there are no expected groups then they will not update, so we should skip this. if len(env.SupervisorUpstreamOIDC.ExpectedGroups) > 0 { sessionData.Fosite.Claims.Extra["groups"] = []string{"some-wrong-group", "some-other-group"} } + return env.SupervisorUpstreamOIDC.ExpectedGroups }, }, { @@ -284,10 +284,95 @@ func TestSupervisorLogin_Browser(t *testing.T) { false, ) }, - editRefreshSessionDataWithoutBreaking: func(t *testing.T, sessionData *psession.PinnipedSession, _, _ string) { + editRefreshSessionDataWithoutBreaking: func(t *testing.T, sessionData *psession.PinnipedSession, _, _ string) []string { // even if we update this group to the wrong thing, we expect that it will return to the correct // value after we refresh. sessionData.Fosite.Claims.Extra["groups"] = []string{"some-wrong-group", "some-other-group"} + return env.SupervisorUpstreamLDAP.TestUserDirectGroupsDNs + }, + breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession, _, _ string) { + customSessionData := pinnipedSession.Custom + require.Equal(t, psession.ProviderTypeLDAP, customSessionData.ProviderType) + require.NotEmpty(t, customSessionData.LDAP.UserDN) + fositeSessionData := pinnipedSession.Fosite + fositeSessionData.Claims.Subject = "not-right" + }, + // the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute + wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta( + "ldaps://"+env.SupervisorUpstreamLDAP.Host+ + "?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+ + "&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)), + ) + "$", + // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute + wantDownstreamIDTokenUsernameToMatch: func(_ string) string { + return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$" + }, + wantDownstreamIDTokenGroups: env.SupervisorUpstreamLDAP.TestUserDirectGroupsDNs, + }, + { + name: "ldap skip group refresh", + maybeSkip: func(t *testing.T) { + t.Helper() + if len(env.ToolsNamespace) == 0 && !env.HasCapability(testlib.CanReachInternetLDAPPorts) { + t.Skip("LDAP integration test requires connectivity to an LDAP server") + } + }, + createIDP: func(t *testing.T) string { + t.Helper() + secret := testlib.CreateTestSecret(t, env.SupervisorNamespace, "ldap-service-account", v1.SecretTypeBasicAuth, + map[string]string{ + v1.BasicAuthUsernameKey: env.SupervisorUpstreamLDAP.BindUsername, + v1.BasicAuthPasswordKey: env.SupervisorUpstreamLDAP.BindPassword, + }, + ) + ldapIDP := testlib.CreateTestLDAPIdentityProvider(t, idpv1alpha1.LDAPIdentityProviderSpec{ + Host: env.SupervisorUpstreamLDAP.Host, + TLS: &idpv1alpha1.TLSSpec{ + CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.CABundle)), + }, + Bind: idpv1alpha1.LDAPIdentityProviderBind{ + SecretName: secret.Name, + }, + UserSearch: idpv1alpha1.LDAPIdentityProviderUserSearch{ + Base: env.SupervisorUpstreamLDAP.UserSearchBase, + Filter: "", + Attributes: idpv1alpha1.LDAPIdentityProviderUserSearchAttributes{ + Username: env.SupervisorUpstreamLDAP.TestUserMailAttributeName, + UID: env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeName, + }, + }, + GroupSearch: idpv1alpha1.LDAPIdentityProviderGroupSearch{ + Base: env.SupervisorUpstreamLDAP.GroupSearchBase, + Filter: "", + Attributes: idpv1alpha1.LDAPIdentityProviderGroupSearchAttributes{ + GroupName: "dn", + }, + SkipGroupRefresh: true, + }, + }, idpv1alpha1.LDAPPhaseReady) + expectedMsg := fmt.Sprintf( + `successfully able to connect to "%s" and bind as user "%s" [validated with Secret "%s" at version "%s"]`, + env.SupervisorUpstreamLDAP.Host, env.SupervisorUpstreamLDAP.BindUsername, + secret.Name, secret.ResourceVersion, + ) + requireSuccessfulLDAPIdentityProviderConditions(t, ldapIDP, expectedMsg) + return ldapIDP.Name + }, + requestAuthorization: func(t *testing.T, downstreamAuthorizeURL, _, _, _ string, httpClient *http.Client) { + requestAuthorizationUsingCLIPasswordFlow(t, + downstreamAuthorizeURL, + env.SupervisorUpstreamLDAP.TestUserMailAttributeValue, // username to present to server during login + env.SupervisorUpstreamLDAP.TestUserPassword, // password to present to server during login + httpClient, + false, + ) + }, + editRefreshSessionDataWithoutBreaking: func(t *testing.T, sessionData *psession.PinnipedSession, _, _ string) []string { + // update the list of groups to the wrong thing and see that they do not get updated because + // skip group refresh is set + wrongGroups := []string{"some-wrong-group", "some-other-group"} + sessionData.Fosite.Claims.Extra["groups"] = wrongGroups + return wrongGroups }, breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession, _, _ string) { customSessionData := pinnipedSession.Custom @@ -372,10 +457,11 @@ func TestSupervisorLogin_Browser(t *testing.T) { false, ) }, - editRefreshSessionDataWithoutBreaking: func(t *testing.T, sessionData *psession.PinnipedSession, _, _ string) { + editRefreshSessionDataWithoutBreaking: func(t *testing.T, sessionData *psession.PinnipedSession, _, _ string) []string { // even if we update this group to the wrong thing, we expect that it will return to the correct - // value after we refresh. + // value (no groups) after we refresh. sessionData.Fosite.Claims.Extra["groups"] = []string{"some-wrong-group", "some-other-group"} + return []string{} }, breakRefreshSessionData: func(t *testing.T, pinnipedSession *psession.PinnipedSession, _, _ string) { customSessionData := pinnipedSession.Custom @@ -1420,7 +1506,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { false, ) }, - editRefreshSessionDataWithoutBreaking: func(t *testing.T, pinnipedSession *psession.PinnipedSession, idpName, _ string) { + editRefreshSessionDataWithoutBreaking: func(t *testing.T, pinnipedSession *psession.PinnipedSession, idpName, _ string) []string { // get the idp, update the config. client := testlib.NewSupervisorClientset(t) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) @@ -1434,6 +1520,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { _, err = upstreams.Update(ctx, ldapIDP, metav1.UpdateOptions{}) require.NoError(t, err) time.Sleep(10 * time.Second) // wait for controllers to pick up the change + return []string{} }, // the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta( @@ -1445,8 +1532,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$" }, - wantDownstreamIDTokenGroups: env.SupervisorUpstreamLDAP.TestUserDirectGroupsDNs, - wantDownstreamIDTokenGroupsAfterRefresh: []string{}, + wantDownstreamIDTokenGroups: env.SupervisorUpstreamLDAP.TestUserDirectGroupsDNs, }, } for _, test := range tests { @@ -1464,7 +1550,6 @@ func TestSupervisorLogin_Browser(t *testing.T) { tt.wantDownstreamIDTokenSubjectToMatch, tt.wantDownstreamIDTokenUsernameToMatch, tt.wantDownstreamIDTokenGroups, - tt.wantDownstreamIDTokenGroupsAfterRefresh, tt.wantErrorDescription, tt.wantErrorType, ) @@ -1593,14 +1678,13 @@ func testSupervisorLogin( t *testing.T, createIDP func(t *testing.T) string, requestAuthorization func(t *testing.T, downstreamAuthorizeURL string, downstreamCallbackURL string, username string, password string, httpClient *http.Client), - editRefreshSessionDataWithoutBreaking func(t *testing.T, pinnipedSession *psession.PinnipedSession, idpName, username string), + editRefreshSessionDataWithoutBreaking func(t *testing.T, pinnipedSession *psession.PinnipedSession, idpName, username string) []string, breakRefreshSessionData func(t *testing.T, pinnipedSession *psession.PinnipedSession, idpName, username string), createTestUser func(t *testing.T) (string, string), deleteTestUser func(t *testing.T, username string), wantDownstreamIDTokenSubjectToMatch string, wantDownstreamIDTokenUsernameToMatch func(username string) string, wantDownstreamIDTokenGroups []string, - wantDownstreamIDTokenGroupsAfterRefresh []string, wantErrorDescription string, wantErrorType string, ) { @@ -1754,6 +1838,7 @@ func testSupervisorLogin( // token exchange on the original token doTokenExchange(t, &downstreamOAuth2Config, tokenResponse, httpClient, discovery) + refreshedGroups := wantDownstreamIDTokenGroups if editRefreshSessionDataWithoutBreaking != nil { latestRefreshToken := tokenResponse.RefreshToken signatureOfLatestRefreshToken := getFositeDataSignature(t, latestRefreshToken) @@ -1769,7 +1854,7 @@ func testSupervisorLogin( pinnipedSession, ok := storedRefreshSession.GetSession().(*psession.PinnipedSession) require.True(t, ok, "should have been able to cast session data to PinnipedSession") - editRefreshSessionDataWithoutBreaking(t, pinnipedSession, idpName, username) + refreshedGroups = editRefreshSessionDataWithoutBreaking(t, pinnipedSession, idpName, username) // Then save the mutated Secret back to Kubernetes. // There is no update function, so delete and create again at the same name. @@ -1783,12 +1868,9 @@ func testSupervisorLogin( // When refreshing, expect to get an "at_hash" claim, but no "nonce" claim. expectRefreshedIDTokenClaims := []string{"iss", "exp", "sub", "aud", "auth_time", "iat", "jti", "rat", "username", "groups", "at_hash"} - if wantDownstreamIDTokenGroupsAfterRefresh == nil { - wantDownstreamIDTokenGroupsAfterRefresh = wantDownstreamIDTokenGroups - } verifyTokenResponse(t, refreshedTokenResponse, discovery, downstreamOAuth2Config, "", - expectRefreshedIDTokenClaims, wantDownstreamIDTokenSubjectToMatch, wantDownstreamIDTokenUsernameToMatch(username), wantDownstreamIDTokenGroupsAfterRefresh) + expectRefreshedIDTokenClaims, wantDownstreamIDTokenSubjectToMatch, wantDownstreamIDTokenUsernameToMatch(username), refreshedGroups) require.NotEqual(t, tokenResponse.AccessToken, refreshedTokenResponse.AccessToken) require.NotEqual(t, tokenResponse.RefreshToken, refreshedTokenResponse.RefreshToken)