Skip to content

Commit e385efc

Browse files
committed
Merge branch 'master' of github.com:gogits/gogs
2 parents 61e2922 + 5edd57e commit e385efc

File tree

18 files changed

+208
-46
lines changed

18 files changed

+208
-46
lines changed

modules/base/tool.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,17 @@ func EncodeMd5(str string) string {
2525
return hex.EncodeToString(m.Sum(nil))
2626
}
2727

28-
// Random generate string
29-
func GetRandomString(n int) string {
28+
// GetRandomString generate random string by specify chars.
29+
func GetRandomString(n int, alphabets ...byte) string {
3030
const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
3131
var bytes = make([]byte, n)
3232
rand.Read(bytes)
3333
for i, b := range bytes {
34-
bytes[i] = alphanum[b%byte(len(alphanum))]
34+
if len(alphabets) == 0 {
35+
bytes[i] = alphanum[b%byte(len(alphanum))]
36+
} else {
37+
bytes[i] = alphabets[b%byte(len(alphabets))]
38+
}
3539
}
3640
return string(bytes)
3741
}

modules/middleware/auth.go

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,39 +10,45 @@ import (
1010
"github.com/gogits/gogs/modules/base"
1111
)
1212

13-
// SignInRequire requires user to sign in.
14-
func SignInRequire(redirect bool) martini.Handler {
15-
return func(ctx *Context) {
16-
if !ctx.IsSigned {
17-
if redirect {
18-
ctx.Redirect("/user/login")
19-
}
20-
return
21-
} else if !ctx.User.IsActive && base.Service.RegisterEmailConfirm {
22-
ctx.Data["Title"] = "Activate Your Account"
23-
ctx.HTML(200, "user/active")
24-
return
25-
}
26-
}
13+
type ToggleOptions struct {
14+
SignInRequire bool
15+
SignOutRequire bool
16+
AdminRequire bool
17+
DisableCsrf bool
2718
}
2819

29-
// SignOutRequire requires user to sign out.
30-
func SignOutRequire() martini.Handler {
20+
func Toggle(options *ToggleOptions) martini.Handler {
3121
return func(ctx *Context) {
32-
if ctx.IsSigned {
22+
if options.SignOutRequire && ctx.IsSigned {
3323
ctx.Redirect("/")
3424
return
3525
}
36-
}
37-
}
3826

39-
// AdminRequire requires user signed in as administor.
40-
func AdminRequire() martini.Handler {
41-
return func(ctx *Context) {
42-
if !ctx.User.IsAdmin {
43-
ctx.Error(403)
44-
return
27+
if !options.DisableCsrf {
28+
if ctx.Req.Method == "POST" {
29+
if !ctx.CsrfTokenValid() {
30+
ctx.Error(403, "CSRF token does not match")
31+
return
32+
}
33+
}
34+
}
35+
36+
if options.SignInRequire {
37+
if !ctx.IsSigned {
38+
ctx.Redirect("/user/login")
39+
return
40+
} else if !ctx.User.IsActive && base.Service.RegisterEmailConfirm {
41+
ctx.Data["Title"] = "Activate Your Account"
42+
ctx.HTML(200, "user/active")
43+
return
44+
}
45+
}
46+
47+
if options.AdminRequire {
48+
if !ctx.User.IsAdmin {
49+
ctx.Error(403)
50+
return
51+
}
4552
}
46-
ctx.Data["PageIsAdmin"] = true
4753
}
4854
}

modules/middleware/context.go

Lines changed: 104 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package middleware
66

77
import (
88
"fmt"
9+
"html/template"
910
"net/http"
1011
"time"
1112

@@ -32,6 +33,8 @@ type Context struct {
3233
User *models.User
3334
IsSigned bool
3435

36+
csrfToken string
37+
3538
Repo struct {
3639
IsValid bool
3740
IsOwner bool
@@ -90,6 +93,95 @@ func (ctx *Context) Handle(status int, title string, err error) {
9093
ctx.HTML(status, fmt.Sprintf("status/%d", status))
9194
}
9295

96+
func (ctx *Context) GetCookie(name string) string {
97+
cookie, err := ctx.Req.Cookie(name)
98+
if err != nil {
99+
return ""
100+
}
101+
return cookie.Value
102+
}
103+
104+
func (ctx *Context) SetCookie(name string, value string, others ...interface{}) {
105+
cookie := http.Cookie{}
106+
cookie.Name = name
107+
cookie.Value = value
108+
109+
if len(others) > 0 {
110+
switch v := others[0].(type) {
111+
case int:
112+
cookie.MaxAge = v
113+
case int64:
114+
cookie.MaxAge = int(v)
115+
case int32:
116+
cookie.MaxAge = int(v)
117+
}
118+
}
119+
120+
// default "/"
121+
if len(others) > 1 {
122+
if v, ok := others[1].(string); ok && len(v) > 0 {
123+
cookie.Path = v
124+
}
125+
} else {
126+
cookie.Path = "/"
127+
}
128+
129+
// default empty
130+
if len(others) > 2 {
131+
if v, ok := others[2].(string); ok && len(v) > 0 {
132+
cookie.Domain = v
133+
}
134+
}
135+
136+
// default empty
137+
if len(others) > 3 {
138+
switch v := others[3].(type) {
139+
case bool:
140+
cookie.Secure = v
141+
default:
142+
if others[3] != nil {
143+
cookie.Secure = true
144+
}
145+
}
146+
}
147+
148+
// default false. for session cookie default true
149+
if len(others) > 4 {
150+
if v, ok := others[4].(bool); ok && v {
151+
cookie.HttpOnly = true
152+
}
153+
}
154+
155+
ctx.Res.Header().Add("Set-Cookie", cookie.String())
156+
}
157+
158+
func (ctx *Context) CsrfToken() string {
159+
if len(ctx.csrfToken) > 0 {
160+
return ctx.csrfToken
161+
}
162+
163+
token := ctx.GetCookie("_csrf")
164+
if len(token) == 0 {
165+
token = base.GetRandomString(30)
166+
ctx.SetCookie("_csrf", token)
167+
}
168+
ctx.csrfToken = token
169+
return token
170+
}
171+
172+
func (ctx *Context) CsrfTokenValid() bool {
173+
token := ctx.Query("_csrf")
174+
if token == "" {
175+
token = ctx.Req.Header.Get("X-Csrf-Token")
176+
}
177+
if token == "" {
178+
return false
179+
} else if ctx.csrfToken != token {
180+
return false
181+
}
182+
return true
183+
}
184+
93185
// InitContext initializes a classic context for a request.
94186
func InitContext() martini.Handler {
95187
return func(res http.ResponseWriter, r *http.Request, c martini.Context, rd *Render) {
@@ -103,11 +195,14 @@ func InitContext() martini.Handler {
103195
Render: rd,
104196
}
105197

198+
ctx.Data["PageStartTime"] = time.Now()
199+
106200
// start session
107201
ctx.Session = base.SessionManager.SessionStart(res, r)
108-
defer func() {
202+
rw := res.(martini.ResponseWriter)
203+
rw.Before(func(martini.ResponseWriter) {
109204
ctx.Session.SessionRelease(res)
110-
}()
205+
})
111206

112207
// Get user from session if logined.
113208
user := auth.SignedInUser(ctx.Session)
@@ -121,9 +216,15 @@ func InitContext() martini.Handler {
121216
ctx.Data["SignedUserId"] = user.Id
122217
ctx.Data["SignedUserName"] = user.LowerName
123218
ctx.Data["IsAdmin"] = ctx.User.IsAdmin
219+
220+
if ctx.User.IsAdmin {
221+
ctx.Data["PageIsAdmin"] = true
222+
}
124223
}
125224

126-
ctx.Data["PageStartTime"] = time.Now()
225+
// get or create csrf token
226+
ctx.Data["CsrfToken"] = ctx.CsrfToken()
227+
ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + ctx.csrfToken + `">`)
127228

128229
c.Map(ctx)
129230

modules/middleware/render.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,8 +242,11 @@ func (r *Render) HTMLString(name string, binding interface{}, htmlOpt ...HTMLOpt
242242
}
243243
}
244244

245-
func (r *Render) Error(status int) {
245+
func (r *Render) Error(status int, message ...string) {
246246
r.WriteHeader(status)
247+
if len(message) > 0 {
248+
r.Write([]byte(message[0]))
249+
}
247250
}
248251

249252
func (r *Render) Redirect(location string, status ...int) {

public/js/app.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,39 @@ var Gogits = {
22
"PageIsSignup": false
33
};
44

5+
(function($){
6+
// extend jQuery ajax, set csrf token value
7+
var ajax = $.ajax;
8+
$.extend({
9+
ajax: function(url, options) {
10+
if (typeof url === 'object') {
11+
options = url;
12+
url = undefined;
13+
}
14+
options = options || {};
15+
url = options.url;
16+
var csrftoken = $('meta[name=_csrf]').attr('content');
17+
var headers = options.headers || {};
18+
var domain = document.domain.replace(/\./ig, '\\.');
19+
if (!/^(http:|https:).*/.test(url) || eval('/^(http:|https:)\\/\\/(.+\\.)*' + domain + '.*/').test(url)) {
20+
headers = $.extend(headers, {'X-Csrf-Token':csrftoken});
21+
}
22+
options.headers = headers;
23+
var callback = options.success;
24+
options.success = function(data){
25+
if(data.once){
26+
// change all _once value if ajax data.once exist
27+
$('[name=_once]').val(data.once);
28+
}
29+
if(callback){
30+
callback.apply(this, arguments);
31+
}
32+
};
33+
return ajax(url, options);
34+
}
35+
});
36+
}(jQuery));
37+
538
(function ($) {
639

740
Gogits.showTab = function (selector, index) {

templates/admin/users/edit.tmpl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<br/>
1313
<form action="/admin/users/{{.User.Id}}" method="post" class="form-horizontal">
1414
{{if .IsSuccess}}<p class="alert alert-success">Account profile has been successfully updated.</p>{{else if .HasError}}<p class="alert alert-danger form-error">{{.ErrorMsg}}</p>{{end}}
15+
{{.CsrfTokenHtml}}
1516
<input type="hidden" value="{{.User.Id}}" name="userId"/>
1617
<div class="form-group">
1718
<label class="col-md-3 control-label">Username: </label>

templates/admin/users/new.tmpl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
<div class="panel-body">
1212
<br/>
1313
<form action="/admin/users/new" method="post" class="form-horizontal">
14+
{{.CsrfTokenHtml}}
1415
<div class="alert alert-danger form-error{{if .HasError}}{{else}} hidden{{end}}">{{.ErrorMsg}}</div>
1516
<div class="form-group {{if .Err_UserName}}has-error has-feedback{{end}}">
1617
<label class="col-md-3 control-label">Username: </label>

templates/base/head.tmpl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
<meta name="author" content="Gogs - Go Git Service" />
99
<meta name="description" content="Gogs(Go Git Service) is a GitHub-like clone in the Go Programming Language" />
1010
<meta name="keywords" content="go, git">
11+
<meta name="_csrf" content="{{.CsrfToken}}" />
1112

1213
<!-- Stylesheets -->
1314
<link href="/css/bootstrap.min.css" rel="stylesheet" />

templates/repo/create.tmpl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
{{template "base/navbar" .}}
33
<div class="container" id="gogs-body">
44
<form action="/repo/create" method="post" class="form-horizontal gogs-card" id="gogs-repo-create">
5+
{{.CsrfTokenHtml}}
56
<h3>Create New Repository</h3>
67
<div class="alert alert-danger form-error{{if .HasError}}{{else}} hidden{{end}}">{{.ErrorMsg}}</div>
78
<div class="form-group">

templates/repo/setting.tmpl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
<div class="modal fade" id="delete-repository-modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
4141
<div class="modal-dialog">
4242
<form action="/{{.Owner.Name}}/{{.Repository.Name}}/settings" method="post" class="modal-content">
43+
{{.CsrfTokenHtml}}
4344
<input type="hidden" name="action" value="delete">
4445

4546
<div class="modal-header">

templates/user/active.tmpl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
{{template "base/head" .}}
22
{{template "base/navbar" .}}
33
<div id="gogs-body" class="container">
4-
<form action="/user/activate" method="get" class="form-horizontal gogs-card" id="gogs-login-card">
4+
<form action="/user/activate" method="post" class="form-horizontal gogs-card" id="gogs-login-card">
5+
{{.CsrfTokenHtml}}
56
<h3>Activate Your Account</h3>
67
{{if .IsActivatePage}}
78
{{if .ServiceNotEnabled}}

templates/user/delete.tmpl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
<div class="modal fade" id="delete-account-modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
2323
<div class="modal-dialog">
2424
<form action="/user/delete" method="post" class="modal-content" id="gogs-user-delete">
25+
{{.CsrfTokenHtml}}
2526
<div class="modal-header">
2627
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
2728
<h4 class="modal-title" id="myModalLabel">Delete Account</h4>

templates/user/password.tmpl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
<div id="gogs-user-setting-container" class="col-md-9">
66
<div id="gogs-setting-pwd">
77
<h4>Password</h4>
8-
<form class="form-horizontal" id="gogs-password-form" method="post" action="/user/setting/password">{{if .IsSuccess}}
8+
<form class="form-horizontal" id="gogs-password-form" method="post" action="/user/setting/password">
9+
{{.CsrfTokenHtml}}
10+
{{if .IsSuccess}}
911
<p class="alert alert-success">Password is changed successfully. You can now sign in via new password.</p>{{else if .HasError}}<p class="alert alert-danger form-error">{{.ErrorMsg}}</p>{{end}}
1012
<div class="form-group">
1113
<label class="col-md-3 control-label">Old Password<strong class="text-danger">*</strong></label>

templates/user/publickey.tmpl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
<div class="modal fade" id="ssh-add-modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
2323
<div class="modal-dialog">
2424
<form class="modal-content form-horizontal" id="gogs-ssh-form" method="post" action="/user/setting/ssh/">
25+
{{.CsrfTokenHtml}}
2526
<div class="modal-header">
2627
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
2728
<h4 class="modal-title" id="myModalLabel">Add SSH Key</h4>

templates/user/setting.tmpl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
<div id="gogs-setting-pwd">
77
<h4>Account Profile</h4>
88
<form class="form-horizontal" id="gogs-password-form" method="post" action="/user/setting">
9+
{{.CsrfTokenHtml}}
910
{{if .IsSuccess}}<p class="alert alert-success">Your profile has been successfully updated.</p>{{else if .HasError}}<p class="alert alert-danger form-error">{{.ErrorMsg}}</p>{{end}}
1011
<p>Your Email will be public and used for Account related notifications and any web based operations made via the web.</p>
1112
<div class="form-group">

templates/user/signin.tmpl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
{{template "base/navbar" .}}
33
<div class="container" id="gogs-body" data-page="user-signin">
44
<form action="/user/login" method="post" class="form-horizontal gogs-card" id="gogs-login-card">
5+
{{.CsrfTokenHtml}}
56
<h3>Log in</h3>
67
<div class="alert alert-danger form-error{{if .HasError}}{{else}} hidden{{end}}">{{.ErrorMsg}}</div>
78
<div class="form-group {{if .Err_UserName}}has-error has-feedback{{end}}">

templates/user/signup.tmpl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
{{template "base/navbar" .}}
33
<div class="container" id="gogs-body" data-page="user-signup">
44
<form action="/user/sign_up" method="post" class="form-horizontal gogs-card" id="gogs-login-card">
5+
{{.CsrfTokenHtml}}
56
{{if .DisenableRegisteration}}
67
Sorry, registeration has been disenabled, you can only get account from administrator.
78
{{else}}

0 commit comments

Comments
 (0)