| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660 |
- // Copyright 2019 Yunion
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package tokens
- import (
- "context"
- "database/sql"
- "time"
- "yunion.io/x/jsonutils"
- "yunion.io/x/log"
- "yunion.io/x/pkg/errors"
- "yunion.io/x/pkg/util/s3auth"
- "yunion.io/x/sqlchemy"
- api "yunion.io/x/onecloud/pkg/apis/identity"
- notify_api "yunion.io/x/onecloud/pkg/apis/notify"
- "yunion.io/x/onecloud/pkg/httperrors"
- "yunion.io/x/onecloud/pkg/keystone/driver"
- "yunion.io/x/onecloud/pkg/keystone/models"
- "yunion.io/x/onecloud/pkg/keystone/options"
- "yunion.io/x/onecloud/pkg/keystone/saml"
- "yunion.io/x/onecloud/pkg/mcclient"
- notify_modules "yunion.io/x/onecloud/pkg/mcclient/modules/notify"
- "yunion.io/x/onecloud/pkg/util/logclient"
- )
- func authUserByTokenV2(ctx context.Context, input mcclient.SAuthenticationInputV2) (*api.SUserExtended, error) {
- return authUserByToken(ctx, input.Auth.Token.Id)
- }
- func authUserByTokenV3(ctx context.Context, input mcclient.SAuthenticationInputV3) (*api.SUserExtended, error) {
- return authUserByToken(ctx, input.Auth.Identity.Token.Id)
- }
- func authUserByToken(ctx context.Context, tokenStr string) (*api.SUserExtended, error) {
- token, err := TokenStrDecode(ctx, tokenStr)
- if err != nil {
- return nil, errors.Wrap(err, "token.TokenStrDecode")
- }
- extUser, err := models.UserManager.FetchUserExtended(token.UserId, "", "", "")
- if err != nil {
- return nil, errors.Wrap(err, "FetchUserExtended")
- }
- extUser.AuditIds = []string{tokenStr}
- return extUser, nil
- }
- func authUserByPasswordV2(ctx context.Context, input mcclient.SAuthenticationInputV2) (*api.SUserExtended, error) {
- ident := mcclient.SAuthenticationIdentity{}
- ident.Methods = []string{api.AUTH_METHOD_PASSWORD}
- ident.Password.User.Name = input.Auth.PasswordCredentials.Username
- ident.Password.User.Password = input.Auth.PasswordCredentials.Password
- ident.Password.User.Domain.Id = api.DEFAULT_DOMAIN_ID
- return authUserByIdentity(ctx, ident, input.Auth.Context)
- }
- func authUserByIdentityV3(ctx context.Context, input mcclient.SAuthenticationInputV3) (*api.SUserExtended, error) {
- return authUserByIdentity(ctx, input.Auth.Identity, input.Auth.Context)
- }
- func authUserByIdentity(ctx context.Context, ident mcclient.SAuthenticationIdentity, authCtx mcclient.SAuthContext) (*api.SUserExtended, error) {
- usr, err := authUserByIdentityInternal(ctx, &ident)
- if err != nil {
- // log event
- ident.Password.User.Password = "***"
- log.Errorf("authenticate fail for %s reason: %s", jsonutils.Marshal(ident), err)
- user := logclient.NewSimpleObject(ident.Password.User.Id, ident.Password.User.Name, "user")
- token := GetDefaultAdminCredToken()
- token.(*mcclient.SSimpleToken).Context = authCtx
- logclient.AddActionLogWithContext(ctx, user, logclient.ACT_AUTHENTICATE, err, token, false)
- }
- return usr, err
- }
- func authUserByIdentityInternal(ctx context.Context, ident *mcclient.SAuthenticationIdentity) (*api.SUserExtended, error) {
- var idpId string
- if len(ident.Password.User.Name) == 0 && len(ident.Password.User.Id) == 0 && len(ident.Password.User.Domain.Id) == 0 && len(ident.Password.User.Domain.Name) == 0 {
- return nil, ErrEmptyAuth
- }
- if len(ident.Password.User.Name) > 0 && len(ident.Password.User.Id) == 0 && len(ident.Password.User.Domain.Id) == 0 && len(ident.Password.User.Domain.Name) == 0 {
- // no user domain specified, try to find user domain
- q := models.UserManager.Query().Equals("name", ident.Password.User.Name)
- usrCnt, err := q.CountWithError()
- if err != nil {
- return nil, errors.Wrap(err, "Query user by name")
- }
- if usrCnt > 1 {
- log.Errorf("find %d user with name %s", usrCnt, ident.Password.User.Name)
- return nil, sqlchemy.ErrDuplicateEntry
- } else if usrCnt == 0 {
- log.Errorf("find no user with name %s", ident.Password.User.Name)
- return nil, httperrors.ErrUserNotFound
- } else {
- // userCnt == 1
- usr := models.SUser{}
- usr.SetModelManager(models.UserManager, &usr)
- err := q.First(&usr)
- if err != nil {
- return nil, errors.Wrap(err, "Query user")
- }
- ident.Password.User.Domain.Id = usr.DomainId
- ident.Password.User.Id = usr.Id
- }
- }
- usrExt, err := models.UserManager.FetchUserExtended(ident.Password.User.Id, ident.Password.User.Name,
- ident.Password.User.Domain.Id, ident.Password.User.Domain.Name)
- if err != nil && errors.Cause(err) != httperrors.ErrUserNotFound {
- return nil, errors.Wrap(err, "UserManager.FetchUserExtended")
- }
- if err != nil {
- log.Errorf("no such user %s locally, query external IDP: %s", ident.Password.User.Name, err)
- // no such user locally, query domain idp
- domain, err := models.DomainManager.FetchDomain(ident.Password.User.Domain.Id, ident.Password.User.Domain.Name)
- if err != nil {
- if errors.Cause(err) != sql.ErrNoRows {
- return nil, errors.Wrap(err, "DomainManager.FetchDomain")
- } else {
- return nil, errors.Wrapf(httperrors.ErrUserNotFound, "domain %s", ident.Password.User.Domain.Name)
- }
- }
- mapping, err := models.IdmappingManager.FetchFirstEntity(domain.Id, api.IdMappingEntityDomain)
- if err != nil {
- if errors.Cause(err) != sql.ErrNoRows {
- return nil, errors.Wrap(err, "IdmappingManager.FetchEntity")
- } else {
- return nil, errors.Wrapf(httperrors.ErrUserNotFound, "idp")
- }
- }
- idpId = mapping.IdpId
- } else {
- // check enable
- if !usrExt.Enabled {
- if usrExt.IsLocal && usrExt.LocalFailedAuthCount > options.Options.PasswordErrorLockCount {
- // user locked
- return nil, httperrors.ErrUserLocked
- }
- // user disabled
- return nil, httperrors.ErrUserDisabled
- }
- // user is enabled, check expired time
- if !usrExt.ExpiredAt.IsZero() && usrExt.ExpiredAt.Before(time.Now()) {
- return nil, httperrors.ErrUserExpired
- }
- // user exists, query user's idp
- idps, err := models.IdentityProviderManager.FetchIdentityProvidersByUserId(usrExt.Id, api.PASSWORD_PROTECTED_IDPS)
- if err != nil {
- return nil, errors.Wrap(err, "IdentityProviderManager.FetchIdentityProvidersByUserId")
- }
- if len(idps) == 0 {
- idpId = api.DEFAULT_IDP_ID
- } else if len(idps) == 1 {
- idpId = idps[0].Id
- } else {
- return nil, sqlchemy.ErrDuplicateEntry
- }
- }
- if len(idpId) == 0 {
- idpId = api.DEFAULT_IDP_ID
- }
- idpObj, err := models.IdentityProviderManager.FetchById(idpId)
- if err != nil {
- return nil, errors.Wrap(err, "IdentityProviderManager.FetchById")
- }
- idp := idpObj.(*models.SIdentityProvider)
- if idp.Enabled.IsFalse() {
- return nil, errors.Wrap(httperrors.ErrInvalidIdpStatus, "idp disabled")
- }
- if idp.Status != api.IdentityDriverStatusConnected && idp.Status != api.IdentityDriverStatusDisconnected {
- return nil, errors.Wrapf(httperrors.ErrInvalidIdpStatus, "invalid idp status %s", idp.Status)
- }
- conf, err := models.GetConfigs(idp, true, nil, nil)
- if err != nil {
- return nil, errors.Wrap(err, "GetConfig")
- }
- backend, err := driver.GetDriver(idp.Driver, idp.Id, idp.Name, idp.Template, idp.TargetDomainId, conf)
- if err != nil {
- return nil, errors.Wrap(err, "driver.GetDriver")
- }
- usr, err := backend.Authenticate(ctx, *ident)
- if err != nil {
- return nil, errors.Wrap(err, "Authenticate")
- }
- if idp.Status == api.IdentityDriverStatusDisconnected {
- idp.MarkConnected(ctx, models.GetDefaultAdminCred())
- }
- return usr, nil
- }
- func authUserByCASV3(ctx context.Context, input mcclient.SAuthenticationInputV3) (*api.SUserExtended, error) {
- var idp *models.SIdentityProvider
- var err error
- if len(input.Auth.Identity.Id) > 0 {
- idp, err = models.IdentityProviderManager.FetchIdentityProviderById(input.Auth.Identity.Id)
- if err != nil {
- if errors.Cause(err) == sql.ErrNoRows {
- return nil, errors.Wrapf(httperrors.ErrResourceNotFound, "idp %s not found", input.Auth.Identity.Id)
- } else {
- return nil, errors.Wrap(err, "FetchIdentityProviderById")
- }
- }
- } else {
- idps, err := models.IdentityProviderManager.FetchEnabledProviders(api.IdentityDriverCAS)
- if err != nil {
- return nil, errors.Wrap(err, "models.fetchEnabledProviders")
- }
- if len(idps) == 0 {
- return nil, errors.Error("No cas identity provider")
- }
- if len(idps) > 1 {
- return nil, errors.Error("more than 1 cas identity providers?")
- }
- idp = &idps[0]
- }
- conf, err := models.GetConfigs(idp, true, nil, nil)
- if err != nil {
- return nil, errors.Wrap(err, "idp.GetConfig")
- }
- backend, err := driver.GetDriver(idp.Driver, idp.Id, idp.Name, idp.Template, idp.TargetDomainId, conf)
- if err != nil {
- return nil, errors.Wrap(err, "driver.GetDriver")
- }
- usr, err := backend.Authenticate(ctx, input.Auth.Identity)
- if err != nil {
- return nil, errors.Wrap(err, "Authenticate")
- }
- if idp.Status == api.IdentityDriverStatusDisconnected {
- idp.MarkConnected(ctx, models.GetDefaultAdminCred())
- }
- return usr, nil
- }
- func authUserBySAML(ctx context.Context, input mcclient.SAuthenticationInputV3) (*api.SUserExtended, error) {
- if !saml.IsSAMLEnabled() {
- return nil, errors.Wrap(httperrors.ErrNotSupported, "unsupported SAML backend")
- }
- idp, err := models.IdentityProviderManager.FetchIdentityProviderById(input.Auth.Identity.Id)
- if err != nil {
- if errors.Cause(err) == sql.ErrNoRows {
- return nil, errors.Wrapf(httperrors.ErrResourceNotFound, "idp %s not found", input.Auth.Identity.Id)
- } else {
- return nil, errors.Wrap(err, "FetchIdentityProviderById")
- }
- }
- conf, err := models.GetConfigs(idp, true, nil, nil)
- if err != nil {
- return nil, errors.Wrap(err, "idp.GetConfig")
- }
- backend, err := driver.GetDriver(idp.Driver, idp.Id, idp.Name, idp.Template, idp.TargetDomainId, conf)
- if err != nil {
- return nil, errors.Wrap(err, "driver.GetDriver")
- }
- usr, err := backend.Authenticate(ctx, input.Auth.Identity)
- if err != nil {
- return nil, errors.Wrap(err, "Authenticate")
- }
- if idp.Status == api.IdentityDriverStatusDisconnected {
- idp.MarkConnected(ctx, models.GetDefaultAdminCred())
- }
- return usr, nil
- }
- func authUserByOIDC(ctx context.Context, input mcclient.SAuthenticationInputV3) (*api.SUserExtended, error) {
- idp, err := models.IdentityProviderManager.FetchIdentityProviderById(input.Auth.Identity.Id)
- if err != nil {
- if errors.Cause(err) == sql.ErrNoRows {
- return nil, errors.Wrapf(httperrors.ErrResourceNotFound, "idp %s not found", input.Auth.Identity.Id)
- } else {
- return nil, errors.Wrap(err, "FetchIdentityProviderById")
- }
- }
- conf, err := models.GetConfigs(idp, true, nil, nil)
- if err != nil {
- return nil, errors.Wrap(err, "idp.GetConfig")
- }
- backend, err := driver.GetDriver(idp.Driver, idp.Id, idp.Name, idp.Template, idp.TargetDomainId, conf)
- if err != nil {
- return nil, errors.Wrap(err, "driver.GetDriver")
- }
- usr, err := backend.Authenticate(ctx, input.Auth.Identity)
- if err != nil {
- return nil, errors.Wrap(err, "Authenticate")
- }
- if idp.Status == api.IdentityDriverStatusDisconnected {
- idp.MarkConnected(ctx, models.GetDefaultAdminCred())
- }
- return usr, nil
- }
- func authUserByOAuth2(ctx context.Context, input mcclient.SAuthenticationInputV3) (*api.SUserExtended, error) {
- idp, err := models.IdentityProviderManager.FetchIdentityProviderById(input.Auth.Identity.Id)
- if err != nil {
- if errors.Cause(err) == sql.ErrNoRows {
- return nil, errors.Wrapf(httperrors.ErrResourceNotFound, "idp %s not found", input.Auth.Identity.Id)
- } else {
- return nil, errors.Wrap(err, "FetchIdentityProviderById")
- }
- }
- conf, err := models.GetConfigs(idp, true, nil, nil)
- if err != nil {
- return nil, errors.Wrap(err, "idp.GetConfig")
- }
- backend, err := driver.GetDriver(idp.Driver, idp.Id, idp.Name, idp.Template, idp.TargetDomainId, conf)
- if err != nil {
- return nil, errors.Wrap(err, "driver.GetDriver")
- }
- usr, err := backend.Authenticate(ctx, input.Auth.Identity)
- if err != nil {
- return nil, errors.Wrap(err, "Authenticate")
- }
- if idp.Status == api.IdentityDriverStatusDisconnected {
- idp.MarkConnected(ctx, models.GetDefaultAdminCred())
- }
- return usr, nil
- }
- func authUserByAccessKeyV3(ctx context.Context, input mcclient.SAuthenticationInputV3) (*api.SUserExtended, string, api.SAccessKeySecretInfo, error) {
- var aksk api.SAccessKeySecretInfo
- akskRequest, err := s3auth.Decode(input.Auth.Identity.AccessKeyRequest)
- if err != nil {
- return nil, "", aksk, errors.Wrap(err, "s3auth.Decode")
- }
- keyId := akskRequest.GetAccessKey()
- obj, err := models.CredentialManager.FetchById(keyId)
- if err != nil {
- if err == sql.ErrNoRows {
- return nil, "", aksk, ErrInvalidAccessKeyId
- } else {
- return nil, "", aksk, errors.Wrap(err, "CredentialManager.FetchById")
- }
- }
- credential := obj.(*models.SCredential)
- if !credential.Enabled.IsTrue() {
- return nil, "", aksk, errors.Wrap(httperrors.ErrInvalidStatus, "Access Key disabled")
- }
- akBlob, err := credential.GetAccessKeySecret()
- if err != nil {
- return nil, "", aksk, errors.Wrap(err, "credential.GetAccessKeySecret")
- }
- if !akBlob.IsValid() {
- return nil, "", aksk, ErrExpiredAccessKey
- }
- aksk.AccessKey = keyId
- aksk.Secret = akBlob.Secret
- aksk.Expire = akBlob.Expire
- err = akskRequest.Verify(akBlob.Secret)
- if err != nil {
- return nil, "", aksk, errors.Wrap(err, "Verify")
- }
- usrExt, err := models.UserManager.FetchUserExtended(credential.UserId, "", "", "")
- if err != nil {
- return nil, "", aksk, errors.Wrap(err, "UserManager.FetchUserExtended")
- }
- usrExt.AuditIds = []string{keyId}
- return usrExt, credential.ProjectId, aksk, nil
- }
- func authUserByVerify(ctx context.Context, input mcclient.SAuthenticationInputV3) (*api.SUserExtended, error) {
- extUser, err := models.UserManager.FetchUserExtended(input.Auth.Identity.Verify.Uid, "", "", "")
- if err != nil {
- return nil, errors.Wrap(err, "FetchUserExtended")
- }
- s, err := GetDefaulAdminSession(ctx, "")
- if err != nil {
- return nil, errors.Wrap(err, "GetDefaultAdminSession")
- }
- verifyInput := notify_api.ReceiverVerifyInput{
- ContactType: input.Auth.Identity.Verify.ContactType,
- Token: input.Auth.Identity.Verify.VerifyCode,
- }
- _, err = notify_modules.NotifyReceiver.PerformAction(s, extUser.Id, "verify", jsonutils.Marshal(verifyInput))
- if err != nil {
- return nil, errors.Wrap(err, "Verify")
- }
- extUser.AuditIds = []string{input.Auth.Identity.Verify.Uid}
- return extUser, nil
- }
- // +onecloud:swagger-gen-route-method=POST
- // +onecloud:swagger-gen-route-path=/v3/auth/tokens
- // +onecloud:swagger-gen-route-tag=authentication
- // +onecloud:swagger-gen-param-body-index=1
- // +onecloud:swagger-gen-resp-index=0
- // +onecloud:swagger-gen-resp-header=X-Subject-Token
- // +onecloud:swagger-gen-resp-header=验证成功的keystone V3 token
- // keystone v3认证API
- func AuthenticateV3(ctx context.Context, input mcclient.SAuthenticationInputV3) (*mcclient.TokenCredentialV3, error) {
- var akskInfo api.SAccessKeySecretInfo
- var user *api.SUserExtended
- var err error
- if len(input.Auth.Identity.Methods) != 1 {
- return nil, ErrInvalidAuthMethod
- }
- method := input.Auth.Identity.Methods[0]
- switch method {
- case api.AUTH_METHOD_TOKEN:
- // auth by token
- user, err = authUserByTokenV3(ctx, input)
- if err != nil {
- return nil, errors.Wrap(err, "authUserByTokenV3")
- }
- case api.AUTH_METHOD_AKSK:
- // auth by aksk
- user, input.Auth.Scope.Project.Id, akskInfo, err = authUserByAccessKeyV3(ctx, input)
- if err != nil {
- return nil, errors.Wrap(err, "authUserByAccessKeyV3")
- }
- case api.AUTH_METHOD_CAS:
- // auth by apereo CAS
- user, err = authUserByCASV3(ctx, input)
- if err != nil {
- return nil, errors.Wrap(err, "authUserByCASV3")
- }
- case api.AUTH_METHOD_SAML:
- // auth by SAML 2.0 IDP, keystone acts as a SAML SP
- user, err = authUserBySAML(ctx, input)
- if err != nil {
- return nil, errors.Wrap(err, "authUserBySAML")
- }
- case api.AUTH_METHOD_OIDC:
- // auth by OpenID Connect, keystone acts as an OpenID Connect client
- user, err = authUserByOIDC(ctx, input)
- if err != nil {
- return nil, errors.Wrap(err, "authUserByOIDC")
- }
- case api.AUTH_METHOD_OAuth2:
- // auth by customized OAuth2.0 provider, keystone acts as an OAuth2.0 app
- user, err = authUserByOAuth2(ctx, input)
- if err != nil {
- return nil, errors.Wrap(err, "authUserByOAuth2")
- }
- case api.AUTH_METHOD_VERIFY:
- user, err = authUserByVerify(ctx, input)
- if err != nil {
- return nil, errors.Wrap(err, "authUserByVerify")
- }
- case api.AUTH_METHOD_ASSUME:
- user, err = authUserByAssume(ctx, input)
- if err != nil {
- return nil, errors.Wrap(err, "authUserByAssume")
- }
- default:
- // auth by other methods, e.g. password , etc...
- user, err = authUserByIdentityV3(ctx, input)
- if err != nil {
- return nil, err
- }
- }
- // user not found
- if user == nil {
- return nil, ErrUserNotFound
- }
- // user is not enabled
- if !user.Enabled {
- return nil, ErrUserDisabled
- }
- // user is expired
- if !user.ExpiredAt.IsZero() && user.ExpiredAt.Before(time.Now()) {
- return nil, httperrors.ErrUserExpired
- }
- if !user.DomainEnabled {
- return nil, ErrDomainDisabled
- }
- token := SAuthToken{}
- token.UserId = user.Id
- token.Method = method
- token.AuditIds = user.AuditIds
- now := time.Now().UTC()
- token.ExpiresAt = now.Add(time.Duration(options.Options.TokenExpirationSeconds) * time.Second)
- if !user.ExpiredAt.IsZero() && user.ExpiredAt.Before(token.ExpiresAt) {
- token.ExpiresAt = user.ExpiredAt
- }
- token.Context = input.Auth.Context
- if len(input.Auth.Scope.Project.Id) == 0 && len(input.Auth.Scope.Project.Name) == 0 && len(input.Auth.Scope.Domain.Id) == 0 && len(input.Auth.Scope.Domain.Name) == 0 {
- // unscoped auth
- return token.getTokenV3(ctx, user, nil, nil, akskInfo)
- }
- var projExt *models.SProjectExtended
- var domain *models.SDomain
- if len(input.Auth.Scope.Project.Id) > 0 || len(input.Auth.Scope.Project.Name) > 0 {
- project, err := models.ProjectManager.FetchProject(
- input.Auth.Scope.Project.Id,
- input.Auth.Scope.Project.Name,
- input.Auth.Scope.Project.Domain.Id,
- input.Auth.Scope.Project.Domain.Name,
- )
- if err != nil {
- return nil, errors.Wrap(err, "ProjectManager.FetchProject")
- }
- // if project.Enabled.IsFalse() {
- // return nil, ErrProjectDisabled
- // }
- projExt, err = project.FetchExtend()
- if err != nil {
- return nil, errors.Wrap(err, "project.FetchExtend")
- }
- token.ProjectId = project.Id
- } else {
- domain, err = models.DomainManager.FetchDomain(input.Auth.Scope.Domain.Id,
- input.Auth.Scope.Domain.Name)
- if err != nil {
- return nil, errors.Wrap(err, "DomainManager.FetchDomain")
- }
- if domain.Enabled.IsFalse() {
- return nil, ErrDomainDisabled
- }
- token.DomainId = domain.Id
- }
- tokenV3, err := token.getTokenV3(ctx, user, projExt, domain, akskInfo)
- if err != nil {
- return nil, errors.Wrap(err, "getTokenV3")
- }
- return tokenV3, nil
- }
- type SAuthenticateV2ResponseBody struct {
- Access mcclient.TokenCredentialV2 `json:"access"`
- }
- // +onecloud:swagger-gen-route-method=POST
- // +onecloud:swagger-gen-route-path=/v2.0/tokens
- // +onecloud:swagger-gen-route-tag=authentication
- // +onecloud:swagger-gen-param-body-index=1
- // +onecloud:swagger-gen-resp-index=0
- // keystone v2 认证接口,通过用户名/密码或者 token 认证
- func AuthenticateV2(ctx context.Context, input mcclient.SAuthenticationInputV2) (*SAuthenticateV2ResponseBody, error) {
- token, err := _authenticateV2(ctx, input)
- if err != nil {
- return nil, errors.Wrap(err, "_authenticateV2")
- }
- body := SAuthenticateV2ResponseBody{
- Access: *token,
- }
- return &body, nil
- }
- func _authenticateV2(ctx context.Context, input mcclient.SAuthenticationInputV2) (*mcclient.TokenCredentialV2, error) {
- var user *api.SUserExtended
- var err error
- var method string
- if len(input.Auth.Token.Id) > 0 {
- // auth by token
- user, err = authUserByTokenV2(ctx, input)
- if err != nil {
- return nil, errors.Wrap(err, "authUserByTokenV2")
- }
- method = api.AUTH_METHOD_TOKEN
- } else {
- // auth by password
- user, err = authUserByPasswordV2(ctx, input)
- if err != nil {
- return nil, errors.Wrap(err, "authUserByPasswordV2")
- }
- method = api.AUTH_METHOD_PASSWORD
- }
- // user not found
- if user == nil {
- return nil, ErrUserNotFound
- }
- // user is not enabled
- if !user.Enabled {
- return nil, ErrUserDisabled
- }
- if !user.DomainEnabled {
- return nil, ErrDomainDisabled
- }
- token := SAuthToken{}
- token.UserId = user.Id
- token.Method = method
- token.AuditIds = user.AuditIds
- now := time.Now().UTC()
- token.ExpiresAt = now.Add(time.Duration(options.Options.TokenExpirationSeconds) * time.Second)
- token.Context = input.Auth.Context
- if len(input.Auth.TenantId) == 0 && len(input.Auth.TenantName) == 0 {
- // unscoped auth
- return token.getTokenV2(ctx, user, nil)
- }
- project, err := models.ProjectManager.FetchProject(
- input.Auth.TenantId,
- input.Auth.TenantName,
- api.DEFAULT_DOMAIN_ID, "")
- if err != nil {
- return nil, errors.Wrap(err, "ProjectManager.FetchProject")
- }
- // if project.Enabled.IsFalse() {
- // return nil, ErrProjectDisabled
- // }
- token.ProjectId = project.Id
- projExt, err := project.FetchExtend()
- if err != nil {
- return nil, errors.Wrap(err, "project.FetchExtend")
- }
- tokenV2, err := token.getTokenV2(ctx, user, projExt)
- if err != nil {
- return nil, errors.Wrap(err, "getTokenV2")
- }
- return tokenV2, nil
- }
|