Browse Source
auth: Moved token and user to models package, added graphql endpoint to check token
1.0
auth: Moved token and user to models package, added graphql endpoint to check token
1.0
Gisle Aune
6 years ago
14 changed files with 206 additions and 120 deletions
-
4graph2/gqlgen.yml
-
4graph2/graph.go
-
18graph2/queries/token.go
-
4graph2/schema/root.gql
-
8graph2/schema/types/Token.gql
-
8graph2/schema/types/User.gql
-
17graph2/types/token.go
-
69internal/auth/token.go
-
63internal/auth/user.go
-
48models/token.go
-
23models/user.go
-
14models/users/db.go
-
33models/users/ensure.go
-
13models/users/find.go
@ -0,0 +1,18 @@ |
|||||
|
package queries |
||||
|
|
||||
|
import ( |
||||
|
"context" |
||||
|
"errors" |
||||
|
|
||||
|
"git.aiterp.net/rpdata/api/internal/auth" |
||||
|
"git.aiterp.net/rpdata/api/models" |
||||
|
) |
||||
|
|
||||
|
func (r *resolver) Token(ctx context.Context) (models.Token, error) { |
||||
|
token := auth.TokenFromContext(ctx) |
||||
|
if !token.Authenticated() { |
||||
|
return models.Token{}, errors.New("No (valid) token") |
||||
|
} |
||||
|
|
||||
|
return *token, nil |
||||
|
} |
@ -0,0 +1,8 @@ |
|||||
|
# Token represents the data parsed and validated from the bearer token. |
||||
|
type Token { |
||||
|
# The user represented in the token. |
||||
|
user: User! |
||||
|
|
||||
|
# The permissions for the token. |
||||
|
permissions: [String!]! |
||||
|
} |
@ -0,0 +1,8 @@ |
|||||
|
# A user represents a member on the RPData platform. |
||||
|
type User { |
||||
|
# The user's unique ID / username |
||||
|
id: String! |
||||
|
|
||||
|
# What the user is permitted to do. |
||||
|
permissions: [String!]! |
||||
|
} |
@ -0,0 +1,17 @@ |
|||||
|
package types |
||||
|
|
||||
|
import ( |
||||
|
"context" |
||||
|
|
||||
|
"git.aiterp.net/rpdata/api/models" |
||||
|
"git.aiterp.net/rpdata/api/models/users" |
||||
|
) |
||||
|
|
||||
|
type tokenResolver struct{} |
||||
|
|
||||
|
func (r *tokenResolver) User(ctx context.Context, token *models.Token) (models.User, error) { |
||||
|
return users.Find(token.UserID) |
||||
|
} |
||||
|
|
||||
|
// TokenResolver is a resolver
|
||||
|
var TokenResolver tokenResolver |
@ -1,63 +0,0 @@ |
|||||
package auth |
|
||||
|
|
||||
import ( |
|
||||
"git.aiterp.net/rpdata/api/internal/store" |
|
||||
"github.com/globalsign/mgo" |
|
||||
) |
|
||||
|
|
||||
var userCollection *mgo.Collection |
|
||||
|
|
||||
// A User represents user information about a user that has logged in.
|
|
||||
type User struct { |
|
||||
ID string `bson:"_id" json:"id"` |
|
||||
Nick string `bson:"nick,omitempty" json:"nick,omitempty"` |
|
||||
Permissions []string `bson:"permissions" json:"permissions"` |
|
||||
} |
|
||||
|
|
||||
// Permitted returns true if either of the permissions can be found
|
|
||||
//
|
|
||||
// `token.UserID == page.Author || token.Permitted("story.edit")`
|
|
||||
func (user *User) Permitted(permissions ...string) bool { |
|
||||
for i := range permissions { |
|
||||
for j := range user.Permissions { |
|
||||
if permissions[i] == user.Permissions[j] { |
|
||||
return true |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return false |
|
||||
} |
|
||||
|
|
||||
// FindUser finds a user by userid
|
|
||||
func FindUser(userid string) (User, error) { |
|
||||
user := User{} |
|
||||
err := userCollection.FindId(userid).One(&user) |
|
||||
|
|
||||
if err == mgo.ErrNotFound { |
|
||||
user := User{ |
|
||||
ID: userid, |
|
||||
Nick: "", |
|
||||
Permissions: []string{ |
|
||||
"member", |
|
||||
"log.edit", |
|
||||
"post.edit", |
|
||||
"post.move", |
|
||||
"file.upload", |
|
||||
}, |
|
||||
} |
|
||||
|
|
||||
err := userCollection.Insert(user) |
|
||||
if err != nil { |
|
||||
return User{}, err |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return user, err |
|
||||
} |
|
||||
|
|
||||
func init() { |
|
||||
store.HandleInit(func(db *mgo.Database) { |
|
||||
userCollection = db.C("core.users") |
|
||||
}) |
|
||||
} |
|
@ -0,0 +1,48 @@ |
|||||
|
package models |
||||
|
|
||||
|
// A Token contains the parsed results from an bearer token. Its methods are safe to use with a nil receiver, but
|
||||
|
// the userID should be checked.
|
||||
|
type Token struct { |
||||
|
UserID string |
||||
|
Permissions []string |
||||
|
} |
||||
|
|
||||
|
// Authenticated returns true if the token is non-nil and parsed
|
||||
|
func (token *Token) Authenticated() bool { |
||||
|
return token != nil && token.UserID != "" |
||||
|
} |
||||
|
|
||||
|
// Permitted returns true if the token is non-nil and has the given permission or the "admin" permission
|
||||
|
func (token *Token) Permitted(permissions ...string) bool { |
||||
|
if token == nil { |
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
for _, tokenPermission := range token.Permissions { |
||||
|
if tokenPermission == "admin" { |
||||
|
return true |
||||
|
} |
||||
|
|
||||
|
for _, permission := range permissions { |
||||
|
if permission == tokenPermission { |
||||
|
return true |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
// PermittedUser checks the first permission if the user matches, the second otherwise. This is a common
|
||||
|
// pattern.
|
||||
|
func (token *Token) PermittedUser(userID, permissionIfUser, permissionOtherwise string) bool { |
||||
|
if token == nil { |
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
if token.UserID == userID { |
||||
|
return token.Permitted(permissionIfUser) |
||||
|
} |
||||
|
|
||||
|
return token.Permitted(permissionOtherwise) |
||||
|
} |
@ -0,0 +1,23 @@ |
|||||
|
package models |
||||
|
|
||||
|
// A User represents user information about a user that has logged in.
|
||||
|
type User struct { |
||||
|
ID string `bson:"_id" json:"id"` |
||||
|
Nick string `bson:"nick,omitempty" json:"nick,omitempty"` |
||||
|
Permissions []string `bson:"permissions" json:"permissions"` |
||||
|
} |
||||
|
|
||||
|
// Permitted returns true if either of the permissions can be found
|
||||
|
//
|
||||
|
// `token.UserID == page.Author || token.Permitted("story.edit")`
|
||||
|
func (user *User) Permitted(permissions ...string) bool { |
||||
|
for i := range permissions { |
||||
|
for j := range user.Permissions { |
||||
|
if permissions[i] == user.Permissions[j] { |
||||
|
return true |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return false |
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
package users |
||||
|
|
||||
|
import ( |
||||
|
"git.aiterp.net/rpdata/api/internal/store" |
||||
|
"github.com/globalsign/mgo" |
||||
|
) |
||||
|
|
||||
|
var collection *mgo.Collection |
||||
|
|
||||
|
func init() { |
||||
|
store.HandleInit(func(db *mgo.Database) { |
||||
|
collection = db.C("core.users") |
||||
|
}) |
||||
|
} |
@ -0,0 +1,33 @@ |
|||||
|
package users |
||||
|
|
||||
|
import ( |
||||
|
"git.aiterp.net/rpdata/api/models" |
||||
|
"github.com/globalsign/mgo" |
||||
|
) |
||||
|
|
||||
|
// Ensure finds a user by id, or makes a new one.
|
||||
|
func Ensure(id string) (models.User, error) { |
||||
|
user := models.User{} |
||||
|
err := collection.FindId(id).One(&user) |
||||
|
|
||||
|
if err == mgo.ErrNotFound { |
||||
|
user = models.User{ |
||||
|
ID: id, |
||||
|
Nick: "", |
||||
|
Permissions: []string{ |
||||
|
"member", |
||||
|
"log.edit", |
||||
|
"post.edit", |
||||
|
"post.move", |
||||
|
"file.upload", |
||||
|
}, |
||||
|
} |
||||
|
|
||||
|
err := collection.Insert(user) |
||||
|
if err != nil { |
||||
|
return models.User{}, err |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return user, err |
||||
|
} |
@ -0,0 +1,13 @@ |
|||||
|
package users |
||||
|
|
||||
|
import ( |
||||
|
"git.aiterp.net/rpdata/api/models" |
||||
|
) |
||||
|
|
||||
|
// Find finds a user by id
|
||||
|
func Find(id string) (models.User, error) { |
||||
|
user := models.User{} |
||||
|
err := collection.FindId(id).One(&user) |
||||
|
|
||||
|
return user, err |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue