added 'remember me'
This commit is contained in:
parent
61fcf2dcfe
commit
ab454f4b99
9 changed files with 63 additions and 35 deletions
|
|
@ -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)), " ")
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue