mirror of https://github.com/rancher/shepherd.git
Merge pull request #451 from dasarinaidu/backport-openldap-v2.10
Backport PR for v210 to enable OpenLdap test automation
This commit is contained in:
commit
6105e162ea
|
|
@ -0,0 +1,23 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"github.com/rancher/shepherd/clients/rancher/auth/openldap"
|
||||
management "github.com/rancher/shepherd/clients/rancher/generated/management/v3"
|
||||
"github.com/rancher/shepherd/pkg/session"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
OLDAP *openldap.OLDAPClient
|
||||
}
|
||||
|
||||
// NewAuth constructs the Auth Provider Struct
|
||||
func NewClient(mgmt *management.Client, session *session.Session) (*Client, error) {
|
||||
oLDAP, err := openldap.NewOLDAP(mgmt, session)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Client{
|
||||
OLDAP: oLDAP,
|
||||
}, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package auth
|
||||
|
||||
type Provider string
|
||||
|
||||
const (
|
||||
LocalAuth Provider = "local"
|
||||
OpenLDAPAuth Provider = "openLdap"
|
||||
)
|
||||
|
||||
// String stringer for the AuthProvider
|
||||
func (a Provider) String() string {
|
||||
return string(a)
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
package openldap
|
||||
|
||||
const (
|
||||
ConfigurationFileKey = "openLDAP"
|
||||
)
|
||||
|
||||
// Config represents the OpenLDAP authentication configuration structure
|
||||
// used for configuring LDAP connection parameters, user search settings,
|
||||
// and group membership configuration.
|
||||
type Config struct {
|
||||
Hostname string `json:"hostname" yaml:"hostname"`
|
||||
IP string `json:"IP" yaml:"IP"`
|
||||
ServiceAccount *ServiceAccount `json:"serviceAccount" yaml:"serviceAccount"`
|
||||
Groups *Groups `json:"groups" yaml:"groups"`
|
||||
Users *Users `json:"users" yaml:"users"`
|
||||
AccessMode string `json:"accessMode" yaml:"accessMode" default:"unrestricted"`
|
||||
}
|
||||
|
||||
type ServiceAccount struct {
|
||||
DistinguishedName string `json:"distinguishedName" yaml:"distinguishedName"`
|
||||
Password string `json:"password" yaml:"password"`
|
||||
}
|
||||
|
||||
// Users represents LDAP Groups, used in test scenarios for validating Groups search.
|
||||
type Groups struct {
|
||||
ObjectClass string `json:"objectClass" yaml:"objectClass"`
|
||||
MemberMappingAttribute string `json:"memberMappingAttribute" yaml:"memberMappingAttribute"`
|
||||
NestedGroupMembershipEnabled bool `json:"nestedGroupMembershipEnabled,omitempty" yaml:"nestedGroupMembershipEnabled,omitempty"`
|
||||
SearchDirectGroupMemberships bool `json:"searchDirectGroupMemberships,omitempty" yaml:"searchDirectGroupMemberships,omitempty"`
|
||||
SearchBase string `json:"searchBase" yaml:"searchBase"`
|
||||
}
|
||||
|
||||
// Users represents LDAP users, used in test scenarios for validating users search.
|
||||
|
||||
type Users struct {
|
||||
Admin *User `json:"admin" yaml:"admin"`
|
||||
SearchBase string `json:"searchBase" yaml:"searchBase"`
|
||||
}
|
||||
|
||||
// User represents an LDAP user with authentication credentials, used in test scenarios for validating user authentication.
|
||||
type User struct {
|
||||
Password string `json:"password,omitempty" yaml:"password,omitempty"`
|
||||
Username string `json:"username,omitempty" yaml:"username,omitempty"`
|
||||
}
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
package openldap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
apisv3 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3"
|
||||
|
||||
management "github.com/rancher/shepherd/clients/rancher/generated/management/v3"
|
||||
"github.com/rancher/shepherd/pkg/config"
|
||||
"github.com/rancher/shepherd/pkg/session"
|
||||
)
|
||||
|
||||
type OLDAPOperations interface {
|
||||
Enable() error
|
||||
Disable() error
|
||||
Update(existing, updates *management.AuthConfig) (*management.AuthConfig, error)
|
||||
}
|
||||
|
||||
const (
|
||||
resourceType = "openldap"
|
||||
schemaType = "openLdapConfigs"
|
||||
)
|
||||
|
||||
type OLDAPClient struct {
|
||||
client *management.Client
|
||||
session *session.Session
|
||||
|
||||
Config *Config
|
||||
}
|
||||
|
||||
// NewOLDAP constructs OLDAP struct after it reads Open LDAP from the configuration file
|
||||
func NewOLDAP(client *management.Client, session *session.Session) (*OLDAPClient, error) {
|
||||
ldapConfig := new(Config)
|
||||
config.LoadConfig(ConfigurationFileKey, ldapConfig)
|
||||
|
||||
return &OLDAPClient{
|
||||
client: client,
|
||||
session: session,
|
||||
Config: ldapConfig,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Enable is a method of OLDAP, makes a request to the action with the given
|
||||
// configuration values
|
||||
func (o *OLDAPClient) Enable() error {
|
||||
var jsonResp map[string]interface{}
|
||||
|
||||
url := o.newActionURL("testAndApply")
|
||||
enableActionInput, err := o.newEnableInputFromConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = o.client.Ops.DoModify("POST", url, enableActionInput, &jsonResp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
o.session.RegisterCleanupFunc(func() error {
|
||||
return o.Disable()
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update is a method of OLDAP, makes an update with the given configuration values
|
||||
func (o *OLDAPClient) Update(
|
||||
existing, updates *management.AuthConfig,
|
||||
) (*management.AuthConfig, error) {
|
||||
return o.client.AuthConfig.Update(existing, updates)
|
||||
}
|
||||
|
||||
// Disable is a method of OLDAP, makes a request to disable Open LDAP
|
||||
func (o *OLDAPClient) Disable() error {
|
||||
var jsonResp map[string]any
|
||||
|
||||
url := o.newActionURL("disable")
|
||||
disableActionInput := o.newDisableInput()
|
||||
|
||||
return o.client.Ops.DoModify("POST", url, &disableActionInput, &jsonResp)
|
||||
}
|
||||
|
||||
func (o *OLDAPClient) newActionURL(action string) string {
|
||||
return fmt.Sprintf(
|
||||
"%v/%v/%v?action=%v",
|
||||
o.client.Opts.URL,
|
||||
schemaType,
|
||||
resourceType,
|
||||
action,
|
||||
)
|
||||
}
|
||||
|
||||
func (o *OLDAPClient) newEnableInputFromConfig() (*apisv3.LdapTestAndApplyInput, error) {
|
||||
var resource apisv3.LdapTestAndApplyInput
|
||||
|
||||
var server string
|
||||
if o.Config.Hostname == "" && o.Config.IP == "" {
|
||||
return nil, fmt.Errorf("open LDAP Hostname and IP are empty, please provide one of them")
|
||||
}
|
||||
server = o.Config.Hostname
|
||||
if server == "" {
|
||||
server = o.Config.IP
|
||||
}
|
||||
|
||||
resource.Enabled = true
|
||||
resource.AccessMode = o.Config.AccessMode
|
||||
|
||||
resource.UserSearchBase = o.Config.Users.SearchBase
|
||||
resource.GroupSearchBase = o.Config.Groups.SearchBase
|
||||
|
||||
if o.Config.Users.Admin.Username == "" || o.Config.Users.Admin.Password == "" {
|
||||
return nil, fmt.Errorf("admin username or password are empty, please provide them")
|
||||
}
|
||||
|
||||
resource.Username = o.Config.Users.Admin.Username
|
||||
resource.Password = o.Config.Users.Admin.Password
|
||||
|
||||
resource.Servers = []string{server}
|
||||
|
||||
resource.ServiceAccountDistinguishedName = o.Config.ServiceAccount.DistinguishedName
|
||||
resource.ServiceAccountPassword = o.Config.ServiceAccount.Password
|
||||
|
||||
resource.GroupMemberUserAttribute = o.Config.Groups.MemberMappingAttribute
|
||||
resource.NestedGroupMembershipEnabled = o.Config.Groups.NestedGroupMembershipEnabled
|
||||
resource.GroupObjectClass = o.Config.Groups.ObjectClass
|
||||
|
||||
return &resource, nil
|
||||
}
|
||||
|
||||
func (o *OLDAPClient) newDisableInput() []byte {
|
||||
return []byte(`{"action": "disable"}`)
|
||||
}
|
||||
|
|
@ -9,6 +9,8 @@ import (
|
|||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/rancher/shepherd/clients/rancher/auth"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rancher/norman/httperror"
|
||||
frameworkDynamic "github.com/rancher/shepherd/clients/dynamic"
|
||||
|
|
@ -49,6 +51,8 @@ type Client struct {
|
|||
// CLI is the client used to interact with the Rancher CLI
|
||||
CLI *ranchercli.Client
|
||||
// Session is the session object used by the client to track all the resources being created by the client.
|
||||
Auth *auth.Client
|
||||
// Session is the session object used by the client to track all the resources being created by the client.
|
||||
Session *session.Session
|
||||
// Flags is the environment flags used by the client to test selectively against a rancher instance.
|
||||
Flags *environmentflag.EnvironmentFlags
|
||||
|
|
@ -136,6 +140,12 @@ func newClient(c *Client, bearerToken string, config *Config, session *session.S
|
|||
}
|
||||
|
||||
c.WranglerContext = wranglerContext
|
||||
auth, err := auth.NewClient(c.Management, session)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.Auth = auth
|
||||
|
||||
splitBearerKey := strings.Split(bearerToken, ":")
|
||||
token, err := c.Management.Token.ByID(splitBearerKey[0])
|
||||
|
|
@ -220,7 +230,7 @@ func (c *Client) doAction(endpoint, action string, body []byte, output interface
|
|||
// AsUser accepts a user object, and then creates a token for said `user`. Then it instantiates and returns a Client using the token created.
|
||||
// This function uses the login action, and user must have a correct username and password combination.
|
||||
func (c *Client) AsUser(user *management.User) (*Client, error) {
|
||||
returnedToken, err := c.login(user)
|
||||
returnedToken, err := c.login(user, auth.LocalAuth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -228,15 +238,15 @@ func (c *Client) AsUser(user *management.User) (*Client, error) {
|
|||
return NewClient(returnedToken.Token, c.Session)
|
||||
}
|
||||
|
||||
// AsUserForConfig accepts a Config and a user object, and then creates a token for said `user`. Then it instantiates and returns a Client using the token created.
|
||||
// AsAuthUser accepts a user object, and then creates a token for said `user`. Then it instantiates and returns a Client using the token created.
|
||||
// This function uses the login action, and user must have a correct username and password combination.
|
||||
func (c *Client) AsUserForConfig(rancherConfig *Config, user *management.User) (*Client, error) {
|
||||
returnedToken, err := c.login(user)
|
||||
func (c *Client) AsAuthUser(user *management.User, authProvider auth.Provider) (*Client, error) {
|
||||
returnedToken, err := c.login(user, authProvider)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewClientForConfig(returnedToken.Token, rancherConfig, c.Session)
|
||||
return NewClientForConfig(returnedToken.Token, c.RancherConfig, c.Session)
|
||||
}
|
||||
|
||||
// ReLogin reinstantiates a Client to update its API schema. This function would be used for a non admin user that needs to be
|
||||
|
|
@ -369,7 +379,7 @@ func (c *Client) GetManagementWatchInterface(schemaType string, opts metav1.List
|
|||
}
|
||||
|
||||
// login uses the local authentication provider to authenticate a user and return the subsequent token.
|
||||
func (c *Client) login(user *management.User) (*management.Token, error) {
|
||||
func (c *Client) login(user *management.User, provider auth.Provider) (*management.Token, error) {
|
||||
token := &management.Token{}
|
||||
bodyContent, err := json.Marshal(struct {
|
||||
Username string `json:"username"`
|
||||
|
|
@ -381,7 +391,8 @@ func (c *Client) login(user *management.User) (*management.Token, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = c.doAction("/v3-public/localProviders/local", "login", bodyContent, token)
|
||||
endpoint := fmt.Sprintf("/v3-public/%vProviders/%v", provider.String(), strings.ToLower(provider.String()))
|
||||
err = c.doAction(endpoint, "login", bodyContent, token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,6 +49,22 @@ func UserConfig() (user *management.User) {
|
|||
return
|
||||
}
|
||||
|
||||
// RefreshGroupMembership is helper function that sends a POST request to user action refresh auth provider access
|
||||
func RefreshGroupMembership(client *rancher.Client) error {
|
||||
endpoint := fmt.Sprintf("https://%v/v3/%v?action=%v", client.RancherConfig.Host, "users", "refreshauthprovideraccess")
|
||||
|
||||
var jsonResp map[string]any
|
||||
|
||||
bodyContent := []byte(`{}`)
|
||||
|
||||
err := client.Management.Ops.DoModify("POST", endpoint, &bodyContent, &jsonResp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateUserWithRole is helper function that creates a user with a role or multiple roles
|
||||
func CreateUserWithRole(rancherClient *rancher.Client, user *management.User, roles ...string) (*management.User, error) {
|
||||
createdUser, err := rancherClient.Management.User.Create(user)
|
||||
|
|
@ -76,9 +92,7 @@ func CreateUserWithRole(rancherClient *rancher.Client, user *management.User, ro
|
|||
// AddProjectMember is a helper function that adds a project role to `user`. It uses the watch.WatchWait to ensure BackingNamespaceCreated is true.
|
||||
// If a list of ResourceAttributes is given, then the function blocks until all
|
||||
// attributes are allowed by SelfSubjectAccessReviews OR the function times out.
|
||||
func AddProjectMember(rancherClient *rancher.Client, project *management.Project,
|
||||
user *management.User, projectRole string, attrs []*authzv1.ResourceAttributes,
|
||||
) error {
|
||||
func AddProjectMember(rancherClient *rancher.Client, project *management.Project, user *management.User, projectRole string, attrs []*authzv1.ResourceAttributes) error {
|
||||
role := &management.ProjectRoleTemplateBinding{
|
||||
ProjectID: project.ID,
|
||||
UserPrincipalID: user.PrincipalIDs[0],
|
||||
|
|
@ -186,9 +200,7 @@ func RemoveProjectMember(rancherClient *rancher.Client, user *management.User) e
|
|||
// AddClusterRoleToUser is a helper function that adds a cluster role to `user`.
|
||||
// If a list of ResourceAttributes is given, then the function blocks until all
|
||||
// attributes are allowed by SelfSubjectAccessReviews OR the function times out.
|
||||
func AddClusterRoleToUser(rancherClient *rancher.Client, cluster *management.Cluster,
|
||||
user *management.User, clusterRole string, attrs []*authzv1.ResourceAttributes,
|
||||
) error {
|
||||
func AddClusterRoleToUser(rancherClient *rancher.Client, cluster *management.Cluster, user *management.User, clusterRole string, attrs []*authzv1.ResourceAttributes) error {
|
||||
role := &management.ClusterRoleTemplateBinding{
|
||||
ClusterID: cluster.Resource.ID,
|
||||
UserPrincipalID: user.PrincipalIDs[0],
|
||||
|
|
|
|||
Loading…
Reference in New Issue