File view with blame information shown in the left gutter beside each line.

barerepo / server / internal/store/tokens.go
122 lines · 3.8kb · 133728eaa05486504991b230f9d4c7e986b7defc
log files threads runs releases config jump to file t
133728e barerepo 1mo
1
package store
133728e barerepo 1mo
2
133728e barerepo 1mo
3
import (
133728e barerepo 1mo
4
"context"
133728e barerepo 1mo
5
"database/sql"
133728e barerepo 1mo
6
"errors"
133728e barerepo 1mo
7
"time"
133728e barerepo 1mo
8
133728e barerepo 1mo
9
"github.com/barerepo/server/internal/token"
133728e barerepo 1mo
10
)
133728e barerepo 1mo
11
133728e barerepo 1mo
12
// Token is one row of item 3 on the closed list.
133728e barerepo 1mo
13
type Token struct {
133728e barerepo 1mo
14
ID int64
133728e barerepo 1mo
15
Kind token.Kind
133728e barerepo 1mo
16
Account string
133728e barerepo 1mo
17
Scope string // owner/name for a runner token, empty for a git token
133728e barerepo 1mo
18
Label string
133728e barerepo 1mo
19
Created time.Time
133728e barerepo 1mo
20
LastUsed time.Time
133728e barerepo 1mo
21
}
133728e barerepo 1mo
22
133728e barerepo 1mo
23
// CreateToken issues a token and returns it. The caller shows it once.
133728e barerepo 1mo
24
func (db *DB) CreateToken(ctx context.Context, kind token.Kind, account, scope, label string) (string, *Token, error) {
133728e barerepo 1mo
25
tok, hash, err := token.New(kind)
133728e barerepo 1mo
26
if err != nil {
133728e barerepo 1mo
27
return "", nil, err
133728e barerepo 1mo
28
}
133728e barerepo 1mo
29
now := now()
133728e barerepo 1mo
30
var id int64
133728e barerepo 1mo
31
err = db.QueryRowContext(ctx,
133728e barerepo 1mo
32
`INSERT INTO tokens (kind, hash, account, scope, label, created_at)
133728e barerepo 1mo
33
VALUES (?, ?, ?, ?, ?, ?) RETURNING id`,
133728e barerepo 1mo
34
string(kind), hash, account, scope, label, now.Unix()).Scan(&id)
133728e barerepo 1mo
35
if err != nil {
133728e barerepo 1mo
36
return "", nil, err
133728e barerepo 1mo
37
}
133728e barerepo 1mo
38
return tok, &Token{ID: id, Kind: kind, Account: account, Scope: scope, Label: label, Created: now}, nil
133728e barerepo 1mo
39
}
133728e barerepo 1mo
40
133728e barerepo 1mo
41
// AccountForToken names the owner and stamps last_used, which the keys page shows.
133728e barerepo 1mo
42
func (db *DB) AccountForToken(ctx context.Context, kind token.Kind, tok string) (*Token, error) {
133728e barerepo 1mo
43
if k, err := token.KindOf(tok); err != nil || k != kind {
133728e barerepo 1mo
44
return nil, ErrNotFound
133728e barerepo 1mo
45
}
133728e barerepo 1mo
46
var t Token
133728e barerepo 1mo
47
var created int64
133728e barerepo 1mo
48
var expires sql.NullInt64
133728e barerepo 1mo
49
err := db.QueryRowContext(ctx,
133728e barerepo 1mo
50
`SELECT id, account, scope, label, created_at, expires_at FROM tokens WHERE kind = ? AND hash = ?`,
133728e barerepo 1mo
51
string(kind), token.Hash(tok)).Scan(&t.ID, &t.Account, &t.Scope, &t.Label, &created, &expires)
133728e barerepo 1mo
52
if errors.Is(err, sql.ErrNoRows) {
133728e barerepo 1mo
53
return nil, ErrNotFound
133728e barerepo 1mo
54
}
133728e barerepo 1mo
55
if err != nil {
133728e barerepo 1mo
56
return nil, err
133728e barerepo 1mo
57
}
133728e barerepo 1mo
58
nowT := now()
133728e barerepo 1mo
59
if expires.Valid && nowT.Unix() > expires.Int64 {
133728e barerepo 1mo
60
return nil, ErrNotFound
133728e barerepo 1mo
61
}
133728e barerepo 1mo
62
t.Kind = kind
133728e barerepo 1mo
63
t.Created = time.Unix(created, 0)
133728e barerepo 1mo
64
if _, err := db.ExecContext(ctx, `UPDATE tokens SET last_used = ? WHERE id = ?`, nowT.Unix(), t.ID); err != nil {
133728e barerepo 1mo
65
return nil, err
133728e barerepo 1mo
66
}
133728e barerepo 1mo
67
return &t, nil
133728e barerepo 1mo
68
}
133728e barerepo 1mo
69
133728e barerepo 1mo
70
// DeleteToken revokes one token belonging to account.
133728e barerepo 1mo
71
func (db *DB) DeleteToken(ctx context.Context, account string, id int64) error {
133728e barerepo 1mo
72
res, err := db.ExecContext(ctx, `DELETE FROM tokens WHERE account = ? AND id = ?`, account, id)
133728e barerepo 1mo
73
if err != nil {
133728e barerepo 1mo
74
return err
133728e barerepo 1mo
75
}
133728e barerepo 1mo
76
if n, _ := res.RowsAffected(); n == 0 {
133728e barerepo 1mo
77
return ErrNotFound
133728e barerepo 1mo
78
}
133728e barerepo 1mo
79
return nil
133728e barerepo 1mo
80
}
133728e barerepo 1mo
81
133728e barerepo 1mo
82
// TokensOf lists tokens newest first, without sessions, which nobody manages by hand.
133728e barerepo 1mo
83
func (db *DB) TokensOf(ctx context.Context, account string) ([]Token, error) {
133728e barerepo 1mo
84
rows, err := db.QueryContext(ctx,
133728e barerepo 1mo
85
`SELECT id, kind, account, scope, label, created_at, last_used
133728e barerepo 1mo
86
FROM tokens WHERE account = ? AND kind != ? AND kind != ? ORDER BY created_at DESC, id DESC`,
133728e barerepo 1mo
87
account, string(token.Session), string(token.Claim))
133728e barerepo 1mo
88
if err != nil {
133728e barerepo 1mo
89
return nil, err
133728e barerepo 1mo
90
}
133728e barerepo 1mo
91
defer rows.Close()
133728e barerepo 1mo
92
var out []Token
133728e barerepo 1mo
93
for rows.Next() {
133728e barerepo 1mo
94
var t Token
133728e barerepo 1mo
95
var kind string
133728e barerepo 1mo
96
var created int64
133728e barerepo 1mo
97
var used sql.NullInt64
133728e barerepo 1mo
98
if err := rows.Scan(&t.ID, &kind, &t.Account, &t.Scope, &t.Label, &created, &used); err != nil {
133728e barerepo 1mo
99
return nil, err
133728e barerepo 1mo
100
}
133728e barerepo 1mo
101
t.Kind = token.Kind(kind)
133728e barerepo 1mo
102
t.Created = time.Unix(created, 0)
133728e barerepo 1mo
103
if used.Valid {
133728e barerepo 1mo
104
t.LastUsed = time.Unix(used.Int64, 0)
133728e barerepo 1mo
105
}
133728e barerepo 1mo
106
out = append(out, t)
133728e barerepo 1mo
107
}
133728e barerepo 1mo
108
return out, rows.Err()
133728e barerepo 1mo
109
}
133728e barerepo 1mo
110
133728e barerepo 1mo
111
// RunnerTokenGrace is how long an unused runner token survives a reload of the page that made it.
133728e barerepo 1mo
112
const RunnerTokenGrace = time.Hour
133728e barerepo 1mo
113
133728e barerepo 1mo
114
// DropStaleRunnerTokens revokes runner tokens nobody pasted, so reloading the page cannot pile them up.
133728e barerepo 1mo
115
func (db *DB) DropStaleRunnerTokens(ctx context.Context, account, repo string) error {
133728e barerepo 1mo
116
_, err := db.ExecContext(ctx,
133728e barerepo 1mo
117
`DELETE FROM tokens WHERE kind = ? AND account = ? AND scope = ?
133728e barerepo 1mo
118
AND last_used IS NULL AND created_at < ?
133728e barerepo 1mo
119
AND id NOT IN (SELECT token_id FROM runners)`,
133728e barerepo 1mo
120
string(token.Runner), account, repo, now().Add(-RunnerTokenGrace).Unix())
133728e barerepo 1mo
121
return err
133728e barerepo 1mo
122
}
history · rawbarerepo 0.1.0