Improve rpc authentication methods
This commit is contained in:
parent
c47fe55ffb
commit
2249086887
3 changed files with 149 additions and 38 deletions
38
web/rpc.go
38
web/rpc.go
|
@ -25,26 +25,38 @@ import (
|
||||||
|
|
||||||
type rpc struct{}
|
type rpc struct{}
|
||||||
|
|
||||||
|
// --------------------------------------------------
|
||||||
|
// Methods for authentication
|
||||||
|
// --------------------------------------------------
|
||||||
|
|
||||||
func (r *rpc) Uniq(c *fibre.Context) (interface{}, error) {
|
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) {
|
func (r *rpc) Info(c *fibre.Context) (interface{}, error) {
|
||||||
return c.Get("auth").(*cnf.Auth).Data, nil
|
return c.Get("auth").(*cnf.Auth).Data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rpc) Auth(c *fibre.Context, auth string) (interface{}, error) {
|
func (r *rpc) Signup(c *fibre.Context, vars map[string]interface{}) (interface{}, error) {
|
||||||
return c.Get("auth").(*cnf.Auth).Data, checkBearer(c, auth, ignore)
|
return signupRpc(c, vars)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rpc) Let(c *fibre.Context, key string, val interface{}) (interface{}, error) {
|
func (r *rpc) Signin(c *fibre.Context, vars map[string]interface{}) (interface{}, error) {
|
||||||
return c.Get("keep").(*data.Doc).Set(val, key)
|
return signinRpc(c, vars)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rpc) Query(c *fibre.Context, sql string, vars map[string]interface{}) (interface{}, error) {
|
func (r *rpc) Invalidate(c *fibre.Context) (interface{}, error) {
|
||||||
return db.Execute(c, sql, vars)
|
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) {
|
func (r *rpc) Kill(c *fibre.Context, query string) (interface{}, error) {
|
||||||
return db.Execute(c, "KILL $query", map[string]interface{}{
|
return db.Execute(c, "KILL $query", map[string]interface{}{
|
||||||
"query": query,
|
"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) {
|
func (r *rpc) Select(c *fibre.Context, class string, thing interface{}) (interface{}, error) {
|
||||||
switch thing := thing.(type) {
|
switch thing := thing.(type) {
|
||||||
case *fibre.RPCNull:
|
case *fibre.RPCNull:
|
||||||
|
|
|
@ -34,6 +34,37 @@ func signin(c *fibre.Context) (err error) {
|
||||||
|
|
||||||
c.Bind(&vars)
|
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)
|
n, nok := vars[varKeyNs].(string)
|
||||||
d, dok := vars[varKeyDb].(string)
|
d, dok := vars[varKeyDb].(string)
|
||||||
s, sok := vars[varKeySc].(string)
|
s, sok := vars[varKeySc].(string)
|
||||||
|
@ -58,7 +89,6 @@ func signin(c *fibre.Context) (err error) {
|
||||||
|
|
||||||
var ok bool
|
var ok bool
|
||||||
var txn kvs.TX
|
var txn kvs.TX
|
||||||
var str string
|
|
||||||
var doc *sql.Thing
|
var doc *sql.Thing
|
||||||
var res []*db.Response
|
var res []*db.Response
|
||||||
var exp *sql.SubExpression
|
var exp *sql.SubExpression
|
||||||
|
@ -67,7 +97,7 @@ func signin(c *fibre.Context) (err error) {
|
||||||
// Start a new read transaction.
|
// Start a new read transaction.
|
||||||
|
|
||||||
if txn, err = db.Begin(false); err != nil {
|
if txn, err = db.Begin(false); err != nil {
|
||||||
return fibre.NewHTTPError(500)
|
return str, fibre.NewHTTPError(500)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the transaction closes.
|
// 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 {
|
if scp, err = mem.NewWithTX(txn).GetSC(n, d, s); err != nil {
|
||||||
m := "Authentication scope does not exist"
|
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.
|
// Check that the scope allows signin.
|
||||||
|
|
||||||
if exp, ok = scp.Signin.(*sql.SubExpression); !ok {
|
if exp, ok = scp.Signin.(*sql.SubExpression); !ok {
|
||||||
m := "Authentication scope does not allow signin"
|
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.
|
// 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 {
|
if res, err = db.Process(c, query, vars); err != nil {
|
||||||
m := "Authentication scope signin was unsuccessful: Query failed"
|
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 the response is not 1 record then return a 403 error.
|
||||||
|
|
||||||
if len(res) != 1 {
|
if len(res) != 1 {
|
||||||
m := "Authentication scope signin was unsuccessful: Query failed"
|
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 the response has an error set then return a 403 error.
|
||||||
|
|
||||||
if res[0].Status != "OK" {
|
if res[0].Status != "OK" {
|
||||||
m := "Authentication scope signin was unsuccessful: " + res[0].Detail
|
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 the response has no record set then return a 403 error.
|
||||||
|
|
||||||
if len(res[0].Result) != 1 {
|
if len(res[0].Result) != 1 {
|
||||||
m := "Authentication scope signin was unsuccessful: No record returned"
|
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 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 {
|
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"
|
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.
|
// 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 {
|
if str, err = signr.SignedString(scp.Code); err != nil {
|
||||||
m := "Problem with signing method: " + err.Error()
|
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 {
|
if !uok || !pok {
|
||||||
m := "Username or password is missing"
|
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.
|
// Start a new read transaction.
|
||||||
|
|
||||||
if usr, err = signinDB(n, d, u, p); err != nil {
|
if usr, err = signinDB(n, d, u, p); err != nil {
|
||||||
return err
|
return str, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new token signer with the default claims.
|
// 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 {
|
if str, err = signr.SignedString(usr.Code); err != nil {
|
||||||
m := "Problem with signing method: " + err.Error()
|
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 {
|
if !uok || !pok {
|
||||||
m := "Username or password is missing"
|
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 {
|
if usr, err = signinNS(n, u, p); err != nil {
|
||||||
return err
|
return str, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new token signer with the default claims.
|
// 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 {
|
if str, err = signr.SignedString(usr.Code); err != nil {
|
||||||
m := "Problem with signing method: " + err.Error()
|
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)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
package web
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/abcum/fibre"
|
"github.com/abcum/fibre"
|
||||||
"github.com/abcum/surreal/cnf"
|
"github.com/abcum/surreal/cnf"
|
||||||
"github.com/abcum/surreal/db"
|
"github.com/abcum/surreal/db"
|
||||||
|
@ -22,6 +24,7 @@ import (
|
||||||
"github.com/abcum/surreal/mem"
|
"github.com/abcum/surreal/mem"
|
||||||
"github.com/abcum/surreal/sql"
|
"github.com/abcum/surreal/sql"
|
||||||
"github.com/abcum/surreal/util/data"
|
"github.com/abcum/surreal/util/data"
|
||||||
|
"github.com/dgrijalva/jwt-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
func signup(c *fibre.Context) (err error) {
|
func signup(c *fibre.Context) (err error) {
|
||||||
|
@ -30,6 +33,37 @@ func signup(c *fibre.Context) (err error) {
|
||||||
|
|
||||||
c.Bind(&vars)
|
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)
|
n, nok := vars[varKeyNs].(string)
|
||||||
d, dok := vars[varKeyDb].(string)
|
d, dok := vars[varKeyDb].(string)
|
||||||
s, sok := vars[varKeySc].(string)
|
s, sok := vars[varKeySc].(string)
|
||||||
|
@ -54,6 +88,7 @@ func signup(c *fibre.Context) (err error) {
|
||||||
|
|
||||||
var ok bool
|
var ok bool
|
||||||
var txn kvs.TX
|
var txn kvs.TX
|
||||||
|
var doc *sql.Thing
|
||||||
var res []*db.Response
|
var res []*db.Response
|
||||||
var exp *sql.SubExpression
|
var exp *sql.SubExpression
|
||||||
var scp *sql.DefineScopeStatement
|
var scp *sql.DefineScopeStatement
|
||||||
|
@ -61,7 +96,7 @@ func signup(c *fibre.Context) (err error) {
|
||||||
// Start a new read transaction.
|
// Start a new read transaction.
|
||||||
|
|
||||||
if txn, err = db.Begin(false); err != nil {
|
if txn, err = db.Begin(false); err != nil {
|
||||||
return fibre.NewHTTPError(500)
|
return str, fibre.NewHTTPError(500)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the transaction closes.
|
// 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 {
|
if scp, err = mem.NewWithTX(txn).GetSC(n, d, s); err != nil {
|
||||||
m := "Authentication scope does not exist"
|
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.
|
// Check that the scope allows signup.
|
||||||
|
|
||||||
if exp, ok = scp.Signup.(*sql.SubExpression); !ok {
|
if exp, ok = scp.Signup.(*sql.SubExpression); !ok {
|
||||||
m := "Authentication scope does not allow signup"
|
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.
|
// 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 {
|
if res, err = db.Process(c, query, vars); err != nil {
|
||||||
m := "Authentication scope signup was unsuccessful: Query failed"
|
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 the response is not 1 record then return a 403 error.
|
||||||
|
|
||||||
if len(res) != 1 {
|
if len(res) != 1 {
|
||||||
m := "Authentication scope signup was unsuccessful: Query failed"
|
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 the response has an error set then return a 403 error.
|
||||||
|
|
||||||
if res[0].Status != "OK" {
|
if res[0].Status != "OK" {
|
||||||
m := "Authentication scope signin was unsuccessful: " + res[0].Detail
|
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 the response has no record set then return a 403 error.
|
||||||
|
|
||||||
if len(res[0].Result) != 1 {
|
if len(res[0].Result) != 1 {
|
||||||
m := "Authentication scope signup was unsuccessful: No record created"
|
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 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"
|
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)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue