From 76cd448e7925997b60a54e8d9431ffd0826cc24e Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 22 Mar 2014 06:20:00 -0400 Subject: Add admin delete user --- web.go | 1 + 1 file changed, 1 insertion(+) (limited to 'web.go') diff --git a/web.go b/web.go index bb316a67..595b8f74 100644 --- a/web.go +++ b/web.go @@ -119,6 +119,7 @@ func runWeb(*cli.Context) { m.Get("/admin/users", reqSignIn, adminReq, admin.Users) m.Any("/admin/users/new", reqSignIn, adminReq, binding.BindIgnErr(auth.RegisterForm{}), admin.NewUser) m.Any("/admin/users/:userid", reqSignIn, adminReq, binding.BindIgnErr(auth.AdminEditUserForm{}), admin.EditUser) + m.Any("/admin/users/:userid/delete", reqSignIn, adminReq, admin.DeleteUser) m.Get("/admin/repos", reqSignIn, adminReq, admin.Repositories) m.Get("/admin/config", reqSignIn, adminReq, admin.Config) -- cgit v1.2.3 From f9c07c4186b61a1548d9a908fe6228bd130f4f92 Mon Sep 17 00:00:00 2001 From: slene Date: Sat, 22 Mar 2014 20:49:53 +0800 Subject: update session --- .gitignore | 1 + conf/app.ini | 27 +++++++++++++++++++++++++++ modules/auth/user.go | 11 ++++++----- modules/base/conf.go | 30 ++++++++++++++++++++++++++++++ modules/middleware/context.go | 24 ++++++++++++++---------- routers/user/user.go | 2 +- web.go | 5 ----- 7 files changed, 79 insertions(+), 21 deletions(-) (limited to 'web.go') diff --git a/.gitignore b/.gitignore index ad27cc8b..d201223e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ gogs *.db *.log custom/ +data/ .vendor/ .idea/ *.iml \ No newline at end of file diff --git a/conf/app.ini b/conf/app.ini index cf99c9da..cf2ae31d 100644 --- a/conf/app.ini +++ b/conf/app.ini @@ -72,6 +72,33 @@ INTERVAL = 60 ; memcache: "127.0.0.1:11211" HOST = +[session] +; Either "memory", "file", "redis" or "mysql", default is "memory" +PROVIDER = file +; provider config +; memory: not have any config yet +; file: session file path +; e.g. tmp/sessions +; redis: config like redis server addr,poolSize,password +; e.g. 127.0.0.1:6379,100,astaxie +; mysql: go-sql-driver/mysql dsn config string +; e.g. root:password@/session_table +PROVIDER_CONFIG = data/sessions +; session cookie name +COOKIE_NAME = i_like_gogits +; if you use session in https only, default is false +COOKIE_SECURE = false +; enable set cookie, default is true +ENABLE_SET_COOKIE = true +; session gc time interval, default is 86400 +GC_INTERVAL_TIME = 86400 +; session life time, default is 86400 +SESSION_LIFE_TIME = 86400 +; session id hash func, default is sha1 +SESSION_ID_HASHFUNC = sha1 +; session hash key, default is use random string +SESSION_ID_HASHKEY = + [picture] ; The place to picture data, either "server" or "qiniu", default is "server" SERVICE = server diff --git a/modules/auth/user.go b/modules/auth/user.go index f8d8f661..cb8db1b2 100644 --- a/modules/auth/user.go +++ b/modules/auth/user.go @@ -9,7 +9,8 @@ import ( "reflect" "github.com/codegangsta/martini" - "github.com/martini-contrib/sessions" + + "github.com/gogits/session" "github.com/gogits/binding" @@ -19,7 +20,7 @@ import ( ) // SignedInId returns the id of signed in user. -func SignedInId(session sessions.Session) int64 { +func SignedInId(session session.SessionStore) int64 { userId := session.Get("userId") if userId == nil { return 0 @@ -34,7 +35,7 @@ func SignedInId(session sessions.Session) int64 { } // SignedInName returns the name of signed in user. -func SignedInName(session sessions.Session) string { +func SignedInName(session session.SessionStore) string { userName := session.Get("userName") if userName == nil { return "" @@ -46,7 +47,7 @@ func SignedInName(session sessions.Session) string { } // SignedInUser returns the user object of signed user. -func SignedInUser(session sessions.Session) *models.User { +func SignedInUser(session session.SessionStore) *models.User { id := SignedInId(session) if id <= 0 { return nil @@ -61,7 +62,7 @@ func SignedInUser(session sessions.Session) *models.User { } // IsSignedIn check if any user has signed in. -func IsSignedIn(session sessions.Session) bool { +func IsSignedIn(session session.SessionStore) bool { return SignedInId(session) > 0 } diff --git a/modules/base/conf.go b/modules/base/conf.go index 8c6ee628..d5e27d04 100644 --- a/modules/base/conf.go +++ b/modules/base/conf.go @@ -16,6 +16,7 @@ import ( "github.com/Unknwon/goconfig" "github.com/gogits/cache" + "github.com/gogits/session" "github.com/gogits/gogs/modules/log" ) @@ -49,6 +50,10 @@ var ( LogMode string LogConfig string + + SessionProvider string + SessionConfig *session.Config + SessionManager *session.Manager ) var Service struct { @@ -164,6 +169,30 @@ func newCacheService() { log.Info("Cache Service Enabled") } +func newSessionService() { + SessionProvider = Cfg.MustValue("session", "PROVIDER", "memory") + + SessionConfig = new(session.Config) + SessionConfig.ProviderConfig = Cfg.MustValue("session", "PROVIDER_CONFIG") + SessionConfig.CookieName = Cfg.MustValue("session", "COOKIE_NAME", "i_like_gogits") + SessionConfig.CookieSecure = Cfg.MustBool("session", "COOKIE_SECURE") + SessionConfig.EnableSetCookie = Cfg.MustBool("session", "ENABLE_SET_COOKIE", true) + SessionConfig.GcIntervalTime = Cfg.MustInt64("session", "GC_INTERVAL_TIME", 86400) + SessionConfig.SessionLifeTime = Cfg.MustInt64("session", "SESSION_LIFE_TIME", 86400) + SessionConfig.SessionIDHashFunc = Cfg.MustValue("session", "SESSION_ID_HASHFUNC", "sha1") + SessionConfig.SessionIDHashKey = Cfg.MustValue("session", "SESSION_ID_HASHKEY") + + var err error + SessionManager, err = session.NewManager(SessionProvider, *SessionConfig) + if err != nil { + fmt.Printf("Init session system failed, provider: %s, %v\n", + SessionProvider, err) + os.Exit(2) + } + + log.Info("Session Service Enabled") +} + func newMailService() { // Check mailer setting. if Cfg.MustBool("mailer", "ENABLED") { @@ -234,6 +263,7 @@ func NewServices() { newService() newLogService() newCacheService() + newSessionService() newMailService() newRegisterMailService() } diff --git a/modules/middleware/context.go b/modules/middleware/context.go index a25a3dbb..c958c1d6 100644 --- a/modules/middleware/context.go +++ b/modules/middleware/context.go @@ -10,9 +10,9 @@ import ( "time" "github.com/codegangsta/martini" - "github.com/martini-contrib/sessions" "github.com/gogits/cache" + "github.com/gogits/session" "github.com/gogits/gogs/models" "github.com/gogits/gogs/modules/auth" @@ -27,7 +27,7 @@ type Context struct { p martini.Params Req *http.Request Res http.ResponseWriter - Session sessions.Session + Session session.SessionStore Cache cache.Cache User *models.User IsSigned bool @@ -92,21 +92,25 @@ func (ctx *Context) Handle(status int, title string, err error) { // InitContext initializes a classic context for a request. func InitContext() martini.Handler { - return func(res http.ResponseWriter, r *http.Request, c martini.Context, - session sessions.Session, rd *Render) { + return func(res http.ResponseWriter, r *http.Request, c martini.Context, rd *Render) { ctx := &Context{ c: c, // p: p, - Req: r, - Res: res, - Session: session, - Cache: base.Cache, - Render: rd, + Req: r, + Res: res, + Cache: base.Cache, + Render: rd, } + // start session + ctx.Session = base.SessionManager.SessionStart(res, r) + defer func() { + ctx.Session.SessionRelease(res) + }() + // Get user from session if logined. - user := auth.SignedInUser(session) + user := auth.SignedInUser(ctx.Session) ctx.User = user ctx.IsSigned = user != nil diff --git a/routers/user/user.go b/routers/user/user.go index d38eb1ce..22446977 100644 --- a/routers/user/user.go +++ b/routers/user/user.go @@ -88,7 +88,7 @@ func SignIn(ctx *middleware.Context, form auth.LogInForm) { user, err := models.LoginUserPlain(form.UserName, form.Password) if err != nil { - if err.Error() == models.ErrUserNotExist.Error() { + if err == models.ErrUserNotExist { ctx.RenderWithErr("Username or password is not correct", "user/signin", &form) return } diff --git a/web.go b/web.go index 595b8f74..ac5761d7 100644 --- a/web.go +++ b/web.go @@ -12,7 +12,6 @@ import ( "github.com/codegangsta/cli" "github.com/codegangsta/martini" - "github.com/martini-contrib/sessions" "github.com/gogits/binding" @@ -81,10 +80,6 @@ func runWeb(*cli.Context) { // Middlewares. m.Use(middleware.Renderer(middleware.RenderOptions{Funcs: []template.FuncMap{base.TemplateFuncs}})) - // TODO: should use other store because cookie store is not secure. - store := sessions.NewCookieStore([]byte("secret123")) - m.Use(sessions.Sessions("my_session", store)) - m.Use(middleware.InitContext()) reqSignIn := middleware.SignInRequire(true) -- cgit v1.2.3 From 076fc98d981aea3533eea363ca1c7e43f77b9802 Mon Sep 17 00:00:00 2001 From: slene Date: Sun, 23 Mar 2014 01:44:02 +0800 Subject: add csrf check --- modules/base/tool.go | 10 ++-- modules/middleware/auth.go | 58 ++++++++++++---------- modules/middleware/context.go | 107 ++++++++++++++++++++++++++++++++++++++-- modules/middleware/render.go | 5 +- public/js/app.js | 33 +++++++++++++ templates/admin/users/edit.tmpl | 1 + templates/admin/users/new.tmpl | 1 + templates/base/head.tmpl | 1 + templates/repo/create.tmpl | 1 + templates/repo/setting.tmpl | 1 + templates/user/active.tmpl | 3 +- templates/user/delete.tmpl | 1 + templates/user/password.tmpl | 4 +- templates/user/publickey.tmpl | 1 + templates/user/setting.tmpl | 1 + templates/user/signin.tmpl | 1 + templates/user/signup.tmpl | 1 + web.go | 24 ++++----- 18 files changed, 208 insertions(+), 46 deletions(-) (limited to 'web.go') diff --git a/modules/base/tool.go b/modules/base/tool.go index 8fabb8c5..a2aeebf1 100644 --- a/modules/base/tool.go +++ b/modules/base/tool.go @@ -25,13 +25,17 @@ func EncodeMd5(str string) string { return hex.EncodeToString(m.Sum(nil)) } -// Random generate string -func GetRandomString(n int) string { +// GetRandomString generate random string by specify chars. +func GetRandomString(n int, alphabets ...byte) string { const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" var bytes = make([]byte, n) rand.Read(bytes) for i, b := range bytes { - bytes[i] = alphanum[b%byte(len(alphanum))] + if len(alphabets) == 0 { + bytes[i] = alphanum[b%byte(len(alphanum))] + } else { + bytes[i] = alphabets[b%byte(len(alphabets))] + } } return string(bytes) } diff --git a/modules/middleware/auth.go b/modules/middleware/auth.go index f211de32..b557188e 100644 --- a/modules/middleware/auth.go +++ b/modules/middleware/auth.go @@ -10,39 +10,45 @@ import ( "github.com/gogits/gogs/modules/base" ) -// SignInRequire requires user to sign in. -func SignInRequire(redirect bool) martini.Handler { - return func(ctx *Context) { - if !ctx.IsSigned { - if redirect { - ctx.Redirect("/user/login") - } - return - } else if !ctx.User.IsActive && base.Service.RegisterEmailConfirm { - ctx.Data["Title"] = "Activate Your Account" - ctx.HTML(200, "user/active") - return - } - } +type ToggleOptions struct { + SignInRequire bool + SignOutRequire bool + AdminRequire bool + DisableCsrf bool } -// SignOutRequire requires user to sign out. -func SignOutRequire() martini.Handler { +func Toggle(options *ToggleOptions) martini.Handler { return func(ctx *Context) { - if ctx.IsSigned { + if options.SignOutRequire && ctx.IsSigned { ctx.Redirect("/") return } - } -} -// AdminRequire requires user signed in as administor. -func AdminRequire() martini.Handler { - return func(ctx *Context) { - if !ctx.User.IsAdmin { - ctx.Error(403) - return + if !options.DisableCsrf { + if ctx.Req.Method == "POST" { + if !ctx.CsrfTokenValid() { + ctx.Error(403, "CSRF token does not match") + return + } + } + } + + if options.SignInRequire { + if !ctx.IsSigned { + ctx.Redirect("/user/login") + return + } else if !ctx.User.IsActive && base.Service.RegisterEmailConfirm { + ctx.Data["Title"] = "Activate Your Account" + ctx.HTML(200, "user/active") + return + } + } + + if options.AdminRequire { + if !ctx.User.IsAdmin { + ctx.Error(403) + return + } } - ctx.Data["PageIsAdmin"] = true } } diff --git a/modules/middleware/context.go b/modules/middleware/context.go index c958c1d6..b28953fc 100644 --- a/modules/middleware/context.go +++ b/modules/middleware/context.go @@ -6,6 +6,7 @@ package middleware import ( "fmt" + "html/template" "net/http" "time" @@ -32,6 +33,8 @@ type Context struct { User *models.User IsSigned bool + csrfToken string + Repo struct { IsValid bool IsOwner bool @@ -90,6 +93,95 @@ func (ctx *Context) Handle(status int, title string, err error) { ctx.HTML(status, fmt.Sprintf("status/%d", status)) } +func (ctx *Context) GetCookie(name string) string { + cookie, err := ctx.Req.Cookie(name) + if err != nil { + return "" + } + return cookie.Value +} + +func (ctx *Context) SetCookie(name string, value string, others ...interface{}) { + cookie := http.Cookie{} + cookie.Name = name + cookie.Value = value + + if len(others) > 0 { + switch v := others[0].(type) { + case int: + cookie.MaxAge = v + case int64: + cookie.MaxAge = int(v) + case int32: + cookie.MaxAge = int(v) + } + } + + // default "/" + if len(others) > 1 { + if v, ok := others[1].(string); ok && len(v) > 0 { + cookie.Path = v + } + } else { + cookie.Path = "/" + } + + // default empty + if len(others) > 2 { + if v, ok := others[2].(string); ok && len(v) > 0 { + cookie.Domain = v + } + } + + // default empty + if len(others) > 3 { + switch v := others[3].(type) { + case bool: + cookie.Secure = v + default: + if others[3] != nil { + cookie.Secure = true + } + } + } + + // default false. for session cookie default true + if len(others) > 4 { + if v, ok := others[4].(bool); ok && v { + cookie.HttpOnly = true + } + } + + ctx.Res.Header().Add("Set-Cookie", cookie.String()) +} + +func (ctx *Context) CsrfToken() string { + if len(ctx.csrfToken) > 0 { + return ctx.csrfToken + } + + token := ctx.GetCookie("_csrf") + if len(token) == 0 { + token = base.GetRandomString(30) + ctx.SetCookie("_csrf", token) + } + ctx.csrfToken = token + return token +} + +func (ctx *Context) CsrfTokenValid() bool { + token := ctx.Query("_csrf") + if token == "" { + token = ctx.Req.Header.Get("X-Csrf-Token") + } + if token == "" { + return false + } else if ctx.csrfToken != token { + return false + } + return true +} + // InitContext initializes a classic context for a request. func InitContext() martini.Handler { return func(res http.ResponseWriter, r *http.Request, c martini.Context, rd *Render) { @@ -103,11 +195,14 @@ func InitContext() martini.Handler { Render: rd, } + ctx.Data["PageStartTime"] = time.Now() + // start session ctx.Session = base.SessionManager.SessionStart(res, r) - defer func() { + rw := res.(martini.ResponseWriter) + rw.Before(func(martini.ResponseWriter) { ctx.Session.SessionRelease(res) - }() + }) // Get user from session if logined. user := auth.SignedInUser(ctx.Session) @@ -121,9 +216,15 @@ func InitContext() martini.Handler { ctx.Data["SignedUserId"] = user.Id ctx.Data["SignedUserName"] = user.LowerName ctx.Data["IsAdmin"] = ctx.User.IsAdmin + + if ctx.User.IsAdmin { + ctx.Data["PageIsAdmin"] = true + } } - ctx.Data["PageStartTime"] = time.Now() + // get or create csrf token + ctx.Data["CsrfToken"] = ctx.CsrfToken() + ctx.Data["CsrfTokenHtml"] = template.HTML(``) c.Map(ctx) diff --git a/modules/middleware/render.go b/modules/middleware/render.go index 8a541831..869ef9ab 100644 --- a/modules/middleware/render.go +++ b/modules/middleware/render.go @@ -242,8 +242,11 @@ func (r *Render) HTMLString(name string, binding interface{}, htmlOpt ...HTMLOpt } } -func (r *Render) Error(status int) { +func (r *Render) Error(status int, message ...string) { r.WriteHeader(status) + if len(message) > 0 { + r.Write([]byte(message[0])) + } } func (r *Render) Redirect(location string, status ...int) { diff --git a/public/js/app.js b/public/js/app.js index f179342f..df755727 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -2,6 +2,39 @@ var Gogits = { "PageIsSignup": false }; +(function($){ + // extend jQuery ajax, set csrf token value + var ajax = $.ajax; + $.extend({ + ajax: function(url, options) { + if (typeof url === 'object') { + options = url; + url = undefined; + } + options = options || {}; + url = options.url; + var csrftoken = $('meta[name=_csrf]').attr('content'); + var headers = options.headers || {}; + var domain = document.domain.replace(/\./ig, '\\.'); + if (!/^(http:|https:).*/.test(url) || eval('/^(http:|https:)\\/\\/(.+\\.)*' + domain + '.*/').test(url)) { + headers = $.extend(headers, {'X-Csrf-Token':csrftoken}); + } + options.headers = headers; + var callback = options.success; + options.success = function(data){ + if(data.once){ + // change all _once value if ajax data.once exist + $('[name=_once]').val(data.once); + } + if(callback){ + callback.apply(this, arguments); + } + }; + return ajax(url, options); + } + }); +}(jQuery)); + (function ($) { Gogits.showTab = function (selector, index) { diff --git a/templates/admin/users/edit.tmpl b/templates/admin/users/edit.tmpl index 2a988242..08f11fcb 100644 --- a/templates/admin/users/edit.tmpl +++ b/templates/admin/users/edit.tmpl @@ -12,6 +12,7 @@
{{if .IsSuccess}}

Account profile has been successfully updated.

{{else if .HasError}}

{{.ErrorMsg}}

{{end}} + {{.CsrfTokenHtml}}
diff --git a/templates/admin/users/new.tmpl b/templates/admin/users/new.tmpl index 01d976ca..7b41ae43 100644 --- a/templates/admin/users/new.tmpl +++ b/templates/admin/users/new.tmpl @@ -11,6 +11,7 @@

+ {{.CsrfTokenHtml}}
{{.ErrorMsg}}
diff --git a/templates/base/head.tmpl b/templates/base/head.tmpl index f02ea095..7f56ed70 100644 --- a/templates/base/head.tmpl +++ b/templates/base/head.tmpl @@ -8,6 +8,7 @@ + diff --git a/templates/repo/create.tmpl b/templates/repo/create.tmpl index 2de92f51..a43f5104 100644 --- a/templates/repo/create.tmpl +++ b/templates/repo/create.tmpl @@ -2,6 +2,7 @@ {{template "base/navbar" .}}
+ {{.CsrfTokenHtml}}

Create New Repository

{{.ErrorMsg}}
diff --git a/templates/repo/setting.tmpl b/templates/repo/setting.tmpl index a2fb1771..38c3fd3b 100644 --- a/templates/repo/setting.tmpl +++ b/templates/repo/setting.tmpl @@ -40,6 +40,7 @@