@ -0,0 +1,11 @@ | |||
[login] | |||
title = "Sign In" | |||
sign_in = "Sign In" | |||
email = "Email" | |||
email_placeholder = "Email address..." | |||
password = "Password" | |||
password_placeholder = "Password..." | |||
remember_me = "Remember me" | |||
[register] | |||
title = "Register" |
@ -0,0 +1,26 @@ | |||
package user | |||
import ( | |||
"dev.sigpipe.me/dashie/git.txt/context" | |||
) | |||
const ( | |||
LOGIN = "user/auth/login" | |||
REGISTER = "user/auth/register" | |||
ACTIVATE = "user/auth/activate" | |||
FORGOT_PASSWORD = "user/auth/forgot_password" | |||
RESET_PASSWORD = "user/auth/reset_password" | |||
) | |||
func Login(ctx *context.Context) { | |||
ctx.Title("login.title") | |||
// TODO: auto login remember_me | |||
ctx.HTML(200, LOGIN) | |||
} | |||
func Register(ctx *context.Context) { | |||
ctx.Title("register.title") | |||
ctx.HTML(200, REGISTER) | |||
} |
@ -0,0 +1,46 @@ | |||
// Copyright 2017 The Gogs Authors. All rights reserved. | |||
// Use of this source code is governed by a MIT-style license | |||
package markup | |||
import ( | |||
"regexp" | |||
"sync" | |||
"github.com/microcosm-cc/bluemonday" | |||
) | |||
// Sanitizer is a protection wrapper of *bluemonday.Policy which does not allow | |||
// any modification to the underlying policies once it's been created. | |||
type Sanitizer struct { | |||
policy *bluemonday.Policy | |||
init sync.Once | |||
} | |||
var sanitizer = &Sanitizer{ | |||
policy: bluemonday.UGCPolicy(), | |||
} | |||
// NewSanitizer initializes sanitizer with allowed attributes based on settings. | |||
// Multiple calls to this function will only create one instance of Sanitizer during | |||
// entire application lifecycle. | |||
func NewSanitizer() { | |||
sanitizer.init.Do(func() { | |||
// We only want to allow HighlightJS specific classes for code blocks | |||
sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`^language-\w+$`)).OnElements("code") | |||
// Checkboxes | |||
sanitizer.policy.AllowAttrs("type").Matching(regexp.MustCompile(`^checkbox$`)).OnElements("input") | |||
sanitizer.policy.AllowAttrs("checked", "disabled").OnElements("input") | |||
}) | |||
} | |||
// Sanitize takes a string that contains a HTML fragment or document and applies policy whitelist. | |||
func Sanitize(s string) string { | |||
return sanitizer.policy.Sanitize(s) | |||
} | |||
// SanitizeBytes takes a []byte slice that contains a HTML fragment or document and applies policy whitelist. | |||
func SanitizeBytes(b []byte) []byte { | |||
return sanitizer.policy.SanitizeBytes(b) | |||
} |
@ -0,0 +1,37 @@ | |||
// Copyright 2017 The Gogs Authors. All rights reserved. | |||
// Use of this source code is governed by a MIT-style license | |||
package markup_test | |||
import ( | |||
"testing" | |||
. "github.com/smartystreets/goconvey/convey" | |||
. "dev.sigpipe.me/dashie/git.txt/stuff/markup" | |||
) | |||
func Test_Sanitizer(t *testing.T) { | |||
NewSanitizer() | |||
Convey("Sanitize HTML string and bytes", t, func() { | |||
testCases := []string{ | |||
// Regular | |||
`<a onblur="alert(secret)" href="http://www.google.com">Google</a>`, `<a href="http://www.google.com" rel="nofollow">Google</a>`, | |||
// Code highlighting class | |||
`<code class="random string"></code>`, `<code></code>`, | |||
`<code class="language-random ui tab active menu attached animating sidebar following bar center"></code>`, `<code></code>`, | |||
`<code class="language-go"></code>`, `<code class="language-go"></code>`, | |||
// Input checkbox | |||
`<input type="hidden">`, ``, | |||
`<input type="checkbox">`, `<input type="checkbox">`, | |||
`<input checked disabled autofocus>`, `<input checked="" disabled="">`, | |||
} | |||
for i := 0; i < len(testCases); i += 2 { | |||
So(Sanitize(testCases[i]), ShouldEqual, testCases[i+1]) | |||
So(string(SanitizeBytes([]byte(testCases[i]))), ShouldEqual, testCases[i+1]) | |||
} | |||
}) | |||
} |
@ -0,0 +1,20 @@ | |||
{{if .Flash.ErrorMsg}} | |||
<div class="ui negative message"> | |||
<p>{{.Flash.ErrorMsg | Str2html}}</p> | |||
</div> | |||
{{end}} | |||
{{if .Flash.WarningMsg}} | |||
<div class="ui warning message"> | |||
<p>{{.Flash.WarningMsg | Str2html}}</p> | |||
</div> | |||
{{end}} | |||
{{if .Flash.SuccessMsg}} | |||
<div class="ui positive message"> | |||
<p>{{.Flash.SuccessMsg | Str2html}}</p> | |||
</div> | |||
{{end}} | |||
{{if .Flash.InfoMsg}} | |||
<div class="ui info message"> | |||
<p>{{.Flash.InfoMsg | Str2html}}</p> | |||
</div> | |||
{{end}} |
@ -0,0 +1,20 @@ | |||
{{template "base/head" .}} | |||
<form class="form-signin" action="{{.Link}}" method="post"> | |||
{{.CSRFTokenHTML}} | |||
<h2 class="form-signin-heading">{{.i18n.Tr "login.sign_in"}}</h2> | |||
{{template "base/alert" .}} | |||
<label for="inputEmail" class="sr-only">{{.i18n.Tr "login.email"}}</label> | |||
<input type="email" id="inputEmail" class="form-control {{if .Err_UserName}}error{{end}}" placeholder='{{.i18n.Tr "login.email_placeholder"}}' required autofocus> | |||
<label for="inputPassword" class="sr-only">{{.i18n.Tr "login.password"}}</label> | |||
<input type="password" id="inputPassword" class="form-control {{if .Err_UserName}}error{{end}}" placeholder='{{.i18n.Tr "login.password_placeholder"}}' required> | |||
<div class="checkbox"> | |||
<label> | |||
<input type="checkbox" value="remember-me"> {{.i18n.Tr "login.remember_me"}} | |||
</label> | |||
</div> | |||
<button class="btn btn-lg btn-primary btn-block" type="submit">{{.i18n.Tr "login.sign_in"}}</button> | |||
</form> | |||
{{template "base/footer" .}} |