surrealpatch/db/check.go
2017-11-16 20:53:39 +00:00

289 lines
6.3 KiB
Go

// Copyright © 2016 Abcum Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"context"
"github.com/abcum/surreal/cnf"
"github.com/abcum/surreal/sql"
"github.com/abcum/surreal/util/data"
)
func (d *document) check(ctx context.Context, cond sql.Expr) (ok bool, err error) {
val, err := d.i.e.fetch(ctx, cond, d.current)
if val, ok := val.(bool); ok {
return val, err
}
return true, err
}
// Grant checks to see if the table permissions allow
// this record to be accessed for live queries, and
// if not then it errors accordingly.
func (d *document) grant(ctx context.Context, when method) (ok bool, err error) {
var val interface{}
// If we are authenticated using DB, NS,
// or KV permissions level, then we can
// ignore all permissions checks, but we
// must ensure the TB, DB, and NS exist.
if k, ok := ctx.Value(ctxKeyKind).(cnf.Kind); ok {
if k < cnf.AuthSC {
return true, nil
}
}
// Otherwise, get the table definition
// so we can check if the permissions
// allow us to view this document.
tb, err := d.getTB()
if err != nil {
return false, err
}
// We then try to process the relevant
// permissions dependent on the query
// that we are currently processing. If
// there are no permissions specified
// for this table, then because this is
// a scoped request, return an error.
switch p := tb.Perms.(type) {
case *sql.PermExpression:
switch when {
case _CREATE:
val, err = d.i.e.fetch(ctx, p.Select, d.current)
case _UPDATE:
val, err = d.i.e.fetch(ctx, p.Select, d.current)
case _DELETE:
val, err = d.i.e.fetch(ctx, p.Select, d.initial)
}
}
// If the permissions expressions
// returns a boolean value, then we
// return this, dictating whether the
// document is able to be viewed.
if val, ok := val.(bool); ok {
return val, err
}
// Otherwise as this request is scoped,
// return an error, so that the
// document is unable to be viewed.
return false, err
}
// Query checks to see if the table permissions allow
// this record to be accessed for normal queries, and
// if not then it errors accordingly.
func (d *document) allow(ctx context.Context, when method) (ok bool, err error) {
var val interface{}
// If we are authenticated using DB, NS,
// or KV permissions level, then we can
// ignore all permissions checks, but we
// must ensure the TB, DB, and NS exist.
if k, ok := ctx.Value(ctxKeyKind).(cnf.Kind); ok {
if k < cnf.AuthSC {
return true, nil
}
}
// Otherwise, get the table definition
// so we can check if the permissions
// allow us to view this document.
tb, err := d.getTB()
if err != nil {
return false, err
}
// We then try to process the relevant
// permissions dependent on the query
// that we are currently processing. If
// there are no permissions specified
// for this table, then because this is
// a scoped request, return an error.
switch p := tb.Perms.(type) {
case *sql.PermExpression:
switch when {
case _SELECT:
val, err = d.i.e.fetch(ctx, p.Select, d.current)
case _CREATE:
val, err = d.i.e.fetch(ctx, p.Create, d.current)
case _UPDATE:
val, err = d.i.e.fetch(ctx, p.Update, d.current)
case _DELETE:
val, err = d.i.e.fetch(ctx, p.Delete, d.current)
}
}
// If the permissions expressions
// returns a boolean value, then we
// return this, dictating whether the
// document is able to be viewed.
if val, ok := val.(bool); ok {
return val, err
}
// Otherwise as this request is scoped,
// return an error, so that the
// document is unable to be viewed.
return false, err
}
// Event checks if any triggers are specified for this
// table, and executes them in name order.
func (d *document) event(ctx context.Context, when method) (err error) {
// Get the index values specified
// for this table, loop through
// them, and compute the changes.
evs, err := d.getEV()
if err != nil {
return err
}
if len(evs) > 0 {
vars := data.New()
vars.Set(d.id, varKeyThis)
vars.Set(d.current.Data(), varKeyAfter)
vars.Set(d.initial.Data(), varKeyBefore)
ctx = context.WithValue(ctx, ctxKeySubs, vars)
for _, ev := range evs {
val, err := d.i.e.fetch(ctx, ev.When, d.current)
if err != nil {
return err
}
switch v := val.(type) {
case bool:
switch v {
case true:
_, err = d.i.e.fetch(ctx, ev.Then, d.current)
if err != nil {
return err
}
}
}
}
}
return
}
func (d *document) yield(ctx context.Context, stm sql.Statement, output sql.Token) (interface{}, error) {
switch stm := stm.(type) {
case *sql.SelectStatement:
var doc *data.Doc
for _, v := range stm.Expr {
if _, ok := v.Expr.(*sql.All); ok {
doc = d.current
break
}
}
if doc == nil {
doc = data.New()
}
for _, e := range stm.Expr {
switch v := e.Expr.(type) {
case *sql.All:
break
default:
// If the query has a GROUP BY expression
// then let's check if this is an aggregate
// function, and if it is then pass the
// first argument directly through.
if len(stm.Group) > 0 {
if f, ok := e.Expr.(*sql.FuncExpression); ok && f.Aggr {
v, err := d.i.e.fetch(ctx, f.Args[0], d.current)
if err != nil {
return nil, err
}
doc.Set(v, f.String())
continue
}
}
// Otherwise treat the field normally, and
// calculate the value to be inserted into
// the final output document.
v, err := d.i.e.fetch(ctx, v, d.current)
if err != nil {
return nil, err
}
doc.Set(v, e.Field)
}
}
return doc.Data(), nil
default:
switch output {
default:
return nil, nil
case sql.DIFF:
return d.diff().Data(), nil
case sql.AFTER:
return d.current.Data(), nil
case sql.BEFORE:
return d.initial.Data(), nil
case sql.BOTH:
return map[string]interface{}{
"after": d.current.Data(),
"before": d.initial.Data(),
}, nil
}
}
}