added 'remember me'

This commit is contained in:
ston1th 2018-11-14 21:31:23 +01:00
commit ab454f4b99
9 changed files with 63 additions and 35 deletions

View file

@ -33,7 +33,7 @@ func Render(p *core.Page) string {
toc, html := buildTOC(string(rend))
html = strings.Replace(html, "<table>", `<table class="table">`, -1)
html = strings.Replace(html, "</h1>", `</h1><hr>`, -1)
html = strings.Replace(html, "<a", `<a target="_blank"`, -1)
html = strings.Replace(html, "<a", `<a target="_blank" rel="noopener noreferrer"`, -1)
p.TOC = template.HTML(toc)
p.HTML = template.HTML(html)
return whiteSpace.ReplaceAllString(string(filter.Filter(rend)), " ")

View file

@ -21,6 +21,7 @@ const (
sectionClaim = "section"
titleClaim = "title"
totpClaim = "totp"
rememberClaim = "remember"
)
func newContext(w http.ResponseWriter, r *http.Request, s *HTTPServer) (ctx *Context) {
@ -67,7 +68,7 @@ func newContext(w http.ResponseWriter, r *http.Request, s *HTTPServer) (ctx *Con
log.Println("Invalidate:", err)
}
}
ctx.SetCookie(nil)
ctx.SetCookie(nil, 0)
return
}
@ -230,14 +231,26 @@ func (c *Context) LoggedOn() (ok bool) {
func (c *Context) LogSetCookie(msg string, err error) {
log.Printf("ctx: %s %s\n", msg, err)
c.SetCookie(nil)
c.SetCookie(nil, 0)
}
func (c *Context) SetCookie(claims map[string]interface{}) {
c.SetCookieToken(jwt.NewToken(claims, nil))
func (c *Context) Login(u core.User, remember string) {
var d time.Duration
if remember != "" {
d = day * 7
}
c.SetCookie(jwt.Claims{userClaim: u.Username, createdClaim: u.Created, adminClaim: u.Admin}, d)
}
func (c *Context) SetCookieToken(t *jwt.Token) {
func (c *Context) SetCookie(claims jwt.Claims, d time.Duration) {
c.setCookie(jwt.NewToken(claims, nil), d)
}
func (c *Context) setCookie(t *jwt.Token, d time.Duration) {
if d == 0 {
d = jwt.DefaultExpiry
}
t.Claims[jwt.ExpClaim] = jwt.NewExp(d)
if err := c.Srv.JWT.Sign(t); err != nil {
log.Println(err)
return
@ -247,13 +260,12 @@ func (c *Context) SetCookieToken(t *jwt.Token) {
Name: cookieName,
Value: c.Token.String(),
Path: "/",
MaxAge: int(jwt.DefaultExpiry.Seconds()),
MaxAge: int(d.Seconds()),
Secure: c.Srv.Config.SecureCookie,
HttpOnly: true,
SameSite: http.SameSiteStrictMode,
}
if cookie.MaxAge > 0 {
d := time.Duration(cookie.MaxAge) * time.Second
cookie.Expires = time.Now().Add(d)
} else if cookie.MaxAge < 0 {
cookie.Expires = time.Unix(1, 0)
@ -269,6 +281,10 @@ func (c *Context) Totp() string {
return c.Token.Claims.GetString(totpClaim)
}
func (c *Context) Remember() string {
return c.Token.Claims.GetString(rememberClaim)
}
func (c *Context) Admin() bool {
return c.Token.Claims.GetBool(adminClaim)
}

View file

@ -67,7 +67,7 @@ func authHandler(h ctxHandler) ctxHandler {
func staticHandler(ctx *Context) {
var err error
ctx.SetHeader("Content-Type", "text/css; charset=utf-8")
ctx.SetHeader("Expires", time.Now().Add(time.Hour*12).Format(http.TimeFormat))
ctx.SetHeader("Expires", time.Now().UTC().Add(max).Format(http.TimeFormat))
switch ctx.Path() {
case "/bootstrap.css":
err = ctx.Write(ctx.Srv.res["bootstrap.css"])
@ -116,14 +116,14 @@ func loginHandler(ctx *Context) {
}
if u.Secret == "" {
log.Println("login:", u.Username)
ctx.SetCookie(jwt.Claims{userClaim: u.Username, createdClaim: u.Created, adminClaim: u.Admin})
ctx.Login(u, ctx.Form("remember"))
ctx.Redirect(core.IndexURI, http.StatusFound)
return
}
ctx.SetCookieToken(jwt.NewToken(map[string]interface{}{
ctx.SetCookie(jwt.Claims{
totpClaim: u.Username,
jwt.ExpClaim: jwt.NewExp(time.Minute),
}, nil))
rememberClaim: ctx.Form("remember"),
}, time.Minute)
ctx.Redirect(core.TotpURI, http.StatusFound)
}
}
@ -156,7 +156,7 @@ func loginTotpHandler(ctx *Context) {
return
}
log.Println("login:", u.Username)
ctx.SetCookie(jwt.Claims{userClaim: u.Username, createdClaim: u.Created, adminClaim: u.Admin})
ctx.Login(u, ctx.Remember())
ctx.Redirect(core.IndexURI, http.StatusFound)
}
}
@ -676,6 +676,6 @@ func userDelHandler(ctx *Context) {
func logoutHandler(ctx *Context) {
log.Println("logout:", ctx.User())
ctx.Srv.JWT.Invalidate(&ctx.Token)
ctx.SetCookie(nil)
ctx.SetCookie(nil, 0)
ctx.Redirect(core.IndexURI, http.StatusFound)
}

View file

@ -61,7 +61,7 @@ const (
</div>
{{end}}`
index = `<!DOCTYPE html>
<html>
<html lang="en">
<head>
<title>GoWiki | {{.Title}}</title>
<link rel="stylesheet" type="text/css" href="/bootstrap.css" media="screen" integrity="sha256-NeLmQ7cX66J4MdtgGlG3O/TgvsSyP1b9WbaQtxYZUbQ="></link>
@ -120,7 +120,13 @@ const (
</div>
<div class="form-group">
<label class="col-form-label" for="password">Password</label>
<input class="form-control input-sm" type="password" id="password" name="password" autofocus required>
<input class="form-control input-sm" type="password" id="password" name="password" required>
</div>
<div class="form-group">
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="remember" name="remember">
<label class="custom-control-label" for="remember">Remember Me</label>
</div>
</div>
<button class="btn btn-sm btn-primary" type="submit">Login</button>
</form>
@ -339,7 +345,7 @@ const (
<input type="hidden" name="token" value="{{.Token}}">
<div class="form-group">
<label class="col-form-label" for="section">Section</label>
<select class="form-control input-sm" id="section" name="section" autofocus required>
<select class="form-control input-sm" id="section" name="section" required>
<option>wiki</option>
<option>{{.User}}</option>
</select>
@ -528,7 +534,7 @@ const (
</div>
<div class="form-group">
<label class="col-form-label" for="repeat">Repeat</label>
<input class="form-control input-sm" type="password" id="repeat" name="repeat" autofocus>
<input class="form-control input-sm" type="password" id="repeat" name="repeat">
</div>
{{if .Admin}}
<div class="form-group">
@ -635,11 +641,11 @@ const (
</div>
<div class="form-group">
<label class="col-form-label" for="password">Password</label>
<input class="form-control input-sm" type="password" id="password" name="password" autofocus required>
<input class="form-control input-sm" type="password" id="password" name="password" required>
</div>
<div class="form-group">
<label class="col-form-label" for="repeat">Repeat</label>
<input class="form-control input-sm" type="password" id="repeat" name="repeat" autofocus required>
<input class="form-control input-sm" type="password" id="repeat" name="repeat" required>
</div>
<div class="form-group">
<label class="col-form-label" for="admin">Admin Privileges</label>

View file

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html>
<html lang="en">
<head>
<title>GoWiki | {{.Title}}</title>
<link rel="stylesheet" type="text/css" href="/bootstrap.css" media="screen" integrity="sha256-NeLmQ7cX66J4MdtgGlG3O/TgvsSyP1b9WbaQtxYZUbQ="></link>

View file

@ -15,7 +15,13 @@
</div>
<div class="form-group">
<label class="col-form-label" for="password">Password</label>
<input class="form-control input-sm" type="password" id="password" name="password" autofocus required>
<input class="form-control input-sm" type="password" id="password" name="password" required>
</div>
<div class="form-group">
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="remember" name="remember">
<label class="custom-control-label" for="remember">Remember Me</label>
</div>
</div>
<button class="btn btn-sm btn-primary" type="submit">Login</button>
</form>

View file

@ -8,7 +8,7 @@
<input type="hidden" name="token" value="{{.Token}}">
<div class="form-group">
<label class="col-form-label" for="section">Section</label>
<select class="form-control input-sm" id="section" name="section" autofocus required>
<select class="form-control input-sm" id="section" name="section" required>
<option>wiki</option>
<option>{{.User}}</option>
</select>

View file

@ -24,7 +24,7 @@
</div>
<div class="form-group">
<label class="col-form-label" for="repeat">Repeat</label>
<input class="form-control input-sm" type="password" id="repeat" name="repeat" autofocus>
<input class="form-control input-sm" type="password" id="repeat" name="repeat">
</div>
{{if .Admin}}
<div class="form-group">

View file

@ -14,11 +14,11 @@
</div>
<div class="form-group">
<label class="col-form-label" for="password">Password</label>
<input class="form-control input-sm" type="password" id="password" name="password" autofocus required>
<input class="form-control input-sm" type="password" id="password" name="password" required>
</div>
<div class="form-group">
<label class="col-form-label" for="repeat">Repeat</label>
<input class="form-control input-sm" type="password" id="repeat" name="repeat" autofocus required>
<input class="form-control input-sm" type="password" id="repeat" name="repeat" required>
</div>
<div class="form-group">
<label class="col-form-label" for="admin">Admin Privileges</label>