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