token.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541
  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. "strings"
  19. "time"
  20. "yunion.io/x/log"
  21. "yunion.io/x/pkg/errors"
  22. "yunion.io/x/pkg/util/rbacscope"
  23. "yunion.io/x/pkg/utils"
  24. api "yunion.io/x/onecloud/pkg/apis/identity"
  25. "yunion.io/x/onecloud/pkg/keystone/cache"
  26. "yunion.io/x/onecloud/pkg/keystone/keys"
  27. "yunion.io/x/onecloud/pkg/keystone/models"
  28. "yunion.io/x/onecloud/pkg/keystone/options"
  29. "yunion.io/x/onecloud/pkg/mcclient"
  30. "yunion.io/x/onecloud/pkg/util/stringutils2"
  31. )
  32. var (
  33. defaultAuthToken *SAuthToken
  34. defaultAuthTokenStr string
  35. )
  36. func GetDefaultToken() string {
  37. now := time.Now()
  38. if defaultAuthToken == nil || defaultAuthToken.ExpiresAt.Sub(now) < time.Duration(3600) {
  39. simpleToken := models.GetDefaultAdminCred()
  40. defaultAuthToken = &SAuthToken{
  41. UserId: simpleToken.GetUserId(),
  42. Method: api.AUTH_METHOD_TOKEN,
  43. ProjectId: simpleToken.GetProjectId(),
  44. ExpiresAt: now.Add(time.Duration(options.Options.TokenExpirationSeconds) * time.Second),
  45. AuditIds: []string{utils.GenRequestId(16)},
  46. }
  47. var err error
  48. defaultAuthTokenStr, err = defaultAuthToken.encodeFernetToken()
  49. if err != nil {
  50. log.Fatalf("defaultAuthToken.EncodeFernetToken fail: %s", err)
  51. }
  52. if simpleToken.(*mcclient.SSimpleToken).Token != defaultAuthTokenStr {
  53. simpleToken.(*mcclient.SSimpleToken).Token = defaultAuthTokenStr
  54. }
  55. }
  56. return defaultAuthTokenStr
  57. }
  58. func GetDefaultAdminCredToken() mcclient.TokenCredential {
  59. GetDefaultToken()
  60. return models.GetDefaultAdminCred()
  61. }
  62. type SAuthToken struct {
  63. UserId string
  64. Method string
  65. ProjectId string
  66. DomainId string
  67. ExpiresAt time.Time
  68. AuditIds []string
  69. Context mcclient.SAuthContext
  70. }
  71. func (t *SAuthToken) Decode(tk []byte) error {
  72. for _, payload := range []ITokenPayload{
  73. &SProjectScopedPayloadWithContext{},
  74. &SDomainScopedPayloadWithContext{},
  75. &SUnscopedPayloadWithContext{},
  76. &SProjectScopedPayload{},
  77. &SDomainScopedPayload{},
  78. &SUnscopedPayload{},
  79. } {
  80. err := payload.Unmarshal(tk)
  81. if err == nil {
  82. payload.Decode(t)
  83. return nil
  84. }
  85. }
  86. return ErrInvalidFernetToken
  87. }
  88. func (t *SAuthToken) getProjectScopedPayload() ITokenPayload {
  89. p := SProjectScopedPayload{}
  90. p.Version = SProjectScopedPayloadVersion
  91. p.UserId.parse(t.UserId)
  92. p.ProjectId.parse(t.ProjectId)
  93. p.Method = authMethodStr2Id(t.Method)
  94. p.ExpiresAt = float64(t.ExpiresAt.Unix())
  95. p.AuditIds = auditStrings2Bytes(t.AuditIds)
  96. return &p
  97. }
  98. func (t *SAuthToken) getProjectScopedPayloadWithContext() ITokenPayload {
  99. p := SProjectScopedPayloadWithContext{}
  100. p.Version = SProjectScopedPayloadWithContextVersion
  101. p.UserId.parse(t.UserId)
  102. p.ProjectId.parse(t.ProjectId)
  103. p.Method = authMethodStr2Id(t.Method)
  104. p.ExpiresAt = float64(t.ExpiresAt.Unix())
  105. p.AuditIds = auditStrings2Bytes(t.AuditIds)
  106. p.Context = authContext2Payload(t.Context)
  107. return &p
  108. }
  109. func (t *SAuthToken) getDomainScopedPayload() ITokenPayload {
  110. p := SDomainScopedPayload{}
  111. p.Version = SDomainScopedPayloadVersion
  112. p.UserId.parse(t.UserId)
  113. p.DomainId.parse(t.DomainId)
  114. p.Method = authMethodStr2Id(t.Method)
  115. p.ExpiresAt = float64(t.ExpiresAt.Unix())
  116. p.AuditIds = auditStrings2Bytes(t.AuditIds)
  117. return &p
  118. }
  119. func (t *SAuthToken) getDomainScopedPayloadWithContext() ITokenPayload {
  120. p := SDomainScopedPayloadWithContext{}
  121. p.Version = SDomainScopedPayloadVersion
  122. p.UserId.parse(t.UserId)
  123. p.DomainId.parse(t.DomainId)
  124. p.Method = authMethodStr2Id(t.Method)
  125. p.ExpiresAt = float64(t.ExpiresAt.Unix())
  126. p.AuditIds = auditStrings2Bytes(t.AuditIds)
  127. p.Context = authContext2Payload(t.Context)
  128. return &p
  129. }
  130. func (t *SAuthToken) getUnscopedPayload() ITokenPayload {
  131. p := SUnscopedPayload{}
  132. p.Version = SUnscopedPayloadVersion
  133. p.UserId.parse(t.UserId)
  134. p.Method = authMethodStr2Id(t.Method)
  135. p.ExpiresAt = float64(t.ExpiresAt.Unix())
  136. p.AuditIds = auditStrings2Bytes(t.AuditIds)
  137. return &p
  138. }
  139. func (t *SAuthToken) getUnscopedPayloadWithContext() ITokenPayload {
  140. p := SUnscopedPayloadWithContext{}
  141. p.Version = SUnscopedPayloadVersion
  142. p.UserId.parse(t.UserId)
  143. p.Method = authMethodStr2Id(t.Method)
  144. p.ExpiresAt = float64(t.ExpiresAt.Unix())
  145. p.AuditIds = auditStrings2Bytes(t.AuditIds)
  146. p.Context = authContext2Payload(t.Context)
  147. return &p
  148. }
  149. func (t *SAuthToken) getPayload() ITokenPayload {
  150. if len(t.ProjectId) > 0 {
  151. return t.getProjectScopedPayloadWithContext()
  152. }
  153. if len(t.DomainId) > 0 {
  154. return t.getDomainScopedPayloadWithContext()
  155. }
  156. return t.getUnscopedPayloadWithContext()
  157. }
  158. func (t *SAuthToken) Encode() ([]byte, error) {
  159. return t.getPayload().Encode()
  160. }
  161. func (t *SAuthToken) IsExpired() bool {
  162. return t.ValidDuration() <= 0
  163. }
  164. func (t *SAuthToken) ValidDuration() time.Duration {
  165. return time.Until(t.ExpiresAt)
  166. }
  167. func TokenStrDecode(ctx context.Context, tokenStr string) (*SAuthToken, error) {
  168. var fernetToken string
  169. if len(tokenStr) > api.AUTH_TOKEN_LENGTH {
  170. // old style fernet token, convert to new token of sha256 hash of fernetToken
  171. fernetToken = tokenStr
  172. tokenStr = stringutils2.GenId(fernetToken)
  173. }
  174. token, err := models.TokenCacheManager.FetchToken(tokenStr)
  175. if err != nil {
  176. if errors.Cause(err) == sql.ErrNoRows {
  177. // not found
  178. if len(fernetToken) > 0 {
  179. // try to decode fernetToken
  180. token := &SAuthToken{}
  181. err := token.parseFernetToken(fernetToken)
  182. if err != nil {
  183. return nil, errors.Wrap(err, "parseFernetToken")
  184. }
  185. if token.IsExpired() {
  186. return nil, ErrExpiredToken
  187. }
  188. // we should save the token for later use
  189. {
  190. err := models.TokenCacheManager.Save(ctx, tokenStr, token.ExpiresAt, token.Method, token.AuditIds,
  191. token.UserId, token.ProjectId, token.DomainId, token.Context.Source, token.Context.Ip)
  192. if err != nil {
  193. log.Errorf("TokenStrDecode: save token fail %s", err)
  194. }
  195. }
  196. return token, nil
  197. } else {
  198. // ??? not a fernetToken
  199. return nil, errors.Wrapf(ErrTokenNotFound, "token %q not found", tokenStr)
  200. }
  201. } else {
  202. return nil, errors.Wrap(err, "FetchToken")
  203. }
  204. } else {
  205. if !token.Valid {
  206. return nil, ErrInvalidToken
  207. }
  208. return &SAuthToken{
  209. UserId: token.UserId,
  210. ProjectId: token.ProjectId,
  211. DomainId: token.DomainId,
  212. Method: token.Method,
  213. ExpiresAt: token.ExpiredAt,
  214. AuditIds: strings.Split(token.AuditIds, ","),
  215. Context: mcclient.SAuthContext{
  216. Source: token.Source,
  217. Ip: token.Ip,
  218. },
  219. }, nil
  220. }
  221. }
  222. func (t *SAuthToken) parseFernetToken(tokenStr string) error {
  223. tk := keys.TokenKeysManager.Decrypt([]byte(tokenStr)) // , time.Duration(options.Options.TokenExpirationSeconds)*time.Second)
  224. if tk == nil {
  225. return errors.Wrapf(ErrInvalidFernetToken, "%v", tokenStr)
  226. }
  227. err := t.Decode(tk)
  228. if err != nil {
  229. return errors.Wrap(err, "decode error")
  230. }
  231. if t.ExpiresAt.Before(time.Now()) {
  232. return errors.Wrapf(ErrExpiredToken, "expires_at %s", t.ExpiresAt.String())
  233. }
  234. return nil
  235. }
  236. func (t *SAuthToken) encodeFernetToken() (string, error) {
  237. tk, err := t.Encode()
  238. if err != nil {
  239. return "", errors.Wrap(err, "encode error")
  240. }
  241. ftk, err := keys.TokenKeysManager.Encrypt(tk)
  242. if err != nil {
  243. return "", errors.Wrap(err, "TokenKeysManager.Encrypt")
  244. }
  245. return string(ftk), nil
  246. }
  247. func (t *SAuthToken) encodeShortToken() (string, error) {
  248. tk, err := t.encodeFernetToken()
  249. if err != nil {
  250. return "", errors.Wrap(err, "encodeFernetToken")
  251. }
  252. stk := stringutils2.GenId(tk)
  253. // log.Debugf("encodeShortToken %s => %s", tk, stk)
  254. return stk, nil
  255. }
  256. func (t *SAuthToken) GetSimpleUserCred(token string) (mcclient.TokenCredential, error) {
  257. userExt, err := models.UserManager.FetchUserExtended(t.UserId, "", "", "")
  258. if err != nil {
  259. return nil, errors.Wrap(err, "UserManager.FetchUserExtended")
  260. }
  261. ret := mcclient.SSimpleToken{
  262. Token: token,
  263. UserId: t.UserId,
  264. User: userExt.Name,
  265. Domain: userExt.DomainName,
  266. DomainId: userExt.DomainId,
  267. Expires: t.ExpiresAt,
  268. Context: t.Context,
  269. SystemAccount: userExt.IsSystemAccount,
  270. }
  271. var roles []models.SRole
  272. if len(t.ProjectId) > 0 {
  273. proj, err := models.ProjectManager.FetchProjectById(t.ProjectId)
  274. if err != nil {
  275. return nil, errors.Wrap(err, "ProjectManager.FetchProjectById")
  276. }
  277. ret.ProjectId = t.ProjectId
  278. ret.Project = proj.Name
  279. ret.ProjectDomainId = proj.DomainId
  280. ret.ProjectDomain = proj.GetDomain().Name
  281. roles, err = models.AssignmentManager.FetchUserProjectRoles(t.UserId, t.ProjectId)
  282. } else if len(t.DomainId) > 0 {
  283. domain, err := models.DomainManager.FetchDomainById(t.DomainId)
  284. if err != nil {
  285. return nil, errors.Wrap(err, "DomainManager.FetchDomainById")
  286. }
  287. ret.ProjectDomainId = t.DomainId
  288. ret.ProjectDomain = domain.Name
  289. roles, err = models.AssignmentManager.FetchUserProjectRoles(t.UserId, t.DomainId)
  290. }
  291. roleStrs := make([]string, len(roles))
  292. roleIdStrs := make([]string, len(roles))
  293. for i := range roles {
  294. roleStrs[i] = roles[i].Name
  295. roleIdStrs[i] = roles[i].Id
  296. }
  297. ret.Roles = strings.Join(roleStrs, ",")
  298. ret.RoleIds = strings.Join(roleIdStrs, ",")
  299. return &ret, nil
  300. }
  301. func (t *SAuthToken) getRoles() ([]models.SRole, error) {
  302. var roleProjectId string
  303. if len(t.ProjectId) > 0 {
  304. roleProjectId = t.ProjectId
  305. } else if len(t.DomainId) > 0 {
  306. roleProjectId = t.DomainId
  307. }
  308. if len(roleProjectId) > 0 {
  309. return models.AssignmentManager.FetchUserProjectRoles(t.UserId, roleProjectId)
  310. }
  311. return nil, nil
  312. }
  313. func (t *SAuthToken) getTokenV3(
  314. ctx context.Context,
  315. user *api.SUserExtended,
  316. project *models.SProjectExtended,
  317. domain *models.SDomain,
  318. akskInfo api.SAccessKeySecretInfo,
  319. ) (*mcclient.TokenCredentialV3, error) {
  320. token := mcclient.TokenCredentialV3{}
  321. token.Token.AccessKey = akskInfo
  322. token.Token.ExpiresAt = t.ExpiresAt
  323. token.Token.IssuedAt = t.ExpiresAt.Add(-time.Duration(options.Options.TokenExpirationSeconds) * time.Second)
  324. token.Token.AuditIds = t.AuditIds
  325. token.Token.Methods = []string{t.Method}
  326. token.Token.User.Id = user.Id
  327. token.Token.User.Name = user.Name
  328. token.Token.User.Domain.Id = user.DomainId
  329. token.Token.User.Domain.Name = user.DomainName
  330. lastPass, _ := models.PasswordManager.FetchLastPassword(user.LocalId)
  331. if lastPass != nil && !lastPass.ExpiresAt.IsZero() {
  332. token.Token.User.PasswordExpiresAt = lastPass.ExpiresAt
  333. }
  334. token.Token.User.Displayname = user.Displayname
  335. token.Token.User.Email = user.Email
  336. token.Token.User.Mobile = user.Mobile
  337. token.Token.User.IsSystemAccount = user.IsSystemAccount
  338. token.Token.Context = t.Context
  339. tk, err := t.encodeShortToken()
  340. if err != nil {
  341. return nil, errors.Wrap(err, "EncodeFernetToken")
  342. }
  343. token.Id = tk
  344. roles, err := t.getRoles()
  345. if err != nil {
  346. return nil, errors.Wrap(err, "getRoles")
  347. }
  348. if len(roles) == 0 {
  349. if project != nil || domain != nil {
  350. if project != nil {
  351. return nil, errors.Wrapf(ErrUserNotInProject, "project %q", project.Name)
  352. }
  353. if domain != nil {
  354. return nil, errors.Wrapf(ErrUserNotInProject, "domain %q", domain.Name)
  355. }
  356. }
  357. /*extProjs, err := models.ProjectManager.FetchUserProjects(user.Id)
  358. if err != nil {
  359. return nil, errors.Wrap(err, "models.ProjectManager.FetchUserProjects")
  360. }
  361. token.Token.Projects = make([]mcclient.KeystoneProjectV3, len(extProjs))
  362. for i := range extProjs {
  363. token.Token.Projects[i].Id = extProjs[i].Id
  364. token.Token.Projects[i].Name = extProjs[i].Name
  365. token.Token.Projects[i].Domain.Id = extProjs[i].DomainId
  366. token.Token.Projects[i].Domain.Name = extProjs[i].DomainName
  367. }*/
  368. assigns, _, err := models.AssignmentManager.FetchAll(user.Id, "", "", "", "", "",
  369. nil, nil, nil, nil, nil, nil,
  370. true, true, true, true, true, 0, 0)
  371. if err != nil {
  372. return nil, errors.Wrap(err, "models.AssignmentManager.FetchAll")
  373. }
  374. token.Token.RoleAssignments = assigns
  375. token.Token.Projects = make([]mcclient.KeystoneProjectV3, 0)
  376. projMap := make(map[string]bool)
  377. for i := range assigns {
  378. if _, ok := projMap[assigns[i].Scope.Project.Id]; ok {
  379. continue
  380. }
  381. projMap[assigns[i].Scope.Project.Id] = true
  382. p := mcclient.KeystoneProjectV3{
  383. Id: assigns[i].Scope.Project.Id,
  384. Name: assigns[i].Scope.Project.Name,
  385. Domain: mcclient.KeystoneDomainV3{
  386. Id: assigns[i].Scope.Project.Domain.Id,
  387. Name: assigns[i].Scope.Project.Domain.Name,
  388. },
  389. }
  390. token.Token.Projects = append(token.Token.Projects, p)
  391. }
  392. } else {
  393. if project != nil {
  394. token.Token.IsDomain = false
  395. token.Token.Project.Id = project.Id
  396. token.Token.Project.Name = project.Name
  397. token.Token.Project.Domain.Id = project.DomainId
  398. token.Token.Project.Domain.Name = project.DomainName
  399. } else if domain != nil {
  400. token.Token.IsDomain = true
  401. token.Token.Project.Id = domain.Id
  402. token.Token.Project.Name = domain.Name
  403. }
  404. token.Token.Roles = make([]mcclient.KeystoneRoleV3, len(roles))
  405. for i := range roles {
  406. token.Token.Roles[i].Id = roles[i].Id
  407. token.Token.Roles[i].Name = roles[i].Name
  408. }
  409. policyNames, _, _ := models.RolePolicyManager.GetMatchPolicyGroup(&token, time.Time{}, true)
  410. token.Token.Policies.Project = policyNames[rbacscope.ScopeProject]
  411. token.Token.Policies.Domain = policyNames[rbacscope.ScopeDomain]
  412. token.Token.Policies.System = policyNames[rbacscope.ScopeSystem]
  413. endpoints, err := models.EndpointManager.FetchAll()
  414. if err != nil {
  415. return nil, errors.Wrap(err, "EndpointManager.FetchAll")
  416. }
  417. if endpoints != nil {
  418. token.Token.Catalog = endpoints.GetKeystoneCatalogV3()
  419. }
  420. }
  421. {
  422. err := models.TokenCacheManager.Save(ctx, token.Id, t.ExpiresAt, t.Method, t.AuditIds, t.UserId, t.ProjectId, t.DomainId, t.Context.Source, t.Context.Ip)
  423. if err != nil {
  424. return nil, errors.Wrap(err, "Save Token")
  425. }
  426. cache.Save(token.Id, &token)
  427. }
  428. return &token, nil
  429. }
  430. func (t *SAuthToken) getTokenV2(
  431. ctx context.Context,
  432. user *api.SUserExtended,
  433. project *models.SProjectExtended,
  434. ) (*mcclient.TokenCredentialV2, error) {
  435. token := mcclient.TokenCredentialV2{}
  436. token.User.Name = user.Name
  437. token.User.Id = user.Id
  438. token.User.Username = user.Name
  439. token.User.IsSystemAccount = user.IsSystemAccount
  440. token.Context = t.Context
  441. tk, err := t.encodeShortToken()
  442. if err != nil {
  443. return nil, errors.Wrap(err, "EncodeFernetToken")
  444. }
  445. token.Token.Id = tk
  446. token.Token.Expires = t.ExpiresAt
  447. roles, err := t.getRoles()
  448. if err != nil {
  449. return nil, errors.Wrap(err, "getRoles")
  450. }
  451. if len(roles) == 0 {
  452. if project != nil {
  453. return nil, errors.Wrapf(ErrUserNotInProject, "project %q", project.Name)
  454. }
  455. extProjs, err := models.ProjectManager.FetchUserProjects(user.Id)
  456. if err != nil {
  457. return nil, errors.Wrap(err, "models.ProjectManager.FetchUserProjects")
  458. }
  459. token.Tenants = make([]mcclient.KeystoneTenantV2, len(extProjs))
  460. for i := range extProjs {
  461. token.Tenants[i].Id = extProjs[i].Id
  462. token.Tenants[i].Name = extProjs[i].Name
  463. token.Tenants[i].Domain.Id = extProjs[i].DomainId
  464. token.Tenants[i].Domain.Name = extProjs[i].DomainName
  465. token.Tenants[i].Enabled = true
  466. }
  467. } else {
  468. token.Token.Tenant.Id = project.Id
  469. token.Token.Tenant.Name = project.Name
  470. // token.Token.Tenant.Enabled = project.Enabled.Bool()
  471. token.Token.Tenant.Description = project.Description
  472. token.Token.Tenant.Domain.Id = project.DomainId
  473. token.Token.Tenant.Domain.Name = project.DomainName
  474. token.User.Roles = make([]mcclient.KeystoneRoleV2, len(roles))
  475. token.Metadata.Roles = make([]string, len(roles))
  476. for i := range roles {
  477. token.User.Roles[i].Name = roles[i].Name
  478. token.User.Roles[i].Id = roles[i].Id
  479. token.Metadata.Roles[i] = roles[i].Name
  480. }
  481. endpoints, err := models.EndpointManager.FetchAll()
  482. if err != nil {
  483. return nil, errors.Wrap(err, "EndpointManager.FetchAll")
  484. }
  485. if endpoints != nil {
  486. token.ServiceCatalog = endpoints.GetKeystoneCatalogV2()
  487. }
  488. }
  489. {
  490. err := models.TokenCacheManager.Save(ctx, token.Token.Id, t.ExpiresAt, t.Method, t.AuditIds, t.UserId, t.ProjectId, t.DomainId, t.Context.Source, t.Context.Ip)
  491. if err != nil {
  492. return nil, errors.Wrap(err, "Save Token")
  493. }
  494. cache.Save(token.Token.Id, &token)
  495. }
  496. return &token, nil
  497. }