put auth flag to jwt, dbl check in auth

This commit is contained in:
Umputun
2018-05-24 18:43:24 -05:00
parent 1d3900ffa7
commit e1f19c22d9
6 changed files with 58 additions and 36 deletions
+2
View File
@@ -244,6 +244,8 @@ func makeAuthProviders(jwtService *auth.JWT, avatarProxy *proxy.Avatar, opts Opt
RemarkURL: opts.RemarkURL,
Cid: cid,
Csecret: secret,
Admins: opts.Admins,
SecretKey: opts.SecretKey,
}
}
+6 -5
View File
@@ -439,13 +439,14 @@ func (s *Rest) configCtrl(w http.ResponseWriter, r *http.Request) {
LowScore: s.ScoreThresholds.Low,
CriticalScore: s.ScoreThresholds.Critical,
}
authNames := []string{}
cnf.Auth = []string{}
for _, ap := range s.Authenticator.Providers {
authNames = append(authNames, ap.Name)
cnf.Auth = append(cnf.Auth, ap.Name)
}
cnf.Auth = authNames
if s.Authenticator.Admins == nil { // prevent json serialization to nil
s.Authenticator.Admins = []string{}
if cnf.Admins == nil { // prevent json serialization to nil
cnf.Admins = []string{}
}
render.Status(r, http.StatusOK)
render.JSON(w, r, cnf)
+11 -7
View File
@@ -14,8 +14,8 @@ import (
// Authenticator is top level auth object providing middlewares
type Authenticator struct {
JWTService *JWT
Admins []string
Providers []Provider
Admins []string
DevPasswd string
}
@@ -58,12 +58,7 @@ func (a *Authenticator) Auth(reqAuth bool) func(http.Handler) http.Handler {
if claims.User != nil { // if uinfo in token populate it to context
user := *claims.User
for _, admin := range a.Admins {
if admin == user.ID {
user.Admin = true
break
}
}
user.Admin = isAdmin(user.ID, a.Admins) // dbl-check for admin to reset admin flag even if token has it
// refresh token if it close to expiration
if _, err := a.JWTService.Refresh(w, r); err != nil {
log.Printf("[WARN] can't refresh jwt, %s", err)
@@ -124,3 +119,12 @@ func (a *Authenticator) basicDevUser(w http.ResponseWriter, r *http.Request) boo
return true
}
func isAdmin(userID string, admins []string) bool {
for _, admin := range admins {
if admin == userID {
return true
}
}
return false
}
+11 -9
View File
@@ -38,7 +38,8 @@ type Params struct {
RemarkURL string
AvatarProxy *proxy.Avatar
JwtService *JWT
Secret string
SecretKey string
Admins []string
Cid string
Csecret string
}
@@ -53,7 +54,7 @@ func (u userData) value(key string) string {
}
// newProvider makes auth for given provider
func initProvider(p Params, provider Provider) Provider {
func initProvider(p Params, provider *Provider) Provider {
log.Printf("[INFO] create %s auth, id=%s, redir: %s", provider.Name, provider.Cid, provider.RedirectURL)
provider.Params = p
conf := oauth2.Config{
@@ -65,11 +66,11 @@ func initProvider(p Params, provider Provider) Provider {
}
provider.conf = &conf
return provider
return *provider
}
// Routes returns auth routes for given provider
func (p Provider) Routes() chi.Router {
func (p *Provider) Routes() chi.Router {
router := chi.NewRouter()
router.Get("/login", p.loginHandler)
router.Get("/callback", p.authHandler)
@@ -78,7 +79,7 @@ func (p Provider) Routes() chi.Router {
}
// loginHandler - GET /login?from=redirect-back-url
func (p Provider) loginHandler(w http.ResponseWriter, r *http.Request) {
func (p *Provider) loginHandler(w http.ResponseWriter, r *http.Request) {
// make state (random) and store in session
state := p.randToken()
@@ -108,7 +109,7 @@ func (p Provider) loginHandler(w http.ResponseWriter, r *http.Request) {
// authHandler fills user info and redirects to "from" url. This is callback url redirected locally by browser
// GET /callback
func (p Provider) authHandler(w http.ResponseWriter, r *http.Request) {
func (p *Provider) authHandler(w http.ResponseWriter, r *http.Request) {
oauthClaims, err := p.JwtService.Get(r)
if err != nil {
@@ -163,6 +164,7 @@ func (p Provider) authHandler(w http.ResponseWriter, r *http.Request) {
log.Printf("[WARN] failed to proxy avatar, %s", e)
}
}
u.Admin = isAdmin(u.ID, p.Admins)
authClaims := &CustomClaims{
User: &u,
@@ -184,16 +186,16 @@ func (p Provider) authHandler(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, oauthClaims.From, http.StatusTemporaryRedirect)
return
}
render.JSON(w, r, jData)
render.JSON(w, r, &u)
}
// LogoutHandler - GET /logout
func (p Provider) LogoutHandler(w http.ResponseWriter, r *http.Request) {
func (p *Provider) LogoutHandler(w http.ResponseWriter, r *http.Request) {
p.JwtService.Reset(w)
log.Printf("[DEBUG] logout")
}
func (p Provider) randToken() string {
func (p *Provider) randToken() string {
b := make([]byte, 32)
if _, err := rand.Read(b); err != nil {
log.Fatalf("[ERROR] can't get randoms, %s", err)
+24 -11
View File
@@ -20,7 +20,7 @@ import (
func TestLogin(t *testing.T) {
_, ts, ots := mockProvider(t, 8981, 8982)
p, ts, ots := mockProvider(t, 8981, 8982)
defer func() {
ts.Close()
ots.Close()
@@ -46,8 +46,20 @@ func TestLogin(t *testing.T) {
u := store.User{}
err = json.Unmarshal(body, &u)
assert.Nil(t, err)
assert.Equal(t, store.User{Name: "blah", ID: "myuser", Picture: "",
assert.Equal(t, store.User{Name: "blah", ID: "mock_myuser", Picture: "http://exmple.com/pic1.png",
Admin: false, Blocked: false, IP: ""}, u)
// check admin user
p.Admins = []string{"mock_myuser"}
resp, err = client.Get("http://localhost:8981/login")
assert.Nil(t, err)
assert.Equal(t, 200, resp.StatusCode)
body, err = ioutil.ReadAll(resp.Body)
assert.Nil(t, err)
err = json.Unmarshal(body, &u)
assert.Nil(t, err)
assert.Equal(t, store.User{Name: "blah", ID: "mock_myuser", Picture: "http://exmple.com/pic1.png",
Admin: true, Blocked: false, IP: ""}, u)
}
func TestLogout(t *testing.T) {
@@ -78,19 +90,19 @@ func TestLogout(t *testing.T) {
}
func TestInitProvider(t *testing.T) {
params := Params{RemarkURL: "url", Secret: "123456", Cid: "cid", Csecret: "csecret"}
params := Params{RemarkURL: "url", SecretKey: "123456", Cid: "cid", Csecret: "csecret"}
provider := Provider{Name: "test", RedirectURL: "redir"}
res := initProvider(params, provider)
res := initProvider(params, &provider)
assert.Equal(t, "cid", res.conf.ClientID)
assert.Equal(t, "csecret", res.conf.ClientSecret)
assert.Equal(t, "redir", res.RedirectURL)
assert.Equal(t, "123456", res.Secret)
assert.Equal(t, "123456", res.SecretKey)
assert.Equal(t, "test", res.Name)
}
func mockProvider(t *testing.T, loginPort, authPort int) (provider Provider, ts *http.Server, oauth *http.Server) {
func mockProvider(t *testing.T, loginPort, authPort int) (provider *Provider, ts *http.Server, oauth *http.Server) {
provider = Provider{
provider = &Provider{
Name: "mock",
Endpoint: oauth2.Endpoint{
AuthURL: fmt.Sprintf("http://localhost:%d/login/oauth/authorize", authPort),
@@ -101,15 +113,16 @@ func mockProvider(t *testing.T, loginPort, authPort int) (provider Provider, ts
InfoURL: fmt.Sprintf("http://localhost:%d/user", authPort),
MapUser: func(data userData, _ []byte) store.User {
userInfo := store.User{
ID: data.value("id"),
ID: "mock_" + data.value("id"),
Name: data.value("name"),
Picture: data.value("picture"),
}
return userInfo
},
}
jwtServcie := NewJWT("12345", false, time.Hour)
provider = initProvider(Params{RemarkURL: "url", Secret: "123456", Cid: "cid", Csecret: "csecret", JwtService: jwtServcie}, provider)
params := Params{RemarkURL: "url", SecretKey: "123456", Cid: "cid", Csecret: "csecret",
JwtService: NewJWT("12345", false, time.Hour), Admins: []string{""}}
initProvider(params, provider)
ts = &http.Server{Addr: fmt.Sprintf(":%d", loginPort), Handler: provider.Routes()}
@@ -138,7 +151,7 @@ func mockProvider(t *testing.T, loginPort, authPort int) (provider Provider, ts
res := `{
"id":"myuser",
"name":"blah",
"profile": "http://blah.com/p.html"
"picture":"http://exmple.com/pic1.png"
}`
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(200)
+4 -4
View File
@@ -14,7 +14,7 @@ import (
// NewGoogle makes google oauth2 provider
func NewGoogle(p Params) Provider {
return initProvider(p, Provider{
return initProvider(p, &Provider{
Name: "google",
Endpoint: google.Endpoint,
RedirectURL: p.RemarkURL + "/auth/google/callback",
@@ -37,7 +37,7 @@ func NewGoogle(p Params) Provider {
// NewGithub makes github oauth2 provider
func NewGithub(p Params) Provider {
return initProvider(p, Provider{
return initProvider(p, &Provider{
Name: "github",
Endpoint: github.Endpoint,
RedirectURL: p.RemarkURL + "/auth/github/callback",
@@ -75,7 +75,7 @@ func NewFacebook(p Params) Provider {
} `json:"picture"`
}
return initProvider(p, Provider{
return initProvider(p, &Provider{
Name: "facebook",
Endpoint: facebook.Endpoint,
RedirectURL: p.RemarkURL + "/auth/facebook/callback",
@@ -101,7 +101,7 @@ func NewFacebook(p Params) Provider {
// NewDisqus makes disqus oauth2 provider. TODO: WIP - seems to need client_id param
func NewDisqus(p Params) Provider {
return initProvider(p, Provider{
return initProvider(p, &Provider{
Name: "disqus",
Endpoint: oauth2.Endpoint{
AuthURL: "https://disqus.com/api/oauth/2.0/authorize/",