1
0
mirror of https://github.com/mainflux/mainflux.git synced 2025-04-28 13:48:49 +08:00
b1ackd0t 7cc1dd9f89
MF-969 - Add List API Keys Endpoint (#1703)
* initial commit

Signed-off-by: rodneyosodo <socials@rodneyosodo.com>

* Fix CI Test Errors

Signed-off-by: rodneyosodo <blackd0t@protonmail.com>

---------

Signed-off-by: rodneyosodo <socials@rodneyosodo.com>
Signed-off-by: rodneyosodo <blackd0t@protonmail.com>
Co-authored-by: rodneyosodo <socials@rodneyosodo.com>
Co-authored-by: Drasko DRASKOVIC <drasko.draskovic@gmail.com>
2023-05-25 00:13:29 +02:00

174 lines
4.2 KiB
Go

package postgres
import (
"context"
"database/sql"
"fmt"
"strings"
"time"
"github.com/jackc/pgerrcode"
"github.com/jackc/pgx/v5/pgconn"
"github.com/mainflux/mainflux/auth"
"github.com/mainflux/mainflux/pkg/errors"
)
var (
errSave = errors.New("failed to save key in database")
errRetrieve = errors.New("failed to retrieve key from database")
errDelete = errors.New("failed to delete key from database")
)
var _ auth.KeyRepository = (*repo)(nil)
type repo struct {
db Database
}
// New instantiates a PostgreSQL implementation of key repository.
func New(db Database) auth.KeyRepository {
return &repo{
db: db,
}
}
func (kr repo) Save(ctx context.Context, key auth.Key) (string, error) {
q := `INSERT INTO keys (id, type, issuer_id, subject, issued_at, expires_at)
VALUES (:id, :type, :issuer_id, :subject, :issued_at, :expires_at)`
dbKey := toDBKey(key)
if _, err := kr.db.NamedExecContext(ctx, q, dbKey); err != nil {
if pgErr, ok := err.(*pgconn.PgError); ok && pgErr.Code == pgerrcode.UniqueViolation {
return "", errors.Wrap(errors.ErrConflict, err)
}
return "", errors.Wrap(errSave, err)
}
return dbKey.ID, nil
}
func (kr repo) RetrieveByID(ctx context.Context, issuerID, id string) (auth.Key, error) {
q := `SELECT id, type, issuer_id, subject, issued_at, expires_at FROM keys WHERE issuer_id = $1 AND id = $2`
key := dbKey{}
if err := kr.db.QueryRowxContext(ctx, q, issuerID, id).StructScan(&key); err != nil {
pgErr, ok := err.(*pgconn.PgError)
if err == sql.ErrNoRows || ok && pgerrcode.InvalidTextRepresentation == pgErr.Code {
return auth.Key{}, errors.Wrap(errors.ErrNotFound, err)
}
return auth.Key{}, errors.Wrap(errRetrieve, err)
}
return toKey(key), nil
}
func (kr repo) RetrieveAll(ctx context.Context, issuerID string, pm auth.PageMetadata) (auth.KeyPage, error) {
var query []string
var emq string
query = append(query, fmt.Sprintf("issuer_id = '%s'", issuerID))
if pm.Type != 0 {
query = append(query, fmt.Sprintf("type = '%d'", pm.Type))
}
if pm.Subject != "" {
query = append(query, fmt.Sprintf("subject = '%s'", pm.Subject))
}
if len(query) > 0 {
emq = fmt.Sprintf(" WHERE %s", strings.Join(query, " AND "))
}
q := fmt.Sprintf(`SELECT id, type, issuer_id, subject, issued_at, expires_at FROM keys %s ORDER BY issued_at LIMIT :limit OFFSET :offset;`, emq)
params := map[string]interface{}{
"limit": pm.Limit,
"offset": pm.Offset,
}
rows, err := kr.db.NamedQueryContext(ctx, q, params)
if err != nil {
return auth.KeyPage{}, errors.Wrap(errors.ErrViewEntity, err)
}
defer rows.Close()
var items []auth.Key
for rows.Next() {
dbkey := dbKey{}
if err := rows.StructScan(&dbkey); err != nil {
return auth.KeyPage{}, errors.Wrap(errors.ErrViewEntity, err)
}
key := toKey(dbkey)
items = append(items, key)
}
cq := fmt.Sprintf(`SELECT COUNT(*) FROM keys %s;`, emq)
total, err := total(ctx, kr.db, cq, params)
if err != nil {
return auth.KeyPage{}, errors.Wrap(errors.ErrViewEntity, err)
}
page := auth.KeyPage{
Keys: items,
PageMetadata: auth.PageMetadata{
Total: total,
Offset: pm.Offset,
Limit: pm.Limit,
},
}
return page, nil
}
func (kr repo) Remove(ctx context.Context, issuerID, id string) error {
q := `DELETE FROM keys WHERE issuer_id = :issuer_id AND id = :id`
key := dbKey{
ID: id,
IssuerID: issuerID,
}
if _, err := kr.db.NamedExecContext(ctx, q, key); err != nil {
return errors.Wrap(errDelete, err)
}
return nil
}
type dbKey struct {
ID string `db:"id"`
Type uint32 `db:"type"`
IssuerID string `db:"issuer_id"`
Subject string `db:"subject"`
Revoked bool `db:"revoked"`
IssuedAt time.Time `db:"issued_at"`
ExpiresAt sql.NullTime `db:"expires_at"`
}
func toDBKey(key auth.Key) dbKey {
ret := dbKey{
ID: key.ID,
Type: key.Type,
IssuerID: key.IssuerID,
Subject: key.Subject,
IssuedAt: key.IssuedAt,
}
if !key.ExpiresAt.IsZero() {
ret.ExpiresAt = sql.NullTime{Time: key.ExpiresAt, Valid: true}
}
return ret
}
func toKey(key dbKey) auth.Key {
ret := auth.Key{
ID: key.ID,
Type: key.Type,
IssuerID: key.IssuerID,
Subject: key.Subject,
IssuedAt: key.IssuedAt,
}
if key.ExpiresAt.Valid {
ret.ExpiresAt = key.ExpiresAt.Time
}
return ret
}