added new basic auth endpoint
This commit is contained in:
parent
804842e7c5
commit
8c9ab3b8d2
10 changed files with 249 additions and 51 deletions
14
go.mod
14
go.mod
|
|
@ -10,17 +10,17 @@ require (
|
|||
github.com/ory/hydra-client-go v1.11.8
|
||||
github.com/pquerna/otp v1.3.0
|
||||
go.etcd.io/bbolt v1.3.6
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
|
||||
golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8
|
||||
k8s.io/klog/v2 v2.70.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/golang/protobuf v1.4.3 // indirect
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20210323180902-22b0adad7558 // indirect
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect
|
||||
google.golang.org/appengine v1.6.6 // indirect
|
||||
google.golang.org/protobuf v1.25.0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
golang.org/x/net v0.0.0-20220812174116-3211cb980234 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20220808172628-8227340efae7 // indirect
|
||||
golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
)
|
||||
|
|
|
|||
20
go.sum
20
go.sum
|
|
@ -84,6 +84,9 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
|
|||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
|
|
@ -94,6 +97,7 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
|
|
@ -146,6 +150,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
|||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 h1:GIAS/yBem/gq2MUqgNIzUHW7cJMmx3TGZOrnyYaNQ6c=
|
||||
golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
|
|
@ -204,6 +210,9 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R
|
|||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E=
|
||||
golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
|
|
@ -211,6 +220,8 @@ golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4Iltr
|
|||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20210323180902-22b0adad7558 h1:D7nTwh4J0i+5mW4Zjzn5omvlr6YBcWywE6KOcatyNxY=
|
||||
golang.org/x/oauth2 v0.0.0-20210323180902-22b0adad7558/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20220808172628-8227340efae7 h1:dtndE8FcEta75/4kHF3AbpuWzV6f1LjnLrM4pe2SZrw=
|
||||
golang.org/x/oauth2 v0.0.0-20220808172628-8227340efae7/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
|
@ -249,6 +260,9 @@ golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 h1:Sx/u41w+OwrInGdEckYmEuU5gHoGSL4QbDz3S9s6j4U=
|
||||
golang.org/x/sys v0.0.0-20220818161305-2296e01440c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
|
@ -326,6 +340,8 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww
|
|||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
|
|
@ -378,6 +394,10 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
|
|||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
|
|
|
|||
|
|
@ -7,11 +7,10 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
func validatePassword(pw string) (b []byte) {
|
||||
func validatePassword(pw string) []byte {
|
||||
hash := sha256.New()
|
||||
hash.Write(b)
|
||||
b = hash.Sum(nil)
|
||||
return
|
||||
hash.Write([]byte(pw))
|
||||
return hash.Sum(nil)
|
||||
}
|
||||
|
||||
const timeFmt = "2006-01-02 15:04:05"
|
||||
|
|
|
|||
119
pkg/server/auth.go
Normal file
119
pkg/server/auth.go
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
// Copyright (C) 2022 Marius Schellenberger
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
weakrand "math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.giftfish.de/ston1th/goacc/pkg/db"
|
||||
)
|
||||
|
||||
type AuthValidator interface {
|
||||
Login(username, password string) (auth bool)
|
||||
}
|
||||
|
||||
const cacheSize = 100
|
||||
|
||||
func init() {
|
||||
weakrand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
|
||||
type DBLoginValidator struct {
|
||||
db *db.DB
|
||||
}
|
||||
|
||||
func NewDBLoginValidator(db *db.DB) *DBLoginValidator {
|
||||
return &DBLoginValidator{db}
|
||||
}
|
||||
|
||||
func (val *DBLoginValidator) Login(username, password string) bool {
|
||||
_, err := val.db.Login(username, password)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
type cache struct {
|
||||
mu sync.RWMutex
|
||||
m map[string]bool
|
||||
}
|
||||
|
||||
func newCache() *cache {
|
||||
return &cache{m: make(map[string]bool)}
|
||||
}
|
||||
|
||||
func (c *cache) get(k string) (v, ok bool) {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
v, ok = c.m[k]
|
||||
return
|
||||
}
|
||||
|
||||
func (c *cache) set(k string, v bool) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.makeRoom()
|
||||
c.m[k] = v
|
||||
}
|
||||
|
||||
func (c *cache) makeRoom() {
|
||||
n := len(c.m)
|
||||
if n < cacheSize {
|
||||
return
|
||||
}
|
||||
del := n / 10
|
||||
if del < 1 {
|
||||
del = 1
|
||||
}
|
||||
for i := 0; i <= del; i++ {
|
||||
rnd := weakrand.Intn(len(c.m))
|
||||
j := 0
|
||||
for k := range c.m {
|
||||
if j == rnd {
|
||||
delete(c.m, k)
|
||||
break
|
||||
}
|
||||
j++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type BasicAuth struct {
|
||||
mu sync.Mutex
|
||||
auth AuthValidator
|
||||
cache *cache
|
||||
key []byte
|
||||
}
|
||||
|
||||
func NewBasicAuth(auth AuthValidator) (*BasicAuth, error) {
|
||||
key := make([]byte, 32)
|
||||
_, err := io.ReadFull(rand.Reader, key)
|
||||
return &BasicAuth{
|
||||
auth: auth,
|
||||
cache: newCache(),
|
||||
key: key,
|
||||
}, err
|
||||
}
|
||||
|
||||
func (a *BasicAuth) genkey(username, password string) string {
|
||||
h := hmac.New(sha256.New, a.key)
|
||||
h.Write(append([]byte(username), []byte(password)...))
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
func (a *BasicAuth) Auth(username, password string) (auth bool) {
|
||||
k := a.genkey(username, password)
|
||||
auth, ok := a.cache.get(k)
|
||||
if !ok {
|
||||
a.mu.Lock()
|
||||
auth = a.auth.Login(username, password)
|
||||
a.mu.Unlock()
|
||||
a.cache.set(k, auth)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
@ -154,28 +154,46 @@ func (c *Context) Error(i interface{}) {
|
|||
func (c *Context) NotFound() {
|
||||
c.Template("notFoundHandler")
|
||||
c.Data = webData{Title: "404"}
|
||||
c.Status = http.StatusNotFound
|
||||
c.Response.WriteHeader(http.StatusNotFound)
|
||||
c.setStatus(http.StatusNotFound)
|
||||
c.Exec()
|
||||
}
|
||||
|
||||
func (c *Context) Forbidden() {
|
||||
c.Template("forbiddenHandler")
|
||||
c.Data = webData{Title: "403"}
|
||||
c.Status = http.StatusForbidden
|
||||
c.Response.WriteHeader(http.StatusForbidden)
|
||||
c.setStatus(http.StatusForbidden)
|
||||
c.Exec()
|
||||
}
|
||||
|
||||
func (c *Context) setStatus(status int) {
|
||||
c.Status = status
|
||||
c.Response.WriteHeader(status)
|
||||
}
|
||||
|
||||
func (c *Context) plainResponse(msg string, status int) {
|
||||
defer c.accessLog()
|
||||
c.Status = status
|
||||
http.Error(c.Response, msg, status)
|
||||
}
|
||||
|
||||
func (c *Context) PlainUnauthorized() {
|
||||
c.SetHeader("WWW-Authenticate", `Basic realm="auth/basic", charset="UTF-8"`)
|
||||
c.plainResponse("unauthorized", http.StatusUnauthorized)
|
||||
}
|
||||
|
||||
func (c *Context) PlainForbidden() {
|
||||
c.SetHeader("WWW-Authenticate", `Basic realm="auth/basic", charset="UTF-8"`)
|
||||
c.plainResponse("forbidden", http.StatusForbidden)
|
||||
}
|
||||
|
||||
func (c *Context) PlainOK() {
|
||||
c.plainResponse("ok", http.StatusOK)
|
||||
}
|
||||
|
||||
func (c *Context) Template(name string) {
|
||||
c.T = c.Srv.ts.Get(name)
|
||||
}
|
||||
|
||||
func (c *Context) Write(buf []byte) (err error) {
|
||||
_, err = c.Response.Write(buf)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Context) SetHeader(name, value string) {
|
||||
c.Response.Header().Set(name, value)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -576,3 +576,20 @@ func logoutHandler(ctx *Context) {
|
|||
ctx.SetCookie(nil, 0)
|
||||
ctx.Redirect(root, http.StatusFound)
|
||||
}
|
||||
|
||||
func authBasicHandler(ctx *Context) {
|
||||
if ctx.Method() != "GET" {
|
||||
ctx.PlainUnauthorized()
|
||||
return
|
||||
}
|
||||
username, password, ok := ctx.Request.BasicAuth()
|
||||
if !ok {
|
||||
ctx.PlainUnauthorized()
|
||||
return
|
||||
}
|
||||
if ctx.Srv.BasicAuth.Auth(username, password) {
|
||||
ctx.PlainOK()
|
||||
return
|
||||
}
|
||||
ctx.PlainForbidden()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ const (
|
|||
consent = root + "consent"
|
||||
users = root + "users"
|
||||
logout = root + "logout"
|
||||
auth = root + "auth/"
|
||||
uroot = root + "user/"
|
||||
|
||||
userNew = uroot + "new"
|
||||
|
|
@ -24,6 +25,8 @@ const (
|
|||
userTotp = uroot + "totp/{user:[a-zA-Z0-9]+$}"
|
||||
userSessions = uroot + "sessions/{user:[a-zA-Z0-9]+$}"
|
||||
userUnlock = uroot + "unlock/{user:[a-zA-Z0-9]+$}"
|
||||
|
||||
authBasic = auth + "basic"
|
||||
)
|
||||
|
||||
var routes = []route{
|
||||
|
|
@ -115,4 +118,9 @@ var routes = []route{
|
|||
userUnlockHandler)),
|
||||
[]string{"POST"},
|
||||
},
|
||||
{
|
||||
authBasic,
|
||||
authBasicHandler,
|
||||
[]string{"GET"},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,9 +28,10 @@ type Server struct {
|
|||
|
||||
srv *http.Server
|
||||
|
||||
API *client.APIClient
|
||||
DB *db.DB
|
||||
JWT *jwt.JWT
|
||||
API *client.APIClient
|
||||
DB *db.DB
|
||||
JWT *jwt.JWT
|
||||
BasicAuth *BasicAuth
|
||||
|
||||
ts *TemplateStore
|
||||
}
|
||||
|
|
@ -72,6 +73,10 @@ func (s *Server) Start(dblog logr.Logger) (err error) {
|
|||
if err != nil {
|
||||
return errors.New("server: " + err.Error())
|
||||
}
|
||||
s.BasicAuth, err = NewBasicAuth(NewDBLoginValidator(s.DB))
|
||||
if err != nil {
|
||||
return errors.New("server: " + err.Error())
|
||||
}
|
||||
go func() {
|
||||
time.Sleep(time.Second * 2)
|
||||
s.srv.ListenAndServe()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{{define "body"}}
|
||||
{{if .Data}}
|
||||
<div class="row">
|
||||
<div class="col-xs-4 offset-4">
|
||||
<div class="col-lg-6 offset-3">
|
||||
<div class="card border-primary mx-auto">
|
||||
<div class="card-header">
|
||||
<strong>{{.BodyTitle}}</strong>
|
||||
|
|
@ -14,21 +14,27 @@
|
|||
<div class="card-body">
|
||||
<form class="form-horizontal" action="/user/edit/{{.Data.Username}}" method="post">
|
||||
<input type="hidden" name="token" value="{{.Token}}">
|
||||
<div class="form-group">
|
||||
<label class="col-form-label" for="user">Username</label>
|
||||
<input class="form-control input-sm" type="text" id="user" name="user" value="{{.Data.Username}}" disabled>
|
||||
<div class="row">
|
||||
<div class="col-lg-5">
|
||||
<div class="form-group">
|
||||
<label class="col-form-label" for="user">Username</label>
|
||||
<input class="form-control input-sm" type="text" id="user" name="user" value="{{.Data.Username}}" disabled>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-form-label" for="email">Email</label>
|
||||
<input class="form-control input-sm" type="text" id="email" name="email" value="{{.Data.Email}}" disabled>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-form-label" for="email">Email</label>
|
||||
<input class="form-control input-sm" type="text" id="email" name="email" value="{{.Data.Email}}" disabled>
|
||||
<div class="col-lg-5">
|
||||
<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>
|
||||
</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">
|
||||
</div>
|
||||
</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>
|
||||
</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">
|
||||
</div>
|
||||
{{if .Admin}}
|
||||
<div class="form-group">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{{define "body"}}
|
||||
<div class="row">
|
||||
<div class="col-xs-4 offset-4">
|
||||
<div class="col-lg-6 offset-3">
|
||||
<div class="card border-primary mx-auto">
|
||||
<div class="card-header">
|
||||
<strong>{{.BodyTitle}}</strong>
|
||||
|
|
@ -8,21 +8,27 @@
|
|||
<div class="card-body">
|
||||
<form class="form-horizontal" action="/user/new" method="post">
|
||||
<input type="hidden" name="token" value="{{.Token}}">
|
||||
<div class="form-group">
|
||||
<label class="col-form-label" for="user">Username</label>
|
||||
<input class="form-control input-sm" type="text" id="user" name="user" autocomplete="off" autofocus required>
|
||||
<div class="row">
|
||||
<div class="col-lg-5">
|
||||
<div class="form-group">
|
||||
<label class="col-form-label" for="user">Username</label>
|
||||
<input class="form-control input-sm" type="text" id="user" name="user" autocomplete="off" autofocus required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-form-label" for="email">Email</label>
|
||||
<input class="form-control input-sm" type="text" id="email" name="email" autocomplete="off" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-form-label" for="email">Email</label>
|
||||
<input class="form-control input-sm" type="text" id="email" name="email" autocomplete="off" required>
|
||||
<div class="col-lg-5">
|
||||
<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" 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" required>
|
||||
</div>
|
||||
</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" 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" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-checkbox">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue