Improve rpc authentication methods

This commit is contained in:
Tobie Morgan Hitchcock 2018-02-14 13:19:43 +00:00
parent c47fe55ffb
commit 2249086887
3 changed files with 149 additions and 38 deletions

View file

@ -25,26 +25,38 @@ import (
type rpc struct{}
// --------------------------------------------------
// Methods for authentication
// --------------------------------------------------
func (r *rpc) Uniq(c *fibre.Context) (interface{}, error) {
return rand.String(128), nil
return rand.String(64), nil
}
func (r *rpc) Info(c *fibre.Context) (interface{}, error) {
return c.Get("auth").(*cnf.Auth).Data, nil
}
func (r *rpc) Auth(c *fibre.Context, auth string) (interface{}, error) {
return c.Get("auth").(*cnf.Auth).Data, checkBearer(c, auth, ignore)
func (r *rpc) Signup(c *fibre.Context, vars map[string]interface{}) (interface{}, error) {
return signupRpc(c, vars)
}
func (r *rpc) Let(c *fibre.Context, key string, val interface{}) (interface{}, error) {
return c.Get("keep").(*data.Doc).Set(val, key)
func (r *rpc) Signin(c *fibre.Context, vars map[string]interface{}) (interface{}, error) {
return signinRpc(c, vars)
}
func (r *rpc) Query(c *fibre.Context, sql string, vars map[string]interface{}) (interface{}, error) {
return db.Execute(c, sql, vars)
func (r *rpc) Invalidate(c *fibre.Context) (interface{}, error) {
return c.Get("auth").(*cnf.Auth).Reset().Data, nil
}
func (r *rpc) Authenticate(c *fibre.Context, auth string) (interface{}, error) {
return c.Get("auth").(*cnf.Auth).Reset().Data, checkBearer(c, auth, ignore)
}
// --------------------------------------------------
// Methods for live queries
// --------------------------------------------------
func (r *rpc) Kill(c *fibre.Context, query string) (interface{}, error) {
return db.Execute(c, "KILL $query", map[string]interface{}{
"query": query,
@ -57,6 +69,18 @@ func (r *rpc) Live(c *fibre.Context, class string) (interface{}, error) {
})
}
// --------------------------------------------------
// Methods for static queries
// --------------------------------------------------
func (r *rpc) Let(c *fibre.Context, key string, val interface{}) (interface{}, error) {
return c.Get("keep").(*data.Doc).Set(val, key)
}
func (r *rpc) Query(c *fibre.Context, sql string, vars map[string]interface{}) (interface{}, error) {
return db.Execute(c, sql, vars)
}
func (r *rpc) Select(c *fibre.Context, class string, thing interface{}) (interface{}, error) {
switch thing := thing.(type) {
case *fibre.RPCNull:

View file

@ -34,6 +34,37 @@ func signin(c *fibre.Context) (err error) {
c.Bind(&vars)
str, err := signinInternal(c, vars)
switch err {
case nil:
return c.Send(200, str)
default:
return err
}
}
func signinRpc(c *fibre.Context, vars map[string]interface{}) (res interface{}, err error) {
var str string
str, err = signinInternal(c, vars)
if err != nil {
return nil, err
}
err = checkBearer(c, str, ignore)
if err != nil {
return nil, err
}
return str, nil
}
func signinInternal(c *fibre.Context, vars map[string]interface{}) (str string, err error) {
n, nok := vars[varKeyNs].(string)
d, dok := vars[varKeyDb].(string)
s, sok := vars[varKeySc].(string)
@ -58,7 +89,6 @@ func signin(c *fibre.Context) (err error) {
var ok bool
var txn kvs.TX
var str string
var doc *sql.Thing
var res []*db.Response
var exp *sql.SubExpression
@ -67,7 +97,7 @@ func signin(c *fibre.Context) (err error) {
// Start a new read transaction.
if txn, err = db.Begin(false); err != nil {
return fibre.NewHTTPError(500)
return str, fibre.NewHTTPError(500)
}
// Ensure the transaction closes.
@ -82,14 +112,14 @@ func signin(c *fibre.Context) (err error) {
if scp, err = mem.NewWithTX(txn).GetSC(n, d, s); err != nil {
m := "Authentication scope does not exist"
return fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
return str, fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
}
// Check that the scope allows signin.
if exp, ok = scp.Signin.(*sql.SubExpression); !ok {
m := "Authentication scope does not allow signin"
return fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
return str, fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
}
// Process the scope signin statement.
@ -102,35 +132,35 @@ func signin(c *fibre.Context) (err error) {
if res, err = db.Process(c, query, vars); err != nil {
m := "Authentication scope signin was unsuccessful: Query failed"
return fibre.NewHTTPError(501).WithFields(f).WithMessage(m)
return str, fibre.NewHTTPError(501).WithFields(f).WithMessage(m)
}
// If the response is not 1 record then return a 403 error.
if len(res) != 1 {
m := "Authentication scope signin was unsuccessful: Query failed"
return fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
return str, fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
}
// If the response has an error set then return a 403 error.
if res[0].Status != "OK" {
m := "Authentication scope signin was unsuccessful: " + res[0].Detail
return fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
return str, fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
}
// If the response has no record set then return a 403 error.
if len(res[0].Result) != 1 {
m := "Authentication scope signin was unsuccessful: No record returned"
return fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
return str, fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
}
// If the query does not return an id field then return a 403 error.
if doc, ok = data.Consume(res[0].Result[0]).Get("id").Data().(*sql.Thing); !ok {
m := "Authentication scope signin was unsuccessful: No id field found"
return fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
return str, fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
}
// Create a new token signer with the default claims.
@ -152,10 +182,10 @@ func signin(c *fibre.Context) (err error) {
if str, err = signr.SignedString(scp.Code); err != nil {
m := "Problem with signing method: " + err.Error()
return fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
return str, fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
}
return c.Send(200, str)
return str, err
}
@ -181,13 +211,13 @@ func signin(c *fibre.Context) (err error) {
if !uok || !pok {
m := "Username or password is missing"
return fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
return str, fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
}
// Start a new read transaction.
if usr, err = signinDB(n, d, u, p); err != nil {
return err
return str, err
}
// Create a new token signer with the default claims.
@ -207,10 +237,10 @@ func signin(c *fibre.Context) (err error) {
if str, err = signr.SignedString(usr.Code); err != nil {
m := "Problem with signing method: " + err.Error()
return fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
return str, fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
}
return c.Send(200, str)
return str, err
}
@ -236,11 +266,11 @@ func signin(c *fibre.Context) (err error) {
if !uok || !pok {
m := "Username or password is missing"
return fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
return str, fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
}
if usr, err = signinNS(n, u, p); err != nil {
return err
return str, err
}
// Create a new token signer with the default claims.
@ -259,14 +289,14 @@ func signin(c *fibre.Context) (err error) {
if str, err = signr.SignedString(usr.Code); err != nil {
m := "Problem with signing method: " + err.Error()
return fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
return str, fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
}
return c.Send(200, str)
return str, err
}
return fibre.NewHTTPError(403)
return str, fibre.NewHTTPError(403)
}

View file

@ -15,6 +15,8 @@
package web
import (
"time"
"github.com/abcum/fibre"
"github.com/abcum/surreal/cnf"
"github.com/abcum/surreal/db"
@ -22,6 +24,7 @@ import (
"github.com/abcum/surreal/mem"
"github.com/abcum/surreal/sql"
"github.com/abcum/surreal/util/data"
"github.com/dgrijalva/jwt-go"
)
func signup(c *fibre.Context) (err error) {
@ -30,6 +33,37 @@ func signup(c *fibre.Context) (err error) {
c.Bind(&vars)
str, err := signinInternal(c, vars)
switch err {
case nil:
return c.Send(200, str)
default:
return err
}
}
func signupRpc(c *fibre.Context, vars map[string]interface{}) (res interface{}, err error) {
var str string
str, err = signupInternal(c, vars)
if err != nil {
return nil, err
}
err = checkBearer(c, str, ignore)
if err != nil {
return nil, err
}
return str, nil
}
func signupInternal(c *fibre.Context, vars map[string]interface{}) (str string, err error) {
n, nok := vars[varKeyNs].(string)
d, dok := vars[varKeyDb].(string)
s, sok := vars[varKeySc].(string)
@ -54,6 +88,7 @@ func signup(c *fibre.Context) (err error) {
var ok bool
var txn kvs.TX
var doc *sql.Thing
var res []*db.Response
var exp *sql.SubExpression
var scp *sql.DefineScopeStatement
@ -61,7 +96,7 @@ func signup(c *fibre.Context) (err error) {
// Start a new read transaction.
if txn, err = db.Begin(false); err != nil {
return fibre.NewHTTPError(500)
return str, fibre.NewHTTPError(500)
}
// Ensure the transaction closes.
@ -76,14 +111,14 @@ func signup(c *fibre.Context) (err error) {
if scp, err = mem.NewWithTX(txn).GetSC(n, d, s); err != nil {
m := "Authentication scope does not exist"
return fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
return str, fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
}
// Check that the scope allows signup.
if exp, ok = scp.Signup.(*sql.SubExpression); !ok {
m := "Authentication scope does not allow signup"
return fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
return str, fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
}
// Process the scope signup statement.
@ -96,41 +131,63 @@ func signup(c *fibre.Context) (err error) {
if res, err = db.Process(c, query, vars); err != nil {
m := "Authentication scope signup was unsuccessful: Query failed"
return fibre.NewHTTPError(501).WithFields(f).WithMessage(m)
return str, fibre.NewHTTPError(501).WithFields(f).WithMessage(m)
}
// If the response is not 1 record then return a 403 error.
if len(res) != 1 {
m := "Authentication scope signup was unsuccessful: Query failed"
return fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
return str, fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
}
// If the response has an error set then return a 403 error.
if res[0].Status != "OK" {
m := "Authentication scope signin was unsuccessful: " + res[0].Detail
return fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
return str, fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
}
// If the response has no record set then return a 403 error.
if len(res[0].Result) != 1 {
m := "Authentication scope signup was unsuccessful: No record created"
return fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
return str, fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
}
// If the query does not return an id field then return a 403 error.
if _, ok = data.Consume(res[0].Result[0]).Get("id").Data().(*sql.Thing); !ok {
if doc, ok = data.Consume(res[0].Result[0]).Get("id").Data().(*sql.Thing); !ok {
m := "Authentication scope signup was unsuccessful: No id field found"
return fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
return str, fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
}
return c.Code(204)
// Create a new token signer with the default claims.
signr := jwt.NewWithClaims(jwt.SigningMethodHS512, jwt.MapClaims{
"NS": n,
"DB": d,
"SC": s,
"TK": "default",
"iss": "Surreal",
"iat": time.Now().Unix(),
"nbf": time.Now().Unix(),
"exp": time.Now().Add(scp.Time).Unix(),
"TB": doc.TB,
"ID": doc.ID,
})
// Try to create the final signed token as a string.
if str, err = signr.SignedString(scp.Code); err != nil {
m := "Problem with signing method: " + err.Error()
return str, fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
}
return str, err
}
return fibre.NewHTTPError(403)
return str, fibre.NewHTTPError(403)
}