aboutsummaryrefslogtreecommitdiffstats
path: root/routers/web/admin/auths.go
diff options
context:
space:
mode:
Diffstat (limited to 'routers/web/admin/auths.go')
-rw-r--r--routers/web/admin/auths.go410
1 files changed, 410 insertions, 0 deletions
diff --git a/routers/web/admin/auths.go b/routers/web/admin/auths.go
new file mode 100644
index 0000000000..a2f9ab0a5c
--- /dev/null
+++ b/routers/web/admin/auths.go
@@ -0,0 +1,410 @@
+// Copyright 2014 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package admin
+
+import (
+ "errors"
+ "fmt"
+ "net/http"
+ "regexp"
+
+ "code.gitea.io/gitea/models"
+ "code.gitea.io/gitea/modules/auth/ldap"
+ "code.gitea.io/gitea/modules/auth/oauth2"
+ "code.gitea.io/gitea/modules/auth/pam"
+ "code.gitea.io/gitea/modules/base"
+ "code.gitea.io/gitea/modules/context"
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/forms"
+
+ "xorm.io/xorm/convert"
+)
+
+const (
+ tplAuths base.TplName = "admin/auth/list"
+ tplAuthNew base.TplName = "admin/auth/new"
+ tplAuthEdit base.TplName = "admin/auth/edit"
+)
+
+var (
+ separatorAntiPattern = regexp.MustCompile(`[^\w-\.]`)
+ langCodePattern = regexp.MustCompile(`^[a-z]{2}-[A-Z]{2}$`)
+)
+
+// Authentications show authentication config page
+func Authentications(ctx *context.Context) {
+ ctx.Data["Title"] = ctx.Tr("admin.authentication")
+ ctx.Data["PageIsAdmin"] = true
+ ctx.Data["PageIsAdminAuthentications"] = true
+
+ var err error
+ ctx.Data["Sources"], err = models.LoginSources()
+ if err != nil {
+ ctx.ServerError("LoginSources", err)
+ return
+ }
+
+ ctx.Data["Total"] = models.CountLoginSources()
+ ctx.HTML(http.StatusOK, tplAuths)
+}
+
+type dropdownItem struct {
+ Name string
+ Type interface{}
+}
+
+var (
+ authSources = func() []dropdownItem {
+ items := []dropdownItem{
+ {models.LoginNames[models.LoginLDAP], models.LoginLDAP},
+ {models.LoginNames[models.LoginDLDAP], models.LoginDLDAP},
+ {models.LoginNames[models.LoginSMTP], models.LoginSMTP},
+ {models.LoginNames[models.LoginOAuth2], models.LoginOAuth2},
+ {models.LoginNames[models.LoginSSPI], models.LoginSSPI},
+ }
+ if pam.Supported {
+ items = append(items, dropdownItem{models.LoginNames[models.LoginPAM], models.LoginPAM})
+ }
+ return items
+ }()
+
+ securityProtocols = []dropdownItem{
+ {models.SecurityProtocolNames[ldap.SecurityProtocolUnencrypted], ldap.SecurityProtocolUnencrypted},
+ {models.SecurityProtocolNames[ldap.SecurityProtocolLDAPS], ldap.SecurityProtocolLDAPS},
+ {models.SecurityProtocolNames[ldap.SecurityProtocolStartTLS], ldap.SecurityProtocolStartTLS},
+ }
+)
+
+// NewAuthSource render adding a new auth source page
+func NewAuthSource(ctx *context.Context) {
+ ctx.Data["Title"] = ctx.Tr("admin.auths.new")
+ ctx.Data["PageIsAdmin"] = true
+ ctx.Data["PageIsAdminAuthentications"] = true
+
+ ctx.Data["type"] = models.LoginLDAP
+ ctx.Data["CurrentTypeName"] = models.LoginNames[models.LoginLDAP]
+ ctx.Data["CurrentSecurityProtocol"] = models.SecurityProtocolNames[ldap.SecurityProtocolUnencrypted]
+ ctx.Data["smtp_auth"] = "PLAIN"
+ ctx.Data["is_active"] = true
+ ctx.Data["is_sync_enabled"] = true
+ ctx.Data["AuthSources"] = authSources
+ ctx.Data["SecurityProtocols"] = securityProtocols
+ ctx.Data["SMTPAuths"] = models.SMTPAuths
+ ctx.Data["OAuth2Providers"] = models.OAuth2Providers
+ ctx.Data["OAuth2DefaultCustomURLMappings"] = models.OAuth2DefaultCustomURLMappings
+
+ ctx.Data["SSPIAutoCreateUsers"] = true
+ ctx.Data["SSPIAutoActivateUsers"] = true
+ ctx.Data["SSPIStripDomainNames"] = true
+ ctx.Data["SSPISeparatorReplacement"] = "_"
+ ctx.Data["SSPIDefaultLanguage"] = ""
+
+ // only the first as default
+ for key := range models.OAuth2Providers {
+ ctx.Data["oauth2_provider"] = key
+ break
+ }
+
+ ctx.HTML(http.StatusOK, tplAuthNew)
+}
+
+func parseLDAPConfig(form forms.AuthenticationForm) *models.LDAPConfig {
+ var pageSize uint32
+ if form.UsePagedSearch {
+ pageSize = uint32(form.SearchPageSize)
+ }
+ return &models.LDAPConfig{
+ Source: &ldap.Source{
+ Name: form.Name,
+ Host: form.Host,
+ Port: form.Port,
+ SecurityProtocol: ldap.SecurityProtocol(form.SecurityProtocol),
+ SkipVerify: form.SkipVerify,
+ BindDN: form.BindDN,
+ UserDN: form.UserDN,
+ BindPassword: form.BindPassword,
+ UserBase: form.UserBase,
+ AttributeUsername: form.AttributeUsername,
+ AttributeName: form.AttributeName,
+ AttributeSurname: form.AttributeSurname,
+ AttributeMail: form.AttributeMail,
+ AttributesInBind: form.AttributesInBind,
+ AttributeSSHPublicKey: form.AttributeSSHPublicKey,
+ SearchPageSize: pageSize,
+ Filter: form.Filter,
+ GroupsEnabled: form.GroupsEnabled,
+ GroupDN: form.GroupDN,
+ GroupFilter: form.GroupFilter,
+ GroupMemberUID: form.GroupMemberUID,
+ UserUID: form.UserUID,
+ AdminFilter: form.AdminFilter,
+ RestrictedFilter: form.RestrictedFilter,
+ AllowDeactivateAll: form.AllowDeactivateAll,
+ Enabled: true,
+ },
+ }
+}
+
+func parseSMTPConfig(form forms.AuthenticationForm) *models.SMTPConfig {
+ return &models.SMTPConfig{
+ Auth: form.SMTPAuth,
+ Host: form.SMTPHost,
+ Port: form.SMTPPort,
+ AllowedDomains: form.AllowedDomains,
+ TLS: form.TLS,
+ SkipVerify: form.SkipVerify,
+ }
+}
+
+func parseOAuth2Config(form forms.AuthenticationForm) *models.OAuth2Config {
+ var customURLMapping *oauth2.CustomURLMapping
+ if form.Oauth2UseCustomURL {
+ customURLMapping = &oauth2.CustomURLMapping{
+ TokenURL: form.Oauth2TokenURL,
+ AuthURL: form.Oauth2AuthURL,
+ ProfileURL: form.Oauth2ProfileURL,
+ EmailURL: form.Oauth2EmailURL,
+ }
+ } else {
+ customURLMapping = nil
+ }
+ return &models.OAuth2Config{
+ Provider: form.Oauth2Provider,
+ ClientID: form.Oauth2Key,
+ ClientSecret: form.Oauth2Secret,
+ OpenIDConnectAutoDiscoveryURL: form.OpenIDConnectAutoDiscoveryURL,
+ CustomURLMapping: customURLMapping,
+ IconURL: form.Oauth2IconURL,
+ }
+}
+
+func parseSSPIConfig(ctx *context.Context, form forms.AuthenticationForm) (*models.SSPIConfig, error) {
+ if util.IsEmptyString(form.SSPISeparatorReplacement) {
+ ctx.Data["Err_SSPISeparatorReplacement"] = true
+ return nil, errors.New(ctx.Tr("form.SSPISeparatorReplacement") + ctx.Tr("form.require_error"))
+ }
+ if separatorAntiPattern.MatchString(form.SSPISeparatorReplacement) {
+ ctx.Data["Err_SSPISeparatorReplacement"] = true
+ return nil, errors.New(ctx.Tr("form.SSPISeparatorReplacement") + ctx.Tr("form.alpha_dash_dot_error"))
+ }
+
+ if form.SSPIDefaultLanguage != "" && !langCodePattern.MatchString(form.SSPIDefaultLanguage) {
+ ctx.Data["Err_SSPIDefaultLanguage"] = true
+ return nil, errors.New(ctx.Tr("form.lang_select_error"))
+ }
+
+ return &models.SSPIConfig{
+ AutoCreateUsers: form.SSPIAutoCreateUsers,
+ AutoActivateUsers: form.SSPIAutoActivateUsers,
+ StripDomainNames: form.SSPIStripDomainNames,
+ SeparatorReplacement: form.SSPISeparatorReplacement,
+ DefaultLanguage: form.SSPIDefaultLanguage,
+ }, nil
+}
+
+// NewAuthSourcePost response for adding an auth source
+func NewAuthSourcePost(ctx *context.Context) {
+ form := *web.GetForm(ctx).(*forms.AuthenticationForm)
+ ctx.Data["Title"] = ctx.Tr("admin.auths.new")
+ ctx.Data["PageIsAdmin"] = true
+ ctx.Data["PageIsAdminAuthentications"] = true
+
+ ctx.Data["CurrentTypeName"] = models.LoginNames[models.LoginType(form.Type)]
+ ctx.Data["CurrentSecurityProtocol"] = models.SecurityProtocolNames[ldap.SecurityProtocol(form.SecurityProtocol)]
+ ctx.Data["AuthSources"] = authSources
+ ctx.Data["SecurityProtocols"] = securityProtocols
+ ctx.Data["SMTPAuths"] = models.SMTPAuths
+ ctx.Data["OAuth2Providers"] = models.OAuth2Providers
+ ctx.Data["OAuth2DefaultCustomURLMappings"] = models.OAuth2DefaultCustomURLMappings
+
+ ctx.Data["SSPIAutoCreateUsers"] = true
+ ctx.Data["SSPIAutoActivateUsers"] = true
+ ctx.Data["SSPIStripDomainNames"] = true
+ ctx.Data["SSPISeparatorReplacement"] = "_"
+ ctx.Data["SSPIDefaultLanguage"] = ""
+
+ hasTLS := false
+ var config convert.Conversion
+ switch models.LoginType(form.Type) {
+ case models.LoginLDAP, models.LoginDLDAP:
+ config = parseLDAPConfig(form)
+ hasTLS = ldap.SecurityProtocol(form.SecurityProtocol) > ldap.SecurityProtocolUnencrypted
+ case models.LoginSMTP:
+ config = parseSMTPConfig(form)
+ hasTLS = true
+ case models.LoginPAM:
+ config = &models.PAMConfig{
+ ServiceName: form.PAMServiceName,
+ EmailDomain: form.PAMEmailDomain,
+ }
+ case models.LoginOAuth2:
+ config = parseOAuth2Config(form)
+ case models.LoginSSPI:
+ var err error
+ config, err = parseSSPIConfig(ctx, form)
+ if err != nil {
+ ctx.RenderWithErr(err.Error(), tplAuthNew, form)
+ return
+ }
+ existing, err := models.LoginSourcesByType(models.LoginSSPI)
+ if err != nil || len(existing) > 0 {
+ ctx.Data["Err_Type"] = true
+ ctx.RenderWithErr(ctx.Tr("admin.auths.login_source_of_type_exist"), tplAuthNew, form)
+ return
+ }
+ default:
+ ctx.Error(http.StatusBadRequest)
+ return
+ }
+ ctx.Data["HasTLS"] = hasTLS
+
+ if ctx.HasError() {
+ ctx.HTML(http.StatusOK, tplAuthNew)
+ return
+ }
+
+ if err := models.CreateLoginSource(&models.LoginSource{
+ Type: models.LoginType(form.Type),
+ Name: form.Name,
+ IsActived: form.IsActive,
+ IsSyncEnabled: form.IsSyncEnabled,
+ Cfg: config,
+ }); err != nil {
+ if models.IsErrLoginSourceAlreadyExist(err) {
+ ctx.Data["Err_Name"] = true
+ ctx.RenderWithErr(ctx.Tr("admin.auths.login_source_exist", err.(models.ErrLoginSourceAlreadyExist).Name), tplAuthNew, form)
+ } else {
+ ctx.ServerError("CreateSource", err)
+ }
+ return
+ }
+
+ log.Trace("Authentication created by admin(%s): %s", ctx.User.Name, form.Name)
+
+ ctx.Flash.Success(ctx.Tr("admin.auths.new_success", form.Name))
+ ctx.Redirect(setting.AppSubURL + "/admin/auths")
+}
+
+// EditAuthSource render editing auth source page
+func EditAuthSource(ctx *context.Context) {
+ ctx.Data["Title"] = ctx.Tr("admin.auths.edit")
+ ctx.Data["PageIsAdmin"] = true
+ ctx.Data["PageIsAdminAuthentications"] = true
+
+ ctx.Data["SecurityProtocols"] = securityProtocols
+ ctx.Data["SMTPAuths"] = models.SMTPAuths
+ ctx.Data["OAuth2Providers"] = models.OAuth2Providers
+ ctx.Data["OAuth2DefaultCustomURLMappings"] = models.OAuth2DefaultCustomURLMappings
+
+ source, err := models.GetLoginSourceByID(ctx.ParamsInt64(":authid"))
+ if err != nil {
+ ctx.ServerError("GetLoginSourceByID", err)
+ return
+ }
+ ctx.Data["Source"] = source
+ ctx.Data["HasTLS"] = source.HasTLS()
+
+ if source.IsOAuth2() {
+ ctx.Data["CurrentOAuth2Provider"] = models.OAuth2Providers[source.OAuth2().Provider]
+ }
+ ctx.HTML(http.StatusOK, tplAuthEdit)
+}
+
+// EditAuthSourcePost response for editing auth source
+func EditAuthSourcePost(ctx *context.Context) {
+ form := *web.GetForm(ctx).(*forms.AuthenticationForm)
+ ctx.Data["Title"] = ctx.Tr("admin.auths.edit")
+ ctx.Data["PageIsAdmin"] = true
+ ctx.Data["PageIsAdminAuthentications"] = true
+
+ ctx.Data["SMTPAuths"] = models.SMTPAuths
+ ctx.Data["OAuth2Providers"] = models.OAuth2Providers
+ ctx.Data["OAuth2DefaultCustomURLMappings"] = models.OAuth2DefaultCustomURLMappings
+
+ source, err := models.GetLoginSourceByID(ctx.ParamsInt64(":authid"))
+ if err != nil {
+ ctx.ServerError("GetLoginSourceByID", err)
+ return
+ }
+ ctx.Data["Source"] = source
+ ctx.Data["HasTLS"] = source.HasTLS()
+
+ if ctx.HasError() {
+ ctx.HTML(http.StatusOK, tplAuthEdit)
+ return
+ }
+
+ var config convert.Conversion
+ switch models.LoginType(form.Type) {
+ case models.LoginLDAP, models.LoginDLDAP:
+ config = parseLDAPConfig(form)
+ case models.LoginSMTP:
+ config = parseSMTPConfig(form)
+ case models.LoginPAM:
+ config = &models.PAMConfig{
+ ServiceName: form.PAMServiceName,
+ EmailDomain: form.PAMEmailDomain,
+ }
+ case models.LoginOAuth2:
+ config = parseOAuth2Config(form)
+ case models.LoginSSPI:
+ config, err = parseSSPIConfig(ctx, form)
+ if err != nil {
+ ctx.RenderWithErr(err.Error(), tplAuthEdit, form)
+ return
+ }
+ default:
+ ctx.Error(http.StatusBadRequest)
+ return
+ }
+
+ source.Name = form.Name
+ source.IsActived = form.IsActive
+ source.IsSyncEnabled = form.IsSyncEnabled
+ source.Cfg = config
+ if err := models.UpdateSource(source); err != nil {
+ if models.IsErrOpenIDConnectInitialize(err) {
+ ctx.Flash.Error(err.Error(), true)
+ ctx.HTML(http.StatusOK, tplAuthEdit)
+ } else {
+ ctx.ServerError("UpdateSource", err)
+ }
+ return
+ }
+ log.Trace("Authentication changed by admin(%s): %d", ctx.User.Name, source.ID)
+
+ ctx.Flash.Success(ctx.Tr("admin.auths.update_success"))
+ ctx.Redirect(setting.AppSubURL + "/admin/auths/" + fmt.Sprint(form.ID))
+}
+
+// DeleteAuthSource response for deleting an auth source
+func DeleteAuthSource(ctx *context.Context) {
+ source, err := models.GetLoginSourceByID(ctx.ParamsInt64(":authid"))
+ if err != nil {
+ ctx.ServerError("GetLoginSourceByID", err)
+ return
+ }
+
+ if err = models.DeleteSource(source); err != nil {
+ if models.IsErrLoginSourceInUse(err) {
+ ctx.Flash.Error(ctx.Tr("admin.auths.still_in_used"))
+ } else {
+ ctx.Flash.Error(fmt.Sprintf("DeleteSource: %v", err))
+ }
+ ctx.JSON(http.StatusOK, map[string]interface{}{
+ "redirect": setting.AppSubURL + "/admin/auths/" + ctx.Params(":authid"),
+ })
+ return
+ }
+ log.Trace("Authentication deleted by admin(%s): %d", ctx.User.Name, source.ID)
+
+ ctx.Flash.Success(ctx.Tr("admin.auths.deletion_success"))
+ ctx.JSON(http.StatusOK, map[string]interface{}{
+ "redirect": setting.AppSubURL + "/admin/auths",
+ })
+}