harness-drone/store/user/user.go
2021-05-13 13:51:22 -04:00

332 lines
7.9 KiB
Go

// Copyright 2019 Drone IO, Inc.
//
// 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 user
import (
"context"
"fmt"
"github.com/drone/drone/core"
"github.com/drone/drone/store/shared/db"
"github.com/drone/drone/store/shared/encrypt"
)
// New returns a new UserStore.
func New(db *db.DB, enc encrypt.Encrypter) core.UserStore {
return &userStore{db, enc}
}
type userStore struct {
db *db.DB
enc encrypt.Encrypter
}
// Find returns a user from the datastore.
func (s *userStore) Find(ctx context.Context, id int64) (*core.User, error) {
out := &core.User{ID: id}
err := s.db.View(func(queryer db.Queryer, binder db.Binder) error {
params := map[string]interface{}{"user_id": id}
query, args, err := binder.BindNamed(queryKey, params)
if err != nil {
return err
}
row := queryer.QueryRow(query, args...)
return scanRow(s.enc, row, out)
})
return out, err
}
// FindLogin returns a user from the datastore by username.
func (s *userStore) FindLogin(ctx context.Context, login string) (*core.User, error) {
out := &core.User{Login: login}
err := s.db.View(func(queryer db.Queryer, binder db.Binder) error {
params := map[string]interface{}{"user_login": login}
query, args, err := binder.BindNamed(queryLogin, params)
if err != nil {
return err
}
row := queryer.QueryRow(query, args...)
return scanRow(s.enc, row, out)
})
return out, err
}
// FindToken returns a user from the datastore by token.
func (s *userStore) FindToken(ctx context.Context, token string) (*core.User, error) {
out := &core.User{Hash: token}
err := s.db.View(func(queryer db.Queryer, binder db.Binder) error {
params := map[string]interface{}{"user_hash": token}
query, args, err := binder.BindNamed(queryToken, params)
if err != nil {
return err
}
row := queryer.QueryRow(query, args...)
return scanRow(s.enc, row, out)
})
return out, err
}
// List returns a list of users from the datastore.
func (s *userStore) List(ctx context.Context) ([]*core.User, error) {
var out []*core.User
err := s.db.View(func(queryer db.Queryer, binder db.Binder) error {
rows, err := queryer.Query(queryAll)
if err != nil {
return err
}
out, err = scanRows(s.enc, rows)
return err
})
return out, err
}
// ListRange returns a list of users from the datastore.
func (s *userStore) ListRange(ctx context.Context, params core.UserParams) ([]*core.User, error) {
var out []*core.User
err := s.db.View(func(queryer db.Queryer, binder db.Binder) error {
// this query breaks a rule and uses sprintf to inject parameters
// into the query. Normally this should be avoided, however, in this
// case the parameters are set by the internal system and can
// be considered safe.
query := queryRange
switch {
case params.Sort:
query = fmt.Sprintf(query, "user_login", params.Size, params.Page)
default:
query = fmt.Sprintf(query, "user_id", params.Size, params.Page)
}
rows, err := queryer.Query(query)
if err != nil {
return err
}
out, err = scanRows(s.enc, rows)
return err
})
return out, err
}
// Create persists a new user to the datastore.
func (s *userStore) Create(ctx context.Context, user *core.User) error {
if s.db.Driver() == db.Postgres {
return s.createPostgres(ctx, user)
}
return s.create(ctx, user)
}
func (s *userStore) create(ctx context.Context, user *core.User) error {
return s.db.Lock(func(execer db.Execer, binder db.Binder) error {
params, err := toParams(s.enc, user)
if err != nil {
return err
}
stmt, args, err := binder.BindNamed(stmtInsert, params)
if err != nil {
return err
}
res, err := execer.Exec(stmt, args...)
if err != nil {
return err
}
user.ID, err = res.LastInsertId()
return err
})
}
func (s *userStore) createPostgres(ctx context.Context, user *core.User) error {
return s.db.Lock(func(execer db.Execer, binder db.Binder) error {
params, err := toParams(s.enc, user)
if err != nil {
return err
}
stmt, args, err := binder.BindNamed(stmtInsertPg, params)
if err != nil {
return err
}
return execer.QueryRow(stmt, args...).Scan(&user.ID)
})
}
// Update persists an updated user to the datastore.
func (s *userStore) Update(ctx context.Context, user *core.User) error {
return s.db.Lock(func(execer db.Execer, binder db.Binder) error {
params, err := toParams(s.enc, user)
if err != nil {
return err
}
stmt, args, err := binder.BindNamed(stmtUpdate, params)
if err != nil {
return err
}
_, err = execer.Exec(stmt, args...)
return err
})
}
// Delete deletes a user from the datastore.
func (s *userStore) Delete(ctx context.Context, user *core.User) error {
return s.db.Lock(func(execer db.Execer, binder db.Binder) error {
params := map[string]interface{}{"user_id": user.ID}
stmt, args, err := binder.BindNamed(stmtDelete, params)
if err != nil {
return err
}
_, err = execer.Exec(stmt, args...)
return err
})
}
// Count returns a count of active users.
func (s *userStore) Count(ctx context.Context) (int64, error) {
var out int64
err := s.db.View(func(queryer db.Queryer, binder db.Binder) error {
return queryer.QueryRow(queryCount).Scan(&out)
})
return out, err
}
// Count returns a count of active human users.
func (s *userStore) CountHuman(ctx context.Context) (int64, error) {
var out int64
err := s.db.View(func(queryer db.Queryer, binder db.Binder) error {
params := map[string]interface{}{"user_machine": false}
stmt, args, err := binder.BindNamed(queryCountHuman, params)
if err != nil {
return err
}
return queryer.QueryRow(stmt, args...).Scan(&out)
})
return out, err
}
const queryCount = `
SELECT COUNT(*)
FROM users
`
const queryCountHuman = `
SELECT COUNT(*)
FROM users
WHERE user_machine = :user_machine
`
const queryBase = `
SELECT
user_id
,user_login
,user_email
,user_admin
,user_machine
,user_active
,user_avatar
,user_syncing
,user_synced
,user_created
,user_updated
,user_last_login
,user_oauth_token
,user_oauth_refresh
,user_oauth_expiry
,user_hash
`
const queryKey = queryBase + `
FROM users
WHERE user_id = :user_id
`
const queryLogin = queryBase + `
FROM users
WHERE user_login = :user_login
`
const queryToken = queryBase + `
FROM users
WHERE user_hash = :user_hash
`
const queryAll = queryBase + `
FROM users
ORDER BY user_login
`
const queryRange = queryBase + `
FROM users
ORDER BY %s
LIMIT %d
OFFSET %d
`
const stmtUpdate = `
UPDATE users
SET
user_email = :user_email
,user_admin = :user_admin
,user_active = :user_active
,user_avatar = :user_avatar
,user_syncing = :user_syncing
,user_synced = :user_synced
,user_created = :user_created
,user_updated = :user_updated
,user_last_login = :user_last_login
,user_oauth_token = :user_oauth_token
,user_oauth_refresh = :user_oauth_refresh
,user_oauth_expiry = :user_oauth_expiry
,user_hash = :user_hash
WHERE user_id = :user_id
`
const stmtDelete = `
DELETE FROM users WHERE user_id = :user_id
`
const stmtInsert = `
INSERT INTO users (
user_login
,user_email
,user_admin
,user_machine
,user_active
,user_avatar
,user_syncing
,user_synced
,user_created
,user_updated
,user_last_login
,user_oauth_token
,user_oauth_refresh
,user_oauth_expiry
,user_hash
) VALUES (
:user_login
,:user_email
,:user_admin
,:user_machine
,:user_active
,:user_avatar
,:user_syncing
,:user_synced
,:user_created
,:user_updated
,:user_last_login
,:user_oauth_token
,:user_oauth_refresh
,:user_oauth_expiry
,:user_hash
)
`
const stmtInsertPg = stmtInsert + `
RETURNING user_id
`