diff --git a/cmd/web.go b/cmd/web.go index a56885b85..d6679fa25 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -134,6 +134,7 @@ func runWeb(*cli.Context) { r.Get("/users", admin.Users) r.Get("/repos", admin.Repositories) r.Get("/config", admin.Config) + r.Get("/auths", admin.Auths) }, adminReq) m.Group("/admin/users", func(r martini.Router) { r.Get("/new", admin.NewUser) @@ -143,6 +144,14 @@ func runWeb(*cli.Context) { r.Get("/:userid/delete", admin.DeleteUser) }, adminReq) + m.Group("/admin/auths", func(r martini.Router) { + r.Get("/new", admin.NewAuthSource) + r.Post("/new", bindIgnErr(auth.AuthenticationForm{}), admin.NewAuthSourcePost) + r.Get("/:authid", admin.EditAuthSource) + r.Post("/:authid", bindIgnErr(auth.AuthenticationForm{}), admin.EditAuthSourcePost) + r.Get("/:authid/delete", admin.DeleteAuthSource) + }, adminReq) + if martini.Env == martini.Dev { m.Get("/template/**", dev.TemplatePreview) } @@ -195,7 +204,7 @@ func runWeb(*cli.Context) { protocol := base.Cfg.MustValue("server", "PROTOCOL", "http") listenAddr := fmt.Sprintf("%s:%s", - base.Cfg.MustValue("server", "HTTP_ADDR"), + base.Cfg.MustValue("server", "HTTP_ADDR", "0.0.0.0"), base.Cfg.MustValue("server", "HTTP_PORT", "3000")) if protocol == "http" { diff --git a/models/ldap.go b/models/ldap.go index cc9058765..1da5b8754 100644 --- a/models/ldap.go +++ b/models/ldap.go @@ -30,7 +30,7 @@ func LoginUserLdap(name, passwd string) (*User, error) { Email: mail} _, err := RegisterUser(&user) if err != nil { - log.Debug("LDAP local user %s fond (%s) ", name, err) + log.Debug("LDAP local user %s found (%s) ", name, err) } // simulate local user login localUser, err2 := GetUserByName(user.Name) diff --git a/models/login.go b/models/login.go new file mode 100644 index 000000000..d5905eb36 --- /dev/null +++ b/models/login.go @@ -0,0 +1,117 @@ +package models + +import ( + "encoding/json" + "errors" + "time" + + "github.com/go-xorm/core" + "github.com/go-xorm/xorm" + "github.com/gogits/gogs/modules/auth/ldap" +) + +// Login types. +const ( + LT_PLAIN = iota + 1 + LT_LDAP + LT_SMTP +) + +var ( + ErrAuthenticationAlreadyExist = errors.New("Authentication already exist") + ErrAuthenticationNotExist = errors.New("Authentication is not exist") + ErrAuthenticationUserUsed = errors.New("Authentication has been used by some users") +) + +var LoginTypes = map[int]string{ + LT_LDAP: "LDAP", + LT_SMTP: "SMTP", +} + +var _ core.Conversion = &LDAPConfig{} + +type LDAPConfig struct { + ldap.Ldapsource +} + +// implement +func (cfg *LDAPConfig) FromDB(bs []byte) error { + return json.Unmarshal(bs, &cfg.Ldapsource) +} + +func (cfg *LDAPConfig) ToDB() ([]byte, error) { + return json.Marshal(cfg.Ldapsource) +} + +type LoginSource struct { + Id int64 + Type int + Name string `xorm:"unique"` + IsActived bool `xorm:"not null default false"` + Cfg core.Conversion `xorm:"TEXT"` + Created time.Time `xorm:"created"` + Updated time.Time `xorm:"updated"` +} + +func (source *LoginSource) TypeString() string { + return LoginTypes[source.Type] +} + +func (source *LoginSource) LDAP() *LDAPConfig { + return source.Cfg.(*LDAPConfig) +} + +// for xorm callback +func (source *LoginSource) BeforeSet(colName string, val xorm.Cell) { + if colName == "type" { + ty := (*val).(int64) + switch ty { + case LT_LDAP: + source.Cfg = new(LDAPConfig) + } + } +} + +func GetAuths() ([]*LoginSource, error) { + var auths = make([]*LoginSource, 0) + err := orm.Find(&auths) + return auths, err +} + +func GetLoginSourceById(id int64) (*LoginSource, error) { + source := new(LoginSource) + has, err := orm.Id(id).Get(source) + if err != nil { + return nil, err + } + if !has { + return nil, ErrAuthenticationNotExist + } + return source, nil +} + +func AddLDAPSource(name string, cfg *LDAPConfig) error { + _, err := orm.Insert(&LoginSource{Type: LT_LDAP, + Name: name, + IsActived: true, + Cfg: cfg, + }) + return err +} + +func UpdateLDAPSource(source *LoginSource) error { + _, err := orm.AllCols().Id(source.Id).Update(source) + return err +} + +func DelLoginSource(source *LoginSource) error { + cnt, err := orm.Count(&User{LoginSource: source.Id}) + if err != nil { + return err + } + if cnt > 0 { + return ErrAuthenticationUserUsed + } + _, err = orm.Id(source.Id).Delete(&LoginSource{}) + return err +} diff --git a/models/models.go b/models/models.go index 09b530ced..b565f22ab 100644 --- a/models/models.go +++ b/models/models.go @@ -34,7 +34,7 @@ var ( func init() { tables = append(tables, new(User), new(PublicKey), new(Repository), new(Watch), new(Action), new(Access), new(Issue), new(Comment), new(Oauth2), new(Follow), - new(Mirror), new(Release)) + new(Mirror), new(Release), new(LoginSource)) } func LoadModelsConfig() { diff --git a/models/user.go b/models/user.go index 27140cf1c..a4b753f8e 100644 --- a/models/user.go +++ b/models/user.go @@ -26,12 +26,6 @@ const ( UT_ORGANIZATION ) -// Login types. -const ( - LT_PLAIN = iota + 1 - LT_LDAP -) - var ( ErrUserOwnRepos = errors.New("User still have ownership of repositories") ErrUserAlreadyExist = errors.New("User already exist") @@ -50,6 +44,7 @@ type User struct { Email string `xorm:"unique not null"` Passwd string `xorm:"not null"` LoginType int + LoginSource int64 `xorm:"not null default 0"` Type int NumFollowers int NumFollowings int diff --git a/modules/auth/admin.go b/modules/auth/admin.go index 51c1b78cc..c82fa7815 100644 --- a/modules/auth/admin.go +++ b/modules/auth/admin.go @@ -16,12 +16,13 @@ import ( ) type AdminEditUserForm struct { - Email string `form:"email" binding:"Required;Email;MaxSize(50)"` - Website string `form:"website" binding:"MaxSize(50)"` - Location string `form:"location" binding:"MaxSize(50)"` - Avatar string `form:"avatar" binding:"Required;Email;MaxSize(50)"` - Active string `form:"active"` - Admin string `form:"admin"` + Email string `form:"email" binding:"Required;Email;MaxSize(50)"` + Website string `form:"website" binding:"MaxSize(50)"` + Location string `form:"location" binding:"MaxSize(50)"` + Avatar string `form:"avatar" binding:"Required;Email;MaxSize(50)"` + Active string `form:"active"` + Admin string `form:"admin"` + LoginType int `form:"login_type"` } func (f *AdminEditUserForm) Name(field string) string { diff --git a/modules/auth/auth.go b/modules/auth/auth.go index 8f7deaa30..2f7734917 100644 --- a/modules/auth/auth.go +++ b/modules/auth/auth.go @@ -26,6 +26,7 @@ type RegisterForm struct { Email string `form:"email" binding:"Required;Email;MaxSize(50)"` Password string `form:"passwd" binding:"Required;MinSize(6);MaxSize(30)"` RetypePasswd string `form:"retypepasswd"` + LoginType string `form:"logintype"` } func (f *RegisterForm) Name(field string) string { diff --git a/modules/auth/authentication.go b/modules/auth/authentication.go new file mode 100644 index 000000000..d9c61b9d7 --- /dev/null +++ b/modules/auth/authentication.go @@ -0,0 +1,15 @@ +package auth + +type AuthenticationForm struct { + Id int64 `form:"id"` + Type int `form:"type"` + Name string `form:"name" binding:"MaxSize(50)"` + Domain string `form:"domain"` + Host string `form:"host"` + Port int `form:"port"` + BaseDN string `form:"base_dn"` + Attributes string `form:"attributes"` + Filter string `form:"filter"` + MsAdSA string `form:"ms_ad_sa"` + IsActived bool `form:"is_actived"` +} diff --git a/modules/auth/ldap/ldap.go b/modules/auth/ldap/ldap.go index 29773cda5..8578e38a4 100644 --- a/modules/auth/ldap/ldap.go +++ b/modules/auth/ldap/ldap.go @@ -8,12 +8,13 @@ package ldap import ( "fmt" + "github.com/gogits/gogs/modules/log" goldap "github.com/juju2013/goldap" ) // Basic LDAP authentication service -type ldapsource struct { +type Ldapsource struct { Name string // canonical name (ie. corporate.ad) Host string // LDAP host Port int // port number @@ -26,12 +27,12 @@ type ldapsource struct { //Global LDAP directory pool var ( - Authensource []ldapsource + Authensource []Ldapsource ) // Add a new source (LDAP directory) to the global pool func AddSource(name string, host string, port int, basedn string, attributes string, filter string, msadsaformat string) { - ldaphost := ldapsource{name, host, port, basedn, attributes, filter, msadsaformat, true} + ldaphost := Ldapsource{name, host, port, basedn, attributes, filter, msadsaformat, true} Authensource = append(Authensource, ldaphost) } @@ -50,7 +51,7 @@ func LoginUser(name, passwd string) (a string, r bool) { } // searchEntry : search an LDAP source if an entry (name, passwd) is valide and in the specific filter -func (ls ldapsource) searchEntry(name, passwd string) (string, bool) { +func (ls Ldapsource) searchEntry(name, passwd string) (string, bool) { l, err := goldap.Dial("tcp", fmt.Sprintf("%s:%d", ls.Host, ls.Port)) if err != nil { log.Debug("LDAP Connect error, disabled source %s", ls.Host) diff --git a/routers/admin/admin.go b/routers/admin/admin.go index 2d5bdaff2..eafe8cb41 100644 --- a/routers/admin/admin.go +++ b/routers/admin/admin.go @@ -120,6 +120,19 @@ func Users(ctx *middleware.Context) { ctx.HTML(200, "admin/users") } +func Auths(ctx *middleware.Context) { + ctx.Data["Title"] = "Auth Sources" + ctx.Data["PageIsAuths"] = true + + var err error + ctx.Data["Sources"], err = models.GetAuths() + if err != nil { + ctx.Handle(200, "admin.Auths", err) + return + } + ctx.HTML(200, "admin/auths") +} + func Repositories(ctx *middleware.Context) { ctx.Data["Title"] = "Repository Management" ctx.Data["PageIsRepos"] = true diff --git a/routers/admin/auths.go b/routers/admin/auths.go new file mode 100644 index 000000000..892413049 --- /dev/null +++ b/routers/admin/auths.go @@ -0,0 +1,147 @@ +package admin + +import ( + "strings" + + "github.com/go-martini/martini" + "github.com/gogits/gogs/models" + "github.com/gogits/gogs/modules/auth" + "github.com/gogits/gogs/modules/auth/ldap" + "github.com/gogits/gogs/modules/base" + "github.com/gogits/gogs/modules/middleware" + "github.com/gpmgo/gopm/log" +) + +func NewAuthSource(ctx *middleware.Context) { + ctx.Data["Title"] = "New Authentication" + ctx.Data["PageIsAuths"] = true + ctx.Data["LoginTypes"] = models.LoginTypes + ctx.HTML(200, "admin/auths/new") +} + +func NewAuthSourcePost(ctx *middleware.Context, form auth.AuthenticationForm) { + ctx.Data["Title"] = "New Authentication" + ctx.Data["PageIsAuths"] = true + + if ctx.HasError() { + ctx.HTML(200, "admin/auths/new") + return + } + + u := &models.LDAPConfig{ + Ldapsource: ldap.Ldapsource{ + Host: form.Host, + Port: form.Port, + BaseDN: form.BaseDN, + Attributes: form.Attributes, + Filter: form.Filter, + MsAdSAFormat: form.MsAdSA, + Enabled: true, + Name: form.Name, + }, + } + + if err := models.AddLDAPSource(form.Name, u); err != nil { + switch err { + default: + ctx.Handle(500, "admin.auths.NewAuth", err) + } + return + } + + log.Trace("%s Authentication created by admin(%s): %s", ctx.Req.RequestURI, + ctx.User.LowerName, strings.ToLower(form.Name)) + + ctx.Redirect("/admin/auths") +} + +func EditAuthSource(ctx *middleware.Context, params martini.Params) { + ctx.Data["Title"] = "Edit Authentication" + ctx.Data["PageIsAuths"] = true + id, err := base.StrTo(params["authid"]).Int64() + if err != nil { + ctx.Handle(404, "admin.auths.EditAuthSource", err) + return + } + u, err := models.GetLoginSourceById(id) + if err != nil { + ctx.Handle(500, "admin.user.EditUser", err) + return + } + ctx.Data["Source"] = u + ctx.Data["LoginTypes"] = models.LoginTypes + ctx.HTML(200, "admin/auths/edit") +} + +func EditAuthSourcePost(ctx *middleware.Context, form auth.AuthenticationForm) { + ctx.Data["Title"] = "Edit Authentication" + ctx.Data["PageIsAuths"] = true + + if ctx.HasError() { + ctx.HTML(200, "admin/auths/edit") + return + } + + u := models.LoginSource{ + Name: form.Name, + IsActived: form.IsActived, + Type: models.LT_LDAP, + Cfg: &models.LDAPConfig{ + Ldapsource: ldap.Ldapsource{ + Host: form.Host, + Port: form.Port, + BaseDN: form.BaseDN, + Attributes: form.Attributes, + Filter: form.Filter, + MsAdSAFormat: form.MsAdSA, + Enabled: true, + Name: form.Name, + }, + }, + } + + if err := models.UpdateLDAPSource(&u); err != nil { + switch err { + default: + ctx.Handle(500, "admin.auths.EditAuth", err) + } + return + } + + log.Trace("%s Authentication changed by admin(%s): %s", ctx.Req.RequestURI, + ctx.User.LowerName, strings.ToLower(form.Name)) + + ctx.Redirect("/admin/auths") +} + +func DeleteAuthSource(ctx *middleware.Context, params martini.Params) { + ctx.Data["Title"] = "Delete Authentication" + ctx.Data["PageIsAuths"] = true + + id, err := base.StrTo(params["authid"]).Int64() + if err != nil { + ctx.Handle(404, "admin.auths.DeleteAuth", err) + return + } + + a, err := models.GetLoginSourceById(id) + if err != nil { + ctx.Handle(500, "admin.auths.DeleteAuth", err) + return + } + + if err = models.DelLoginSource(a); err != nil { + switch err { + case models.ErrAuthenticationUserUsed: + ctx.Flash.Error("This authentication still has used by some users, you should move them and then delete again.") + ctx.Redirect("/admin/auths/" + params["authid"]) + default: + ctx.Handle(500, "admin.auths.DeleteAuth", err) + } + return + } + log.Trace("%s Authentication deleted by admin(%s): %s", ctx.Req.RequestURI, + ctx.User.LowerName, ctx.User.LowerName) + + ctx.Redirect("/admin/auths") +} diff --git a/routers/admin/user.go b/routers/admin/user.go index fee692202..14c17e975 100644 --- a/routers/admin/user.go +++ b/routers/admin/user.go @@ -5,6 +5,8 @@ package admin import ( + "fmt" + "strconv" "strings" "github.com/go-martini/martini" @@ -19,6 +21,12 @@ import ( func NewUser(ctx *middleware.Context) { ctx.Data["Title"] = "New Account" ctx.Data["PageIsUsers"] = true + auths, err := models.GetAuths() + if err != nil { + ctx.Handle(500, "admin.user.NewUser", err) + return + } + ctx.Data["LoginSources"] = auths ctx.HTML(200, "admin/users/new") } @@ -40,10 +48,18 @@ func NewUserPost(ctx *middleware.Context, form auth.RegisterForm) { } u := &models.User{ - Name: form.UserName, - Email: form.Email, - Passwd: form.Password, - IsActive: true, + Name: form.UserName, + Email: form.Email, + Passwd: form.Password, + IsActive: true, + LoginType: models.LT_PLAIN, + } + + if len(form.LoginType) > 0 { + fields := strings.Split(form.LoginType, "-") + u.LoginType, _ = strconv.Atoi(fields[0]) + u.LoginSource, _ = strconv.ParseInt(fields[1], 10, 64) + fmt.Println(u.LoginSource) } var err error @@ -84,6 +100,12 @@ func EditUser(ctx *middleware.Context, params martini.Params) { } ctx.Data["User"] = u + auths, err := models.GetAuths() + if err != nil { + ctx.Handle(500, "admin.user.NewUser", err) + return + } + ctx.Data["LoginSources"] = auths ctx.HTML(200, "admin/users/edit") } @@ -110,6 +132,7 @@ func EditUserPost(ctx *middleware.Context, params martini.Params, form auth.Admi u.AvatarEmail = form.Avatar u.IsActive = form.Active == "on" u.IsAdmin = form.Admin == "on" + u.LoginType = form.LoginType if err := models.UpdateUser(u); err != nil { ctx.Handle(500, "admin.user.EditUser", err) return @@ -126,7 +149,7 @@ func DeleteUser(ctx *middleware.Context, params martini.Params) { ctx.Data["Title"] = "Delete Account" ctx.Data["PageIsUsers"] = true - log.Info("delete") + //log.Info("delete") uid, err := base.StrTo(params["userid"]).Int() if err != nil { ctx.Handle(404, "admin.user.EditUser", err) diff --git a/templates/admin/auths.tmpl b/templates/admin/auths.tmpl new file mode 100644 index 000000000..87baeafd6 --- /dev/null +++ b/templates/admin/auths.tmpl @@ -0,0 +1,43 @@ +{{template "base/head" .}} +{{template "base/navbar" .}} +
+ {{template "admin/nav" .}} +
+
+
+ Authentication Management +
+ +
+ New Auth Source + + + + + + + + + + + + + + {{range .Sources}} + + + + + + + + + + {{end}} + +
IdNameTypeActivedUpdatedCreatedOperation
{{.Id}}{{.Name}}{{.TypeString}}{{DateFormat .Updated "M d, Y"}}{{DateFormat .Created "M d, Y"}}
+
+
+
+
+{{template "base/footer" .}} \ No newline at end of file diff --git a/templates/admin/auths/edit.tmpl b/templates/admin/auths/edit.tmpl new file mode 100644 index 000000000..1a2548fbe --- /dev/null +++ b/templates/admin/auths/edit.tmpl @@ -0,0 +1,107 @@ +{{template "base/head" .}} +{{template "base/navbar" .}} +
+ {{template "admin/nav" .}} +
+
+
+ Edit Authentication +
+ +
+
+
+ {{.CsrfTokenHtml}} + {{template "base/alert" .}} + +
+ +
+ +
+
+
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+
+
+ +
+
+
+
+
+
+ + Delete this authentication +
+
+
+
+
+ +
+
+{{template "base/footer" .}} \ No newline at end of file diff --git a/templates/admin/auths/new.tmpl b/templates/admin/auths/new.tmpl new file mode 100644 index 000000000..a5da93bd2 --- /dev/null +++ b/templates/admin/auths/new.tmpl @@ -0,0 +1,94 @@ +{{template "base/head" .}} +{{template "base/navbar" .}} +
+ {{template "admin/nav" .}} +
+
+
+ New Authentication +
+ +
+
+
+ {{.CsrfTokenHtml}} + {{template "base/alert" .}} +
+ +
+ +
+
+
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+
+
+ +
+
+
+
+
+ +
+
+{{template "base/footer" .}} \ No newline at end of file diff --git a/templates/admin/nav.tmpl b/templates/admin/nav.tmpl index 33ecfae5e..b9542228b 100644 --- a/templates/admin/nav.tmpl +++ b/templates/admin/nav.tmpl @@ -4,5 +4,6 @@
  • Users
  • Repositories
  • Configuration
  • +
  • Authentication
  • \ No newline at end of file diff --git a/templates/admin/users/edit.tmpl b/templates/admin/users/edit.tmpl index da9a67cfa..9c9c36a28 100644 --- a/templates/admin/users/edit.tmpl +++ b/templates/admin/users/edit.tmpl @@ -14,6 +14,18 @@ {{.CsrfTokenHtml}} {{template "base/alert" .}} +
    + +
    + +
    +
    diff --git a/templates/admin/users/new.tmpl b/templates/admin/users/new.tmpl index 4c851e314..c19cd53c0 100644 --- a/templates/admin/users/new.tmpl +++ b/templates/admin/users/new.tmpl @@ -13,6 +13,17 @@
    {{.CsrfTokenHtml}} {{template "base/alert" .}} +
    + +
    + +
    +