auth.go 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357
  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 handler
  15. import (
  16. "context"
  17. "crypto/md5"
  18. "encoding/base64"
  19. "fmt"
  20. "net/http"
  21. "strings"
  22. "time"
  23. "yunion.io/x/jsonutils"
  24. "yunion.io/x/log"
  25. "yunion.io/x/pkg/errors"
  26. "yunion.io/x/pkg/gotypes"
  27. "yunion.io/x/pkg/util/httputils"
  28. "yunion.io/x/pkg/util/printutils"
  29. "yunion.io/x/pkg/util/rbacscope"
  30. "yunion.io/x/onecloud/pkg/apigateway/clientman"
  31. "yunion.io/x/onecloud/pkg/apigateway/constants"
  32. "yunion.io/x/onecloud/pkg/apigateway/options"
  33. policytool "yunion.io/x/onecloud/pkg/apigateway/policy"
  34. agapi "yunion.io/x/onecloud/pkg/apis/apigateway"
  35. "yunion.io/x/onecloud/pkg/appsrv"
  36. "yunion.io/x/onecloud/pkg/cloudcommon/policy"
  37. "yunion.io/x/onecloud/pkg/httperrors"
  38. "yunion.io/x/onecloud/pkg/mcclient"
  39. "yunion.io/x/onecloud/pkg/mcclient/auth"
  40. "yunion.io/x/onecloud/pkg/mcclient/modulebase"
  41. compute_modules "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
  42. modules "yunion.io/x/onecloud/pkg/mcclient/modules/identity"
  43. "yunion.io/x/onecloud/pkg/mcclient/modules/notify"
  44. "yunion.io/x/onecloud/pkg/mcclient/modules/yunionconf"
  45. "yunion.io/x/onecloud/pkg/util/hashcache"
  46. "yunion.io/x/onecloud/pkg/util/logclient"
  47. "yunion.io/x/onecloud/pkg/util/netutils2"
  48. "yunion.io/x/onecloud/pkg/util/seclib2"
  49. "yunion.io/x/onecloud/pkg/util/stringutils2"
  50. )
  51. func AppContextToken(ctx context.Context) mcclient.TokenCredential {
  52. return auth.FetchUserCredential(ctx, nil)
  53. }
  54. type AuthHandlers struct {
  55. *SHandlers
  56. preLoginHook PreLoginFunc
  57. }
  58. func NewAuthHandlers(prefix string, preLoginHook PreLoginFunc) *AuthHandlers {
  59. return &AuthHandlers{
  60. SHandlers: NewHandlers(prefix),
  61. preLoginHook: preLoginHook,
  62. }
  63. }
  64. func (h *AuthHandlers) AddMethods() {
  65. // no middleware handler
  66. h.AddByMethod(GET, nil,
  67. NewHP(h.getRegions, "regions"),
  68. NewHP(h.getIdpSsoRedirectUri, "sso", "redirect", "<idp_id>"),
  69. NewHP(h.listTotpRecoveryQuestions, "recovery"),
  70. NewHP(h.handleSsoLogin, "ssologin"),
  71. NewHP(h.handleIdpInitSsoLogin, "ssologin", "<idp_id>"),
  72. NewHP(h.postLogoutHandler, "logout"),
  73. NewHP(h.getScopedPolicyBindings, "scopedpolicybindings"),
  74. // oidc auth
  75. NewHP(handleOIDCAuth, "oidc", "auth"),
  76. NewHP(handleOIDCConfiguration, "oidc", ".well-known", "openid-configuration"),
  77. NewHP(handleOIDCJWKeys, "oidc", "keys"),
  78. NewHP(handleOIDCUserInfo, "oidc", "user"),
  79. NewHP(handleOIDCRPInitLogout, "oidc", "logout"),
  80. NewHP(h.handleAssumeLogin, "assume"),
  81. )
  82. h.AddByMethod(POST, nil,
  83. NewHP(h.initTotpSecrets, "initcredential"),
  84. NewHP(h.resetTotpSecrets, "credential"),
  85. NewHP(h.validatePasscode, "passcode"),
  86. NewHP(h.resetTotpRecoveryQuestions, "recovery"),
  87. NewHP(h.postLoginHandler, "login"),
  88. NewHP(h.postLogoutHandler, "logout"),
  89. NewHP(h.handleSsoLogin, "ssologin"),
  90. NewHP(h.handleIdpInitSsoLogin, "ssologin", "<idp_id>"),
  91. NewHP(handleOIDCToken, "oidc", "token"),
  92. NewHP(handleOIDCRPInitLogout, "oidc", "logout"),
  93. )
  94. // auth middleware handler
  95. h.AddByMethod(GET, FetchAuthToken,
  96. NewHP(h.getUser, "user"),
  97. NewHP(h.getStats, "stats"),
  98. NewHP(h.getUserSubscription, "subscriptions"),
  99. NewHP(h.getPermissionDetails, "permissions"),
  100. NewHP(h.getAdminResources, "admin_resources"),
  101. NewHP(h.getResources, "scoped_resources"),
  102. NewHP(fetchIdpBasicConfig, "idp", "<idp_id>", "info"),
  103. NewHP(fetchIdpSAMLMetadata, "idp", "<idp_id>", "saml-metadata"),
  104. )
  105. h.AddByMethod(POST, FetchAuthToken,
  106. NewHP(h.resetUserPassword, "password"),
  107. NewHP(h.getPermissionDetails, "permissions"),
  108. NewHP(h.doCreatePolicies, "policies"),
  109. NewHP(handleUnlinkIdp, "unlink-idp"),
  110. )
  111. h.AddByMethod(PATCH, FetchAuthToken,
  112. NewHP(h.doPatchPolicy, "policies", "<policy_id>"),
  113. )
  114. h.AddByMethod(DELETE, FetchAuthToken,
  115. NewHP(h.doDeletePolicies, "policies"),
  116. )
  117. }
  118. func (h *AuthHandlers) Bind(app *appsrv.Application) {
  119. h.AddMethods()
  120. h.SHandlers.Bind(app)
  121. }
  122. func (h *AuthHandlers) GetRegionsResponse(ctx context.Context, w http.ResponseWriter, req *http.Request) (*agapi.SRegionsReponse, error) {
  123. var currentDomain string
  124. var createUser bool
  125. qs, _ := jsonutils.ParseQueryString(req.URL.RawQuery)
  126. if qs != nil {
  127. currentDomain, _ = qs.GetString("domain")
  128. createUser = jsonutils.QueryBoolean(qs, "auto_create_user", true)
  129. }
  130. adminToken := auth.AdminCredential()
  131. if adminToken == nil {
  132. return nil, errors.Error("failed to get admin credential")
  133. }
  134. regions := adminToken.GetRegions()
  135. if len(regions) == 0 {
  136. return nil, errors.Error("region is empty")
  137. }
  138. ret := &agapi.SRegionsReponse{
  139. Regions: regions,
  140. Domains: []string{},
  141. ReturnFullDomains: false,
  142. Idps: []agapi.SIdp{},
  143. EncryptPasswd: true,
  144. ApiServer: options.Options.ApiServer,
  145. }
  146. s := auth.GetAdminSession(ctx, regions[0])
  147. if options.Options.ReturnFullDomainList {
  148. filters := jsonutils.NewDict()
  149. if len(currentDomain) > 0 {
  150. filters.Add(jsonutils.NewString(currentDomain), "name")
  151. }
  152. filters.Add(jsonutils.NewInt(1000), "limit")
  153. result, e := modules.Domains.List(s, filters)
  154. if e != nil {
  155. return nil, errors.Wrap(e, "list domain")
  156. }
  157. domains := []struct {
  158. Name string
  159. Enabled bool
  160. }{}
  161. jsonutils.Update(&domains, result.Data)
  162. for _, d := range domains {
  163. if len(d.Name) > 0 && d.Enabled {
  164. ret.Domains = append(ret.Domains, d.Name)
  165. }
  166. }
  167. ret.ReturnFullDomains = true
  168. }
  169. filters := jsonutils.NewDict()
  170. filters.Add(jsonutils.JSONTrue, "enabled")
  171. if len(currentDomain) == 0 {
  172. currentDomain = "all"
  173. }
  174. filters.Add(jsonutils.NewString(currentDomain), "sso_domain")
  175. filters.Add(jsonutils.NewString("system"), "scope")
  176. filters.Add(jsonutils.NewInt(1000), "limit")
  177. if !createUser {
  178. filters.Add(jsonutils.JSONFalse, "auto_create_user")
  179. }
  180. idps, err := modules.IdentityProviders.List(s, filters)
  181. if err == nil {
  182. jsonutils.Update(&ret.Idps, idps.Data)
  183. }
  184. // in case api_server changed but not synchronized from Keystone
  185. // fetch this option directly from Keystone
  186. commonCfg, err := modules.ServicesV3.GetSpecific(s, "common", "config", nil)
  187. if err == nil && commonCfg != nil {
  188. apiServer, _ := commonCfg.GetString("config", "default", "api_server")
  189. if len(apiServer) > 0 {
  190. ret.ApiServer = apiServer
  191. }
  192. }
  193. return ret, nil
  194. }
  195. func (h *AuthHandlers) getRegions(ctx context.Context, w http.ResponseWriter, req *http.Request) {
  196. resp, err := h.GetRegionsResponse(ctx, w, req)
  197. if err != nil {
  198. httperrors.GeneralServerError(ctx, w, err)
  199. return
  200. }
  201. appsrv.SendJSON(w, jsonutils.Marshal(resp))
  202. }
  203. var (
  204. bindingCache = hashcache.NewCache(1024, time.Minute)
  205. )
  206. func (h *AuthHandlers) getScopedPolicyBindings(ctx context.Context, w http.ResponseWriter, req *http.Request) {
  207. _, _params, _ := appsrv.FetchEnv(ctx, w, req)
  208. if gotypes.IsNil(_params) {
  209. _params = jsonutils.NewDict()
  210. }
  211. params := _params.(*jsonutils.JSONDict)
  212. token, _, err := fetchAuthInfo(ctx, req)
  213. if err != nil {
  214. httperrors.GeneralServerError(ctx, w, err)
  215. return
  216. }
  217. params.Set("project_id", jsonutils.NewString(token.GetProjectId()))
  218. hash := fmt.Sprintf("%x", md5.Sum([]byte(params.String())))
  219. cache := bindingCache.Get(hash)
  220. if cache != nil {
  221. appsrv.SendJSON(w, jsonutils.Marshal(cache))
  222. return
  223. }
  224. s := auth.GetAdminSession(ctx, options.Options.Region)
  225. resp, err := yunionconf.ScopedPolicyBindings.List(s, params)
  226. if err != nil {
  227. httperrors.GeneralServerError(ctx, w, err)
  228. return
  229. }
  230. bindingCache.AtomicSet(hash, resp)
  231. appsrv.SendJSON(w, jsonutils.Marshal(resp))
  232. }
  233. func (h *AuthHandlers) getUser(ctx context.Context, w http.ResponseWriter, req *http.Request) {
  234. data, err := getUserInfo(ctx, req)
  235. if err != nil {
  236. httperrors.NotFoundError(ctx, w, "%s", err.Error())
  237. return
  238. }
  239. body := jsonutils.NewDict()
  240. body.Add(data, "data")
  241. appsrv.SendJSON(w, body)
  242. }
  243. func (h *AuthHandlers) getUserSubscription(ctx context.Context, w http.ResponseWriter, req *http.Request) {
  244. s := auth.GetAdminSession(ctx, FetchRegion(req))
  245. token := AppContextToken(ctx)
  246. result, err := notify.NotifyReceiver.PerformAction(s, token.GetUserId(), "get-subscription", jsonutils.Marshal(map[string]interface{}{
  247. "receiver": map[string]string{
  248. "id": token.GetUserId(),
  249. },
  250. }))
  251. if err != nil {
  252. httperrors.GeneralServerError(ctx, w, err)
  253. return
  254. }
  255. appsrv.SendJSON(w, result)
  256. }
  257. func getStatsInfo(ctx context.Context, req *http.Request) (jsonutils.JSONObject, error) {
  258. token := AppContextToken(ctx)
  259. s := auth.GetSession(ctx, token, FetchRegion(req))
  260. params, _ := jsonutils.ParseQueryString(req.URL.RawQuery)
  261. if params == nil {
  262. params = jsonutils.NewDict()
  263. }
  264. params.(*jsonutils.JSONDict).Add(jsonutils.NewInt(1), "limit")
  265. ret := struct {
  266. Cloudaccounts int
  267. }{}
  268. accounts, err := compute_modules.Cloudaccounts.List(s, params)
  269. if err != nil && httputils.ErrorCode(err) != 403 {
  270. return nil, err
  271. }
  272. if accounts != nil {
  273. ret.Cloudaccounts = accounts.Total
  274. }
  275. return jsonutils.Marshal(ret), nil
  276. }
  277. func (h *AuthHandlers) getStats(ctx context.Context, w http.ResponseWriter, req *http.Request) {
  278. data, err := getStatsInfo(ctx, req)
  279. if err != nil {
  280. httperrors.GeneralServerError(ctx, w, err)
  281. return
  282. }
  283. body := jsonutils.NewDict()
  284. body.Add(data, "data")
  285. appsrv.SendJSON(w, body)
  286. }
  287. func (h *AuthHandlers) initTotpSecrets(ctx context.Context, w http.ResponseWriter, req *http.Request) {
  288. initTotpSecrets(ctx, w, req)
  289. }
  290. func (h *AuthHandlers) resetTotpSecrets(ctx context.Context, w http.ResponseWriter, req *http.Request) {
  291. resetTotpSecrets(ctx, w, req)
  292. }
  293. func (h *AuthHandlers) validatePasscode(ctx context.Context, w http.ResponseWriter, req *http.Request) {
  294. validatePasscodeHandler(ctx, w, req)
  295. }
  296. func (h *AuthHandlers) resetTotpRecoveryQuestions(ctx context.Context, w http.ResponseWriter, req *http.Request) {
  297. resetTotpRecoveryQuestions(ctx, w, req)
  298. }
  299. func (h *AuthHandlers) listTotpRecoveryQuestions(ctx context.Context, w http.ResponseWriter, req *http.Request) {
  300. listTotpRecoveryQuestions(ctx, w, req)
  301. }
  302. // 返回 token及totp验证状态
  303. func doTenantLogin(ctx context.Context, req *http.Request, body jsonutils.JSONObject) (mcclient.TokenCredential, *clientman.SAuthToken, error) {
  304. tenantId, err := body.GetString("tenantId")
  305. if err != nil {
  306. return nil, nil, httperrors.NewInputParameterError("not found tenantId in body")
  307. }
  308. token, authToken, err := fetchAuthInfo(ctx, req)
  309. if err != nil {
  310. return nil, nil, errors.Wrapf(httperrors.ErrInvalidCredential, "fetchAuthToken fail %s", err)
  311. }
  312. if !authToken.IsTotpVerified() {
  313. return nil, nil, errors.Wrap(httperrors.ErrInvalidCredential, "TOTP authentication failed")
  314. }
  315. ntoken, err := auth.Client().SetProject(tenantId, "", "", token)
  316. if err != nil {
  317. return nil, nil, httperrors.NewInvalidCredentialError("failed to change project")
  318. }
  319. authToken.SetToken(ntoken.GetTokenString())
  320. return ntoken, authToken, nil
  321. }
  322. func fetchUserInfoFromToken(ctx context.Context, req *http.Request, token mcclient.TokenCredential) (jsonutils.JSONObject, error) {
  323. s := auth.GetAdminSession(ctx, FetchRegion(req))
  324. return fetchUserInfoById(s, token.GetUserId())
  325. }
  326. func fetchUserInfoById(s *mcclient.ClientSession, userId string) (jsonutils.JSONObject, error) {
  327. info, err := modules.UsersV3.Get(s, userId, nil)
  328. if err != nil {
  329. return nil, errors.Wrap(err, "UsersV3.Get")
  330. }
  331. return info, nil
  332. }
  333. func isUserEnableTotp(userInfo jsonutils.JSONObject) bool {
  334. return jsonutils.QueryBoolean(userInfo, "enable_mfa", false)
  335. }
  336. func (h *AuthHandlers) doCredentialLogin(ctx context.Context, req *http.Request, body jsonutils.JSONObject) (mcclient.TokenCredential, error) {
  337. var token mcclient.TokenCredential
  338. var err error
  339. var tenant string
  340. // log.Debugf("doCredentialLogin body: %s", body)
  341. cliIp := netutils2.GetHttpRequestIp(req)
  342. if body.Contains("username") {
  343. if h.preLoginHook != nil {
  344. if err = h.preLoginHook(ctx, req, body); err != nil {
  345. return nil, err
  346. }
  347. }
  348. uname, _ := body.GetString("username")
  349. var passwd string
  350. passwd, err = body.GetString("password")
  351. if err != nil {
  352. return nil, httperrors.NewInputParameterError("get password in body")
  353. }
  354. passwd = decodePassword(passwd)
  355. if len(uname) == 0 || len(passwd) == 0 {
  356. return nil, httperrors.NewInputParameterError("username or password is empty")
  357. }
  358. tenant, uname = parseLoginUser(uname)
  359. // var token mcclient.TokenCredential
  360. domain, _ := body.GetString("domain")
  361. token, err = auth.Client().AuthenticateWeb(uname, passwd, domain, "", "", cliIp)
  362. } else if body.Contains("idp_driver") { // sso login
  363. idpId, _ := body.GetString("idp_id")
  364. redirectUri := getSsoCallbackUrl(ctx, req, idpId)
  365. token, err = processSsoLoginData(body, cliIp, redirectUri)
  366. // if err != nil {
  367. // return nil, errors.Wrap(err, "processSsoLoginData")
  368. // }
  369. } else if body.Contains("verify_code") { // verify by mobile
  370. if h.preLoginHook != nil {
  371. if err = h.preLoginHook(ctx, req, body); err != nil {
  372. return nil, err
  373. }
  374. }
  375. verifyCode, _ := body.GetString("verify_code")
  376. uid, _ := body.GetString("uid")
  377. contactType, _ := body.GetString("contact_type")
  378. token, err = processVerifyLoginData(uid, contactType, verifyCode, cliIp)
  379. } else {
  380. return nil, httperrors.NewInputParameterError("missing credential")
  381. }
  382. if err != nil {
  383. switch httperr := err.(type) {
  384. case *httputils.JSONClientError:
  385. if httperr.Code >= 500 {
  386. return nil, err
  387. }
  388. if httperr.Code == 409 || httperr.Code == 429 {
  389. return nil, err
  390. }
  391. switch errors.Error(httperr.Class) {
  392. case httperrors.ErrUserNotFound, httperrors.ErrWrongPassword:
  393. return nil, httperrors.NewJsonClientError(httperrors.ErrIncorrectUsernameOrPassword, "incorrect username or password")
  394. case httperrors.ErrUserLocked:
  395. return nil, httperrors.NewJsonClientError(httperrors.ErrUserLocked, "The user has been locked, please contact the administrator")
  396. case httperrors.ErrUserDisabled:
  397. return nil, httperrors.NewJsonClientError(httperrors.ErrUserDisabled, "The user has been disabled, please contact the administrator")
  398. case httperrors.ErrInvalidIdpStatus:
  399. return nil, httperrors.NewJsonClientError(httperrors.ErrInvalidIdpStatus, "The IDP of user has been disabled or in invalid status")
  400. }
  401. }
  402. return nil, httperrors.NewInvalidCredentialError("invalid credential")
  403. }
  404. uname := token.GetUserName()
  405. if len(tenant) > 0 {
  406. s := auth.GetAdminSession(ctx, FetchRegion(req))
  407. jsonProj, e := modules.Projects.GetById(s, tenant, nil)
  408. if e != nil {
  409. log.Errorf("fail to find preset project %s, reset to empty", tenant)
  410. tenant = ""
  411. } else {
  412. projId, _ := jsonProj.GetString("id")
  413. // projName, _ := jsonProj.GetString("name")
  414. ntoken, e := auth.Client().SetProject(projId, "", "", token)
  415. if e != nil {
  416. log.Errorf("fail to change to preset project %s(%s), reset to empty", tenant, e)
  417. tenant = ""
  418. } else {
  419. token = ntoken
  420. }
  421. }
  422. }
  423. if len(tenant) == 0 {
  424. token3, ok := token.(*mcclient.TokenCredentialV3)
  425. if ok {
  426. targetLevel := rbacscope.ScopeProject
  427. targetProjId := ""
  428. for _, r := range token3.Token.RoleAssignments {
  429. level := rbacscope.ScopeProject
  430. if len(r.Policies.System) > 0 {
  431. level = rbacscope.ScopeSystem
  432. } else if len(r.Policies.Domain) > 0 {
  433. level = rbacscope.ScopeDomain
  434. }
  435. if len(targetProjId) == 0 || level.HigherThan(targetLevel) {
  436. targetProjId = r.Scope.Project.Id
  437. targetLevel = level
  438. }
  439. }
  440. if len(targetProjId) > 0 {
  441. ntoken, e := auth.Client().SetProject(targetProjId, "", "", token)
  442. if e != nil {
  443. log.Errorf("fail to change to project %s(%s), reset to empty", targetProjId, e)
  444. } else {
  445. token = ntoken
  446. tenant = targetProjId
  447. body.(*jsonutils.JSONDict).Set("scope", jsonutils.NewString(string(targetLevel)))
  448. }
  449. }
  450. }
  451. }
  452. if len(tenant) == 0 {
  453. s := auth.GetAdminSession(ctx, FetchRegion(req))
  454. projects, e := modules.UsersV3.GetProjects(s, token.GetUserId())
  455. if e == nil && len(projects.Data) > 0 {
  456. projectJson := projects.Data[0]
  457. for _, pJson := range projects.Data {
  458. pname, _ := pJson.GetString("name")
  459. if pname == uname {
  460. projectJson = pJson
  461. break
  462. }
  463. }
  464. pid, e := projectJson.GetString("id")
  465. if e == nil {
  466. ntoken, e := auth.Client().SetProject(pid, "", "", token)
  467. if e == nil {
  468. token = ntoken
  469. } else {
  470. log.Errorf("fail to change to default project %s(%s), reset to empty", pid, e)
  471. }
  472. }
  473. } else {
  474. log.Errorf("GetProjects for login user error %s project count %d", e, len(projects.Data))
  475. }
  476. }
  477. return token, nil
  478. }
  479. func parseLoginUser(uname string) (string, string) {
  480. slashpos := strings.IndexByte(uname, '/')
  481. tenant := ""
  482. if slashpos > 0 {
  483. tenant = uname[0:slashpos]
  484. uname = uname[slashpos+1:]
  485. }
  486. return tenant, uname
  487. }
  488. func isUserAllowWebconsole(userInfo jsonutils.JSONObject) bool {
  489. return jsonutils.QueryBoolean(userInfo, "allow_web_console", true)
  490. }
  491. func saveCookie(w http.ResponseWriter, name, val, domain string, expire time.Time, base64 bool) {
  492. var maxAge int
  493. if !expire.IsZero() {
  494. diff := time.Until(expire)
  495. maxAge = int(diff.Seconds())
  496. }
  497. // log.Println("Set cookie", name, expire, maxAge, val)
  498. var valenc string
  499. if base64 {
  500. valenc = Base64UrlEncode([]byte(val))
  501. } else {
  502. valenc = val
  503. }
  504. // log.Printf("Set coookie: %s - %s\n", val, valenc)
  505. cookie := &http.Cookie{Name: name, Value: valenc, Path: "/", Expires: expire, MaxAge: maxAge, HttpOnly: false}
  506. if len(domain) > 0 {
  507. cookie.Domain = domain
  508. }
  509. http.SetCookie(w, cookie)
  510. }
  511. func getCookie(r *http.Request, name string) string {
  512. return getCookie2(r, name, true)
  513. }
  514. func getCookie2(r *http.Request, name string, base64 bool) string {
  515. cookie, err := r.Cookie(name)
  516. if err != nil {
  517. log.Errorf("Cookie not found %q", name)
  518. return ""
  519. // } else if cookie.Expires.Before(time.Now()) {
  520. // fmt.Println("Cookie expired ", cookie.Expires, time.Now())
  521. // return ""
  522. } else {
  523. if !base64 {
  524. return cookie.Value
  525. }
  526. val, err := Base64UrlDecode(cookie.Value)
  527. if err != nil {
  528. log.Errorf("Cookie %q fail to decode: %v", name, err)
  529. return ""
  530. }
  531. return string(val)
  532. }
  533. }
  534. func clearCookie(w http.ResponseWriter, name string, domain string) {
  535. cookie := &http.Cookie{Name: name, Expires: time.Now(), Path: "/", MaxAge: -1, HttpOnly: false}
  536. if len(domain) > 0 {
  537. cookie.Domain = domain
  538. }
  539. http.SetCookie(w, cookie)
  540. }
  541. func saveAuthCookie(w http.ResponseWriter, authToken *clientman.SAuthToken, token mcclient.TokenCredential) {
  542. authCookie := authToken.GetAuthCookie(token)
  543. expire := time.Time{}
  544. if !options.Options.SessionLevelAuthCookie {
  545. expire = token.GetExpires()
  546. }
  547. saveCookie(w, constants.YUNION_AUTH_COOKIE, authCookie, options.Options.CookieDomain, expire, true)
  548. }
  549. func clearAuthCookie(w http.ResponseWriter) {
  550. clearCookie(w, constants.YUNION_AUTH_COOKIE, options.Options.CookieDomain)
  551. }
  552. type PreLoginFunc func(ctx context.Context, req *http.Request, body jsonutils.JSONObject) error
  553. func (h *AuthHandlers) postLoginHandler(ctx context.Context, w http.ResponseWriter, req *http.Request) {
  554. body, err := appsrv.FetchJSON(req)
  555. if err != nil {
  556. httperrors.InvalidInputError(ctx, w, "fetch json for request: %v", err)
  557. return
  558. }
  559. err = h.doLogin(ctx, w, req, body)
  560. if err != nil {
  561. httperrors.GeneralServerError(ctx, w, err)
  562. return
  563. }
  564. // normal
  565. appsrv.Send(w, "")
  566. }
  567. func (h *AuthHandlers) doLogin(ctx context.Context, w http.ResponseWriter, req *http.Request, body jsonutils.JSONObject) error {
  568. var err error
  569. var authToken *clientman.SAuthToken
  570. var token mcclient.TokenCredential
  571. var userInfo jsonutils.JSONObject
  572. if body.Contains("tenantId") { // switch project
  573. token, authToken, err = doTenantLogin(ctx, req, body)
  574. if err != nil {
  575. return err
  576. }
  577. userInfo, err = fetchUserInfoFromToken(ctx, req, token)
  578. if err != nil {
  579. return err
  580. }
  581. } else {
  582. // user/password authenticate
  583. // SSO authentication
  584. token, err = h.doCredentialLogin(ctx, req, body)
  585. if err != nil {
  586. return err
  587. }
  588. userInfo, err = fetchUserInfoFromToken(ctx, req, token)
  589. if err != nil {
  590. return err
  591. }
  592. s := auth.GetAdminSession(ctx, FetchRegion(req))
  593. isTotpInit, err := isUserTotpCredInitialed(s, token.GetUserId())
  594. if err != nil {
  595. return err
  596. }
  597. isIdpLogin := body.Contains("idp_driver")
  598. authToken = clientman.NewAuthToken(token.GetTokenString(), isUserEnableTotp(userInfo), isTotpInit, isIdpLogin)
  599. }
  600. if !isUserAllowWebconsole(userInfo) {
  601. return httperrors.NewForbiddenError("user forbidden login from web")
  602. }
  603. saveLoginCookies(w, authToken, token, body)
  604. return nil
  605. }
  606. func saveLoginCookies(w http.ResponseWriter, authToken *clientman.SAuthToken, token mcclient.TokenCredential, body jsonutils.JSONObject) {
  607. saveAuthCookie(w, authToken, token)
  608. if len(token.GetProjectId()) > 0 {
  609. if body != nil && body.Contains("isadmin") {
  610. adminVal := "false"
  611. if policy.PolicyManager.IsScopeCapable(token, rbacscope.ScopeSystem) {
  612. adminVal, _ = body.GetString("isadmin")
  613. }
  614. saveCookie(w, "isadmin", adminVal, "", token.GetExpires(), false)
  615. }
  616. if body != nil && body.Contains("scope") {
  617. scopeStr, _ := body.GetString("scope")
  618. if !policy.PolicyManager.IsScopeCapable(token, rbacscope.TRbacScope(scopeStr)) {
  619. scopeStr = string(rbacscope.ScopeProject)
  620. }
  621. saveCookie(w, "scope", scopeStr, "", token.GetExpires(), false)
  622. }
  623. if body != nil && body.Contains("domain") {
  624. domainStr, _ := body.GetString("domain")
  625. saveCookie(w, "domain", domainStr, "", token.GetExpires(), false)
  626. }
  627. saveCookie(w, "tenant", token.GetProjectId(), "", token.GetExpires(), false)
  628. }
  629. }
  630. func doLogout(ctx context.Context, w http.ResponseWriter, req *http.Request) {
  631. token, _, _ := fetchAuthInfo(ctx, req)
  632. if token != nil {
  633. // valid login, log the event
  634. err := auth.Remove(ctx, token.GetTokenString())
  635. if err != nil {
  636. log.Errorf("remove token fail %s", err)
  637. return
  638. } else {
  639. user := logclient.NewSimpleObject(token.GetUserId(), token.GetUserName(), "user")
  640. logclient.AddActionLogWithContext(ctx, user, logclient.ACT_LOGOUT, "", token, true)
  641. }
  642. }
  643. clearAuthCookie(w)
  644. appsrv.DisableClientCache(w)
  645. }
  646. func (h *AuthHandlers) postLogoutHandler(ctx context.Context, w http.ResponseWriter, req *http.Request) {
  647. doLogout(ctx, w, req)
  648. appsrv.Send(w, "")
  649. }
  650. func FetchRegion(req *http.Request) string {
  651. r, e := req.Cookie("region")
  652. if e == nil && len(r.Value) > 0 {
  653. return r.Value
  654. }
  655. if len(options.Options.DefaultRegion) > 0 {
  656. return options.Options.DefaultRegion
  657. }
  658. adminToken := auth.AdminCredential()
  659. if adminToken == nil {
  660. log.Errorf("FetchRegion: nil adminTken")
  661. return ""
  662. }
  663. regions := adminToken.GetRegions()
  664. if len(regions) == 0 {
  665. log.Errorf("FetchRegion: empty region list")
  666. return ""
  667. }
  668. for _, r := range regions {
  669. if len(r) > 0 {
  670. return r
  671. }
  672. }
  673. log.Errorf("FetchRegion: no valid region")
  674. return ""
  675. }
  676. type role struct {
  677. id string
  678. name string
  679. }
  680. type projectRoles struct {
  681. id string
  682. name string
  683. domain string
  684. domainId string
  685. roles []role
  686. }
  687. func newProjectRoles(projectId, projectName, roleId, roleName string, domainId, domainName string) *projectRoles {
  688. return &projectRoles{
  689. id: projectId,
  690. name: projectName,
  691. domainId: domainId,
  692. domain: domainName,
  693. roles: []role{{id: roleId, name: roleName}},
  694. }
  695. }
  696. func (this *projectRoles) add(roleId, roleName string) {
  697. this.roles = append(this.roles, role{id: roleId, name: roleName})
  698. }
  699. func (this *projectRoles) getToken(user, userId, domain, domainId string, ip string) mcclient.TokenCredential {
  700. return &mcclient.SSimpleToken{
  701. Token: "faketoken",
  702. Domain: domain,
  703. DomainId: domainId,
  704. User: user,
  705. UserId: userId,
  706. Project: this.name,
  707. ProjectId: this.id,
  708. ProjectDomain: this.domain,
  709. ProjectDomainId: this.domainId,
  710. Roles: strings.Join(this.getRoles(), ","),
  711. RoleIds: strings.Join(this.getRoleIds(), ","),
  712. Context: mcclient.SAuthContext{
  713. Ip: ip,
  714. },
  715. }
  716. }
  717. func (this *projectRoles) getRoles() []string {
  718. roles := make([]string, 0)
  719. for _, r := range this.roles {
  720. roles = append(roles, r.name)
  721. }
  722. return roles
  723. }
  724. func (this *projectRoles) getRoleIds() []string {
  725. roles := make([]string, 0)
  726. for _, r := range this.roles {
  727. roles = append(roles, r.id)
  728. }
  729. return roles
  730. }
  731. func (this *projectRoles) json(s *mcclient.ClientSession, user, userId, domain, domainId string, ip string) (jsonutils.JSONObject, map[string][]string) {
  732. obj := jsonutils.NewDict()
  733. obj.Add(jsonutils.NewString(this.id), "id")
  734. obj.Add(jsonutils.NewString(this.name), "name")
  735. obj.Add(jsonutils.NewString(this.domain), "domain")
  736. obj.Add(jsonutils.NewString(this.domainId), "domain_id")
  737. roleIds := make([]string, 0)
  738. roles := jsonutils.NewArray()
  739. for _, r := range this.roles {
  740. role := jsonutils.NewDict()
  741. role.Add(jsonutils.NewString(r.id), "id")
  742. role.Add(jsonutils.NewString(r.name), "name")
  743. roles.Add(role)
  744. roleIds = append(roleIds, r.id)
  745. }
  746. obj.Add(roles, "roles")
  747. policies, _ := modules.RolePolicies.FetchMatchedPolicies(s, roleIds, this.id, ip)
  748. for _, scope := range []rbacscope.TRbacScope{
  749. rbacscope.ScopeProject,
  750. rbacscope.ScopeDomain,
  751. rbacscope.ScopeSystem,
  752. } {
  753. if matches, ok := policies[string(scope)]; ok {
  754. obj.Add(jsonutils.NewStringArray(matches), fmt.Sprintf("%s_policies", scope))
  755. if len(matches) > 0 {
  756. obj.Add(jsonutils.JSONTrue, fmt.Sprintf("%s_capable", scope))
  757. } else {
  758. obj.Add(jsonutils.JSONFalse, fmt.Sprintf("%s_capable", scope))
  759. }
  760. // backward compatible
  761. if scope == rbacscope.ScopeSystem {
  762. if len(matches) > 0 {
  763. obj.Add(jsonutils.JSONTrue, "admin_capable")
  764. } else {
  765. obj.Add(jsonutils.JSONFalse, "admin_capable")
  766. }
  767. }
  768. }
  769. }
  770. return obj, policies
  771. }
  772. func isLBAgentExists(s *mcclient.ClientSession) (bool, error) {
  773. params := jsonutils.NewDict()
  774. params.Add(jsonutils.NewString("hb_last_seen.isnotempty()"), "filter.0")
  775. params.Add(jsonutils.NewInt(1), "limit")
  776. params.Add(jsonutils.JSONFalse, "details")
  777. agents, err := compute_modules.LoadbalancerAgents.List(s, params)
  778. if err != nil {
  779. return false, errors.Wrap(err, "modules.LoadbalancerAgents.List")
  780. }
  781. if len(agents.Data) > 0 {
  782. return true, nil
  783. } else {
  784. return false, nil
  785. }
  786. }
  787. func isBaremetalAgentExists(s *mcclient.ClientSession) (bool, error) {
  788. params := jsonutils.NewDict()
  789. params.Add(jsonutils.NewString("agent_type.equals(baremetal)"), "filter.0")
  790. params.Add(jsonutils.NewInt(1), "limit")
  791. params.Add(jsonutils.JSONFalse, "details")
  792. agents, err := compute_modules.Baremetalagents.List(s, params)
  793. if err != nil {
  794. return false, errors.Wrap(err, "modules.Baremetalagents.List")
  795. }
  796. if len(agents.Data) > 0 {
  797. return true, nil
  798. } else {
  799. return false, nil
  800. }
  801. }
  802. func isEsxiAgentExists(s *mcclient.ClientSession) (bool, error) {
  803. params := jsonutils.NewDict()
  804. params.Add(jsonutils.NewString("agent_type.equals(esxiagent)"), "filter.0")
  805. params.Add(jsonutils.NewInt(1), "limit")
  806. params.Add(jsonutils.JSONFalse, "details")
  807. agents, err := compute_modules.Baremetalagents.List(s, params)
  808. if err != nil {
  809. return false, errors.Wrap(err, "modules.Baremetalagents.List esxiagent")
  810. }
  811. if len(agents.Data) > 0 {
  812. return true, nil
  813. } else {
  814. return false, nil
  815. }
  816. }
  817. func isHostAgentExists(s *mcclient.ClientSession) (bool, error) {
  818. params := jsonutils.NewDict()
  819. params.Add(jsonutils.JSONFalse, "show_emulated")
  820. params.Add(jsonutils.JSONFalse, "baremetal")
  821. params.Add(jsonutils.NewString("system"), "scope")
  822. params.Add(jsonutils.NewInt(1), "limit")
  823. params.Add(jsonutils.JSONFalse, "details")
  824. agents, err := compute_modules.Hosts.List(s, params)
  825. if err != nil {
  826. return false, errors.Wrap(err, "modules.LoadbalancerAgents.List")
  827. }
  828. if len(agents.Data) > 0 {
  829. return true, nil
  830. } else {
  831. return false, nil
  832. }
  833. }
  834. func getUserInfo(ctx context.Context, req *http.Request) (*jsonutils.JSONDict, error) {
  835. token := AppContextToken(ctx)
  836. s := auth.GetAdminSession(ctx, FetchRegion(req))
  837. /*log.Infof("getUserInfo modules.UsersV3.Get")
  838. usr, err := modules.UsersV3.Get(s, token.GetUserId(), nil)
  839. if err != nil {
  840. log.Errorf("modules.UsersV3.Get fail %s", err)
  841. return nil, fmt.Errorf("not found user %s", token.GetUserId())
  842. }*/
  843. // usr, err := fetchUserInfoFromToken(ctx, req, token)
  844. return getUserInfo2(s, token.GetUserId(), token.GetProjectId(), token.GetLoginIp())
  845. }
  846. func getUserInfo2(s *mcclient.ClientSession, uid string, pid string, loginIp string) (*jsonutils.JSONDict, error) {
  847. usr, err := fetchUserInfoById(s, uid)
  848. if err != nil {
  849. return nil, errors.Wrapf(err, "fetchUserInfoFromToken %s", uid)
  850. }
  851. data := jsonutils.NewDict()
  852. for _, k := range []string{
  853. "displayname", "email", "id", "name",
  854. "enabled", "mobile", "allow_web_console",
  855. "created_at", "enable_mfa", "is_system_account",
  856. "last_active_at", "last_login_ip",
  857. "last_login_source",
  858. "password_expires_at", "failed_auth_count", "failed_auth_at",
  859. "need_reset_password", "password_reset_hint",
  860. "idps",
  861. "is_local",
  862. } {
  863. v, e := usr.Get(k)
  864. if e == nil {
  865. data.Add(v, k)
  866. }
  867. }
  868. usrId, _ := usr.GetString("id")
  869. usrName, _ := usr.GetString("name")
  870. usrDomainId, _ := usr.GetString("domain_id")
  871. usrDomainName, _ := usr.GetString("project_domain")
  872. data.Add(jsonutils.NewString(usrDomainId), "domain", "id")
  873. data.Add(jsonutils.NewString(usrDomainName), "domain", "name")
  874. data.Add(jsonutils.NewStringArray(auth.AdminCredential().GetRegions()), "regions")
  875. data.Add(jsonutils.NewBool(options.Options.EnableTotp), "system_totp_on")
  876. var projName string
  877. var projDomainId string
  878. if len(pid) > 0 {
  879. projInfo, err := modules.Projects.GetById(s, pid, nil)
  880. if err != nil {
  881. return nil, errors.Wrapf(err, "fetchProjectById %s", pid)
  882. }
  883. projName, _ = projInfo.GetString("name")
  884. projId, _ := projInfo.GetString("id")
  885. projDomainId, _ = projInfo.GetString("domain_id")
  886. projDomainName, _ := projInfo.GetString("project_domain")
  887. data.Add(jsonutils.NewString(projName), "projectName")
  888. data.Add(jsonutils.NewString(projId), "projectId")
  889. data.Add(jsonutils.NewString(projDomainName), "projectDomain")
  890. data.Add(jsonutils.NewString(projDomainId), "projectDomainId")
  891. pmeta, err := projInfo.Get("metadata")
  892. if pmeta != nil {
  893. data.Add(pmeta, "project_meta")
  894. }
  895. }
  896. log.Infof("getUserInfo modules.RoleAssignments.List")
  897. query := jsonutils.NewDict()
  898. query.Add(jsonutils.JSONNull, "effective")
  899. query.Add(jsonutils.JSONNull, "include_names")
  900. query.Add(jsonutils.JSONNull, "include_system")
  901. query.Add(jsonutils.NewInt(0), "limit")
  902. query.Add(jsonutils.NewString(uid), "user", "id")
  903. roleAssigns, err := modules.RoleAssignments.List(s, query)
  904. if err != nil {
  905. return nil, errors.Wrapf(err, "get RoleAssignments list")
  906. }
  907. currentRoles := make([]string, 0)
  908. projects := make(map[string]*projectRoles)
  909. for _, roleAssign := range roleAssigns.Data {
  910. roleId, _ := roleAssign.GetString("role", "id")
  911. roleName, _ := roleAssign.GetString("role", "name")
  912. projectId, _ := roleAssign.GetString("scope", "project", "id")
  913. projectName, _ := roleAssign.GetString("scope", "project", "name")
  914. domainId, _ := roleAssign.GetString("scope", "project", "domain", "id")
  915. domain, _ := roleAssign.GetString("scope", "project", "domain", "name")
  916. if projectId == pid {
  917. currentRoles = append(currentRoles, roleName)
  918. }
  919. _, ok := projects[projectId]
  920. if ok {
  921. projects[projectId].add(roleId, roleName)
  922. } else {
  923. projects[projectId] = newProjectRoles(projectId, projectName, roleId, roleName, domainId, domain)
  924. }
  925. }
  926. data.Add(jsonutils.NewStringArray(currentRoles), "roles")
  927. var policies map[string][]string
  928. projJson := jsonutils.NewArray()
  929. for _, proj := range projects {
  930. j, p := proj.json(
  931. s,
  932. usrName,
  933. usrId,
  934. usrDomainName,
  935. usrDomainId,
  936. loginIp,
  937. )
  938. projJson.Add(j)
  939. if proj.id == pid {
  940. policies = p
  941. }
  942. }
  943. data.Add(projJson, "projects")
  944. if len(pid) > 0 {
  945. for _, scope := range []rbacscope.TRbacScope{
  946. rbacscope.ScopeSystem,
  947. rbacscope.ScopeDomain,
  948. rbacscope.ScopeProject,
  949. } {
  950. if p, ok := policies[string(scope)]; ok {
  951. data.Add(jsonutils.NewStringArray(p), fmt.Sprintf("%s_policies", scope))
  952. if scope == rbacscope.ScopeSystem {
  953. data.Add(jsonutils.NewStringArray(p), "admin_policies")
  954. } else if scope == rbacscope.ScopeProject {
  955. data.Add(jsonutils.NewStringArray(p), "policies")
  956. }
  957. }
  958. }
  959. }
  960. services := jsonutils.NewArray()
  961. menus := jsonutils.NewArray()
  962. k8s := jsonutils.NewArray()
  963. curReg := s.GetRegion()
  964. srvCat := auth.Client().GetServiceCatalog()
  965. var allsrv []string
  966. var alleps []mcclient.ExternalService
  967. if srvCat != nil {
  968. allsrv = srvCat.GetInternalServices(curReg)
  969. alleps = srvCat.GetServicesByInterface(curReg, "console")
  970. }
  971. log.Infof("getUserInfo checkAgent exists")
  972. for _, cf := range []struct {
  973. existFunc func(*mcclient.ClientSession) (bool, error)
  974. srvName string
  975. }{
  976. {
  977. existFunc: isLBAgentExists,
  978. srvName: "lbagent",
  979. },
  980. {
  981. existFunc: isBaremetalAgentExists,
  982. srvName: "bmagent",
  983. },
  984. {
  985. existFunc: isHostAgentExists,
  986. srvName: "hostagent",
  987. },
  988. {
  989. existFunc: isEsxiAgentExists,
  990. srvName: "esxiagent",
  991. },
  992. } {
  993. exist, err := cf.existFunc(s)
  994. if err != nil {
  995. log.Errorf("isLBAgentExists fail %s", err)
  996. } else if exist {
  997. allsrv = append(allsrv, cf.srvName)
  998. }
  999. }
  1000. for _, srv := range allsrv {
  1001. item := jsonutils.NewDict()
  1002. item.Add(jsonutils.NewString(srv), "type")
  1003. item.Add(jsonutils.JSONTrue, "status")
  1004. services.Add(item)
  1005. }
  1006. for _, ep := range alleps {
  1007. item := jsonutils.NewDict()
  1008. item.Add(jsonutils.NewString(ep.Url), "url")
  1009. item.Add(jsonutils.NewString(ep.Name), "name")
  1010. item.Add(jsonutils.NewString(ep.Service), "service")
  1011. menus.Add(item)
  1012. }
  1013. data.Add(menus, "menus")
  1014. data.Add(k8s, "k8sdashboard")
  1015. data.Add(services, "services")
  1016. if options.Options.NonDefaultDomainProjects {
  1017. data.Add(jsonutils.JSONTrue, "non_default_domain_projects")
  1018. } else {
  1019. data.Add(jsonutils.JSONFalse, "non_default_domain_projects")
  1020. }
  1021. if options.Options.EnableQuotaCheck {
  1022. data.Add(jsonutils.JSONTrue, "enable_quota_check")
  1023. } else {
  1024. data.Add(jsonutils.JSONFalse, "enable_quota_check")
  1025. }
  1026. // data.Add(jsonutils.NewString(getSsoCallbackUrl(ctx, req, idpId)), "sso_callback_url")
  1027. return data, nil
  1028. }
  1029. func (h *AuthHandlers) getPermissionDetails(ctx context.Context, w http.ResponseWriter, req *http.Request) {
  1030. t := AppContextToken(ctx)
  1031. _, query, body := appsrv.FetchEnv(ctx, w, req)
  1032. if body == nil {
  1033. httperrors.InvalidInputError(ctx, w, "request body is empty")
  1034. return
  1035. }
  1036. var name string
  1037. if query != nil {
  1038. name, _ = query.GetString("policy")
  1039. }
  1040. result, err := policy.ExplainRpc(ctx, t, body, name)
  1041. if err != nil {
  1042. httperrors.GeneralServerError(ctx, w, err)
  1043. return
  1044. }
  1045. appsrv.SendJSON(w, result)
  1046. }
  1047. func (h *AuthHandlers) getAdminResources(ctx context.Context, w http.ResponseWriter, req *http.Request) {
  1048. res := policy.GetSystemResources()
  1049. appsrv.SendJSON(w, jsonutils.Marshal(res))
  1050. }
  1051. func (h *AuthHandlers) getResources(ctx context.Context, w http.ResponseWriter, req *http.Request) {
  1052. res := policy.GetResources()
  1053. appsrv.SendJSON(w, jsonutils.Marshal(res))
  1054. }
  1055. func (h *AuthHandlers) doCreatePolicies(ctx context.Context, w http.ResponseWriter, req *http.Request) {
  1056. t := AppContextToken(ctx)
  1057. // if !utils.IsInStringArray("admin", t.GetRoles()) || t.GetProjectName() != "system" {
  1058. // httperrors.ForbiddenError(ctx, w, "not allow to create policy")
  1059. // return
  1060. // }
  1061. _, _, body := appsrv.FetchEnv(ctx, w, req)
  1062. if body == nil {
  1063. httperrors.InvalidInputError(ctx, w, "request body is empty")
  1064. return
  1065. }
  1066. s := auth.GetSession(ctx, t, FetchRegion(req))
  1067. result, err := policytool.PolicyCreate(s, body)
  1068. if err != nil {
  1069. httperrors.GeneralServerError(ctx, w, err)
  1070. return
  1071. }
  1072. appsrv.SendJSON(w, result)
  1073. }
  1074. func (h *AuthHandlers) doPatchPolicy(ctx context.Context, w http.ResponseWriter, req *http.Request) {
  1075. t := AppContextToken(ctx)
  1076. // if !utils.IsInStringArray("admin", t.GetRoles()) || t.GetProjectName() != "system" {
  1077. // httperrors.ForbiddenError(ctx, w, "not allow to create policy")
  1078. // return
  1079. // }
  1080. params, _, body := appsrv.FetchEnv(ctx, w, req)
  1081. if body == nil {
  1082. httperrors.InvalidInputError(ctx, w, "request body is empty")
  1083. return
  1084. }
  1085. s := auth.GetSession(ctx, t, FetchRegion(req))
  1086. result, err := policytool.PolicyPatch(s, params["<policy_id>"], body)
  1087. if err != nil {
  1088. httperrors.GeneralServerError(ctx, w, err)
  1089. return
  1090. }
  1091. appsrv.SendJSON(w, result)
  1092. }
  1093. func (h *AuthHandlers) doDeletePolicies(ctx context.Context, w http.ResponseWriter, req *http.Request) {
  1094. t := AppContextToken(ctx)
  1095. // if !utils.IsInStringArray("admin", t.GetRoles()) || t.GetProjectName() != "system" {
  1096. // httperrors.ForbiddenError(ctx, w, "not allow to create policy")
  1097. // return
  1098. // }
  1099. _, query, _ := appsrv.FetchEnv(ctx, w, req)
  1100. s := auth.GetSession(ctx, t, FetchRegion(req))
  1101. idlist, e := query.GetArray("id")
  1102. if e != nil || len(idlist) == 0 {
  1103. httperrors.InvalidInputError(ctx, w, "missing id")
  1104. return
  1105. }
  1106. idStrList := jsonutils.JSONArray2StringArray(idlist)
  1107. ret := make([]printutils.SubmitResult, len(idStrList))
  1108. for i := range idStrList {
  1109. err := policytool.PolicyDelete(s, idStrList[i])
  1110. if err != nil {
  1111. ret[i] = printutils.SubmitResult{
  1112. Status: 400,
  1113. Id: idStrList[i],
  1114. Data: jsonutils.NewString(err.Error()),
  1115. }
  1116. } else {
  1117. ret[i] = printutils.SubmitResult{
  1118. Status: 200,
  1119. Id: idStrList[i],
  1120. Data: jsonutils.NewDict(),
  1121. }
  1122. }
  1123. }
  1124. w.WriteHeader(207)
  1125. appsrv.SendJSON(w, modulebase.SubmitResults2JSON(ret))
  1126. }
  1127. /*
  1128. 重置密码
  1129. 1.验证新密码正确
  1130. 2.验证原密码正确,且idp_driver为空
  1131. 3.如果已开启MFA,验证 随机密码正确
  1132. 4.重置密码,清除认证token
  1133. */
  1134. func (h *AuthHandlers) resetUserPassword(ctx context.Context, w http.ResponseWriter, req *http.Request) {
  1135. t, authToken, err := fetchAuthInfo(ctx, req)
  1136. if err != nil {
  1137. httperrors.InvalidCredentialError(ctx, w, "fetchAuthInfo fail: %s", err)
  1138. return
  1139. }
  1140. _, _, body := appsrv.FetchEnv(ctx, w, req)
  1141. if body == nil {
  1142. httperrors.InvalidInputError(ctx, w, "request body is empty")
  1143. return
  1144. }
  1145. user, err := fetchUserInfoFromToken(ctx, req, t)
  1146. if err != nil {
  1147. httperrors.GeneralServerError(ctx, w, err)
  1148. return
  1149. }
  1150. input := struct {
  1151. PasswordOld string
  1152. PasswordNew string
  1153. PasswordConfirm string
  1154. Passcode string
  1155. }{}
  1156. body.Unmarshal(&input)
  1157. input.PasswordOld = decodePassword(input.PasswordOld)
  1158. input.PasswordNew = decodePassword(input.PasswordNew)
  1159. input.PasswordConfirm = decodePassword(input.PasswordConfirm)
  1160. if input.PasswordNew != input.PasswordConfirm {
  1161. httperrors.InputParameterError(ctx, w, "new password mismatch")
  1162. return
  1163. }
  1164. // 1.验证原密码正确,且idp_driver为空
  1165. if isIdpUser(user) {
  1166. httperrors.ForbiddenError(ctx, w, "not support reset user password")
  1167. return
  1168. }
  1169. cliIp := netutils2.GetHttpRequestIp(req)
  1170. _, err = auth.Client().AuthenticateWeb(t.GetUserName(), input.PasswordOld, t.GetDomainName(), "", "", cliIp)
  1171. if err != nil {
  1172. switch httperr := err.(type) {
  1173. case *httputils.JSONClientError:
  1174. if httperr.Code == 409 {
  1175. httperrors.GeneralServerError(ctx, w, err)
  1176. return
  1177. }
  1178. }
  1179. httperrors.InputParameterError(ctx, w, "wrong password")
  1180. return
  1181. }
  1182. s := auth.GetAdminSession(ctx, FetchRegion(req))
  1183. // 2.如果已开启MFA,验证 随机密码正确
  1184. if isMfaEnabled(user) {
  1185. err = authToken.VerifyTotpPasscode(s, t.GetUserId(), input.Passcode)
  1186. if err != nil {
  1187. httperrors.InputParameterError(ctx, w, "invalid passcode")
  1188. return
  1189. }
  1190. }
  1191. // 3.重置密码,
  1192. params := jsonutils.NewDict()
  1193. params.Set("password", jsonutils.NewString(input.PasswordNew))
  1194. _, err = modules.UsersV3.Patch(s, t.GetUserId(), params)
  1195. if err != nil {
  1196. httperrors.GeneralServerError(ctx, w, err)
  1197. return
  1198. }
  1199. // 4. 清除认证token, logout
  1200. h.postLogoutHandler(ctx, w, req)
  1201. }
  1202. func isIdpUser(user jsonutils.JSONObject) bool {
  1203. if driver, _ := user.GetString("idp_driver"); len(driver) > 0 {
  1204. return true
  1205. }
  1206. return false
  1207. }
  1208. // refer: isUserEnableTotp
  1209. func isMfaEnabled(user jsonutils.JSONObject) bool {
  1210. if !options.Options.EnableTotp {
  1211. return false
  1212. }
  1213. if ok, _ := user.Bool("enable_mfa"); ok {
  1214. return true
  1215. }
  1216. return false
  1217. }
  1218. func decodePassword(passwd string) string {
  1219. if decPasswd, err := seclib2.AES_256.CbcDecodeBase64(passwd, []byte(agapi.DefaultEncryptKey)); err == nil && stringutils2.IsPrintableAsciiString(string(decPasswd)) {
  1220. // First try AES decryption
  1221. passwd = string(decPasswd)
  1222. } else if decPasswd, err := base64.StdEncoding.DecodeString(passwd); err == nil && stringutils2.IsPrintableAsciiString(string(decPasswd)) {
  1223. // try base64 decryption
  1224. passwd = string(decPasswd)
  1225. }
  1226. return passwd
  1227. }