mirror of https://github.com/harness/drone.git
357 lines
9.3 KiB
Go
357 lines
9.3 KiB
Go
// Copyright 2023 Harness, 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 database
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/harness/gitness/app/store"
|
|
"github.com/harness/gitness/errors"
|
|
"github.com/harness/gitness/store/database"
|
|
"github.com/harness/gitness/store/database/dbtx"
|
|
"github.com/harness/gitness/types"
|
|
"github.com/harness/gitness/types/enum"
|
|
|
|
"github.com/Masterminds/squirrel"
|
|
"github.com/guregu/null"
|
|
"github.com/jmoiron/sqlx"
|
|
)
|
|
|
|
var _ store.PublicKeyStore = PublicKeyStore{}
|
|
|
|
// NewPublicKeyStore returns a new PublicKeyStore.
|
|
func NewPublicKeyStore(db *sqlx.DB) PublicKeyStore {
|
|
return PublicKeyStore{
|
|
db: db,
|
|
}
|
|
}
|
|
|
|
// PublicKeyStore implements a store.PublicKeyStore backed by a relational database.
|
|
type PublicKeyStore struct {
|
|
db *sqlx.DB
|
|
}
|
|
|
|
type publicKey struct {
|
|
ID int64 `db:"public_key_id"`
|
|
|
|
PrincipalID int64 `db:"public_key_principal_id"`
|
|
|
|
Created int64 `db:"public_key_created"`
|
|
Verified null.Int `db:"public_key_verified"`
|
|
|
|
Identifier string `db:"public_key_identifier"`
|
|
Usage string `db:"public_key_usage"`
|
|
|
|
Fingerprint string `db:"public_key_fingerprint"`
|
|
Content string `db:"public_key_content"`
|
|
Comment string `db:"public_key_comment"`
|
|
Type string `db:"public_key_type"`
|
|
}
|
|
|
|
const (
|
|
publicKeyColumns = `
|
|
public_key_id
|
|
,public_key_principal_id
|
|
,public_key_created
|
|
,public_key_verified
|
|
,public_key_identifier
|
|
,public_key_usage
|
|
,public_key_fingerprint
|
|
,public_key_content
|
|
,public_key_comment
|
|
,public_key_type`
|
|
|
|
publicKeySelectBase = `
|
|
SELECT` + publicKeyColumns + `
|
|
FROM public_keys`
|
|
)
|
|
|
|
// Find fetches a job by its unique identifier.
|
|
func (s PublicKeyStore) Find(ctx context.Context, id int64) (*types.PublicKey, error) {
|
|
const sqlQuery = publicKeySelectBase + `
|
|
WHERE public_key_id = $1`
|
|
|
|
db := dbtx.GetAccessor(ctx, s.db)
|
|
|
|
result := &publicKey{}
|
|
if err := db.GetContext(ctx, result, sqlQuery, id); err != nil {
|
|
return nil, database.ProcessSQLErrorf(ctx, err, "Failed to find public key by id")
|
|
}
|
|
|
|
key := mapToPublicKey(result)
|
|
|
|
return &key, nil
|
|
}
|
|
|
|
// FindByIdentifier returns a public key given a principal ID and an identifier.
|
|
func (s PublicKeyStore) FindByIdentifier(
|
|
ctx context.Context,
|
|
principalID int64,
|
|
identifier string,
|
|
) (*types.PublicKey, error) {
|
|
const sqlQuery = publicKeySelectBase + `
|
|
WHERE public_key_principal_id = $1 and LOWER(public_key_identifier) = $2`
|
|
|
|
db := dbtx.GetAccessor(ctx, s.db)
|
|
|
|
result := &publicKey{}
|
|
if err := db.GetContext(ctx, result, sqlQuery, principalID, strings.ToLower(identifier)); err != nil {
|
|
return nil, database.ProcessSQLErrorf(ctx, err, "Failed to find public key by principal and identifier")
|
|
}
|
|
|
|
key := mapToPublicKey(result)
|
|
|
|
return &key, nil
|
|
}
|
|
|
|
// Create creates a new public key.
|
|
func (s PublicKeyStore) Create(ctx context.Context, key *types.PublicKey) error {
|
|
const sqlQuery = `
|
|
INSERT INTO public_keys (
|
|
public_key_principal_id
|
|
,public_key_created
|
|
,public_key_verified
|
|
,public_key_identifier
|
|
,public_key_usage
|
|
,public_key_fingerprint
|
|
,public_key_content
|
|
,public_key_comment
|
|
,public_key_type
|
|
) values (
|
|
:public_key_principal_id
|
|
,:public_key_created
|
|
,:public_key_verified
|
|
,:public_key_identifier
|
|
,:public_key_usage
|
|
,:public_key_fingerprint
|
|
,:public_key_content
|
|
,:public_key_comment
|
|
,:public_key_type
|
|
) RETURNING public_key_id`
|
|
|
|
db := dbtx.GetAccessor(ctx, s.db)
|
|
|
|
dbKey := mapToInternalPublicKey(key)
|
|
|
|
query, arg, err := db.BindNamed(sqlQuery, &dbKey)
|
|
if err != nil {
|
|
return database.ProcessSQLErrorf(ctx, err, "Failed to bind public key object")
|
|
}
|
|
|
|
if err = db.QueryRowContext(ctx, query, arg...).Scan(&dbKey.ID); err != nil {
|
|
return database.ProcessSQLErrorf(ctx, err, "Insert public key query failed")
|
|
}
|
|
|
|
key.ID = dbKey.ID
|
|
|
|
return nil
|
|
}
|
|
|
|
// DeleteByIdentifier deletes a public key.
|
|
func (s PublicKeyStore) DeleteByIdentifier(ctx context.Context, principalID int64, identifier string) error {
|
|
const sqlQuery = `DELETE FROM public_keys WHERE public_key_principal_id = $1 and LOWER(public_key_identifier) = $2`
|
|
|
|
db := dbtx.GetAccessor(ctx, s.db)
|
|
|
|
result, err := db.ExecContext(ctx, sqlQuery, principalID, strings.ToLower(identifier))
|
|
if err != nil {
|
|
return database.ProcessSQLErrorf(ctx, err, "Delete public key query failed")
|
|
}
|
|
|
|
count, err := result.RowsAffected()
|
|
if err != nil {
|
|
return database.ProcessSQLErrorf(ctx, err, "RowsAffected after delete of public key failed")
|
|
}
|
|
|
|
if count == 0 {
|
|
return errors.NotFound("Key not found")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// MarkAsVerified updates the public key to mark it as verified.
|
|
func (s PublicKeyStore) MarkAsVerified(ctx context.Context, id int64, verified int64) error {
|
|
const sqlQuery = `
|
|
UPDATE public_keys
|
|
SET public_key_verified = $1
|
|
WHERE public_key_id = $2`
|
|
|
|
if _, err := dbtx.GetAccessor(ctx, s.db).ExecContext(ctx, sqlQuery, verified, id); err != nil {
|
|
return database.ProcessSQLErrorf(ctx, err, "Failed to mark public key as varified")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s PublicKeyStore) Count(
|
|
ctx context.Context,
|
|
principalID int64,
|
|
filter *types.PublicKeyFilter,
|
|
) (int, error) {
|
|
stmt := database.Builder.
|
|
Select("count(*)").
|
|
From("public_keys").
|
|
Where("public_key_principal_id = ?", principalID)
|
|
|
|
stmt = s.applyQueryFilter(stmt, filter)
|
|
|
|
sql, args, err := stmt.ToSql()
|
|
if err != nil {
|
|
return 0, fmt.Errorf("failed to convert query to sql: %w", err)
|
|
}
|
|
|
|
db := dbtx.GetAccessor(ctx, s.db)
|
|
|
|
var count int
|
|
|
|
if err := db.QueryRowContext(ctx, sql, args...).Scan(&count); err != nil {
|
|
return 0, database.ProcessSQLErrorf(ctx, err, "failed to execute count public keys query")
|
|
}
|
|
|
|
return count, nil
|
|
}
|
|
|
|
// List returns the public keys for the principal.
|
|
func (s PublicKeyStore) List(
|
|
ctx context.Context,
|
|
principalID int64,
|
|
filter *types.PublicKeyFilter,
|
|
) ([]types.PublicKey, error) {
|
|
stmt := database.Builder.
|
|
Select(publicKeyColumns).
|
|
From("public_keys").
|
|
Where("public_key_principal_id = ?", principalID)
|
|
|
|
stmt = s.applyQueryFilter(stmt, filter)
|
|
stmt = s.applySortFilter(stmt, filter)
|
|
|
|
sql, args, err := stmt.ToSql()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to convert query to sql: %w", err)
|
|
}
|
|
|
|
db := dbtx.GetAccessor(ctx, s.db)
|
|
|
|
keys := make([]publicKey, 0)
|
|
if err = db.SelectContext(ctx, &keys, sql, args...); err != nil {
|
|
return nil, database.ProcessSQLErrorf(ctx, err, "failed to execute list public keys query")
|
|
}
|
|
|
|
return mapToPublicKeys(keys), nil
|
|
}
|
|
|
|
// ListByFingerprint returns public keys given a fingerprint and key usage.
|
|
func (s PublicKeyStore) ListByFingerprint(
|
|
ctx context.Context,
|
|
fingerprint string,
|
|
) ([]types.PublicKey, error) {
|
|
stmt := database.Builder.
|
|
Select(publicKeyColumns).
|
|
From("public_keys").
|
|
Where("public_key_fingerprint = ?", fingerprint).
|
|
OrderBy("public_key_created ASC")
|
|
|
|
sql, args, err := stmt.ToSql()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to convert query to sql: %w", err)
|
|
}
|
|
|
|
db := dbtx.GetAccessor(ctx, s.db)
|
|
|
|
keys := make([]publicKey, 0)
|
|
if err = db.SelectContext(ctx, &keys, sql, args...); err != nil {
|
|
return nil, database.ProcessSQLErrorf(ctx, err, "failed to execute public keys by fingerprint query")
|
|
}
|
|
|
|
return mapToPublicKeys(keys), nil
|
|
}
|
|
|
|
func (PublicKeyStore) applyQueryFilter(
|
|
stmt squirrel.SelectBuilder,
|
|
filter *types.PublicKeyFilter,
|
|
) squirrel.SelectBuilder {
|
|
if filter.Query != "" {
|
|
stmt = stmt.Where("LOWER(public_key_identifier) LIKE ?",
|
|
fmt.Sprintf("%%%s%%", strings.ToLower(filter.Query)))
|
|
}
|
|
|
|
return stmt
|
|
}
|
|
|
|
func (PublicKeyStore) applySortFilter(
|
|
stmt squirrel.SelectBuilder,
|
|
filter *types.PublicKeyFilter,
|
|
) squirrel.SelectBuilder {
|
|
stmt = stmt.Limit(database.Limit(filter.Size))
|
|
stmt = stmt.Offset(database.Offset(filter.Page, filter.Size))
|
|
|
|
order := filter.Order
|
|
if order == enum.OrderDefault {
|
|
order = enum.OrderAsc
|
|
}
|
|
|
|
switch filter.Sort {
|
|
case enum.PublicKeySortIdentifier:
|
|
stmt = stmt.OrderBy("public_key_identifier " + order.String())
|
|
case enum.PublicKeySortCreated:
|
|
stmt = stmt.OrderBy("public_key_created " + order.String())
|
|
}
|
|
|
|
return stmt
|
|
}
|
|
|
|
func mapToInternalPublicKey(in *types.PublicKey) publicKey {
|
|
return publicKey{
|
|
ID: in.ID,
|
|
PrincipalID: in.PrincipalID,
|
|
Created: in.Created,
|
|
Verified: null.IntFromPtr(in.Verified),
|
|
Identifier: in.Identifier,
|
|
Usage: string(in.Usage),
|
|
Fingerprint: in.Fingerprint,
|
|
Content: in.Content,
|
|
Comment: in.Comment,
|
|
Type: in.Type,
|
|
}
|
|
}
|
|
|
|
func mapToPublicKey(in *publicKey) types.PublicKey {
|
|
return types.PublicKey{
|
|
ID: in.ID,
|
|
PrincipalID: in.PrincipalID,
|
|
Created: in.Created,
|
|
Verified: in.Verified.Ptr(),
|
|
Identifier: in.Identifier,
|
|
Usage: enum.PublicKeyUsage(in.Usage),
|
|
Fingerprint: in.Fingerprint,
|
|
Content: in.Content,
|
|
Comment: in.Comment,
|
|
Type: in.Type,
|
|
}
|
|
}
|
|
|
|
func mapToPublicKeys(
|
|
keys []publicKey,
|
|
) []types.PublicKey {
|
|
res := make([]types.PublicKey, len(keys))
|
|
for i := 0; i < len(keys); i++ {
|
|
res[i] = mapToPublicKey(&keys[i])
|
|
}
|
|
return res
|
|
}
|