Skip to content

Commit 0875114

Browse files
committed
Added configuration options and route exemptions
1 parent 2ec83ef commit 0875114

File tree

3 files changed

+225
-0
lines changed

3 files changed

+225
-0
lines changed

exemptions.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Management of routes exempted from CSRF checks.
2+
package csrf
3+
4+
import (
5+
"github.com/golang/glog"
6+
"fmt"
7+
pathPackage "path"
8+
"sync"
9+
)
10+
11+
// I'm not cetain that we need a mutex because exempted routes are generally
12+
// configured when the application starts and the list is read-only after.
13+
type globPath struct {
14+
sync.RWMutex
15+
list []string
16+
}
17+
18+
var (
19+
exemptionsFullPath = struct {
20+
sync.RWMutex
21+
list map[string]struct{}
22+
} {
23+
list: make(map[string]struct{}),
24+
}
25+
26+
exemptionsGlobs globPath
27+
)
28+
29+
// Checks if given path is exempt from CSRF checks.
30+
func IsExempted(path string) bool {
31+
exemptionsFullPath.RLock()
32+
_, found := exemptionsFullPath.list[path]
33+
exemptionsFullPath.RUnlock()
34+
if found {
35+
glog.V(2).Infof("REVEL-CSRF: Ignoring exempted route '%s'...", path)
36+
return true
37+
}
38+
39+
for _, glob := range exemptionsGlobs.list {
40+
exemptionsGlobs.RLock()
41+
found, err := pathPackage.Match(glob, path)
42+
exemptionsGlobs.RUnlock()
43+
if err != nil {
44+
// See http://golang.org/pkg/path/#Match for error description.
45+
panic(fmt.Sprintf("REVEL-CSRF: malformed glob pattern: %#v", err))
46+
}
47+
if found {
48+
glog.V(2).Infof("REVEL-CSRF: Ignoring exempted route '%s'...", path)
49+
return true
50+
}
51+
}
52+
return false
53+
}
54+
55+
// Exempts an exact path from CSRF checks.
56+
func ExemptedFullPath(path string) {
57+
glog.V(2).Infof("REVEL-CSRF: Adding exemption '%s'...", path)
58+
exemptionsFullPath.Lock()
59+
exemptionsFullPath.list[path] = struct{}{}
60+
exemptionsFullPath.Unlock()
61+
}
62+
63+
func ExemptedFullPaths(paths ...string) {
64+
for _, v := range paths {
65+
ExemptedFullPath(v)
66+
}
67+
}
68+
69+
// Exempts a path from CSRF checks using pattern matching.
70+
// See http://golang.org/pkg/path/#Match
71+
func ExemptedGlob(path string) {
72+
glog.V(2).Infof("REVEL-CSRF: Adding exemption GLOB '%s'...", path)
73+
exemptionsGlobs.Lock()
74+
exemptionsGlobs.list = append(exemptionsGlobs.list, path)
75+
exemptionsGlobs.Unlock()
76+
}
77+
78+
func ExemptedGlobs(paths ...string) {
79+
for _, v := range paths {
80+
ExemptedGlob(v)
81+
}
82+
}

exemptions_test.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package csrf
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func TestExemptedFullPath(t *testing.T) {
8+
//hand := New(nil)
9+
path := "/Hello"
10+
11+
ExemptedFullPath(path)
12+
if !IsExempted(path) {
13+
t.Errorf("%v is not exempted, but it should be", path)
14+
}
15+
16+
other := "/Goodbye"
17+
if IsExempted(other) {
18+
t.Errorf("%v is exempted, but it shouldn't be", other)
19+
}
20+
}
21+
22+
func TestExemptedFullPaths(t *testing.T) {
23+
//hand := New(nil)
24+
paths := []string{"/home", "/news", "/help"}
25+
ExemptedFullPaths(paths...)
26+
27+
for _, v := range paths {
28+
if !IsExempted(v) {
29+
t.Errorf("%v should be exempted, but it isn't", v)
30+
}
31+
}
32+
33+
other := "/accounts"
34+
35+
if IsExempted(other) {
36+
t.Errorf("%v is exempted, but it shouldn't be")
37+
}
38+
}
39+
40+
func TestExemptedGlob(t *testing.T) {
41+
//hand := New(nil)
42+
glob := "/[m-n]ail"
43+
44+
ExemptedGlob(glob)
45+
46+
test := "/mail"
47+
if !IsExempted(test) {
48+
t.Errorf("%v should be exempted, but it isn't.", test)
49+
}
50+
51+
test = "/nail"
52+
if !IsExempted(test) {
53+
t.Errorf("%v should be exempted, but it isn't.", test)
54+
}
55+
56+
test = "/snail"
57+
if IsExempted(test) {
58+
t.Errorf("%v should not be exempted, but it is.", test)
59+
}
60+
61+
test = "/mail/outbox"
62+
if IsExempted(test) {
63+
t.Errorf("%v should not be exempted, but it is.", test)
64+
}
65+
}
66+
67+
func TestExemptedGlobs(t *testing.T) {
68+
slice := []string{"/", "/accounts/*", "/post/?*"}
69+
matching := []string{"/", "/accounts/", "/accounts/johndoe", "/post/1", "/post/123"}
70+
71+
nonMatching := []string{"", "/accounts",
72+
// Glob's * and ? don't match a forward slash.
73+
"/accounts/johndoe/posts",
74+
"/post/",
75+
}
76+
77+
//hand := New(nil)
78+
ExemptedGlobs(slice...)
79+
80+
for _, v := range matching {
81+
if !IsExempted(v) {
82+
t.Error("%v should be exempted, but it isn't.")
83+
}
84+
}
85+
86+
for _, v := range nonMatching {
87+
if IsExempted(v) {
88+
t.Error("%v shouldn't be exempted, but it is")
89+
}
90+
}
91+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
{{set . "title" "Home"}}
2+
{{template "header.html" .}}
3+
4+
<header class="hero-unit" style="background-color:#A9F16C">
5+
<div class="container">
6+
<div class="row">
7+
<div class="hero-text">
8+
<h1>{{if .name}}Hello {{ .name }}{{else}}Hello anonymous user{{end}}</h1>
9+
<p></p>
10+
</div>
11+
</div>
12+
</div>
13+
</header>
14+
15+
<div class="container">
16+
<div class="row">
17+
<div class="span6">
18+
{{template "flash.html" .}}
19+
</div>
20+
</div>
21+
<div class="row">
22+
<h2>This route is exempted from CSRF checks.</h2>
23+
<p>
24+
See <b>app/init.go</b>: csrf.ExemptedGlob("/Hello[0-9]?3/Exempted")
25+
</p>
26+
</div>
27+
<div class="row">
28+
<p>
29+
Clicking on the Go Back link will trigger generation of a new index
30+
page and reset the CSRF token to a valid value. Click on your browser's
31+
Back button if you want to keep the altered CSRF token. This behaviour
32+
is caused by the demo implementation and does not exhibits any bug in
33+
revel-csrf.
34+
</p>
35+
<p>
36+
<b>Note:</b> Clicking on Go Back is your best option if you reached
37+
this page through an AJAX call. But that will reset the CSRF token to
38+
a valid value!
39+
</p>
40+
</div>
41+
<div class="row">
42+
<p>{{.help1}}</p>
43+
<p>{{.help2}}</p>
44+
</div>
45+
<div class="row">
46+
<div class="span12">
47+
<a href="/">Go Back</a>
48+
</div>
49+
</div>
50+
</div>
51+
52+
{{template "footer.html" .}}

0 commit comments

Comments
 (0)