Add ON SIGNUP and ON SIGNIN events to SQL SCOPE
It is now possible to run queries when a user signs-up or signs-in, by specifying multiple queries within an ON SIGNUP (…) clause, or a ON SIGNIN (...) clause.
This commit is contained in:
parent
0dae9d2366
commit
ca392f87a7
7 changed files with 107 additions and 15 deletions
|
@ -71,6 +71,14 @@ func TestDefine(t *testing.T) {
|
||||||
SIGNIN AS (
|
SIGNIN AS (
|
||||||
SELECT * FROM user WHERE email=$user AND bcrypt.compare(pass, $pass)
|
SELECT * FROM user WHERE email=$user AND bcrypt.compare(pass, $pass)
|
||||||
)
|
)
|
||||||
|
ON SIGNUP (
|
||||||
|
UPDATE $id SET times.created=time.now();
|
||||||
|
CREATE activity SET kind="signup", user=$id;
|
||||||
|
)
|
||||||
|
ON SIGNIN (
|
||||||
|
UPDATE $id SET times.login=time.now();
|
||||||
|
CREATE activity SET kind="signin", user=$id;
|
||||||
|
)
|
||||||
;
|
;
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|
|
@ -344,6 +344,8 @@ type DefineScopeStatement struct {
|
||||||
Signup Expr `cork:"signup" codec:"signup"`
|
Signup Expr `cork:"signup" codec:"signup"`
|
||||||
Signin Expr `cork:"signin" codec:"signin"`
|
Signin Expr `cork:"signin" codec:"signin"`
|
||||||
Connect Expr `cork:"connect" codec:"connect"`
|
Connect Expr `cork:"connect" codec:"connect"`
|
||||||
|
OnSignup Expr `cork:"onsignup" codec:"onsignup"`
|
||||||
|
OnSignin Expr `cork:"onsignin" codec:"onsignin"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveScopeStatement represents an SQL REMOVE SCOPE statement.
|
// RemoveScopeStatement represents an SQL REMOVE SCOPE statement.
|
||||||
|
|
|
@ -1569,6 +1569,8 @@ func (this *DefineScopeStatement) MarshalCORK(w *cork.Writer) (err error) {
|
||||||
w.EncodeAny(this.Signup)
|
w.EncodeAny(this.Signup)
|
||||||
w.EncodeAny(this.Signin)
|
w.EncodeAny(this.Signin)
|
||||||
w.EncodeAny(this.Connect)
|
w.EncodeAny(this.Connect)
|
||||||
|
w.EncodeAny(this.OnSignup)
|
||||||
|
w.EncodeAny(this.OnSignin)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1579,6 +1581,8 @@ func (this *DefineScopeStatement) UnmarshalCORK(r *cork.Reader) (err error) {
|
||||||
r.DecodeAny(&this.Signup)
|
r.DecodeAny(&this.Signup)
|
||||||
r.DecodeAny(&this.Signin)
|
r.DecodeAny(&this.Signin)
|
||||||
r.DecodeAny(&this.Connect)
|
r.DecodeAny(&this.Connect)
|
||||||
|
r.DecodeAny(&this.OnSignup)
|
||||||
|
r.DecodeAny(&this.OnSignin)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
22
sql/scope.go
22
sql/scope.go
|
@ -28,7 +28,7 @@ func (p *parser) parseDefineScopeStatement() (stmt *DefineScopeStatement, err er
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
|
||||||
tok, _, exi := p.mightBe(SESSION, SIGNUP, SIGNIN, CONNECT)
|
tok, _, exi := p.mightBe(SESSION, SIGNUP, SIGNIN, CONNECT, ON)
|
||||||
if !exi {
|
if !exi {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,26 @@ func (p *parser) parseDefineScopeStatement() (stmt *DefineScopeStatement, err er
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if is(tok, ON) {
|
||||||
|
|
||||||
|
tok, _, err = p.shouldBe(SIGNIN, SIGNUP)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch tok {
|
||||||
|
case SIGNUP:
|
||||||
|
if stmt.OnSignup, err = p.parseMult(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
case SIGNIN:
|
||||||
|
if stmt.OnSignin, err = p.parseMult(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
|
@ -334,12 +334,14 @@ func (this RemoveTokenStatement) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this DefineScopeStatement) String() string {
|
func (this DefineScopeStatement) String() string {
|
||||||
return print("DEFINE SCOPE %v%v%v%v%v",
|
return print("DEFINE SCOPE %v%v%v%v%v%v%v",
|
||||||
this.Name,
|
this.Name,
|
||||||
maybe(this.Time > 0, print(" SESSION %v", this.Time)),
|
maybe(this.Time > 0, print(" SESSION %v", this.Time)),
|
||||||
maybe(this.Signup != nil, print(" SIGNUP AS %v", this.Signup)),
|
maybe(this.Signup != nil, print(" SIGNUP AS %v", this.Signup)),
|
||||||
maybe(this.Signin != nil, print(" SIGNIN AS %v", this.Signin)),
|
maybe(this.Signin != nil, print(" SIGNIN AS %v", this.Signin)),
|
||||||
maybe(this.Connect != nil, print(" CONNECT AS %v", this.Connect)),
|
maybe(this.Connect != nil, print(" CONNECT AS %v", this.Connect)),
|
||||||
|
maybe(this.OnSignup != nil, print(" ON SIGNUP %v", this.OnSignup)),
|
||||||
|
maybe(this.OnSignin != nil, print(" ON SIGNIN %v", this.OnSignin)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -92,6 +92,7 @@ func signinInternal(c *fibre.Context, vars map[string]interface{}) (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
|
||||||
|
var evt *sql.MultExpression
|
||||||
var scp *sql.DefineScopeStatement
|
var scp *sql.DefineScopeStatement
|
||||||
|
|
||||||
// Start a new read transaction.
|
// Start a new read transaction.
|
||||||
|
@ -104,6 +105,10 @@ func signinInternal(c *fibre.Context, vars map[string]interface{}) (str string,
|
||||||
|
|
||||||
defer txn.Cancel()
|
defer txn.Cancel()
|
||||||
|
|
||||||
|
// Give full permissions to scope.
|
||||||
|
|
||||||
|
c.Set(varKeyAuth, &cnf.Auth{Kind: cnf.AuthDB})
|
||||||
|
|
||||||
// Specify fields to show in logs.
|
// Specify fields to show in logs.
|
||||||
|
|
||||||
f := map[string]interface{}{"ns": n, "db": d, "sc": s}
|
f := map[string]interface{}{"ns": n, "db": d, "sc": s}
|
||||||
|
@ -124,8 +129,6 @@ func signinInternal(c *fibre.Context, vars map[string]interface{}) (str string,
|
||||||
|
|
||||||
// Process the scope signin statement.
|
// Process the scope signin statement.
|
||||||
|
|
||||||
c.Set(varKeyAuth, &cnf.Auth{Kind: cnf.AuthDB})
|
|
||||||
|
|
||||||
query := &sql.Query{Statements: []sql.Statement{exp.Expr}}
|
query := &sql.Query{Statements: []sql.Statement{exp.Expr}}
|
||||||
|
|
||||||
// If the query fails then return a 501 error.
|
// If the query fails then return a 501 error.
|
||||||
|
@ -186,6 +189,31 @@ func signinInternal(c *fibre.Context, vars map[string]interface{}) (str string,
|
||||||
return str, fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
|
return str, fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check that the scope allows signup.
|
||||||
|
|
||||||
|
if evt, ok = scp.OnSignin.(*sql.MultExpression); ok {
|
||||||
|
|
||||||
|
stmts := make([]sql.Statement, len(evt.Expr))
|
||||||
|
|
||||||
|
for k := range evt.Expr {
|
||||||
|
stmts[k] = evt.Expr[k]
|
||||||
|
}
|
||||||
|
|
||||||
|
query := &sql.Query{Statements: stmts}
|
||||||
|
|
||||||
|
qvars := map[string]interface{}{
|
||||||
|
"id": doc,
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the query fails then return a 501 error.
|
||||||
|
|
||||||
|
if res, err = db.Process(c, query, qvars); err != nil {
|
||||||
|
m := "Authentication scope signin was unsuccessful: `ON SIGNIN` failed:" + err.Error()
|
||||||
|
return str, fibre.NewHTTPError(501).WithFields(f).WithMessage(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
return str, err
|
return str, err
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,6 +91,7 @@ func signupInternal(c *fibre.Context, vars map[string]interface{}) (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
|
||||||
|
var evt *sql.MultExpression
|
||||||
var scp *sql.DefineScopeStatement
|
var scp *sql.DefineScopeStatement
|
||||||
|
|
||||||
// Start a new read transaction.
|
// Start a new read transaction.
|
||||||
|
@ -103,6 +104,10 @@ func signupInternal(c *fibre.Context, vars map[string]interface{}) (str string,
|
||||||
|
|
||||||
defer txn.Cancel()
|
defer txn.Cancel()
|
||||||
|
|
||||||
|
// Give full permissions to scope.
|
||||||
|
|
||||||
|
c.Set(varKeyAuth, &cnf.Auth{Kind: cnf.AuthDB})
|
||||||
|
|
||||||
// Specify fields to show in logs.
|
// Specify fields to show in logs.
|
||||||
|
|
||||||
f := map[string]interface{}{"ns": n, "db": d, "sc": s}
|
f := map[string]interface{}{"ns": n, "db": d, "sc": s}
|
||||||
|
@ -123,8 +128,6 @@ func signupInternal(c *fibre.Context, vars map[string]interface{}) (str string,
|
||||||
|
|
||||||
// Process the scope signup statement.
|
// Process the scope signup statement.
|
||||||
|
|
||||||
c.Set(varKeyAuth, &cnf.Auth{Kind: cnf.AuthDB})
|
|
||||||
|
|
||||||
query := &sql.Query{Statements: []sql.Statement{exp.Expr}}
|
query := &sql.Query{Statements: []sql.Statement{exp.Expr}}
|
||||||
|
|
||||||
// If the query fails then return a 501 error.
|
// If the query fails then return a 501 error.
|
||||||
|
@ -185,6 +188,31 @@ func signupInternal(c *fibre.Context, vars map[string]interface{}) (str string,
|
||||||
return str, fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
|
return str, fibre.NewHTTPError(403).WithFields(f).WithMessage(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check that the scope allows signup.
|
||||||
|
|
||||||
|
if evt, ok = scp.OnSignup.(*sql.MultExpression); ok {
|
||||||
|
|
||||||
|
stmts := make([]sql.Statement, len(evt.Expr))
|
||||||
|
|
||||||
|
for k := range evt.Expr {
|
||||||
|
stmts[k] = evt.Expr[k]
|
||||||
|
}
|
||||||
|
|
||||||
|
query := &sql.Query{Statements: stmts}
|
||||||
|
|
||||||
|
qvars := map[string]interface{}{
|
||||||
|
"id": doc,
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the query fails then return a 501 error.
|
||||||
|
|
||||||
|
if res, err = db.Process(c, query, qvars); err != nil {
|
||||||
|
m := "Authentication scope signup was unsuccessful: `ON SIGNUP` failed:" + err.Error()
|
||||||
|
return str, fibre.NewHTTPError(501).WithFields(f).WithMessage(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
return str, err
|
return str, err
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue