Improve web layer authentication and session process
This commit is contained in:
parent
1a5267e2e2
commit
2609d761be
4 changed files with 376 additions and 166 deletions
|
@ -23,6 +23,10 @@ var Settings *Options
|
||||||
|
|
||||||
type Kind int
|
type Kind int
|
||||||
|
|
||||||
|
func (k Kind) MarshalText() (data []byte, err error) {
|
||||||
|
return []byte(k.String()), err
|
||||||
|
}
|
||||||
|
|
||||||
func (k Kind) String() string {
|
func (k Kind) String() string {
|
||||||
switch k {
|
switch k {
|
||||||
default:
|
default:
|
||||||
|
@ -53,7 +57,8 @@ const (
|
||||||
|
|
||||||
type Auth struct {
|
type Auth struct {
|
||||||
Kind Kind
|
Kind Kind
|
||||||
Data map[string]interface{}
|
Data interface{}
|
||||||
|
Scope string
|
||||||
Possible struct {
|
Possible struct {
|
||||||
NS string
|
NS string
|
||||||
DB string
|
DB string
|
||||||
|
|
210
web/auth.go
210
web/auth.go
|
@ -28,10 +28,26 @@ import (
|
||||||
"github.com/abcum/surreal/db"
|
"github.com/abcum/surreal/db"
|
||||||
"github.com/abcum/surreal/kvs"
|
"github.com/abcum/surreal/kvs"
|
||||||
"github.com/abcum/surreal/mem"
|
"github.com/abcum/surreal/mem"
|
||||||
|
"github.com/abcum/surreal/sql"
|
||||||
"github.com/dgrijalva/jwt-go"
|
"github.com/dgrijalva/jwt-go"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
varKeyIp = "ip"
|
||||||
|
varKeyNs = "NS"
|
||||||
|
varKeyDb = "DB"
|
||||||
|
varKeySc = "SC"
|
||||||
|
varKeyTk = "TK"
|
||||||
|
varKeyUs = "US"
|
||||||
|
varKeyTb = "TB"
|
||||||
|
varKeyId = "ID"
|
||||||
|
varKeyAuth = "auth"
|
||||||
|
varKeyUser = "user"
|
||||||
|
varKeyPass = "pass"
|
||||||
|
varKeyOrigin = "origin"
|
||||||
|
)
|
||||||
|
|
||||||
func cidr(ip net.IP, networks []*net.IPNet) bool {
|
func cidr(ip net.IP, networks []*net.IPNet) bool {
|
||||||
for _, network := range networks {
|
for _, network := range networks {
|
||||||
if network.Contains(ip) {
|
if network.Contains(ip) {
|
||||||
|
@ -46,13 +62,7 @@ func auth() fibre.MiddlewareFunc {
|
||||||
return func(c *fibre.Context) (err error) {
|
return func(c *fibre.Context) (err error) {
|
||||||
|
|
||||||
auth := &cnf.Auth{}
|
auth := &cnf.Auth{}
|
||||||
c.Set("auth", auth)
|
c.Set(varKeyAuth, auth)
|
||||||
|
|
||||||
// Ensure that the authentication data
|
|
||||||
// object is initiated at the beginning
|
|
||||||
// so it is present when serialized.
|
|
||||||
|
|
||||||
auth.Data = make(map[string]interface{})
|
|
||||||
|
|
||||||
// Start off with an authentication level
|
// Start off with an authentication level
|
||||||
// which prevents running any sql queries,
|
// which prevents running any sql queries,
|
||||||
|
@ -77,7 +87,6 @@ func auth() fibre.MiddlewareFunc {
|
||||||
subs := strings.Split(bits[0], "-")
|
subs := strings.Split(bits[0], "-")
|
||||||
|
|
||||||
if len(subs) == 2 {
|
if len(subs) == 2 {
|
||||||
auth.Kind = cnf.AuthSC
|
|
||||||
auth.Possible.NS = subs[0]
|
auth.Possible.NS = subs[0]
|
||||||
auth.Selected.NS = subs[0]
|
auth.Selected.NS = subs[0]
|
||||||
auth.Possible.DB = subs[1]
|
auth.Possible.DB = subs[1]
|
||||||
|
@ -88,8 +97,7 @@ func auth() fibre.MiddlewareFunc {
|
||||||
// the request headers, then mark it as
|
// the request headers, then mark it as
|
||||||
// the selected namespace.
|
// the selected namespace.
|
||||||
|
|
||||||
if ns := c.Request().Header().Get("NS"); len(ns) != 0 {
|
if ns := c.Request().Header().Get(varKeyNs); len(ns) != 0 {
|
||||||
auth.Kind = cnf.AuthSC
|
|
||||||
auth.Possible.NS = ns
|
auth.Possible.NS = ns
|
||||||
auth.Selected.NS = ns
|
auth.Selected.NS = ns
|
||||||
}
|
}
|
||||||
|
@ -98,8 +106,7 @@ func auth() fibre.MiddlewareFunc {
|
||||||
// the request headers, then mark it as
|
// the request headers, then mark it as
|
||||||
// the selected database.
|
// the selected database.
|
||||||
|
|
||||||
if db := c.Request().Header().Get("DB"); len(db) != 0 {
|
if db := c.Request().Header().Get(varKeyDb); len(db) != 0 {
|
||||||
auth.Kind = cnf.AuthSC
|
|
||||||
auth.Possible.DB = db
|
auth.Possible.DB = db
|
||||||
auth.Selected.DB = db
|
auth.Selected.DB = db
|
||||||
}
|
}
|
||||||
|
@ -117,7 +124,6 @@ func auth() fibre.MiddlewareFunc {
|
||||||
if len(head) == 0 {
|
if len(head) == 0 {
|
||||||
for _, prot := range websocket.Subprotocols(c.Request().Request) {
|
for _, prot := range websocket.Subprotocols(c.Request().Request) {
|
||||||
if len(prot) > 7 && prot[0:7] == "bearer-" {
|
if len(prot) > 7 && prot[0:7] == "bearer-" {
|
||||||
head = "Bearer " + prot[7:]
|
|
||||||
return checkBearer(c, prot[7:], func() error {
|
return checkBearer(c, prot[7:], func() error {
|
||||||
return h(c)
|
return h(c)
|
||||||
})
|
})
|
||||||
|
@ -129,8 +135,8 @@ func auth() fibre.MiddlewareFunc {
|
||||||
// is a Basic Auth header, and if it is then
|
// is a Basic Auth header, and if it is then
|
||||||
// process this as root authentication.
|
// process this as root authentication.
|
||||||
|
|
||||||
if len(head) > 0 && head[:5] == "Basic" {
|
if len(head) > 6 && head[:5] == "Basic" {
|
||||||
return checkMaster(c, head[6:], func() error {
|
return checkBasics(c, head[6:], func() error {
|
||||||
return h(c)
|
return h(c)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -139,7 +145,7 @@ func auth() fibre.MiddlewareFunc {
|
||||||
// is a Bearer Auth header, and if it is then
|
// is a Bearer Auth header, and if it is then
|
||||||
// process this as default authentication.
|
// process this as default authentication.
|
||||||
|
|
||||||
if len(head) > 0 && head[:6] == "Bearer" {
|
if len(head) > 7 && head[:6] == "Bearer" {
|
||||||
return checkBearer(c, head[7:], func() error {
|
return checkBearer(c, head[7:], func() error {
|
||||||
return h(c)
|
return h(c)
|
||||||
})
|
})
|
||||||
|
@ -151,56 +157,90 @@ func auth() fibre.MiddlewareFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkRoot(c *fibre.Context, user, pass string, callback func() error) (err error) {
|
func checkBasics(c *fibre.Context, info string, callback func() error) (err error) {
|
||||||
|
|
||||||
auth := c.Get("auth").(*cnf.Auth)
|
var base []byte
|
||||||
|
var cred [][]byte
|
||||||
|
|
||||||
if cidr(c.IP(), cnf.Settings.Auth.Nets) {
|
auth := c.Get(varKeyAuth).(*cnf.Auth)
|
||||||
|
|
||||||
if user == cnf.Settings.Auth.User && pass == cnf.Settings.Auth.Pass {
|
|
||||||
auth.Kind = cnf.AuthKV
|
|
||||||
auth.Possible.NS = "*"
|
|
||||||
auth.Possible.DB = "*"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return callback()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkMaster(c *fibre.Context, info string, callback func() error) (err error) {
|
|
||||||
|
|
||||||
auth := c.Get("auth").(*cnf.Auth)
|
|
||||||
user := []byte(cnf.Settings.Auth.User)
|
user := []byte(cnf.Settings.Auth.User)
|
||||||
pass := []byte(cnf.Settings.Auth.Pass)
|
pass := []byte(cnf.Settings.Auth.Pass)
|
||||||
|
|
||||||
base, err := base64.StdEncoding.DecodeString(info)
|
// Parse the base64 encoded basic auth data
|
||||||
|
|
||||||
if err == nil && cidr(c.IP(), cnf.Settings.Auth.Nets) {
|
if base, err = base64.StdEncoding.DecodeString(info); err != nil {
|
||||||
|
return fibre.NewHTTPError(401).WithMessage("Problem with basic auth data")
|
||||||
|
}
|
||||||
|
|
||||||
cred := bytes.SplitN(base, []byte(":"), 2)
|
// Split the basic auth USER and PASS details
|
||||||
|
|
||||||
if len(cred) == 2 && bytes.Equal(cred[0], user) && bytes.Equal(cred[1], pass) {
|
if cred = bytes.SplitN(base, []byte(":"), 2); len(cred) != 2 {
|
||||||
|
return fibre.NewHTTPError(401).WithMessage("Problem with basic auth data")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check to see if IP, USER, and PASS match server settings
|
||||||
|
|
||||||
|
if bytes.Equal(cred[0], user) && bytes.Equal(cred[1], pass) {
|
||||||
|
|
||||||
|
if cidr(c.IP(), cnf.Settings.Auth.Nets) {
|
||||||
auth.Kind = cnf.AuthKV
|
auth.Kind = cnf.AuthKV
|
||||||
auth.Possible.NS = "*"
|
auth.Possible.NS = "*"
|
||||||
auth.Possible.DB = "*"
|
auth.Possible.DB = "*"
|
||||||
|
return callback()
|
||||||
|
}
|
||||||
|
|
||||||
|
return fibre.NewHTTPError(403).WithMessage("IP invalid for root authentication")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no KV authentication, then try to authenticate as NS user
|
||||||
|
|
||||||
|
if auth.Selected.NS != "" {
|
||||||
|
|
||||||
|
n := auth.Selected.NS
|
||||||
|
u := string(cred[0])
|
||||||
|
p := string(cred[1])
|
||||||
|
|
||||||
|
if _, err = signinNS(n, u, p); err == nil {
|
||||||
|
auth.Kind = cnf.AuthNS
|
||||||
|
auth.Possible.NS = n
|
||||||
|
auth.Possible.DB = "*"
|
||||||
|
return callback()
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no NS authentication, then try to authenticate as DB user
|
||||||
|
|
||||||
|
if auth.Selected.DB != "" {
|
||||||
|
|
||||||
|
n := auth.Selected.NS
|
||||||
|
d := auth.Selected.DB
|
||||||
|
u := string(cred[0])
|
||||||
|
p := string(cred[1])
|
||||||
|
|
||||||
|
if _, err = signinDB(n, d, u, p); err == nil {
|
||||||
|
auth.Kind = cnf.AuthDB
|
||||||
|
auth.Possible.NS = n
|
||||||
|
auth.Possible.DB = d
|
||||||
|
return callback()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return callback()
|
return fibre.NewHTTPError(401).WithMessage("Invalid authentication details")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkBearer(c *fibre.Context, info string, callback func() error) (err error) {
|
func checkBearer(c *fibre.Context, info string, callback func() error) (err error) {
|
||||||
|
|
||||||
auth := c.Get("auth").(*cnf.Auth)
|
auth := c.Get(varKeyAuth).(*cnf.Auth)
|
||||||
|
|
||||||
var txn kvs.TX
|
var txn kvs.TX
|
||||||
|
var res []*db.Response
|
||||||
var vars jwt.MapClaims
|
var vars jwt.MapClaims
|
||||||
var nok, dok, sok, tok, uok bool
|
var nsk, dbk, sck, tkk, usk, tbk, idk bool
|
||||||
var nsv, dbv, scv, tkv, usv string
|
var nsv, dbv, scv, tkv, usv, tbv, idv string
|
||||||
|
|
||||||
// Start a new read transaction.
|
// Start a new read transaction.
|
||||||
|
|
||||||
|
@ -212,6 +252,10 @@ func checkBearer(c *fibre.Context, info string, callback func() error) (err erro
|
||||||
|
|
||||||
defer txn.Cancel()
|
defer txn.Cancel()
|
||||||
|
|
||||||
|
// Setup the kvs layer cache.
|
||||||
|
|
||||||
|
cache := mem.NewWithTX(txn)
|
||||||
|
|
||||||
// Parse the specified JWT Token.
|
// Parse the specified JWT Token.
|
||||||
|
|
||||||
token, err := jwt.Parse(info, func(token *jwt.Token) (interface{}, error) {
|
token, err := jwt.Parse(info, func(token *jwt.Token) (interface{}, error) {
|
||||||
|
@ -222,15 +266,13 @@ func checkBearer(c *fibre.Context, info string, callback func() error) (err erro
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if val, ok := vars["auth"].(map[string]interface{}); ok {
|
nsv, nsk = vars[varKeyNs].(string) // Namespace
|
||||||
auth.Data = val
|
dbv, dbk = vars[varKeyDb].(string) // Database
|
||||||
}
|
scv, sck = vars[varKeySc].(string) // Scope
|
||||||
|
tkv, tkk = vars[varKeyTk].(string) // Token
|
||||||
nsv, nok = vars["NS"].(string) // Namespace
|
usv, usk = vars[varKeyUs].(string) // Login
|
||||||
dbv, dok = vars["DB"].(string) // Database
|
tbv, tbk = vars[varKeyTb].(string) // Table
|
||||||
scv, sok = vars["SC"].(string) // Scope
|
idv, idk = vars[varKeyId].(string) // Thing
|
||||||
tkv, tok = vars["TK"].(string) // Token
|
|
||||||
usv, uok = vars["US"].(string) // Login
|
|
||||||
|
|
||||||
if tkv == "default" {
|
if tkv == "default" {
|
||||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||||
|
@ -238,17 +280,53 @@ func checkBearer(c *fibre.Context, info string, callback func() error) (err erro
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if nok && dok && sok && tok {
|
if nsk && dbk && sck && tkk {
|
||||||
|
|
||||||
scp, err := mem.New(txn).GetSC(nsv, dbv, scv)
|
scp, err := cache.GetSC(nsv, dbv, scv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Credentials failed")
|
return nil, fmt.Errorf("Credentials failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
auth.Data["scope"] = scp.Name
|
// Store the authenticated scope.
|
||||||
|
|
||||||
|
auth.Scope = scp.Name.ID
|
||||||
|
|
||||||
|
// Store the authenticated thing.
|
||||||
|
|
||||||
|
auth.Data = sql.NewThing(tbv, idv)
|
||||||
|
|
||||||
|
// Check that the scope specifies connect.
|
||||||
|
|
||||||
|
if exp, ok := scp.Connect.(*sql.SubExpression); ok {
|
||||||
|
|
||||||
|
// Process the scope connect statement.
|
||||||
|
|
||||||
|
c := fibre.NewContext(c.Request(), c.Response(), c.Fibre())
|
||||||
|
|
||||||
|
c.Set(varKeyAuth, &cnf.Auth{Kind: cnf.AuthDB})
|
||||||
|
|
||||||
|
qvars := map[string]interface{}{"id": auth.Data}
|
||||||
|
|
||||||
|
query := &sql.Query{Statements: []sql.Statement{exp.Expr}}
|
||||||
|
|
||||||
|
// If the query fails then fail authentication.
|
||||||
|
|
||||||
|
if res, err = db.Process(c, query, qvars); err != nil {
|
||||||
|
return nil, fmt.Errorf("Credentials failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the response is not 1 record then fail authentication.
|
||||||
|
|
||||||
|
if len(res) != 1 || len(res[0].Result) != 1 {
|
||||||
|
return nil, fmt.Errorf("Credentials failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
auth.Data = res[0].Result[0]
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
if tkv != "default" {
|
if tkv != "default" {
|
||||||
key, err := mem.New(txn).GetST(nsv, dbv, scv, tkv)
|
key, err := cache.GetST(nsv, dbv, scv, tkv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Credentials failed")
|
return nil, fmt.Errorf("Credentials failed")
|
||||||
}
|
}
|
||||||
|
@ -262,10 +340,10 @@ func checkBearer(c *fibre.Context, info string, callback func() error) (err erro
|
||||||
return scp.Code, nil
|
return scp.Code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if nok && dok && tok {
|
} else if nsk && dbk && tkk {
|
||||||
|
|
||||||
if tkv != "default" {
|
if tkv != "default" {
|
||||||
key, err := mem.New(txn).GetDT(nsv, dbv, tkv)
|
key, err := cache.GetDT(nsv, dbv, tkv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Credentials failed")
|
return nil, fmt.Errorf("Credentials failed")
|
||||||
}
|
}
|
||||||
|
@ -274,8 +352,8 @@ func checkBearer(c *fibre.Context, info string, callback func() error) (err erro
|
||||||
}
|
}
|
||||||
auth.Kind = cnf.AuthDB
|
auth.Kind = cnf.AuthDB
|
||||||
return key.Code, nil
|
return key.Code, nil
|
||||||
} else if uok {
|
} else if usk {
|
||||||
usr, err := mem.New(txn).GetDU(nsv, dbv, usv)
|
usr, err := cache.GetDU(nsv, dbv, usv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Credentials failed")
|
return nil, fmt.Errorf("Credentials failed")
|
||||||
}
|
}
|
||||||
|
@ -283,10 +361,10 @@ func checkBearer(c *fibre.Context, info string, callback func() error) (err erro
|
||||||
return usr.Code, nil
|
return usr.Code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if nok && tok {
|
} else if nsk && tkk {
|
||||||
|
|
||||||
if tkv != "default" {
|
if tkv != "default" {
|
||||||
key, err := mem.New(txn).GetNT(nsv, tkv)
|
key, err := cache.GetNT(nsv, tkv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Credentials failed")
|
return nil, fmt.Errorf("Credentials failed")
|
||||||
}
|
}
|
||||||
|
@ -295,8 +373,8 @@ func checkBearer(c *fibre.Context, info string, callback func() error) (err erro
|
||||||
}
|
}
|
||||||
auth.Kind = cnf.AuthNS
|
auth.Kind = cnf.AuthNS
|
||||||
return key.Code, nil
|
return key.Code, nil
|
||||||
} else if uok {
|
} else if usk {
|
||||||
usr, err := mem.New(txn).GetNU(nsv, usv)
|
usr, err := cache.GetNU(nsv, usv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Credentials failed")
|
return nil, fmt.Errorf("Credentials failed")
|
||||||
}
|
}
|
||||||
|
@ -332,8 +410,10 @@ func checkBearer(c *fibre.Context, info string, callback func() error) (err erro
|
||||||
auth.Selected.DB = dbv
|
auth.Selected.DB = dbv
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return callback()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return callback()
|
return fibre.NewHTTPError(401).WithMessage("Invalid authentication details")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
247
web/signin.go
247
web/signin.go
|
@ -18,11 +18,12 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/abcum/fibre"
|
"github.com/abcum/fibre"
|
||||||
|
"github.com/abcum/surreal/cnf"
|
||||||
"github.com/abcum/surreal/db"
|
"github.com/abcum/surreal/db"
|
||||||
"github.com/abcum/surreal/kvs"
|
"github.com/abcum/surreal/kvs"
|
||||||
"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/dgrijalva/jwt-go"
|
"github.com/dgrijalva/jwt-go"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
@ -33,9 +34,21 @@ func signin(c *fibre.Context) (err error) {
|
||||||
|
|
||||||
c.Bind(&vars)
|
c.Bind(&vars)
|
||||||
|
|
||||||
n, nok := vars["NS"].(string)
|
n, nok := vars[varKeyNs].(string)
|
||||||
d, dok := vars["DB"].(string)
|
d, dok := vars[varKeyDb].(string)
|
||||||
s, sok := vars["SC"].(string)
|
s, sok := vars[varKeySc].(string)
|
||||||
|
|
||||||
|
// Ensure that the IP address of the
|
||||||
|
// user signing in is available so that
|
||||||
|
// it can be used within signin queries.
|
||||||
|
|
||||||
|
vars[varKeyIp] = c.IP().String()
|
||||||
|
|
||||||
|
// Ensure that the website origin of the
|
||||||
|
// user signing in is available so that
|
||||||
|
// it can be used within signin queries.
|
||||||
|
|
||||||
|
vars[varKeyOrigin] = c.Origin()
|
||||||
|
|
||||||
// If we have a namespace, database, and
|
// If we have a namespace, database, and
|
||||||
// scope defined, then we are logging in
|
// scope defined, then we are logging in
|
||||||
|
@ -43,9 +56,12 @@ func signin(c *fibre.Context) (err error) {
|
||||||
|
|
||||||
if nok && len(n) > 0 && dok && len(d) > 0 && sok && len(s) > 0 {
|
if nok && len(n) > 0 && dok && len(d) > 0 && sok && len(s) > 0 {
|
||||||
|
|
||||||
|
var ok bool
|
||||||
var txn kvs.TX
|
var txn kvs.TX
|
||||||
var str string
|
var str string
|
||||||
|
var doc *sql.Thing
|
||||||
var res []*db.Response
|
var res []*db.Response
|
||||||
|
var exp *sql.SubExpression
|
||||||
var scp *sql.DefineScopeStatement
|
var scp *sql.DefineScopeStatement
|
||||||
|
|
||||||
// Start a new read transaction.
|
// Start a new read transaction.
|
||||||
|
@ -60,7 +76,7 @@ func signin(c *fibre.Context) (err error) {
|
||||||
|
|
||||||
// Get the specified signin scope.
|
// Get the specified signin scope.
|
||||||
|
|
||||||
if scp, err = mem.New(txn).GetSC(n, d, s); err != nil {
|
if scp, err = mem.NewWithTX(txn).GetSC(n, d, s); err != nil {
|
||||||
return fibre.NewHTTPError(403).WithFields(map[string]interface{}{
|
return fibre.NewHTTPError(403).WithFields(map[string]interface{}{
|
||||||
"ns": n,
|
"ns": n,
|
||||||
"db": d,
|
"db": d,
|
||||||
|
@ -68,11 +84,25 @@ func signin(c *fibre.Context) (err error) {
|
||||||
}).WithMessage("Authentication scope does not exist")
|
}).WithMessage("Authentication scope does not exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check that the scope allows signin.
|
||||||
|
|
||||||
|
if exp, ok = scp.Signin.(*sql.SubExpression); !ok {
|
||||||
|
return fibre.NewHTTPError(403).WithFields(map[string]interface{}{
|
||||||
|
"ns": n,
|
||||||
|
"db": d,
|
||||||
|
"sc": s,
|
||||||
|
}).WithMessage("Authentication scope signup was unsuccessful")
|
||||||
|
}
|
||||||
|
|
||||||
// Process the scope signin statement.
|
// Process the scope signin statement.
|
||||||
|
|
||||||
qury := &sql.Query{Statements: []sql.Statement{scp.Signup}}
|
c.Set(varKeyAuth, &cnf.Auth{Kind: cnf.AuthDB})
|
||||||
|
|
||||||
if res, err = db.Process(c, qury, vars); err != nil {
|
query := &sql.Query{Statements: []sql.Statement{exp.Expr}}
|
||||||
|
|
||||||
|
// If the query fails then return a 501 error.
|
||||||
|
|
||||||
|
if res, err = db.Process(c, query, vars); err != nil {
|
||||||
return fibre.NewHTTPError(501).WithFields(map[string]interface{}{
|
return fibre.NewHTTPError(501).WithFields(map[string]interface{}{
|
||||||
"ns": n,
|
"ns": n,
|
||||||
"db": d,
|
"db": d,
|
||||||
|
@ -80,7 +110,19 @@ func signin(c *fibre.Context) (err error) {
|
||||||
}).WithMessage("Authentication scope signin was unsuccessful")
|
}).WithMessage("Authentication scope signin was unsuccessful")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(res) != 1 && len(res[0].Result) != 1 {
|
// If the response is not 1 record then return a 403 error.
|
||||||
|
|
||||||
|
if len(res) != 1 || len(res[0].Result) != 1 {
|
||||||
|
return fibre.NewHTTPError(403).WithFields(map[string]interface{}{
|
||||||
|
"ns": n,
|
||||||
|
"db": d,
|
||||||
|
"sc": s,
|
||||||
|
}).WithMessage("Authentication scope signin was unsuccessful")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
return fibre.NewHTTPError(403).WithFields(map[string]interface{}{
|
return fibre.NewHTTPError(403).WithFields(map[string]interface{}{
|
||||||
"ns": n,
|
"ns": n,
|
||||||
"db": d,
|
"db": d,
|
||||||
|
@ -91,15 +133,16 @@ func signin(c *fibre.Context) (err error) {
|
||||||
// Create a new token signer with the default claims.
|
// Create a new token signer with the default claims.
|
||||||
|
|
||||||
signr := jwt.NewWithClaims(jwt.SigningMethodHS512, jwt.MapClaims{
|
signr := jwt.NewWithClaims(jwt.SigningMethodHS512, jwt.MapClaims{
|
||||||
"NS": n,
|
"NS": n,
|
||||||
"DB": d,
|
"DB": d,
|
||||||
"SC": s,
|
"SC": s,
|
||||||
"TK": "default",
|
"TK": "default",
|
||||||
"iss": "Surreal",
|
"iss": "Surreal",
|
||||||
"iat": time.Now().Unix(),
|
"iat": time.Now().Unix(),
|
||||||
"nbf": time.Now().Unix(),
|
"nbf": time.Now().Unix(),
|
||||||
"exp": time.Now().Add(scp.Time).Unix(),
|
"exp": time.Now().Add(scp.Time).Unix(),
|
||||||
"auth": res[0].Result[0],
|
"TB": doc.TB,
|
||||||
|
"ID": doc.ID,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Try to create the final signed token as a string.
|
// Try to create the final signed token as a string.
|
||||||
|
@ -112,7 +155,7 @@ func signin(c *fibre.Context) (err error) {
|
||||||
}).WithMessage("Problem with signing string")
|
}).WithMessage("Problem with signing string")
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Text(200, str)
|
return c.Send(200, str)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,16 +165,15 @@ func signin(c *fibre.Context) (err error) {
|
||||||
|
|
||||||
if nok && len(n) > 0 && dok && len(d) > 0 {
|
if nok && len(n) > 0 && dok && len(d) > 0 {
|
||||||
|
|
||||||
var txn kvs.TX
|
|
||||||
var str string
|
var str string
|
||||||
var usr *sql.DefineLoginStatement
|
var usr *sql.DefineLoginStatement
|
||||||
|
|
||||||
// Get the specified user and password.
|
// Get the specified user and password.
|
||||||
|
|
||||||
u, uok := vars["user"].(string)
|
u, uok := vars[varKeyUser].(string)
|
||||||
p, pok := vars["pass"].(string)
|
p, pok := vars[varKeyPass].(string)
|
||||||
|
|
||||||
if !uok || len(u) == 0 || !pok || len(p) == 0 {
|
if !uok || !pok {
|
||||||
return fibre.NewHTTPError(403).WithFields(map[string]interface{}{
|
return fibre.NewHTTPError(403).WithFields(map[string]interface{}{
|
||||||
"ns": n,
|
"ns": n,
|
||||||
"db": d,
|
"db": d,
|
||||||
|
@ -141,32 +183,8 @@ 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 usr, err = signinDB(n, d, u, p); err != nil {
|
||||||
return fibre.NewHTTPError(500)
|
return err
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the transaction closes.
|
|
||||||
|
|
||||||
defer txn.Cancel()
|
|
||||||
|
|
||||||
// Get the specified database login.
|
|
||||||
|
|
||||||
if usr, err = mem.New(txn).GetDU(n, d, u); err != nil {
|
|
||||||
return fibre.NewHTTPError(403).WithFields(map[string]interface{}{
|
|
||||||
"ns": n,
|
|
||||||
"db": d,
|
|
||||||
"du": u,
|
|
||||||
}).WithMessage("Database login does not exist")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare the hashed and stored passwords.
|
|
||||||
|
|
||||||
if err = bcrypt.CompareHashAndPassword(usr.Pass, []byte(p)); err != nil {
|
|
||||||
return fibre.NewHTTPError(403).WithFields(map[string]interface{}{
|
|
||||||
"ns": n,
|
|
||||||
"db": d,
|
|
||||||
"du": u,
|
|
||||||
}).WithMessage("Database signin was unsuccessful")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new token signer with the default claims.
|
// Create a new token signer with the default claims.
|
||||||
|
@ -189,10 +207,10 @@ func signin(c *fibre.Context) (err error) {
|
||||||
"ns": n,
|
"ns": n,
|
||||||
"db": d,
|
"db": d,
|
||||||
"du": u,
|
"du": u,
|
||||||
}).WithMessage("Problem with singing string")
|
}).WithMessage("Problem with signing string")
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Text(200, str)
|
return c.Send(200, str)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,48 +220,23 @@ func signin(c *fibre.Context) (err error) {
|
||||||
|
|
||||||
if nok && len(n) > 0 {
|
if nok && len(n) > 0 {
|
||||||
|
|
||||||
var txn kvs.TX
|
|
||||||
var str string
|
var str string
|
||||||
var usr *sql.DefineLoginStatement
|
var usr *sql.DefineLoginStatement
|
||||||
|
|
||||||
// Get the specified user and password.
|
// Get the specified user and password.
|
||||||
|
|
||||||
u, uok := vars["user"].(string)
|
u, uok := vars[varKeyUser].(string)
|
||||||
p, pok := vars["pass"].(string)
|
p, pok := vars[varKeyPass].(string)
|
||||||
|
|
||||||
if !uok || len(u) == 0 || !pok || len(p) == 0 {
|
if !uok || !pok {
|
||||||
return fibre.NewHTTPError(403).WithFields(map[string]interface{}{
|
return fibre.NewHTTPError(403).WithFields(map[string]interface{}{
|
||||||
"ns": n,
|
"ns": n,
|
||||||
"nu": u,
|
"nu": u,
|
||||||
}).WithMessage("Database signin was unsuccessful")
|
}).WithMessage("Database signin was unsuccessful")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start a new read transaction.
|
if usr, err = signinNS(n, u, p); err != nil {
|
||||||
|
return err
|
||||||
if txn, err = db.Begin(false); err != nil {
|
|
||||||
return fibre.NewHTTPError(500)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the transaction closes.
|
|
||||||
|
|
||||||
defer txn.Cancel()
|
|
||||||
|
|
||||||
// Get the specified namespace login.
|
|
||||||
|
|
||||||
if usr, err = mem.New(txn).GetNU(n, u); err != nil {
|
|
||||||
return fibre.NewHTTPError(403).WithFields(map[string]interface{}{
|
|
||||||
"ns": n,
|
|
||||||
"nu": u,
|
|
||||||
}).WithMessage("Namespace login does not exist")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare the hashed and stored passwords.
|
|
||||||
|
|
||||||
if err = bcrypt.CompareHashAndPassword(usr.Pass, []byte(p)); err != nil {
|
|
||||||
return fibre.NewHTTPError(403).WithFields(map[string]interface{}{
|
|
||||||
"ns": n,
|
|
||||||
"nu": u,
|
|
||||||
}).WithMessage("Namespace signin was unsuccessful")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new token signer with the default claims.
|
// Create a new token signer with the default claims.
|
||||||
|
@ -264,13 +257,103 @@ func signin(c *fibre.Context) (err error) {
|
||||||
return fibre.NewHTTPError(403).WithFields(map[string]interface{}{
|
return fibre.NewHTTPError(403).WithFields(map[string]interface{}{
|
||||||
"ns": n,
|
"ns": n,
|
||||||
"nu": u,
|
"nu": u,
|
||||||
}).WithMessage("Problem with singing string")
|
}).WithMessage("Problem with signing string")
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Text(200, str)
|
return c.Send(200, str)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return fibre.NewHTTPError(403)
|
return fibre.NewHTTPError(403)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func signinDB(n, d, u, p string) (usr *sql.DefineLoginStatement, err error) {
|
||||||
|
|
||||||
|
var txn kvs.TX
|
||||||
|
|
||||||
|
// Start a new read transaction.
|
||||||
|
|
||||||
|
if txn, err = db.Begin(false); err != nil {
|
||||||
|
return nil, fibre.NewHTTPError(500)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the transaction closes.
|
||||||
|
|
||||||
|
defer txn.Cancel()
|
||||||
|
|
||||||
|
// Get the specified user and password.
|
||||||
|
|
||||||
|
if len(u) == 0 || len(p) == 0 {
|
||||||
|
return nil, fibre.NewHTTPError(403).WithFields(map[string]interface{}{
|
||||||
|
"ns": n,
|
||||||
|
"nu": u,
|
||||||
|
}).WithMessage("Database signin was unsuccessful")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the specified namespace login.
|
||||||
|
|
||||||
|
if usr, err = mem.NewWithTX(txn).GetDU(n, d, u); err != nil {
|
||||||
|
return nil, fibre.NewHTTPError(403).WithFields(map[string]interface{}{
|
||||||
|
"ns": n,
|
||||||
|
"nu": u,
|
||||||
|
}).WithMessage("Database login does not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare the hashed and stored passwords.
|
||||||
|
|
||||||
|
if err = bcrypt.CompareHashAndPassword(usr.Pass, []byte(p)); err != nil {
|
||||||
|
return nil, fibre.NewHTTPError(403).WithFields(map[string]interface{}{
|
||||||
|
"ns": n,
|
||||||
|
"nu": u,
|
||||||
|
}).WithMessage("Database signin was unsuccessful")
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func signinNS(n, u, p string) (usr *sql.DefineLoginStatement, err error) {
|
||||||
|
|
||||||
|
var txn kvs.TX
|
||||||
|
|
||||||
|
// Start a new read transaction.
|
||||||
|
|
||||||
|
if txn, err = db.Begin(false); err != nil {
|
||||||
|
return nil, fibre.NewHTTPError(500)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the transaction closes.
|
||||||
|
|
||||||
|
defer txn.Cancel()
|
||||||
|
|
||||||
|
// Get the specified user and password.
|
||||||
|
|
||||||
|
if len(u) == 0 || len(p) == 0 {
|
||||||
|
return nil, fibre.NewHTTPError(403).WithFields(map[string]interface{}{
|
||||||
|
"ns": n,
|
||||||
|
"nu": u,
|
||||||
|
}).WithMessage("Database signin was unsuccessful")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the specified namespace login.
|
||||||
|
|
||||||
|
if usr, err = mem.NewWithTX(txn).GetNU(n, u); err != nil {
|
||||||
|
return nil, fibre.NewHTTPError(403).WithFields(map[string]interface{}{
|
||||||
|
"ns": n,
|
||||||
|
"nu": u,
|
||||||
|
}).WithMessage("Namespace login does not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare the hashed and stored passwords.
|
||||||
|
|
||||||
|
if err = bcrypt.CompareHashAndPassword(usr.Pass, []byte(p)); err != nil {
|
||||||
|
return nil, fibre.NewHTTPError(403).WithFields(map[string]interface{}{
|
||||||
|
"ns": n,
|
||||||
|
"nu": u,
|
||||||
|
}).WithMessage("Namespace signin was unsuccessful")
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -16,10 +16,12 @@ package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/abcum/fibre"
|
"github.com/abcum/fibre"
|
||||||
|
"github.com/abcum/surreal/cnf"
|
||||||
"github.com/abcum/surreal/db"
|
"github.com/abcum/surreal/db"
|
||||||
"github.com/abcum/surreal/kvs"
|
"github.com/abcum/surreal/kvs"
|
||||||
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
func signup(c *fibre.Context) (err error) {
|
func signup(c *fibre.Context) (err error) {
|
||||||
|
@ -28,9 +30,21 @@ func signup(c *fibre.Context) (err error) {
|
||||||
|
|
||||||
c.Bind(&vars)
|
c.Bind(&vars)
|
||||||
|
|
||||||
n, nok := vars["NS"].(string)
|
n, nok := vars[varKeyNs].(string)
|
||||||
d, dok := vars["DB"].(string)
|
d, dok := vars[varKeyDb].(string)
|
||||||
s, sok := vars["SC"].(string)
|
s, sok := vars[varKeySc].(string)
|
||||||
|
|
||||||
|
// Ensure that the IP address of the
|
||||||
|
// user signing up is available so that
|
||||||
|
// it can be used within signup queries.
|
||||||
|
|
||||||
|
vars[varKeyIp] = c.IP().String()
|
||||||
|
|
||||||
|
// Ensure that the website origin of the
|
||||||
|
// user signing up is available so that
|
||||||
|
// it can be used within signup queries.
|
||||||
|
|
||||||
|
vars[varKeyOrigin] = c.Origin()
|
||||||
|
|
||||||
// If we have a namespace, database, and
|
// If we have a namespace, database, and
|
||||||
// scope defined, then we are logging in
|
// scope defined, then we are logging in
|
||||||
|
@ -38,8 +52,10 @@ func signup(c *fibre.Context) (err error) {
|
||||||
|
|
||||||
if nok && len(n) > 0 && dok && len(d) > 0 && sok && len(s) > 0 {
|
if nok && len(n) > 0 && dok && len(d) > 0 && sok && len(s) > 0 {
|
||||||
|
|
||||||
|
var ok bool
|
||||||
var txn kvs.TX
|
var txn kvs.TX
|
||||||
var res []*db.Response
|
var res []*db.Response
|
||||||
|
var exp *sql.SubExpression
|
||||||
var scp *sql.DefineScopeStatement
|
var scp *sql.DefineScopeStatement
|
||||||
|
|
||||||
// Start a new read transaction.
|
// Start a new read transaction.
|
||||||
|
@ -54,7 +70,7 @@ func signup(c *fibre.Context) (err error) {
|
||||||
|
|
||||||
// Get the specified signin scope.
|
// Get the specified signin scope.
|
||||||
|
|
||||||
if scp, err = mem.New(txn).GetSC(n, d, s); err != nil {
|
if scp, err = mem.NewWithTX(txn).GetSC(n, d, s); err != nil {
|
||||||
return fibre.NewHTTPError(403).WithFields(map[string]interface{}{
|
return fibre.NewHTTPError(403).WithFields(map[string]interface{}{
|
||||||
"ns": n,
|
"ns": n,
|
||||||
"db": d,
|
"db": d,
|
||||||
|
@ -62,19 +78,9 @@ func signup(c *fibre.Context) (err error) {
|
||||||
}).WithMessage("Authentication scope does not exist")
|
}).WithMessage("Authentication scope does not exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process the scope signup statement.
|
// Check that the scope allows signup.
|
||||||
|
|
||||||
qury := &sql.Query{Statements: []sql.Statement{scp.Signup}}
|
if exp, ok = scp.Signup.(*sql.SubExpression); !ok {
|
||||||
|
|
||||||
if res, err = db.Process(c, qury, vars); err != nil {
|
|
||||||
return fibre.NewHTTPError(501).WithFields(map[string]interface{}{
|
|
||||||
"ns": n,
|
|
||||||
"db": d,
|
|
||||||
"sc": s,
|
|
||||||
}).WithMessage("Authentication scope signup was unsuccessful")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(res) != 1 && len(res[0].Result) != 1 {
|
|
||||||
return fibre.NewHTTPError(403).WithFields(map[string]interface{}{
|
return fibre.NewHTTPError(403).WithFields(map[string]interface{}{
|
||||||
"ns": n,
|
"ns": n,
|
||||||
"db": d,
|
"db": d,
|
||||||
|
@ -82,10 +88,46 @@ func signup(c *fibre.Context) (err error) {
|
||||||
}).WithMessage("Authentication scope signup was unsuccessful")
|
}).WithMessage("Authentication scope signup was unsuccessful")
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Code(200)
|
// Process the scope signup statement.
|
||||||
|
|
||||||
|
c.Set(varKeyAuth, &cnf.Auth{Kind: cnf.AuthDB})
|
||||||
|
|
||||||
|
query := &sql.Query{Statements: []sql.Statement{exp.Expr}}
|
||||||
|
|
||||||
|
// If the query fails then return a 501 error.
|
||||||
|
|
||||||
|
if res, err = db.Process(c, query, vars); err != nil {
|
||||||
|
return fibre.NewHTTPError(501).WithFields(map[string]interface{}{
|
||||||
|
"ns": n,
|
||||||
|
"db": d,
|
||||||
|
"sc": s,
|
||||||
|
}).WithMessage("Authentication scope signup was unsuccessful")
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the response is not 1 record then return a 403 error.
|
||||||
|
|
||||||
|
if len(res) != 1 || len(res[0].Result) != 1 {
|
||||||
|
return fibre.NewHTTPError(403).WithFields(map[string]interface{}{
|
||||||
|
"ns": n,
|
||||||
|
"db": d,
|
||||||
|
"sc": s,
|
||||||
|
}).WithMessage("Authentication scope signup was unsuccessful")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
return fibre.NewHTTPError(403).WithFields(map[string]interface{}{
|
||||||
|
"ns": n,
|
||||||
|
"db": d,
|
||||||
|
"sc": s,
|
||||||
|
}).WithMessage("Authentication scope signup was unsuccessful")
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Code(204)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return fibre.NewHTTPError(401)
|
return fibre.NewHTTPError(403)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue