From 35ad335d499977b5086649343e72cd6754d204d2 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Tue, 14 Apr 2020 14:43:00 -0400 Subject: [PATCH] api/auth: use squirrel for SQL query construction --- api/auth/auth.go | 32 +++++++++--------- api/graph/model/filter.go | 70 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 15 deletions(-) create mode 100644 api/graph/model/filter.go diff --git a/api/auth/auth.go b/api/auth/auth.go index bd73a90..257e15a 100644 --- a/api/auth/auth.go +++ b/api/auth/auth.go @@ -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 '`, 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() diff --git a/api/graph/model/filter.go b/api/graph/model/filter.go new file mode 100644 index 0000000..92da5c1 --- /dev/null +++ b/api/graph/model/filter.go @@ -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 +} -- 2.38.4