~edwargix/git.sr.ht

35ad335d499977b5086649343e72cd6754d204d2 — Drew DeVault 5 years ago 8b27e00
api/auth: use squirrel for SQL query construction
2 files changed, 87 insertions(+), 15 deletions(-)

M api/auth/auth.go
A api/graph/model/filter.go
M api/auth/auth.go => api/auth/auth.go +17 -15
@@ 14,6 14,8 @@ import (
	"time"

	"github.com/vektah/gqlparser/gqlerror"

	"git.sr.ht/~sircmpwn/git.sr.ht/api/database"
)

var userCtxKey = &contextKey{"user"}


@@ 104,21 106,21 @@ Expected 'Authentication: Bearer <token>'`, http.StatusForbidden)
				scopes  string
				user    User
			)
			if rows, err = db.Query(`
					SELECT
						ot.expires,
						ot.scopes,
						u.id, u.username,
						u.created, u.updated,
						u.email,
						u.user_type,
						u.url, u.location, u.bio,
						u.suspension_notice
					FROM oauthtoken ot
					JOIN "user" u ON u.id = ot.user_id
					WHERE ot.token_hash = $1;
				`, bearer); err != nil {

			query := database.
				Select(context.TODO(), []string{
					`ot.expires`,
					`ot.scopes`,
					`u.id`, `u.username`,
					`u.created`, `u.updated`,
					`u.email`,
					`u.user_type`,
					`u.url`, `u.location`, `u.bio`,
					`u.suspension_notice`,
				}).
				From(`oauthtoken ot`).
				Join(`"user" u ON u.id = ot.user_id`).
				Where(`ot.token_hash = ?`, bearer)
			if rows, err = query.RunWith(db).Query(); err != nil {
				panic(err)
			}
			defer rows.Close()

A api/graph/model/filter.go => api/graph/model/filter.go +70 -0
@@ 0,0 1,70 @@
package model

import (
	"fmt"
	"strings"

	"github.com/google/shlex"
	"github.com/lib/pq"
)

type KeyFunc func(tbl, value string) (string, error)

type SearchTerm struct {
	Key     string
	Value   string
	Inverse bool
}

type Searchable interface {
	// Returns the default WHERE clause for a given search term with no key.
	Default(tbl, term string) (string, error)

	// Returns a map of search functions for a given key, where each function
	// returns the appropriate WHERE clause for searching with the given
	// string.
	Keys() map[string]KeyFunc

	// Returns a WHERE clause for a given search key and value, where the key
	// was not found in the Keys() map.
	Fallback(tbl, key, value string) (string, error)
}

type WhereClause struct {
	Clause     string
	Parameters []interface{}
}

// Returns a WHERE clause for the given fitler
func Where(query *string, tbl string, param int,
	resource Searchable) (*WhereClause, error) {
	if query == nil {
		return &WhereClause{"true /* No search terms */", nil}, nil
	}

	tbl = pq.QuoteIdentifier(tbl)
	terms, err := shlex.Split(*query)
	if err != nil {
		return nil, err
	}

	var (
		clauses []string
		params  []interface{}
	)
	for _, term := range terms {
		parts := strings.SplitN(term, ":", 2)
		variable := fmt.Sprintf("$%d", param)
		param += 1
		if len(parts) == 1 {
			clause, err := resource.Default(tbl, variable)
			if err != nil {
				return nil, err
			}
			clauses = append(clauses, fmt.Sprintf("(%s)", clause))
			params = append(params, term)
		}
	}

	return &WhereClause{strings.Join(clauses, " AND "), params}, nil
}