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 (
|
||||
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;
|
||||
)
|
||||
;
|
||||
`
|
||||
|
||||
|
|
20
sql/ast.go
20
sql/ast.go
|
@ -335,15 +335,17 @@ type RemoveTokenStatement struct {
|
|||
|
||||
// DefineScopeStatement represents an SQL DEFINE SCOPE statement.
|
||||
type DefineScopeStatement struct {
|
||||
KV string `cork:"-" codec:"-"`
|
||||
NS string `cork:"-" codec:"-"`
|
||||
DB string `cork:"-" codec:"-"`
|
||||
Name *Ident `cork:"name" codec:"name"`
|
||||
Time time.Duration `cork:"time" codec:"time"`
|
||||
Code []byte `cork:"code" codec:"code"`
|
||||
Signup Expr `cork:"signup" codec:"signup"`
|
||||
Signin Expr `cork:"signin" codec:"signin"`
|
||||
Connect Expr `cork:"connect" codec:"connect"`
|
||||
KV string `cork:"-" codec:"-"`
|
||||
NS string `cork:"-" codec:"-"`
|
||||
DB string `cork:"-" codec:"-"`
|
||||
Name *Ident `cork:"name" codec:"name"`
|
||||
Time time.Duration `cork:"time" codec:"time"`
|
||||
Code []byte `cork:"code" codec:"code"`
|
||||
Signup Expr `cork:"signup" codec:"signup"`
|
||||
Signin Expr `cork:"signin" codec:"signin"`
|
||||
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.
|
||||
|
|
|
@ -1569,6 +1569,8 @@ func (this *DefineScopeStatement) MarshalCORK(w *cork.Writer) (err error) {
|
|||
w.EncodeAny(this.Signup)
|
||||
w.EncodeAny(this.Signin)
|
||||
w.EncodeAny(this.Connect)
|
||||
w.EncodeAny(this.OnSignup)
|
||||
w.EncodeAny(this.OnSignin)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1579,6 +1581,8 @@ func (this *DefineScopeStatement) UnmarshalCORK(r *cork.Reader) (err error) {
|
|||
r.DecodeAny(&this.Signup)
|
||||
r.DecodeAny(&this.Signin)
|
||||
r.DecodeAny(&this.Connect)
|
||||
r.DecodeAny(&this.OnSignup)
|
||||
r.DecodeAny(&this.OnSignin)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
22
sql/scope.go
22
sql/scope.go
|
@ -28,7 +28,7 @@ func (p *parser) parseDefineScopeStatement() (stmt *DefineScopeStatement, err er
|
|||
|
||||
for {
|
||||
|
||||
tok, _, exi := p.mightBe(SESSION, SIGNUP, SIGNIN, CONNECT)
|
||||
tok, _, exi := p.mightBe(SESSION, SIGNUP, SIGNIN, CONNECT, ON)
|
||||
if !exi {
|
||||
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
|
||||
|
|
|
@ -334,12 +334,14 @@ func (this RemoveTokenStatement) 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,
|
||||
maybe(this.Time > 0, print(" SESSION %v", this.Time)),
|
||||
maybe(this.Signup != nil, print(" SIGNUP AS %v", this.Signup)),
|
||||
maybe(this.Signin != nil, print(" SIGNIN AS %v", this.Signin)),
|
||||
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 res []*db.Response
|
||||
var exp *sql.SubExpression
|
||||
var evt *sql.MultExpression
|
||||
var scp *sql.DefineScopeStatement
|
||||
|
||||
// Start a new read transaction.
|
||||
|
@ -104,6 +105,10 @@ func signinInternal(c *fibre.Context, vars map[string]interface{}) (str string,
|
|||
|
||||
defer txn.Cancel()
|
||||
|
||||
// Give full permissions to scope.
|
||||
|
||||
c.Set(varKeyAuth, &cnf.Auth{Kind: cnf.AuthDB})
|
||||
|
||||
// Specify fields to show in logs.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
@ -186,6 +189,31 @@ func signinInternal(c *fibre.Context, vars map[string]interface{}) (str string,
|
|||
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
|
||||
|
||||
}
|
||||
|
|
|
@ -91,6 +91,7 @@ func signupInternal(c *fibre.Context, vars map[string]interface{}) (str string,
|
|||
var doc *sql.Thing
|
||||
var res []*db.Response
|
||||
var exp *sql.SubExpression
|
||||
var evt *sql.MultExpression
|
||||
var scp *sql.DefineScopeStatement
|
||||
|
||||
// Start a new read transaction.
|
||||
|
@ -103,6 +104,10 @@ func signupInternal(c *fibre.Context, vars map[string]interface{}) (str string,
|
|||
|
||||
defer txn.Cancel()
|
||||
|
||||
// Give full permissions to scope.
|
||||
|
||||
c.Set(varKeyAuth, &cnf.Auth{Kind: cnf.AuthDB})
|
||||
|
||||
// Specify fields to show in logs.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
@ -185,6 +188,31 @@ func signupInternal(c *fibre.Context, vars map[string]interface{}) (str string,
|
|||
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
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue