From 8c799e3a5eb4a9b96ec8bb4258d0476cfd043ddd Mon Sep 17 00:00:00 2001 From: Qiu Jian Date: Thu, 30 May 2019 19:07:45 +0800 Subject: [PATCH] fix: client auth needs project domain info --- cmd/climc/climc.go | 5 +++- cmd/openstackcli/main.go | 23 +++++++------- pkg/cloudcommon/app/auth.go | 7 +++-- pkg/cloudcommon/options/options.go | 1 + pkg/dns/dns.go | 2 +- pkg/keystone/models/domains.go | 2 +- pkg/keystone/models/identity_provider.go | 1 + pkg/keystone/models/projects.go | 38 +++++++++++++++++------- pkg/keystone/tokens/auth.go | 1 + pkg/mcclient/auth/auth.go | 22 +++++++------- pkg/mcclient/mcclient.go | 37 ++++++++++++----------- pkg/util/openstack/openstack.go | 24 ++++++++------- pkg/util/openstack/provider/provider.go | 4 +-- 13 files changed, 100 insertions(+), 67 deletions(-) diff --git a/cmd/climc/climc.go b/cmd/climc/climc.go index 578610dde0..c49640aafe 100644 --- a/cmd/climc/climc.go +++ b/cmd/climc/climc.go @@ -52,7 +52,9 @@ type BaseOptions struct { OsUsername string `default:"$OS_USERNAME" help:"Username, defaults to env[OS_USERNAME]"` OsPassword string `default:"$OS_PASSWORD" help:"Password, defaults to env[OS_PASSWORD]"` // OsProjectId string `default:"$OS_PROJECT_ID" help:"Proejct ID, defaults to env[OS_PROJECT_ID]"` - OsProjectName string `default:"$OS_PROJECT_NAME" help:"Project name, defaults to env[OS_PROJECT_NAME]"` + OsProjectName string `default:"$OS_PROJECT_NAME" help:"Project name, defaults to env[OS_PROJECT_NAME]"` + OsProjectDomain string `default:"$OS_PROJECT_DOMAIN" help:"Domain name of project, defaults to env[OS_PROJECT_DOMAIN]"` + OsDomainName string `default:"$OS_DOMAIN_NAME" help:"Domain name, defaults to env[OS_DOMAIN_NAME]"` OsAuthURL string `default:"$OS_AUTH_URL" help:"Defaults to env[OS_AUTH_URL]"` OsRegionName string `default:"$OS_REGION_NAME" help:"Defaults to env[OS_REGION_NAME]"` @@ -178,6 +180,7 @@ func newClientSession(options *BaseOptions) (*mcclient.ClientSession, error) { options.OsPassword, options.OsDomainName, options.OsProjectName, + options.OsProjectDomain, mcclient.AuthSourceCli) if err != nil { return nil, err diff --git a/cmd/openstackcli/main.go b/cmd/openstackcli/main.go index 6ef3d31b63..751ab6c5d4 100644 --- a/cmd/openstackcli/main.go +++ b/cmd/openstackcli/main.go @@ -27,16 +27,17 @@ import ( ) type BaseOptions struct { - Debug bool `help:"debug mode"` - Help bool `help:"Show help"` - AuthURL string `help:"Auth URL" default:"$OPENSTACK_AUTH_URL"` - Username string `help:"Username" default:"$OPENSTACK_USERNAME"` - Password string `help:"Password" default:"$OPENSTACK_PASSWORD"` - Project string `help:"Project" default:"$OPENSTACK_PROJECT"` - EndpointType string `help:"Project" default:"$OPENSTACK_ENDPOINT_TYPE|internal"` - DomainName string `help:"DomainName" default:"$OPENSTACK_DOMAIN_NAME"` - RegionID string `help:"RegionId" default:"$OPENSTACK_REGION_ID"` - SUBCOMMAND string `help:"openstackcli subcommand" subcommand:"true"` + Debug bool `help:"debug mode"` + Help bool `help:"Show help"` + AuthURL string `help:"Auth URL" default:"$OPENSTACK_AUTH_URL"` + Username string `help:"Username" default:"$OPENSTACK_USERNAME"` + Password string `help:"Password" default:"$OPENSTACK_PASSWORD"` + Project string `help:"Project" default:"$OPENSTACK_PROJECT"` + EndpointType string `help:"Project" default:"$OPENSTACK_ENDPOINT_TYPE|internal"` + DomainName string `help:"Domain of user" default:"$OPENSTACK_DOMAIN_NAME"` + ProjectDomain string `help:"Domain of project" default:"$OPENSTACK_PROJECT_DOMAIN"` + RegionID string `help:"RegionId" default:"$OPENSTACK_REGION_ID"` + SUBCOMMAND string `help:"openstackcli subcommand" subcommand:"true"` } func getSubcommandParser() (*structarg.ArgumentParser, error) { @@ -92,7 +93,7 @@ func newClient(options *BaseOptions) (*openstack.SRegion, error) { return nil, fmt.Errorf("Missing Password") } - cli, err := openstack.NewOpenStackClient("", "", options.AuthURL, options.Username, options.Password, options.Project, options.EndpointType, options.DomainName, options.Debug) + cli, err := openstack.NewOpenStackClient("", "", options.AuthURL, options.Username, options.Password, options.Project, options.EndpointType, options.DomainName, options.ProjectDomain, options.Debug) if err != nil { return nil, err } diff --git a/pkg/cloudcommon/app/auth.go b/pkg/cloudcommon/app/auth.go index d13f1d8c78..c642683b2a 100644 --- a/pkg/cloudcommon/app/auth.go +++ b/pkg/cloudcommon/app/auth.go @@ -48,11 +48,14 @@ func InitAuth(options *common_options.CommonOptions, authComplete auth.AuthCompl os.Exit(1) } - a := auth.NewAuthInfo(options.AuthURL, + a := auth.NewAuthInfo( + options.AuthURL, options.AdminDomain, options.AdminUser, options.AdminPassword, - options.AdminProject) + options.AdminProject, + options.AdminProjectDomain, + ) // debug := options.LogLevel == "debug" diff --git a/pkg/cloudcommon/options/options.go b/pkg/cloudcommon/options/options.go index 1c8d14f5b4..d6c1dc9528 100644 --- a/pkg/cloudcommon/options/options.go +++ b/pkg/cloudcommon/options/options.go @@ -74,6 +74,7 @@ type CommonOptions struct { AdminDomain string `help:"Admin user domain"` AdminPassword string `help:"Admin password" alias:"admin-passwd"` AdminProject string `help:"Admin project" default:"system" alias:"admin-tenant-name"` + AdminProjectDomain string `help:"Domain of Admin project"` AuthTokenCacheSize uint32 `help:"Auth token Cache Size" default:"2048"` BaseOptions diff --git a/pkg/dns/dns.go b/pkg/dns/dns.go index a854969d5d..4d8f20c9e2 100644 --- a/pkg/dns/dns.go +++ b/pkg/dns/dns.go @@ -125,7 +125,7 @@ func (r *SRegionDNS) getAdminSession(ctx context.Context) *mcclient.ClientSessio } func (r *SRegionDNS) initAuth() { - authInfo := auth.NewAuthInfo(r.AuthUrl, "", r.AdminUser, r.AdminPassword, r.AdminProject) + authInfo := auth.NewAuthInfo(r.AuthUrl, "", r.AdminUser, r.AdminPassword, r.AdminProject, "") auth.Init(authInfo, false, true, "", "") } diff --git a/pkg/keystone/models/domains.go b/pkg/keystone/models/domains.go index 3a214d361c..b16681128d 100644 --- a/pkg/keystone/models/domains.go +++ b/pkg/keystone/models/domains.go @@ -125,7 +125,7 @@ func (manager *SDomainManager) FetchDomainById(domainId string) (*SDomain, error if err != nil { return nil, err } - q := manager.Query().Equals("id", domainId).NotEquals("id", api.KeystoneDomainRoot) + q := manager.Query().Equals("id", domainId) err = q.First(obj) if err != nil { return nil, err diff --git a/pkg/keystone/models/identity_provider.go b/pkg/keystone/models/identity_provider.go index bd04ecf002..c790ca1c60 100644 --- a/pkg/keystone/models/identity_provider.go +++ b/pkg/keystone/models/identity_provider.go @@ -269,6 +269,7 @@ func (manager *SIdentityProviderManager) ValidateCreateData(ctx context.Context, return nil, httperrors.NewInputParameterError("invalid template") } drvName = api.IdpTemplateDriver[template] + data.Set("driver", jsonutils.NewString(drvName)) } else { drvName, _ = data.GetString("driver") if len(drvName) == 0 { diff --git a/pkg/keystone/models/projects.go b/pkg/keystone/models/projects.go index f5d379c6d5..16537820d7 100644 --- a/pkg/keystone/models/projects.go +++ b/pkg/keystone/models/projects.go @@ -126,18 +126,36 @@ func (manager *SProjectManager) Query(fields ...string) *sqlchemy.SQuery { func (manager *SProjectManager) FetchProjectByName(projectName string, domainId, domainName string) (*SProject, error) { obj, err := db.NewModelObject(manager) if err != nil { - return nil, err + return nil, errors.Wrap(err, "db.NewModelObject") } - domain, err := DomainManager.FetchDomain(domainId, domainName) - if err != nil { - return nil, err + if len(domainId) == 0 && len(domainName) == 0 { + q := manager.Query().Equals("name", projectName) + cnt, err := q.CountWithError() + if err != nil { + return nil, errors.Wrap(err, "CountWithError") + } + if cnt == 0 { + return nil, sql.ErrNoRows + } + if cnt > 1 { + return nil, sqlchemy.ErrDuplicateEntry + } + err = q.First(obj) + if err != nil { + return nil, errors.Wrap(err, "q.First") + } + } else { + domain, err := DomainManager.FetchDomain(domainId, domainName) + if err != nil { + return nil, errors.Wrap(err, "DomainManager.FetchDomain") + } + q := manager.Query().Equals("name", projectName).Equals("domain_id", domain.Id) + err = q.First(obj) + if err != nil { + return nil, errors.Wrap(err, "q.First") + } } - q := manager.Query().Equals("name", projectName).Equals("domain_id", domain.Id) - err = q.First(obj) - if err != nil { - return nil, err - } - return obj.(*SProject), err + return obj.(*SProject), nil } func (manager *SProjectManager) FetchProjectById(projectId string) (*SProject, error) { diff --git a/pkg/keystone/tokens/auth.go b/pkg/keystone/tokens/auth.go index d07e625b74..89ccb1fded 100644 --- a/pkg/keystone/tokens/auth.go +++ b/pkg/keystone/tokens/auth.go @@ -90,6 +90,7 @@ func authUserByIdentity(ctx context.Context, ident mcclient.SAuthenticationIdent if err != nil { return nil, errors.Wrap(err, "Query user") } + ident.Password.User.Domain.Id = usr.DomainId idmap, err := models.IdmappingManager.FetchEntity(usr.Id, api.IdMappingEntityUser) if err != nil && err != sql.ErrNoRows { return nil, errors.Wrap(err, "IdmappingManager.FetchEntity") diff --git a/pkg/mcclient/auth/auth.go b/pkg/mcclient/auth/auth.go index 8d678b8706..577e9cf6df 100644 --- a/pkg/mcclient/auth/auth.go +++ b/pkg/mcclient/auth/auth.go @@ -40,7 +40,8 @@ type AuthInfo struct { Username string Passwd string // Project is tenant when v2 auth - Project string + Project string + ProjectDomain string } func SetTimeout(t time.Duration) { @@ -48,16 +49,17 @@ func SetTimeout(t time.Duration) { } func NewV2AuthInfo(authUrl, user, passwd, tenant string) *AuthInfo { - return NewAuthInfo(authUrl, "", user, passwd, tenant) + return NewAuthInfo(authUrl, "", user, passwd, tenant, "") } -func NewAuthInfo(authUrl, domain, user, passwd, project string) *AuthInfo { +func NewAuthInfo(authUrl, domain, user, passwd, project, projectDomain string) *AuthInfo { return &AuthInfo{ - AuthUrl: authUrl, - Domain: domain, - Username: user, - Passwd: passwd, - Project: project, + AuthUrl: authUrl, + Domain: domain, + Username: user, + Passwd: passwd, + Project: project, + ProjectDomain: projectDomain, } } @@ -154,8 +156,8 @@ func (a *authManager) authAdmin() error { var token mcclient.TokenCredential var err error token, err = a.client.AuthenticateWithSource( - a.info.Username, a.info.Passwd, - a.info.Domain, a.info.Project, mcclient.AuthSourceSrv) + a.info.Username, a.info.Passwd, a.info.Domain, + a.info.Project, a.info.ProjectDomain, mcclient.AuthSourceSrv) if err != nil { log.Errorf("Admin auth failed: %s", err) return err diff --git a/pkg/mcclient/mcclient.go b/pkg/mcclient/mcclient.go index 402b98caf8..54f7ddb82e 100644 --- a/pkg/mcclient/mcclient.go +++ b/pkg/mcclient/mcclient.go @@ -127,7 +127,7 @@ func (this *Client) jsonRequest(ctx context.Context, endpoint string, token stri return httputils.JSONRequest(this.httpconn, ctx, method, joinUrl(endpoint, url), getDefaultHeader(header, token), body, this.debug) } -func (this *Client) _authV3(domainName, uname, passwd, projectId, projectName, token string, aCtx SAuthContext) (TokenCredential, error) { +func (this *Client) _authV3(domainName, uname, passwd, projectId, projectName, projectDomain, token string, aCtx SAuthContext) (TokenCredential, error) { input := SAuthenticationInputV3{} if len(uname) > 0 && len(passwd) > 0 { // Password authentication input.Auth.Identity.Methods = []string{api.AUTH_METHOD_PASSWORD} @@ -148,11 +148,12 @@ func (this *Client) _authV3(domainName, uname, passwd, projectId, projectName, t } if len(projectName) > 0 { input.Auth.Scope.Project.Name = projectName - if len(domainName) > 0 { + if len(projectDomain) > 0 { input.Auth.Scope.Project.Domain.Name = domainName - } else { - input.Auth.Scope.Project.Domain.Id = api.DEFAULT_DOMAIN_ID } + // else { + // input.Auth.Scope.Project.Domain.Id = api.DEFAULT_DOMAIN_ID + // } } input.Auth.Context = aCtx hdr, rbody, err := this.jsonRequest(context.Background(), this.authUrl, "", "POST", "/auth/tokens", nil, jsonutils.Marshal(&input)) @@ -189,32 +190,32 @@ func (this *Client) _authV2(uname, passwd, tenantId, tenantName, token string, a return this.unmarshalV2Token(rbody) } -func (this *Client) Authenticate(uname, passwd, domainName, tenantName string) (TokenCredential, error) { - return this.AuthenticateApi(uname, passwd, domainName, tenantName) +func (this *Client) Authenticate(uname, passwd, domainName, tenantName, tenantDomain string) (TokenCredential, error) { + return this.AuthenticateApi(uname, passwd, domainName, tenantName, tenantDomain) } -func (this *Client) AuthenticateApi(uname, passwd, domainName, tenantName string) (TokenCredential, error) { - return this.AuthenticateWithSource(uname, passwd, domainName, tenantName, AuthSourceAPI) +func (this *Client) AuthenticateApi(uname, passwd, domainName, tenantName, tenantDomain string) (TokenCredential, error) { + return this.AuthenticateWithSource(uname, passwd, domainName, tenantName, tenantDomain, AuthSourceAPI) } -func (this *Client) AuthenticateWeb(uname, passwd, domainName, tenantName string, cliIp string) (TokenCredential, error) { +func (this *Client) AuthenticateWeb(uname, passwd, domainName, tenantName, tenantDomain string, cliIp string) (TokenCredential, error) { aCtx := SAuthContext{ Source: AuthSourceWeb, Ip: cliIp, } - return this.authenticateWithContext(uname, passwd, domainName, tenantName, aCtx) + return this.authenticateWithContext(uname, passwd, domainName, tenantName, tenantDomain, aCtx) } -func (this *Client) AuthenticateWithSource(uname, passwd, domainName, tenantName string, source string) (TokenCredential, error) { +func (this *Client) AuthenticateWithSource(uname, passwd, domainName, tenantName, tenantDomain string, source string) (TokenCredential, error) { aCtx := SAuthContext{ Source: source, } - return this.authenticateWithContext(uname, passwd, domainName, tenantName, aCtx) + return this.authenticateWithContext(uname, passwd, domainName, tenantName, tenantDomain, aCtx) } -func (this *Client) authenticateWithContext(uname, passwd, domainName, tenantName string, aCtx SAuthContext) (TokenCredential, error) { +func (this *Client) authenticateWithContext(uname, passwd, domainName, tenantName, tenantDomain string, aCtx SAuthContext) (TokenCredential, error) { if this.AuthVersion() == "v3" { - return this._authV3(domainName, uname, passwd, "", tenantName, "", aCtx) + return this._authV3(domainName, uname, passwd, "", tenantName, tenantDomain, "", aCtx) } return this._authV2(uname, passwd, "", tenantName, "", aCtx) } @@ -281,17 +282,17 @@ func (this *Client) Verify(adminToken, token string) (cred TokenCredential, err return this.verifyV2(adminToken, token) } -func (this *Client) SetTenant(tenantId, tenantName string, token TokenCredential) (TokenCredential, error) { - return this.SetProject(tenantId, tenantName, token) +func (this *Client) SetTenant(tenantId, tenantName, tenantDomain string, token TokenCredential) (TokenCredential, error) { + return this.SetProject(tenantId, tenantName, tenantDomain, token) } -func (this *Client) SetProject(tenantId, tenantName string, token TokenCredential) (TokenCredential, error) { +func (this *Client) SetProject(tenantId, tenantName, tenantDomain string, token TokenCredential) (TokenCredential, error) { aCtx := SAuthContext{ Source: token.GetLoginSource(), Ip: token.GetLoginIp(), } if this.AuthVersion() == "v3" { - return this._authV3("", "", "", tenantId, tenantName, token.GetTokenString(), aCtx) + return this._authV3("", "", "", tenantId, tenantName, tenantDomain, token.GetTokenString(), aCtx) } else { return this._authV2("", "", "", tenantName, token.GetTokenString(), aCtx) } diff --git a/pkg/util/openstack/openstack.go b/pkg/util/openstack/openstack.go index c69a382684..6c3d1f4b2e 100644 --- a/pkg/util/openstack/openstack.go +++ b/pkg/util/openstack/openstack.go @@ -44,6 +44,7 @@ type SOpenStackClient struct { username string password string project string + projectDomain string endpointType string domainName string client *mcclient.Client @@ -53,17 +54,18 @@ type SOpenStackClient struct { Debug bool } -func NewOpenStackClient(providerID string, providerName string, authURL string, username string, password string, project string, endpointType string, domainName string, isDebug bool) (*SOpenStackClient, error) { +func NewOpenStackClient(providerID string, providerName string, authURL string, username string, password string, project string, endpointType string, domainName string, projectDomainName string, isDebug bool) (*SOpenStackClient, error) { cli := &SOpenStackClient{ - providerID: providerID, - providerName: providerName, - authURL: strings.TrimRight(authURL, "/"), - username: username, - password: password, - project: project, - endpointType: endpointType, - domainName: domainName, - Debug: isDebug, + providerID: providerID, + providerName: providerName, + authURL: strings.TrimRight(authURL, "/"), + username: username, + password: password, + project: project, + projectDomain: projectDomainName, + endpointType: endpointType, + domainName: domainName, + Debug: isDebug, } return cli, cli.fetchRegions() } @@ -192,7 +194,7 @@ func (cli *SOpenStackClient) getVersion(region string, service string) (string, func (cli *SOpenStackClient) connect() error { cli.client = mcclient.NewClient(cli.authURL, 5, cli.Debug, false, "", "") - tokenCredential, err := cli.client.Authenticate(cli.username, cli.password, cli.domainName, cli.project) + tokenCredential, err := cli.client.Authenticate(cli.username, cli.password, cli.domainName, cli.project, cli.projectDomain) if err != nil { return err } diff --git a/pkg/util/openstack/provider/provider.go b/pkg/util/openstack/provider/provider.go index 93daa8034a..2d7a0070bf 100644 --- a/pkg/util/openstack/provider/provider.go +++ b/pkg/util/openstack/provider/provider.go @@ -112,11 +112,11 @@ func (self *SOpenStackProviderFactory) GetProvider(providerId, providerName, url if len(accountInfo) < 2 { return nil, fmt.Errorf("Missing username or project name %s", account) } - project, username, endpointType, domainName := accountInfo[0], accountInfo[1], "internal", "" + project, username, endpointType, domainName, projectDomainName := accountInfo[0], accountInfo[1], "internal", "", "" if len(accountInfo) == 3 { domainName = accountInfo[2] } - client, err := openstack.NewOpenStackClient(providerId, providerName, url, username, password, project, endpointType, domainName, false) + client, err := openstack.NewOpenStackClient(providerId, providerName, url, username, password, project, endpointType, domainName, projectDomainName, false) if err != nil { return nil, err }