gitea源码

admin_auth_oauth.go 9.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. // Copyright 2023 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package cmd
  4. import (
  5. "context"
  6. "errors"
  7. "fmt"
  8. "net/url"
  9. auth_model "code.gitea.io/gitea/models/auth"
  10. "code.gitea.io/gitea/modules/util"
  11. "code.gitea.io/gitea/services/auth/source/oauth2"
  12. "github.com/urfave/cli/v3"
  13. )
  14. func oauthCLIFlags() []cli.Flag {
  15. return []cli.Flag{
  16. &cli.StringFlag{
  17. Name: "name",
  18. Value: "",
  19. Usage: "Application Name",
  20. },
  21. &cli.StringFlag{
  22. Name: "provider",
  23. Value: "",
  24. Usage: "OAuth2 Provider",
  25. },
  26. &cli.StringFlag{
  27. Name: "key",
  28. Value: "",
  29. Usage: "Client ID (Key)",
  30. },
  31. &cli.StringFlag{
  32. Name: "secret",
  33. Value: "",
  34. Usage: "Client Secret",
  35. },
  36. &cli.StringFlag{
  37. Name: "auto-discover-url",
  38. Value: "",
  39. Usage: "OpenID Connect Auto Discovery URL (only required when using OpenID Connect as provider)",
  40. },
  41. &cli.StringFlag{
  42. Name: "use-custom-urls",
  43. Value: "false",
  44. Usage: "Use custom URLs for GitLab/GitHub OAuth endpoints",
  45. },
  46. &cli.StringFlag{
  47. Name: "custom-tenant-id",
  48. Value: "",
  49. Usage: "Use custom Tenant ID for OAuth endpoints",
  50. },
  51. &cli.StringFlag{
  52. Name: "custom-auth-url",
  53. Value: "",
  54. Usage: "Use a custom Authorization URL (option for GitLab/GitHub)",
  55. },
  56. &cli.StringFlag{
  57. Name: "custom-token-url",
  58. Value: "",
  59. Usage: "Use a custom Token URL (option for GitLab/GitHub)",
  60. },
  61. &cli.StringFlag{
  62. Name: "custom-profile-url",
  63. Value: "",
  64. Usage: "Use a custom Profile URL (option for GitLab/GitHub)",
  65. },
  66. &cli.StringFlag{
  67. Name: "custom-email-url",
  68. Value: "",
  69. Usage: "Use a custom Email URL (option for GitHub)",
  70. },
  71. &cli.StringFlag{
  72. Name: "icon-url",
  73. Value: "",
  74. Usage: "Custom icon URL for OAuth2 login source",
  75. },
  76. &cli.BoolFlag{
  77. Name: "skip-local-2fa",
  78. Usage: "Set to true to skip local 2fa for users authenticated by this source",
  79. },
  80. &cli.StringSliceFlag{
  81. Name: "scopes",
  82. Value: nil,
  83. Usage: "Scopes to request when to authenticate against this OAuth2 source",
  84. },
  85. &cli.StringFlag{
  86. Name: "ssh-public-key-claim-name",
  87. Usage: "Claim name that provides SSH public keys",
  88. },
  89. &cli.StringFlag{
  90. Name: "full-name-claim-name",
  91. Usage: "Claim name that provides user's full name",
  92. },
  93. &cli.StringFlag{
  94. Name: "required-claim-name",
  95. Value: "",
  96. Usage: "Claim name that has to be set to allow users to login with this source",
  97. },
  98. &cli.StringFlag{
  99. Name: "required-claim-value",
  100. Value: "",
  101. Usage: "Claim value that has to be set to allow users to login with this source",
  102. },
  103. &cli.StringFlag{
  104. Name: "group-claim-name",
  105. Value: "",
  106. Usage: "Claim name providing group names for this source",
  107. },
  108. &cli.StringFlag{
  109. Name: "admin-group",
  110. Value: "",
  111. Usage: "Group Claim value for administrator users",
  112. },
  113. &cli.StringFlag{
  114. Name: "restricted-group",
  115. Value: "",
  116. Usage: "Group Claim value for restricted users",
  117. },
  118. &cli.StringFlag{
  119. Name: "group-team-map",
  120. Value: "",
  121. Usage: "JSON mapping between groups and org teams",
  122. },
  123. &cli.BoolFlag{
  124. Name: "group-team-map-removal",
  125. Usage: "Activate automatic team membership removal depending on groups",
  126. },
  127. }
  128. }
  129. func microcmdAuthAddOauth() *cli.Command {
  130. return &cli.Command{
  131. Name: "add-oauth",
  132. Usage: "Add new Oauth authentication source",
  133. Action: func(ctx context.Context, cmd *cli.Command) error {
  134. return newAuthService().runAddOauth(ctx, cmd)
  135. },
  136. Flags: oauthCLIFlags(),
  137. }
  138. }
  139. func microcmdAuthUpdateOauth() *cli.Command {
  140. return &cli.Command{
  141. Name: "update-oauth",
  142. Usage: "Update existing Oauth authentication source",
  143. Action: func(ctx context.Context, cmd *cli.Command) error {
  144. return newAuthService().runUpdateOauth(ctx, cmd)
  145. },
  146. Flags: append(oauthCLIFlags()[:1], append([]cli.Flag{&cli.Int64Flag{
  147. Name: "id",
  148. Usage: "ID of authentication source",
  149. }}, oauthCLIFlags()[1:]...)...),
  150. }
  151. }
  152. func parseOAuth2Config(c *cli.Command) *oauth2.Source {
  153. var customURLMapping *oauth2.CustomURLMapping
  154. if c.IsSet("use-custom-urls") {
  155. customURLMapping = &oauth2.CustomURLMapping{
  156. TokenURL: c.String("custom-token-url"),
  157. AuthURL: c.String("custom-auth-url"),
  158. ProfileURL: c.String("custom-profile-url"),
  159. EmailURL: c.String("custom-email-url"),
  160. Tenant: c.String("custom-tenant-id"),
  161. }
  162. } else {
  163. customURLMapping = nil
  164. }
  165. return &oauth2.Source{
  166. Provider: c.String("provider"),
  167. ClientID: c.String("key"),
  168. ClientSecret: c.String("secret"),
  169. OpenIDConnectAutoDiscoveryURL: c.String("auto-discover-url"),
  170. CustomURLMapping: customURLMapping,
  171. IconURL: c.String("icon-url"),
  172. Scopes: c.StringSlice("scopes"),
  173. RequiredClaimName: c.String("required-claim-name"),
  174. RequiredClaimValue: c.String("required-claim-value"),
  175. GroupClaimName: c.String("group-claim-name"),
  176. AdminGroup: c.String("admin-group"),
  177. RestrictedGroup: c.String("restricted-group"),
  178. GroupTeamMap: c.String("group-team-map"),
  179. GroupTeamMapRemoval: c.Bool("group-team-map-removal"),
  180. SSHPublicKeyClaimName: c.String("ssh-public-key-claim-name"),
  181. FullNameClaimName: c.String("full-name-claim-name"),
  182. }
  183. }
  184. func (a *authService) runAddOauth(ctx context.Context, c *cli.Command) error {
  185. if err := a.initDB(ctx); err != nil {
  186. return err
  187. }
  188. config := parseOAuth2Config(c)
  189. if config.Provider == "openidConnect" {
  190. discoveryURL, err := url.Parse(config.OpenIDConnectAutoDiscoveryURL)
  191. if err != nil || (discoveryURL.Scheme != "http" && discoveryURL.Scheme != "https") {
  192. return fmt.Errorf("invalid Auto Discovery URL: %s (this must be a valid URL starting with http:// or https://)", config.OpenIDConnectAutoDiscoveryURL)
  193. }
  194. }
  195. return a.createAuthSource(ctx, &auth_model.Source{
  196. Type: auth_model.OAuth2,
  197. Name: c.String("name"),
  198. IsActive: true,
  199. Cfg: config,
  200. TwoFactorPolicy: util.Iif(c.Bool("skip-local-2fa"), "skip", ""),
  201. })
  202. }
  203. func (a *authService) runUpdateOauth(ctx context.Context, c *cli.Command) error {
  204. if !c.IsSet("id") {
  205. return errors.New("--id flag is missing")
  206. }
  207. if err := a.initDB(ctx); err != nil {
  208. return err
  209. }
  210. source, err := a.getAuthSourceByID(ctx, c.Int64("id"))
  211. if err != nil {
  212. return err
  213. }
  214. oAuth2Config := source.Cfg.(*oauth2.Source)
  215. if c.IsSet("name") {
  216. source.Name = c.String("name")
  217. }
  218. if c.IsSet("provider") {
  219. oAuth2Config.Provider = c.String("provider")
  220. }
  221. if c.IsSet("key") {
  222. oAuth2Config.ClientID = c.String("key")
  223. }
  224. if c.IsSet("secret") {
  225. oAuth2Config.ClientSecret = c.String("secret")
  226. }
  227. if c.IsSet("auto-discover-url") {
  228. oAuth2Config.OpenIDConnectAutoDiscoveryURL = c.String("auto-discover-url")
  229. }
  230. if c.IsSet("icon-url") {
  231. oAuth2Config.IconURL = c.String("icon-url")
  232. }
  233. if c.IsSet("scopes") {
  234. oAuth2Config.Scopes = c.StringSlice("scopes")
  235. }
  236. if c.IsSet("required-claim-name") {
  237. oAuth2Config.RequiredClaimName = c.String("required-claim-name")
  238. }
  239. if c.IsSet("required-claim-value") {
  240. oAuth2Config.RequiredClaimValue = c.String("required-claim-value")
  241. }
  242. if c.IsSet("group-claim-name") {
  243. oAuth2Config.GroupClaimName = c.String("group-claim-name")
  244. }
  245. if c.IsSet("admin-group") {
  246. oAuth2Config.AdminGroup = c.String("admin-group")
  247. }
  248. if c.IsSet("restricted-group") {
  249. oAuth2Config.RestrictedGroup = c.String("restricted-group")
  250. }
  251. if c.IsSet("group-team-map") {
  252. oAuth2Config.GroupTeamMap = c.String("group-team-map")
  253. }
  254. if c.IsSet("group-team-map-removal") {
  255. oAuth2Config.GroupTeamMapRemoval = c.Bool("group-team-map-removal")
  256. }
  257. if c.IsSet("ssh-public-key-claim-name") {
  258. oAuth2Config.SSHPublicKeyClaimName = c.String("ssh-public-key-claim-name")
  259. }
  260. if c.IsSet("full-name-claim-name") {
  261. oAuth2Config.FullNameClaimName = c.String("full-name-claim-name")
  262. }
  263. // update custom URL mapping
  264. customURLMapping := &oauth2.CustomURLMapping{}
  265. if oAuth2Config.CustomURLMapping != nil {
  266. customURLMapping.TokenURL = oAuth2Config.CustomURLMapping.TokenURL
  267. customURLMapping.AuthURL = oAuth2Config.CustomURLMapping.AuthURL
  268. customURLMapping.ProfileURL = oAuth2Config.CustomURLMapping.ProfileURL
  269. customURLMapping.EmailURL = oAuth2Config.CustomURLMapping.EmailURL
  270. customURLMapping.Tenant = oAuth2Config.CustomURLMapping.Tenant
  271. }
  272. if c.IsSet("use-custom-urls") && c.IsSet("custom-token-url") {
  273. customURLMapping.TokenURL = c.String("custom-token-url")
  274. }
  275. if c.IsSet("use-custom-urls") && c.IsSet("custom-auth-url") {
  276. customURLMapping.AuthURL = c.String("custom-auth-url")
  277. }
  278. if c.IsSet("use-custom-urls") && c.IsSet("custom-profile-url") {
  279. customURLMapping.ProfileURL = c.String("custom-profile-url")
  280. }
  281. if c.IsSet("use-custom-urls") && c.IsSet("custom-email-url") {
  282. customURLMapping.EmailURL = c.String("custom-email-url")
  283. }
  284. if c.IsSet("use-custom-urls") && c.IsSet("custom-tenant-id") {
  285. customURLMapping.Tenant = c.String("custom-tenant-id")
  286. }
  287. oAuth2Config.CustomURLMapping = customURLMapping
  288. source.Cfg = oAuth2Config
  289. source.TwoFactorPolicy = util.Iif(c.Bool("skip-local-2fa"), "skip", "")
  290. return a.updateAuthSource(ctx, source)
  291. }