···226226227227 // Create a copy of command to test
228228 app := cli.NewApp()
229229- app.Flags = cmdAuthAddLdapBindDn.Flags
229229+ app.Flags = microcmdAuthAddLdapBindDn.Flags
230230 app.Action = service.addLdapBindDn
231231232232 // Run it
···457457458458 // Create a copy of command to test
459459 app := cli.NewApp()
460460- app.Flags = cmdAuthAddLdapSimpleAuth.Flags
460460+ app.Flags = microcmdAuthAddLdapSimpleAuth.Flags
461461 app.Action = service.addLdapSimpleAuth
462462463463 // Run it
···920920921921 // Create a copy of command to test
922922 app := cli.NewApp()
923923- app.Flags = cmdAuthUpdateLdapBindDn.Flags
923923+ app.Flags = microcmdAuthUpdateLdapBindDn.Flags
924924 app.Action = service.updateLdapBindDn
925925926926 // Run it
···1310131013111311 // Create a copy of command to test
13121312 app := cli.NewApp()
13131313- app.Flags = cmdAuthUpdateLdapSimpleAuth.Flags
13131313+ app.Flags = microcmdAuthUpdateLdapSimpleAuth.Flags
13141314 app.Action = service.updateLdapSimpleAuth
1315131513161316 // Run it
+298
cmd/admin_auth_oauth.go
···11+// Copyright 2023 The Gitea Authors. All rights reserved.
22+// SPDX-License-Identifier: MIT
33+44+package cmd
55+66+import (
77+ "fmt"
88+ "net/url"
99+1010+ auth_model "code.gitea.io/gitea/models/auth"
1111+ "code.gitea.io/gitea/services/auth/source/oauth2"
1212+1313+ "github.com/urfave/cli/v2"
1414+)
1515+1616+var (
1717+ oauthCLIFlags = []cli.Flag{
1818+ &cli.StringFlag{
1919+ Name: "name",
2020+ Value: "",
2121+ Usage: "Application Name",
2222+ },
2323+ &cli.StringFlag{
2424+ Name: "provider",
2525+ Value: "",
2626+ Usage: "OAuth2 Provider",
2727+ },
2828+ &cli.StringFlag{
2929+ Name: "key",
3030+ Value: "",
3131+ Usage: "Client ID (Key)",
3232+ },
3333+ &cli.StringFlag{
3434+ Name: "secret",
3535+ Value: "",
3636+ Usage: "Client Secret",
3737+ },
3838+ &cli.StringFlag{
3939+ Name: "auto-discover-url",
4040+ Value: "",
4141+ Usage: "OpenID Connect Auto Discovery URL (only required when using OpenID Connect as provider)",
4242+ },
4343+ &cli.StringFlag{
4444+ Name: "use-custom-urls",
4545+ Value: "false",
4646+ Usage: "Use custom URLs for GitLab/GitHub OAuth endpoints",
4747+ },
4848+ &cli.StringFlag{
4949+ Name: "custom-tenant-id",
5050+ Value: "",
5151+ Usage: "Use custom Tenant ID for OAuth endpoints",
5252+ },
5353+ &cli.StringFlag{
5454+ Name: "custom-auth-url",
5555+ Value: "",
5656+ Usage: "Use a custom Authorization URL (option for GitLab/GitHub)",
5757+ },
5858+ &cli.StringFlag{
5959+ Name: "custom-token-url",
6060+ Value: "",
6161+ Usage: "Use a custom Token URL (option for GitLab/GitHub)",
6262+ },
6363+ &cli.StringFlag{
6464+ Name: "custom-profile-url",
6565+ Value: "",
6666+ Usage: "Use a custom Profile URL (option for GitLab/GitHub)",
6767+ },
6868+ &cli.StringFlag{
6969+ Name: "custom-email-url",
7070+ Value: "",
7171+ Usage: "Use a custom Email URL (option for GitHub)",
7272+ },
7373+ &cli.StringFlag{
7474+ Name: "icon-url",
7575+ Value: "",
7676+ Usage: "Custom icon URL for OAuth2 login source",
7777+ },
7878+ &cli.BoolFlag{
7979+ Name: "skip-local-2fa",
8080+ Usage: "Set to true to skip local 2fa for users authenticated by this source",
8181+ },
8282+ &cli.StringSliceFlag{
8383+ Name: "scopes",
8484+ Value: nil,
8585+ Usage: "Scopes to request when to authenticate against this OAuth2 source",
8686+ },
8787+ &cli.StringFlag{
8888+ Name: "required-claim-name",
8989+ Value: "",
9090+ Usage: "Claim name that has to be set to allow users to login with this source",
9191+ },
9292+ &cli.StringFlag{
9393+ Name: "required-claim-value",
9494+ Value: "",
9595+ Usage: "Claim value that has to be set to allow users to login with this source",
9696+ },
9797+ &cli.StringFlag{
9898+ Name: "group-claim-name",
9999+ Value: "",
100100+ Usage: "Claim name providing group names for this source",
101101+ },
102102+ &cli.StringFlag{
103103+ Name: "admin-group",
104104+ Value: "",
105105+ Usage: "Group Claim value for administrator users",
106106+ },
107107+ &cli.StringFlag{
108108+ Name: "restricted-group",
109109+ Value: "",
110110+ Usage: "Group Claim value for restricted users",
111111+ },
112112+ &cli.StringFlag{
113113+ Name: "group-team-map",
114114+ Value: "",
115115+ Usage: "JSON mapping between groups and org teams",
116116+ },
117117+ &cli.BoolFlag{
118118+ Name: "group-team-map-removal",
119119+ Usage: "Activate automatic team membership removal depending on groups",
120120+ },
121121+ }
122122+123123+ microcmdAuthAddOauth = &cli.Command{
124124+ Name: "add-oauth",
125125+ Usage: "Add new Oauth authentication source",
126126+ Action: runAddOauth,
127127+ Flags: oauthCLIFlags,
128128+ }
129129+130130+ microcmdAuthUpdateOauth = &cli.Command{
131131+ Name: "update-oauth",
132132+ Usage: "Update existing Oauth authentication source",
133133+ Action: runUpdateOauth,
134134+ Flags: append(oauthCLIFlags[:1], append([]cli.Flag{idFlag}, oauthCLIFlags[1:]...)...),
135135+ }
136136+)
137137+138138+func parseOAuth2Config(c *cli.Context) *oauth2.Source {
139139+ var customURLMapping *oauth2.CustomURLMapping
140140+ if c.IsSet("use-custom-urls") {
141141+ customURLMapping = &oauth2.CustomURLMapping{
142142+ TokenURL: c.String("custom-token-url"),
143143+ AuthURL: c.String("custom-auth-url"),
144144+ ProfileURL: c.String("custom-profile-url"),
145145+ EmailURL: c.String("custom-email-url"),
146146+ Tenant: c.String("custom-tenant-id"),
147147+ }
148148+ } else {
149149+ customURLMapping = nil
150150+ }
151151+ return &oauth2.Source{
152152+ Provider: c.String("provider"),
153153+ ClientID: c.String("key"),
154154+ ClientSecret: c.String("secret"),
155155+ OpenIDConnectAutoDiscoveryURL: c.String("auto-discover-url"),
156156+ CustomURLMapping: customURLMapping,
157157+ IconURL: c.String("icon-url"),
158158+ SkipLocalTwoFA: c.Bool("skip-local-2fa"),
159159+ Scopes: c.StringSlice("scopes"),
160160+ RequiredClaimName: c.String("required-claim-name"),
161161+ RequiredClaimValue: c.String("required-claim-value"),
162162+ GroupClaimName: c.String("group-claim-name"),
163163+ AdminGroup: c.String("admin-group"),
164164+ RestrictedGroup: c.String("restricted-group"),
165165+ GroupTeamMap: c.String("group-team-map"),
166166+ GroupTeamMapRemoval: c.Bool("group-team-map-removal"),
167167+ }
168168+}
169169+170170+func runAddOauth(c *cli.Context) error {
171171+ ctx, cancel := installSignals()
172172+ defer cancel()
173173+174174+ if err := initDB(ctx); err != nil {
175175+ return err
176176+ }
177177+178178+ config := parseOAuth2Config(c)
179179+ if config.Provider == "openidConnect" {
180180+ discoveryURL, err := url.Parse(config.OpenIDConnectAutoDiscoveryURL)
181181+ if err != nil || (discoveryURL.Scheme != "http" && discoveryURL.Scheme != "https") {
182182+ return fmt.Errorf("invalid Auto Discovery URL: %s (this must be a valid URL starting with http:// or https://)", config.OpenIDConnectAutoDiscoveryURL)
183183+ }
184184+ }
185185+186186+ return auth_model.CreateSource(&auth_model.Source{
187187+ Type: auth_model.OAuth2,
188188+ Name: c.String("name"),
189189+ IsActive: true,
190190+ Cfg: config,
191191+ })
192192+}
193193+194194+func runUpdateOauth(c *cli.Context) error {
195195+ if !c.IsSet("id") {
196196+ return fmt.Errorf("--id flag is missing")
197197+ }
198198+199199+ ctx, cancel := installSignals()
200200+ defer cancel()
201201+202202+ if err := initDB(ctx); err != nil {
203203+ return err
204204+ }
205205+206206+ source, err := auth_model.GetSourceByID(c.Int64("id"))
207207+ if err != nil {
208208+ return err
209209+ }
210210+211211+ oAuth2Config := source.Cfg.(*oauth2.Source)
212212+213213+ if c.IsSet("name") {
214214+ source.Name = c.String("name")
215215+ }
216216+217217+ if c.IsSet("provider") {
218218+ oAuth2Config.Provider = c.String("provider")
219219+ }
220220+221221+ if c.IsSet("key") {
222222+ oAuth2Config.ClientID = c.String("key")
223223+ }
224224+225225+ if c.IsSet("secret") {
226226+ oAuth2Config.ClientSecret = c.String("secret")
227227+ }
228228+229229+ if c.IsSet("auto-discover-url") {
230230+ oAuth2Config.OpenIDConnectAutoDiscoveryURL = c.String("auto-discover-url")
231231+ }
232232+233233+ if c.IsSet("icon-url") {
234234+ oAuth2Config.IconURL = c.String("icon-url")
235235+ }
236236+237237+ if c.IsSet("scopes") {
238238+ oAuth2Config.Scopes = c.StringSlice("scopes")
239239+ }
240240+241241+ if c.IsSet("required-claim-name") {
242242+ oAuth2Config.RequiredClaimName = c.String("required-claim-name")
243243+ }
244244+ if c.IsSet("required-claim-value") {
245245+ oAuth2Config.RequiredClaimValue = c.String("required-claim-value")
246246+ }
247247+248248+ if c.IsSet("group-claim-name") {
249249+ oAuth2Config.GroupClaimName = c.String("group-claim-name")
250250+ }
251251+ if c.IsSet("admin-group") {
252252+ oAuth2Config.AdminGroup = c.String("admin-group")
253253+ }
254254+ if c.IsSet("restricted-group") {
255255+ oAuth2Config.RestrictedGroup = c.String("restricted-group")
256256+ }
257257+ if c.IsSet("group-team-map") {
258258+ oAuth2Config.GroupTeamMap = c.String("group-team-map")
259259+ }
260260+ if c.IsSet("group-team-map-removal") {
261261+ oAuth2Config.GroupTeamMapRemoval = c.Bool("group-team-map-removal")
262262+ }
263263+264264+ // update custom URL mapping
265265+ customURLMapping := &oauth2.CustomURLMapping{}
266266+267267+ if oAuth2Config.CustomURLMapping != nil {
268268+ customURLMapping.TokenURL = oAuth2Config.CustomURLMapping.TokenURL
269269+ customURLMapping.AuthURL = oAuth2Config.CustomURLMapping.AuthURL
270270+ customURLMapping.ProfileURL = oAuth2Config.CustomURLMapping.ProfileURL
271271+ customURLMapping.EmailURL = oAuth2Config.CustomURLMapping.EmailURL
272272+ customURLMapping.Tenant = oAuth2Config.CustomURLMapping.Tenant
273273+ }
274274+ if c.IsSet("use-custom-urls") && c.IsSet("custom-token-url") {
275275+ customURLMapping.TokenURL = c.String("custom-token-url")
276276+ }
277277+278278+ if c.IsSet("use-custom-urls") && c.IsSet("custom-auth-url") {
279279+ customURLMapping.AuthURL = c.String("custom-auth-url")
280280+ }
281281+282282+ if c.IsSet("use-custom-urls") && c.IsSet("custom-profile-url") {
283283+ customURLMapping.ProfileURL = c.String("custom-profile-url")
284284+ }
285285+286286+ if c.IsSet("use-custom-urls") && c.IsSet("custom-email-url") {
287287+ customURLMapping.EmailURL = c.String("custom-email-url")
288288+ }
289289+290290+ if c.IsSet("use-custom-urls") && c.IsSet("custom-tenant-id") {
291291+ customURLMapping.Tenant = c.String("custom-tenant-id")
292292+ }
293293+294294+ oAuth2Config.CustomURLMapping = customURLMapping
295295+ source.Cfg = oAuth2Config
296296+297297+ return auth_model.UpdateSource(source)
298298+}
+201
cmd/admin_auth_stmp.go
···11+// Copyright 2023 The Gitea Authors. All rights reserved.
22+// SPDX-License-Identifier: MIT
33+44+package cmd
55+66+import (
77+ "errors"
88+ "fmt"
99+ "strings"
1010+1111+ auth_model "code.gitea.io/gitea/models/auth"
1212+ "code.gitea.io/gitea/modules/util"
1313+ "code.gitea.io/gitea/services/auth/source/smtp"
1414+1515+ "github.com/urfave/cli/v2"
1616+)
1717+1818+var (
1919+ smtpCLIFlags = []cli.Flag{
2020+ &cli.StringFlag{
2121+ Name: "name",
2222+ Value: "",
2323+ Usage: "Application Name",
2424+ },
2525+ &cli.StringFlag{
2626+ Name: "auth-type",
2727+ Value: "PLAIN",
2828+ Usage: "SMTP Authentication Type (PLAIN/LOGIN/CRAM-MD5) default PLAIN",
2929+ },
3030+ &cli.StringFlag{
3131+ Name: "host",
3232+ Value: "",
3333+ Usage: "SMTP Host",
3434+ },
3535+ &cli.IntFlag{
3636+ Name: "port",
3737+ Usage: "SMTP Port",
3838+ },
3939+ &cli.BoolFlag{
4040+ Name: "force-smtps",
4141+ Usage: "SMTPS is always used on port 465. Set this to force SMTPS on other ports.",
4242+ Value: true,
4343+ },
4444+ &cli.BoolFlag{
4545+ Name: "skip-verify",
4646+ Usage: "Skip TLS verify.",
4747+ Value: true,
4848+ },
4949+ &cli.StringFlag{
5050+ Name: "helo-hostname",
5151+ Value: "",
5252+ Usage: "Hostname sent with HELO. Leave blank to send current hostname",
5353+ },
5454+ &cli.BoolFlag{
5555+ Name: "disable-helo",
5656+ Usage: "Disable SMTP helo.",
5757+ Value: true,
5858+ },
5959+ &cli.StringFlag{
6060+ Name: "allowed-domains",
6161+ Value: "",
6262+ Usage: "Leave empty to allow all domains. Separate multiple domains with a comma (',')",
6363+ },
6464+ &cli.BoolFlag{
6565+ Name: "skip-local-2fa",
6666+ Usage: "Skip 2FA to log on.",
6767+ Value: true,
6868+ },
6969+ &cli.BoolFlag{
7070+ Name: "active",
7171+ Usage: "This Authentication Source is Activated.",
7272+ Value: true,
7373+ },
7474+ }
7575+7676+ microcmdAuthAddSMTP = &cli.Command{
7777+ Name: "add-smtp",
7878+ Usage: "Add new SMTP authentication source",
7979+ Action: runAddSMTP,
8080+ Flags: smtpCLIFlags,
8181+ }
8282+8383+ microcmdAuthUpdateSMTP = &cli.Command{
8484+ Name: "update-smtp",
8585+ Usage: "Update existing SMTP authentication source",
8686+ Action: runUpdateSMTP,
8787+ Flags: append(smtpCLIFlags[:1], append([]cli.Flag{idFlag}, smtpCLIFlags[1:]...)...),
8888+ }
8989+)
9090+9191+func parseSMTPConfig(c *cli.Context, conf *smtp.Source) error {
9292+ if c.IsSet("auth-type") {
9393+ conf.Auth = c.String("auth-type")
9494+ validAuthTypes := []string{"PLAIN", "LOGIN", "CRAM-MD5"}
9595+ if !util.SliceContainsString(validAuthTypes, strings.ToUpper(c.String("auth-type"))) {
9696+ return errors.New("Auth must be one of PLAIN/LOGIN/CRAM-MD5")
9797+ }
9898+ conf.Auth = c.String("auth-type")
9999+ }
100100+ if c.IsSet("host") {
101101+ conf.Host = c.String("host")
102102+ }
103103+ if c.IsSet("port") {
104104+ conf.Port = c.Int("port")
105105+ }
106106+ if c.IsSet("allowed-domains") {
107107+ conf.AllowedDomains = c.String("allowed-domains")
108108+ }
109109+ if c.IsSet("force-smtps") {
110110+ conf.ForceSMTPS = c.Bool("force-smtps")
111111+ }
112112+ if c.IsSet("skip-verify") {
113113+ conf.SkipVerify = c.Bool("skip-verify")
114114+ }
115115+ if c.IsSet("helo-hostname") {
116116+ conf.HeloHostname = c.String("helo-hostname")
117117+ }
118118+ if c.IsSet("disable-helo") {
119119+ conf.DisableHelo = c.Bool("disable-helo")
120120+ }
121121+ if c.IsSet("skip-local-2fa") {
122122+ conf.SkipLocalTwoFA = c.Bool("skip-local-2fa")
123123+ }
124124+ return nil
125125+}
126126+127127+func runAddSMTP(c *cli.Context) error {
128128+ ctx, cancel := installSignals()
129129+ defer cancel()
130130+131131+ if err := initDB(ctx); err != nil {
132132+ return err
133133+ }
134134+135135+ if !c.IsSet("name") || len(c.String("name")) == 0 {
136136+ return errors.New("name must be set")
137137+ }
138138+ if !c.IsSet("host") || len(c.String("host")) == 0 {
139139+ return errors.New("host must be set")
140140+ }
141141+ if !c.IsSet("port") {
142142+ return errors.New("port must be set")
143143+ }
144144+ active := true
145145+ if c.IsSet("active") {
146146+ active = c.Bool("active")
147147+ }
148148+149149+ var smtpConfig smtp.Source
150150+ if err := parseSMTPConfig(c, &smtpConfig); err != nil {
151151+ return err
152152+ }
153153+154154+ // If not set default to PLAIN
155155+ if len(smtpConfig.Auth) == 0 {
156156+ smtpConfig.Auth = "PLAIN"
157157+ }
158158+159159+ return auth_model.CreateSource(&auth_model.Source{
160160+ Type: auth_model.SMTP,
161161+ Name: c.String("name"),
162162+ IsActive: active,
163163+ Cfg: &smtpConfig,
164164+ })
165165+}
166166+167167+func runUpdateSMTP(c *cli.Context) error {
168168+ if !c.IsSet("id") {
169169+ return fmt.Errorf("--id flag is missing")
170170+ }
171171+172172+ ctx, cancel := installSignals()
173173+ defer cancel()
174174+175175+ if err := initDB(ctx); err != nil {
176176+ return err
177177+ }
178178+179179+ source, err := auth_model.GetSourceByID(c.Int64("id"))
180180+ if err != nil {
181181+ return err
182182+ }
183183+184184+ smtpConfig := source.Cfg.(*smtp.Source)
185185+186186+ if err := parseSMTPConfig(c, smtpConfig); err != nil {
187187+ return err
188188+ }
189189+190190+ if c.IsSet("name") {
191191+ source.Name = c.String("name")
192192+ }
193193+194194+ if c.IsSet("active") {
195195+ source.IsActive = c.Bool("active")
196196+ }
197197+198198+ source.Cfg = smtpConfig
199199+200200+ return auth_model.UpdateSource(source)
201201+}