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)) toc, html := buildTOC(string(rend))
html = strings.Replace(html, "<table>", `<table class="table">`, -1) html = strings.Replace(html, "<table>", `<table class="table">`, -1)
html = strings.Replace(html, "</h1>", `</h1><hr>`, -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.TOC = template.HTML(toc)
p.HTML = template.HTML(html) p.HTML = template.HTML(html)
return whiteSpace.ReplaceAllString(string(filter.Filter(rend)), " ") return whiteSpace.ReplaceAllString(string(filter.Filter(rend)), " ")

View file

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

View file

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

View file

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

View file

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

View file

@ -15,7 +15,13 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="col-form-label" for="password">Password</label> <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> </div>
<button class="btn btn-sm btn-primary" type="submit">Login</button> <button class="btn btn-sm btn-primary" type="submit">Login</button>
</form> </form>

View file

@ -8,7 +8,7 @@
<input type="hidden" name="token" value="{{.Token}}"> <input type="hidden" name="token" value="{{.Token}}">
<div class="form-group"> <div class="form-group">
<label class="col-form-label" for="section">Section</label> <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>wiki</option>
<option>{{.User}}</option> <option>{{.User}}</option>
</select> </select>

View file

@ -24,7 +24,7 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="col-form-label" for="repeat">Repeat</label> <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> </div>
{{if .Admin}} {{if .Admin}}
<div class="form-group"> <div class="form-group">

View file

@ -14,11 +14,11 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="col-form-label" for="password">Password</label> <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>
<div class="form-group"> <div class="form-group">
<label class="col-form-label" for="repeat">Repeat</label> <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>
<div class="form-group"> <div class="form-group">
<label class="col-form-label" for="admin">Admin Privileges</label> <label class="col-form-label" for="admin">Admin Privileges</label>