auths.go 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. package admin
  2. import (
  3. "fmt"
  4. "net/http"
  5. "strconv"
  6. "strings"
  7. log "unknwon.dev/clog/v2"
  8. "gogs.io/gogs/internal/auth"
  9. "gogs.io/gogs/internal/auth/github"
  10. "gogs.io/gogs/internal/auth/ldap"
  11. "gogs.io/gogs/internal/auth/pam"
  12. "gogs.io/gogs/internal/auth/smtp"
  13. "gogs.io/gogs/internal/conf"
  14. "gogs.io/gogs/internal/context"
  15. "gogs.io/gogs/internal/database"
  16. "gogs.io/gogs/internal/form"
  17. )
  18. const (
  19. tmplAdminAuthList = "admin/auth/list"
  20. tmplAdminAuthNew = "admin/auth/new"
  21. tmplAdminAuthEdit = "admin/auth/edit"
  22. )
  23. func Authentications(c *context.Context) {
  24. c.Title("admin.authentication")
  25. c.PageIs("Admin")
  26. c.PageIs("AdminAuthentications")
  27. var err error
  28. c.Data["Sources"], err = database.Handle.LoginSources().List(c.Req.Context(), database.ListLoginSourceOptions{})
  29. if err != nil {
  30. c.Error(err, "list login sources")
  31. return
  32. }
  33. c.Data["Total"] = database.Handle.LoginSources().Count(c.Req.Context())
  34. c.Success(tmplAdminAuthList)
  35. }
  36. type dropdownItem struct {
  37. Name string
  38. Type any
  39. }
  40. var (
  41. authSources = []dropdownItem{
  42. {auth.Name(auth.LDAP), auth.LDAP},
  43. {auth.Name(auth.DLDAP), auth.DLDAP},
  44. {auth.Name(auth.SMTP), auth.SMTP},
  45. {auth.Name(auth.PAM), auth.PAM},
  46. {auth.Name(auth.GitHub), auth.GitHub},
  47. }
  48. securityProtocols = []dropdownItem{
  49. {ldap.SecurityProtocolName(ldap.SecurityProtocolUnencrypted), ldap.SecurityProtocolUnencrypted},
  50. {ldap.SecurityProtocolName(ldap.SecurityProtocolLDAPS), ldap.SecurityProtocolLDAPS},
  51. {ldap.SecurityProtocolName(ldap.SecurityProtocolStartTLS), ldap.SecurityProtocolStartTLS},
  52. }
  53. )
  54. func NewAuthSource(c *context.Context) {
  55. c.Title("admin.auths.new")
  56. c.PageIs("Admin")
  57. c.PageIs("AdminAuthentications")
  58. c.Data["type"] = auth.LDAP
  59. c.Data["CurrentTypeName"] = auth.Name(auth.LDAP)
  60. c.Data["CurrentSecurityProtocol"] = ldap.SecurityProtocolName(ldap.SecurityProtocolUnencrypted)
  61. c.Data["smtp_auth"] = "PLAIN"
  62. c.Data["is_active"] = true
  63. c.Data["is_default"] = true
  64. c.Data["AuthSources"] = authSources
  65. c.Data["SecurityProtocols"] = securityProtocols
  66. c.Data["SMTPAuths"] = smtp.AuthTypes
  67. c.Success(tmplAdminAuthNew)
  68. }
  69. func parseLDAPConfig(f form.Authentication) *ldap.Config {
  70. return &ldap.Config{
  71. Host: f.Host,
  72. Port: f.Port,
  73. SecurityProtocol: ldap.SecurityProtocol(f.SecurityProtocol),
  74. SkipVerify: f.SkipVerify,
  75. BindDN: f.BindDN,
  76. UserDN: f.UserDN,
  77. BindPassword: f.BindPassword,
  78. UserBase: f.UserBase,
  79. AttributeUsername: f.AttributeUsername,
  80. AttributeName: f.AttributeName,
  81. AttributeSurname: f.AttributeSurname,
  82. AttributeMail: f.AttributeMail,
  83. AttributesInBind: f.AttributesInBind,
  84. Filter: f.Filter,
  85. GroupEnabled: f.GroupEnabled,
  86. GroupDN: f.GroupDN,
  87. GroupFilter: f.GroupFilter,
  88. GroupMemberUID: f.GroupMemberUID,
  89. UserUID: f.UserUID,
  90. AdminFilter: f.AdminFilter,
  91. }
  92. }
  93. func parseSMTPConfig(f form.Authentication) *smtp.Config {
  94. return &smtp.Config{
  95. Auth: f.SMTPAuth,
  96. Host: f.SMTPHost,
  97. Port: f.SMTPPort,
  98. AllowedDomains: f.AllowedDomains,
  99. TLS: f.TLS,
  100. SkipVerify: f.SkipVerify,
  101. }
  102. }
  103. func NewAuthSourcePost(c *context.Context, f form.Authentication) {
  104. c.Title("admin.auths.new")
  105. c.PageIs("Admin")
  106. c.PageIs("AdminAuthentications")
  107. c.Data["CurrentTypeName"] = auth.Name(auth.Type(f.Type))
  108. c.Data["CurrentSecurityProtocol"] = ldap.SecurityProtocolName(ldap.SecurityProtocol(f.SecurityProtocol))
  109. c.Data["AuthSources"] = authSources
  110. c.Data["SecurityProtocols"] = securityProtocols
  111. c.Data["SMTPAuths"] = smtp.AuthTypes
  112. hasTLS := false
  113. var config any
  114. switch auth.Type(f.Type) {
  115. case auth.LDAP, auth.DLDAP:
  116. config = parseLDAPConfig(f)
  117. hasTLS = ldap.SecurityProtocol(f.SecurityProtocol) > ldap.SecurityProtocolUnencrypted
  118. case auth.SMTP:
  119. config = parseSMTPConfig(f)
  120. hasTLS = true
  121. case auth.PAM:
  122. config = &pam.Config{
  123. ServiceName: f.PAMServiceName,
  124. }
  125. case auth.GitHub:
  126. config = &github.Config{
  127. APIEndpoint: strings.TrimSuffix(f.GitHubAPIEndpoint, "/") + "/",
  128. SkipVerify: f.SkipVerify,
  129. }
  130. hasTLS = true
  131. default:
  132. c.Status(http.StatusBadRequest)
  133. return
  134. }
  135. c.Data["HasTLS"] = hasTLS
  136. if c.HasError() {
  137. c.HTML(http.StatusBadRequest, tmplAdminAuthNew)
  138. return
  139. }
  140. source, err := database.Handle.LoginSources().Create(c.Req.Context(),
  141. database.CreateLoginSourceOptions{
  142. Type: auth.Type(f.Type),
  143. Name: f.Name,
  144. Activated: f.IsActive,
  145. Default: f.IsDefault,
  146. Config: config,
  147. },
  148. )
  149. if err != nil {
  150. if database.IsErrLoginSourceAlreadyExist(err) {
  151. c.FormErr("Name")
  152. c.RenderWithErr(c.Tr("admin.auths.login_source_exist", f.Name), http.StatusUnprocessableEntity, tmplAdminAuthNew, f)
  153. } else {
  154. c.Error(err, "create login source")
  155. }
  156. return
  157. }
  158. if source.IsDefault {
  159. err = database.Handle.LoginSources().ResetNonDefault(c.Req.Context(), source)
  160. if err != nil {
  161. c.Error(err, "reset non-default login sources")
  162. return
  163. }
  164. }
  165. log.Trace("Authentication created by admin(%s): %s", c.User.Name, f.Name)
  166. c.Flash.Success(c.Tr("admin.auths.new_success", f.Name))
  167. c.Redirect(conf.Server.Subpath + "/admin/auths")
  168. }
  169. func EditAuthSource(c *context.Context) {
  170. c.Title("admin.auths.edit")
  171. c.PageIs("Admin")
  172. c.PageIs("AdminAuthentications")
  173. c.Data["SecurityProtocols"] = securityProtocols
  174. c.Data["SMTPAuths"] = smtp.AuthTypes
  175. source, err := database.Handle.LoginSources().GetByID(c.Req.Context(), c.ParamsInt64(":authid"))
  176. if err != nil {
  177. c.Error(err, "get login source by ID")
  178. return
  179. }
  180. c.Data["Source"] = source
  181. c.Data["HasTLS"] = source.Provider.HasTLS()
  182. c.Success(tmplAdminAuthEdit)
  183. }
  184. func EditAuthSourcePost(c *context.Context, f form.Authentication) {
  185. c.Title("admin.auths.edit")
  186. c.PageIs("Admin")
  187. c.PageIs("AdminAuthentications")
  188. c.Data["SMTPAuths"] = smtp.AuthTypes
  189. source, err := database.Handle.LoginSources().GetByID(c.Req.Context(), c.ParamsInt64(":authid"))
  190. if err != nil {
  191. c.Error(err, "get login source by ID")
  192. return
  193. }
  194. c.Data["Source"] = source
  195. c.Data["HasTLS"] = source.Provider.HasTLS()
  196. if c.HasError() {
  197. c.HTML(http.StatusBadRequest, tmplAdminAuthEdit)
  198. return
  199. }
  200. var provider auth.Provider
  201. switch auth.Type(f.Type) {
  202. case auth.LDAP:
  203. provider = ldap.NewProvider(false, parseLDAPConfig(f))
  204. case auth.DLDAP:
  205. provider = ldap.NewProvider(true, parseLDAPConfig(f))
  206. case auth.SMTP:
  207. provider = smtp.NewProvider(parseSMTPConfig(f))
  208. case auth.PAM:
  209. provider = pam.NewProvider(&pam.Config{
  210. ServiceName: f.PAMServiceName,
  211. })
  212. case auth.GitHub:
  213. provider = github.NewProvider(&github.Config{
  214. APIEndpoint: strings.TrimSuffix(f.GitHubAPIEndpoint, "/") + "/",
  215. SkipVerify: f.SkipVerify,
  216. })
  217. default:
  218. c.Status(http.StatusBadRequest)
  219. return
  220. }
  221. source.Name = f.Name
  222. source.IsActived = f.IsActive
  223. source.IsDefault = f.IsDefault
  224. source.Provider = provider
  225. if err := database.Handle.LoginSources().Save(c.Req.Context(), source); err != nil {
  226. c.Error(err, "update login source")
  227. return
  228. }
  229. if source.IsDefault {
  230. err = database.Handle.LoginSources().ResetNonDefault(c.Req.Context(), source)
  231. if err != nil {
  232. c.Error(err, "reset non-default login sources")
  233. return
  234. }
  235. }
  236. log.Trace("Authentication changed by admin '%s': %d", c.User.Name, source.ID)
  237. c.Flash.Success(c.Tr("admin.auths.update_success"))
  238. c.Redirect(conf.Server.Subpath + "/admin/auths/" + strconv.FormatInt(f.ID, 10))
  239. }
  240. func DeleteAuthSource(c *context.Context) {
  241. id := c.ParamsInt64(":authid")
  242. if err := database.Handle.LoginSources().DeleteByID(c.Req.Context(), id); err != nil {
  243. if database.IsErrLoginSourceInUse(err) {
  244. c.Flash.Error(c.Tr("admin.auths.still_in_used"))
  245. } else {
  246. c.Flash.Error(fmt.Sprintf("DeleteSource: %v", err))
  247. }
  248. c.JSONSuccess(map[string]any{
  249. "redirect": conf.Server.Subpath + "/admin/auths/" + c.Params(":authid"),
  250. })
  251. return
  252. }
  253. log.Trace("Authentication deleted by admin(%s): %d", c.User.Name, id)
  254. c.Flash.Success(c.Tr("admin.auths.deletion_success"))
  255. c.JSONSuccess(map[string]any{
  256. "redirect": conf.Server.Subpath + "/admin/auths",
  257. })
  258. }