auth.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package tokens
  15. import (
  16. "context"
  17. "database/sql"
  18. "time"
  19. "yunion.io/x/jsonutils"
  20. "yunion.io/x/log"
  21. "yunion.io/x/pkg/errors"
  22. "yunion.io/x/pkg/util/s3auth"
  23. "yunion.io/x/sqlchemy"
  24. api "yunion.io/x/onecloud/pkg/apis/identity"
  25. notify_api "yunion.io/x/onecloud/pkg/apis/notify"
  26. "yunion.io/x/onecloud/pkg/httperrors"
  27. "yunion.io/x/onecloud/pkg/keystone/driver"
  28. "yunion.io/x/onecloud/pkg/keystone/models"
  29. "yunion.io/x/onecloud/pkg/keystone/options"
  30. "yunion.io/x/onecloud/pkg/keystone/saml"
  31. "yunion.io/x/onecloud/pkg/mcclient"
  32. notify_modules "yunion.io/x/onecloud/pkg/mcclient/modules/notify"
  33. "yunion.io/x/onecloud/pkg/util/logclient"
  34. )
  35. func authUserByTokenV2(ctx context.Context, input mcclient.SAuthenticationInputV2) (*api.SUserExtended, error) {
  36. return authUserByToken(ctx, input.Auth.Token.Id)
  37. }
  38. func authUserByTokenV3(ctx context.Context, input mcclient.SAuthenticationInputV3) (*api.SUserExtended, error) {
  39. return authUserByToken(ctx, input.Auth.Identity.Token.Id)
  40. }
  41. func authUserByToken(ctx context.Context, tokenStr string) (*api.SUserExtended, error) {
  42. token, err := TokenStrDecode(ctx, tokenStr)
  43. if err != nil {
  44. return nil, errors.Wrap(err, "token.TokenStrDecode")
  45. }
  46. extUser, err := models.UserManager.FetchUserExtended(token.UserId, "", "", "")
  47. if err != nil {
  48. return nil, errors.Wrap(err, "FetchUserExtended")
  49. }
  50. extUser.AuditIds = []string{tokenStr}
  51. return extUser, nil
  52. }
  53. func authUserByPasswordV2(ctx context.Context, input mcclient.SAuthenticationInputV2) (*api.SUserExtended, error) {
  54. ident := mcclient.SAuthenticationIdentity{}
  55. ident.Methods = []string{api.AUTH_METHOD_PASSWORD}
  56. ident.Password.User.Name = input.Auth.PasswordCredentials.Username
  57. ident.Password.User.Password = input.Auth.PasswordCredentials.Password
  58. ident.Password.User.Domain.Id = api.DEFAULT_DOMAIN_ID
  59. return authUserByIdentity(ctx, ident, input.Auth.Context)
  60. }
  61. func authUserByIdentityV3(ctx context.Context, input mcclient.SAuthenticationInputV3) (*api.SUserExtended, error) {
  62. return authUserByIdentity(ctx, input.Auth.Identity, input.Auth.Context)
  63. }
  64. func authUserByIdentity(ctx context.Context, ident mcclient.SAuthenticationIdentity, authCtx mcclient.SAuthContext) (*api.SUserExtended, error) {
  65. usr, err := authUserByIdentityInternal(ctx, &ident)
  66. if err != nil {
  67. // log event
  68. ident.Password.User.Password = "***"
  69. log.Errorf("authenticate fail for %s reason: %s", jsonutils.Marshal(ident), err)
  70. user := logclient.NewSimpleObject(ident.Password.User.Id, ident.Password.User.Name, "user")
  71. token := GetDefaultAdminCredToken()
  72. token.(*mcclient.SSimpleToken).Context = authCtx
  73. logclient.AddActionLogWithContext(ctx, user, logclient.ACT_AUTHENTICATE, err, token, false)
  74. }
  75. return usr, err
  76. }
  77. func authUserByIdentityInternal(ctx context.Context, ident *mcclient.SAuthenticationIdentity) (*api.SUserExtended, error) {
  78. var idpId string
  79. 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 {
  80. return nil, ErrEmptyAuth
  81. }
  82. 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 {
  83. // no user domain specified, try to find user domain
  84. q := models.UserManager.Query().Equals("name", ident.Password.User.Name)
  85. usrCnt, err := q.CountWithError()
  86. if err != nil {
  87. return nil, errors.Wrap(err, "Query user by name")
  88. }
  89. if usrCnt > 1 {
  90. log.Errorf("find %d user with name %s", usrCnt, ident.Password.User.Name)
  91. return nil, sqlchemy.ErrDuplicateEntry
  92. } else if usrCnt == 0 {
  93. log.Errorf("find no user with name %s", ident.Password.User.Name)
  94. return nil, httperrors.ErrUserNotFound
  95. } else {
  96. // userCnt == 1
  97. usr := models.SUser{}
  98. usr.SetModelManager(models.UserManager, &usr)
  99. err := q.First(&usr)
  100. if err != nil {
  101. return nil, errors.Wrap(err, "Query user")
  102. }
  103. ident.Password.User.Domain.Id = usr.DomainId
  104. ident.Password.User.Id = usr.Id
  105. }
  106. }
  107. usrExt, err := models.UserManager.FetchUserExtended(ident.Password.User.Id, ident.Password.User.Name,
  108. ident.Password.User.Domain.Id, ident.Password.User.Domain.Name)
  109. if err != nil && errors.Cause(err) != httperrors.ErrUserNotFound {
  110. return nil, errors.Wrap(err, "UserManager.FetchUserExtended")
  111. }
  112. if err != nil {
  113. log.Errorf("no such user %s locally, query external IDP: %s", ident.Password.User.Name, err)
  114. // no such user locally, query domain idp
  115. domain, err := models.DomainManager.FetchDomain(ident.Password.User.Domain.Id, ident.Password.User.Domain.Name)
  116. if err != nil {
  117. if errors.Cause(err) != sql.ErrNoRows {
  118. return nil, errors.Wrap(err, "DomainManager.FetchDomain")
  119. } else {
  120. return nil, errors.Wrapf(httperrors.ErrUserNotFound, "domain %s", ident.Password.User.Domain.Name)
  121. }
  122. }
  123. mapping, err := models.IdmappingManager.FetchFirstEntity(domain.Id, api.IdMappingEntityDomain)
  124. if err != nil {
  125. if errors.Cause(err) != sql.ErrNoRows {
  126. return nil, errors.Wrap(err, "IdmappingManager.FetchEntity")
  127. } else {
  128. return nil, errors.Wrapf(httperrors.ErrUserNotFound, "idp")
  129. }
  130. }
  131. idpId = mapping.IdpId
  132. } else {
  133. // check enable
  134. if !usrExt.Enabled {
  135. if usrExt.IsLocal && usrExt.LocalFailedAuthCount > options.Options.PasswordErrorLockCount {
  136. // user locked
  137. return nil, httperrors.ErrUserLocked
  138. }
  139. // user disabled
  140. return nil, httperrors.ErrUserDisabled
  141. }
  142. // user is enabled, check expired time
  143. if !usrExt.ExpiredAt.IsZero() && usrExt.ExpiredAt.Before(time.Now()) {
  144. return nil, httperrors.ErrUserExpired
  145. }
  146. // user exists, query user's idp
  147. idps, err := models.IdentityProviderManager.FetchIdentityProvidersByUserId(usrExt.Id, api.PASSWORD_PROTECTED_IDPS)
  148. if err != nil {
  149. return nil, errors.Wrap(err, "IdentityProviderManager.FetchIdentityProvidersByUserId")
  150. }
  151. if len(idps) == 0 {
  152. idpId = api.DEFAULT_IDP_ID
  153. } else if len(idps) == 1 {
  154. idpId = idps[0].Id
  155. } else {
  156. return nil, sqlchemy.ErrDuplicateEntry
  157. }
  158. }
  159. if len(idpId) == 0 {
  160. idpId = api.DEFAULT_IDP_ID
  161. }
  162. idpObj, err := models.IdentityProviderManager.FetchById(idpId)
  163. if err != nil {
  164. return nil, errors.Wrap(err, "IdentityProviderManager.FetchById")
  165. }
  166. idp := idpObj.(*models.SIdentityProvider)
  167. if idp.Enabled.IsFalse() {
  168. return nil, errors.Wrap(httperrors.ErrInvalidIdpStatus, "idp disabled")
  169. }
  170. if idp.Status != api.IdentityDriverStatusConnected && idp.Status != api.IdentityDriverStatusDisconnected {
  171. return nil, errors.Wrapf(httperrors.ErrInvalidIdpStatus, "invalid idp status %s", idp.Status)
  172. }
  173. conf, err := models.GetConfigs(idp, true, nil, nil)
  174. if err != nil {
  175. return nil, errors.Wrap(err, "GetConfig")
  176. }
  177. backend, err := driver.GetDriver(idp.Driver, idp.Id, idp.Name, idp.Template, idp.TargetDomainId, conf)
  178. if err != nil {
  179. return nil, errors.Wrap(err, "driver.GetDriver")
  180. }
  181. usr, err := backend.Authenticate(ctx, *ident)
  182. if err != nil {
  183. return nil, errors.Wrap(err, "Authenticate")
  184. }
  185. if idp.Status == api.IdentityDriverStatusDisconnected {
  186. idp.MarkConnected(ctx, models.GetDefaultAdminCred())
  187. }
  188. return usr, nil
  189. }
  190. func authUserByCASV3(ctx context.Context, input mcclient.SAuthenticationInputV3) (*api.SUserExtended, error) {
  191. var idp *models.SIdentityProvider
  192. var err error
  193. if len(input.Auth.Identity.Id) > 0 {
  194. idp, err = models.IdentityProviderManager.FetchIdentityProviderById(input.Auth.Identity.Id)
  195. if err != nil {
  196. if errors.Cause(err) == sql.ErrNoRows {
  197. return nil, errors.Wrapf(httperrors.ErrResourceNotFound, "idp %s not found", input.Auth.Identity.Id)
  198. } else {
  199. return nil, errors.Wrap(err, "FetchIdentityProviderById")
  200. }
  201. }
  202. } else {
  203. idps, err := models.IdentityProviderManager.FetchEnabledProviders(api.IdentityDriverCAS)
  204. if err != nil {
  205. return nil, errors.Wrap(err, "models.fetchEnabledProviders")
  206. }
  207. if len(idps) == 0 {
  208. return nil, errors.Error("No cas identity provider")
  209. }
  210. if len(idps) > 1 {
  211. return nil, errors.Error("more than 1 cas identity providers?")
  212. }
  213. idp = &idps[0]
  214. }
  215. conf, err := models.GetConfigs(idp, true, nil, nil)
  216. if err != nil {
  217. return nil, errors.Wrap(err, "idp.GetConfig")
  218. }
  219. backend, err := driver.GetDriver(idp.Driver, idp.Id, idp.Name, idp.Template, idp.TargetDomainId, conf)
  220. if err != nil {
  221. return nil, errors.Wrap(err, "driver.GetDriver")
  222. }
  223. usr, err := backend.Authenticate(ctx, input.Auth.Identity)
  224. if err != nil {
  225. return nil, errors.Wrap(err, "Authenticate")
  226. }
  227. if idp.Status == api.IdentityDriverStatusDisconnected {
  228. idp.MarkConnected(ctx, models.GetDefaultAdminCred())
  229. }
  230. return usr, nil
  231. }
  232. func authUserBySAML(ctx context.Context, input mcclient.SAuthenticationInputV3) (*api.SUserExtended, error) {
  233. if !saml.IsSAMLEnabled() {
  234. return nil, errors.Wrap(httperrors.ErrNotSupported, "unsupported SAML backend")
  235. }
  236. idp, err := models.IdentityProviderManager.FetchIdentityProviderById(input.Auth.Identity.Id)
  237. if err != nil {
  238. if errors.Cause(err) == sql.ErrNoRows {
  239. return nil, errors.Wrapf(httperrors.ErrResourceNotFound, "idp %s not found", input.Auth.Identity.Id)
  240. } else {
  241. return nil, errors.Wrap(err, "FetchIdentityProviderById")
  242. }
  243. }
  244. conf, err := models.GetConfigs(idp, true, nil, nil)
  245. if err != nil {
  246. return nil, errors.Wrap(err, "idp.GetConfig")
  247. }
  248. backend, err := driver.GetDriver(idp.Driver, idp.Id, idp.Name, idp.Template, idp.TargetDomainId, conf)
  249. if err != nil {
  250. return nil, errors.Wrap(err, "driver.GetDriver")
  251. }
  252. usr, err := backend.Authenticate(ctx, input.Auth.Identity)
  253. if err != nil {
  254. return nil, errors.Wrap(err, "Authenticate")
  255. }
  256. if idp.Status == api.IdentityDriverStatusDisconnected {
  257. idp.MarkConnected(ctx, models.GetDefaultAdminCred())
  258. }
  259. return usr, nil
  260. }
  261. func authUserByOIDC(ctx context.Context, input mcclient.SAuthenticationInputV3) (*api.SUserExtended, error) {
  262. idp, err := models.IdentityProviderManager.FetchIdentityProviderById(input.Auth.Identity.Id)
  263. if err != nil {
  264. if errors.Cause(err) == sql.ErrNoRows {
  265. return nil, errors.Wrapf(httperrors.ErrResourceNotFound, "idp %s not found", input.Auth.Identity.Id)
  266. } else {
  267. return nil, errors.Wrap(err, "FetchIdentityProviderById")
  268. }
  269. }
  270. conf, err := models.GetConfigs(idp, true, nil, nil)
  271. if err != nil {
  272. return nil, errors.Wrap(err, "idp.GetConfig")
  273. }
  274. backend, err := driver.GetDriver(idp.Driver, idp.Id, idp.Name, idp.Template, idp.TargetDomainId, conf)
  275. if err != nil {
  276. return nil, errors.Wrap(err, "driver.GetDriver")
  277. }
  278. usr, err := backend.Authenticate(ctx, input.Auth.Identity)
  279. if err != nil {
  280. return nil, errors.Wrap(err, "Authenticate")
  281. }
  282. if idp.Status == api.IdentityDriverStatusDisconnected {
  283. idp.MarkConnected(ctx, models.GetDefaultAdminCred())
  284. }
  285. return usr, nil
  286. }
  287. func authUserByOAuth2(ctx context.Context, input mcclient.SAuthenticationInputV3) (*api.SUserExtended, error) {
  288. idp, err := models.IdentityProviderManager.FetchIdentityProviderById(input.Auth.Identity.Id)
  289. if err != nil {
  290. if errors.Cause(err) == sql.ErrNoRows {
  291. return nil, errors.Wrapf(httperrors.ErrResourceNotFound, "idp %s not found", input.Auth.Identity.Id)
  292. } else {
  293. return nil, errors.Wrap(err, "FetchIdentityProviderById")
  294. }
  295. }
  296. conf, err := models.GetConfigs(idp, true, nil, nil)
  297. if err != nil {
  298. return nil, errors.Wrap(err, "idp.GetConfig")
  299. }
  300. backend, err := driver.GetDriver(idp.Driver, idp.Id, idp.Name, idp.Template, idp.TargetDomainId, conf)
  301. if err != nil {
  302. return nil, errors.Wrap(err, "driver.GetDriver")
  303. }
  304. usr, err := backend.Authenticate(ctx, input.Auth.Identity)
  305. if err != nil {
  306. return nil, errors.Wrap(err, "Authenticate")
  307. }
  308. if idp.Status == api.IdentityDriverStatusDisconnected {
  309. idp.MarkConnected(ctx, models.GetDefaultAdminCred())
  310. }
  311. return usr, nil
  312. }
  313. func authUserByAccessKeyV3(ctx context.Context, input mcclient.SAuthenticationInputV3) (*api.SUserExtended, string, api.SAccessKeySecretInfo, error) {
  314. var aksk api.SAccessKeySecretInfo
  315. akskRequest, err := s3auth.Decode(input.Auth.Identity.AccessKeyRequest)
  316. if err != nil {
  317. return nil, "", aksk, errors.Wrap(err, "s3auth.Decode")
  318. }
  319. keyId := akskRequest.GetAccessKey()
  320. obj, err := models.CredentialManager.FetchById(keyId)
  321. if err != nil {
  322. if err == sql.ErrNoRows {
  323. return nil, "", aksk, ErrInvalidAccessKeyId
  324. } else {
  325. return nil, "", aksk, errors.Wrap(err, "CredentialManager.FetchById")
  326. }
  327. }
  328. credential := obj.(*models.SCredential)
  329. if !credential.Enabled.IsTrue() {
  330. return nil, "", aksk, errors.Wrap(httperrors.ErrInvalidStatus, "Access Key disabled")
  331. }
  332. akBlob, err := credential.GetAccessKeySecret()
  333. if err != nil {
  334. return nil, "", aksk, errors.Wrap(err, "credential.GetAccessKeySecret")
  335. }
  336. if !akBlob.IsValid() {
  337. return nil, "", aksk, ErrExpiredAccessKey
  338. }
  339. aksk.AccessKey = keyId
  340. aksk.Secret = akBlob.Secret
  341. aksk.Expire = akBlob.Expire
  342. err = akskRequest.Verify(akBlob.Secret)
  343. if err != nil {
  344. return nil, "", aksk, errors.Wrap(err, "Verify")
  345. }
  346. usrExt, err := models.UserManager.FetchUserExtended(credential.UserId, "", "", "")
  347. if err != nil {
  348. return nil, "", aksk, errors.Wrap(err, "UserManager.FetchUserExtended")
  349. }
  350. usrExt.AuditIds = []string{keyId}
  351. return usrExt, credential.ProjectId, aksk, nil
  352. }
  353. func authUserByVerify(ctx context.Context, input mcclient.SAuthenticationInputV3) (*api.SUserExtended, error) {
  354. extUser, err := models.UserManager.FetchUserExtended(input.Auth.Identity.Verify.Uid, "", "", "")
  355. if err != nil {
  356. return nil, errors.Wrap(err, "FetchUserExtended")
  357. }
  358. s, err := GetDefaulAdminSession(ctx, "")
  359. if err != nil {
  360. return nil, errors.Wrap(err, "GetDefaultAdminSession")
  361. }
  362. verifyInput := notify_api.ReceiverVerifyInput{
  363. ContactType: input.Auth.Identity.Verify.ContactType,
  364. Token: input.Auth.Identity.Verify.VerifyCode,
  365. }
  366. _, err = notify_modules.NotifyReceiver.PerformAction(s, extUser.Id, "verify", jsonutils.Marshal(verifyInput))
  367. if err != nil {
  368. return nil, errors.Wrap(err, "Verify")
  369. }
  370. extUser.AuditIds = []string{input.Auth.Identity.Verify.Uid}
  371. return extUser, nil
  372. }
  373. // +onecloud:swagger-gen-route-method=POST
  374. // +onecloud:swagger-gen-route-path=/v3/auth/tokens
  375. // +onecloud:swagger-gen-route-tag=authentication
  376. // +onecloud:swagger-gen-param-body-index=1
  377. // +onecloud:swagger-gen-resp-index=0
  378. // +onecloud:swagger-gen-resp-header=X-Subject-Token
  379. // +onecloud:swagger-gen-resp-header=验证成功的keystone V3 token
  380. // keystone v3认证API
  381. func AuthenticateV3(ctx context.Context, input mcclient.SAuthenticationInputV3) (*mcclient.TokenCredentialV3, error) {
  382. var akskInfo api.SAccessKeySecretInfo
  383. var user *api.SUserExtended
  384. var err error
  385. if len(input.Auth.Identity.Methods) != 1 {
  386. return nil, ErrInvalidAuthMethod
  387. }
  388. method := input.Auth.Identity.Methods[0]
  389. switch method {
  390. case api.AUTH_METHOD_TOKEN:
  391. // auth by token
  392. user, err = authUserByTokenV3(ctx, input)
  393. if err != nil {
  394. return nil, errors.Wrap(err, "authUserByTokenV3")
  395. }
  396. case api.AUTH_METHOD_AKSK:
  397. // auth by aksk
  398. user, input.Auth.Scope.Project.Id, akskInfo, err = authUserByAccessKeyV3(ctx, input)
  399. if err != nil {
  400. return nil, errors.Wrap(err, "authUserByAccessKeyV3")
  401. }
  402. case api.AUTH_METHOD_CAS:
  403. // auth by apereo CAS
  404. user, err = authUserByCASV3(ctx, input)
  405. if err != nil {
  406. return nil, errors.Wrap(err, "authUserByCASV3")
  407. }
  408. case api.AUTH_METHOD_SAML:
  409. // auth by SAML 2.0 IDP, keystone acts as a SAML SP
  410. user, err = authUserBySAML(ctx, input)
  411. if err != nil {
  412. return nil, errors.Wrap(err, "authUserBySAML")
  413. }
  414. case api.AUTH_METHOD_OIDC:
  415. // auth by OpenID Connect, keystone acts as an OpenID Connect client
  416. user, err = authUserByOIDC(ctx, input)
  417. if err != nil {
  418. return nil, errors.Wrap(err, "authUserByOIDC")
  419. }
  420. case api.AUTH_METHOD_OAuth2:
  421. // auth by customized OAuth2.0 provider, keystone acts as an OAuth2.0 app
  422. user, err = authUserByOAuth2(ctx, input)
  423. if err != nil {
  424. return nil, errors.Wrap(err, "authUserByOAuth2")
  425. }
  426. case api.AUTH_METHOD_VERIFY:
  427. user, err = authUserByVerify(ctx, input)
  428. if err != nil {
  429. return nil, errors.Wrap(err, "authUserByVerify")
  430. }
  431. case api.AUTH_METHOD_ASSUME:
  432. user, err = authUserByAssume(ctx, input)
  433. if err != nil {
  434. return nil, errors.Wrap(err, "authUserByAssume")
  435. }
  436. default:
  437. // auth by other methods, e.g. password , etc...
  438. user, err = authUserByIdentityV3(ctx, input)
  439. if err != nil {
  440. return nil, err
  441. }
  442. }
  443. // user not found
  444. if user == nil {
  445. return nil, ErrUserNotFound
  446. }
  447. // user is not enabled
  448. if !user.Enabled {
  449. return nil, ErrUserDisabled
  450. }
  451. // user is expired
  452. if !user.ExpiredAt.IsZero() && user.ExpiredAt.Before(time.Now()) {
  453. return nil, httperrors.ErrUserExpired
  454. }
  455. if !user.DomainEnabled {
  456. return nil, ErrDomainDisabled
  457. }
  458. token := SAuthToken{}
  459. token.UserId = user.Id
  460. token.Method = method
  461. token.AuditIds = user.AuditIds
  462. now := time.Now().UTC()
  463. token.ExpiresAt = now.Add(time.Duration(options.Options.TokenExpirationSeconds) * time.Second)
  464. if !user.ExpiredAt.IsZero() && user.ExpiredAt.Before(token.ExpiresAt) {
  465. token.ExpiresAt = user.ExpiredAt
  466. }
  467. token.Context = input.Auth.Context
  468. 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 {
  469. // unscoped auth
  470. return token.getTokenV3(ctx, user, nil, nil, akskInfo)
  471. }
  472. var projExt *models.SProjectExtended
  473. var domain *models.SDomain
  474. if len(input.Auth.Scope.Project.Id) > 0 || len(input.Auth.Scope.Project.Name) > 0 {
  475. project, err := models.ProjectManager.FetchProject(
  476. input.Auth.Scope.Project.Id,
  477. input.Auth.Scope.Project.Name,
  478. input.Auth.Scope.Project.Domain.Id,
  479. input.Auth.Scope.Project.Domain.Name,
  480. )
  481. if err != nil {
  482. return nil, errors.Wrap(err, "ProjectManager.FetchProject")
  483. }
  484. // if project.Enabled.IsFalse() {
  485. // return nil, ErrProjectDisabled
  486. // }
  487. projExt, err = project.FetchExtend()
  488. if err != nil {
  489. return nil, errors.Wrap(err, "project.FetchExtend")
  490. }
  491. token.ProjectId = project.Id
  492. } else {
  493. domain, err = models.DomainManager.FetchDomain(input.Auth.Scope.Domain.Id,
  494. input.Auth.Scope.Domain.Name)
  495. if err != nil {
  496. return nil, errors.Wrap(err, "DomainManager.FetchDomain")
  497. }
  498. if domain.Enabled.IsFalse() {
  499. return nil, ErrDomainDisabled
  500. }
  501. token.DomainId = domain.Id
  502. }
  503. tokenV3, err := token.getTokenV3(ctx, user, projExt, domain, akskInfo)
  504. if err != nil {
  505. return nil, errors.Wrap(err, "getTokenV3")
  506. }
  507. return tokenV3, nil
  508. }
  509. type SAuthenticateV2ResponseBody struct {
  510. Access mcclient.TokenCredentialV2 `json:"access"`
  511. }
  512. // +onecloud:swagger-gen-route-method=POST
  513. // +onecloud:swagger-gen-route-path=/v2.0/tokens
  514. // +onecloud:swagger-gen-route-tag=authentication
  515. // +onecloud:swagger-gen-param-body-index=1
  516. // +onecloud:swagger-gen-resp-index=0
  517. // keystone v2 认证接口,通过用户名/密码或者 token 认证
  518. func AuthenticateV2(ctx context.Context, input mcclient.SAuthenticationInputV2) (*SAuthenticateV2ResponseBody, error) {
  519. token, err := _authenticateV2(ctx, input)
  520. if err != nil {
  521. return nil, errors.Wrap(err, "_authenticateV2")
  522. }
  523. body := SAuthenticateV2ResponseBody{
  524. Access: *token,
  525. }
  526. return &body, nil
  527. }
  528. func _authenticateV2(ctx context.Context, input mcclient.SAuthenticationInputV2) (*mcclient.TokenCredentialV2, error) {
  529. var user *api.SUserExtended
  530. var err error
  531. var method string
  532. if len(input.Auth.Token.Id) > 0 {
  533. // auth by token
  534. user, err = authUserByTokenV2(ctx, input)
  535. if err != nil {
  536. return nil, errors.Wrap(err, "authUserByTokenV2")
  537. }
  538. method = api.AUTH_METHOD_TOKEN
  539. } else {
  540. // auth by password
  541. user, err = authUserByPasswordV2(ctx, input)
  542. if err != nil {
  543. return nil, errors.Wrap(err, "authUserByPasswordV2")
  544. }
  545. method = api.AUTH_METHOD_PASSWORD
  546. }
  547. // user not found
  548. if user == nil {
  549. return nil, ErrUserNotFound
  550. }
  551. // user is not enabled
  552. if !user.Enabled {
  553. return nil, ErrUserDisabled
  554. }
  555. if !user.DomainEnabled {
  556. return nil, ErrDomainDisabled
  557. }
  558. token := SAuthToken{}
  559. token.UserId = user.Id
  560. token.Method = method
  561. token.AuditIds = user.AuditIds
  562. now := time.Now().UTC()
  563. token.ExpiresAt = now.Add(time.Duration(options.Options.TokenExpirationSeconds) * time.Second)
  564. token.Context = input.Auth.Context
  565. if len(input.Auth.TenantId) == 0 && len(input.Auth.TenantName) == 0 {
  566. // unscoped auth
  567. return token.getTokenV2(ctx, user, nil)
  568. }
  569. project, err := models.ProjectManager.FetchProject(
  570. input.Auth.TenantId,
  571. input.Auth.TenantName,
  572. api.DEFAULT_DOMAIN_ID, "")
  573. if err != nil {
  574. return nil, errors.Wrap(err, "ProjectManager.FetchProject")
  575. }
  576. // if project.Enabled.IsFalse() {
  577. // return nil, ErrProjectDisabled
  578. // }
  579. token.ProjectId = project.Id
  580. projExt, err := project.FetchExtend()
  581. if err != nil {
  582. return nil, errors.Wrap(err, "project.FetchExtend")
  583. }
  584. tokenV2, err := token.getTokenV2(ctx, user, projExt)
  585. if err != nil {
  586. return nil, errors.Wrap(err, "getTokenV2")
  587. }
  588. return tokenV2, nil
  589. }