oficina / home Public

Clone
git clone https://amadan.net/oficina/home

Plain git — no account needed to clone.

Download

Download this file

1package operator
2
3import (
4 "bytes"
5 "context"
6 "database/sql"
7 "encoding/base64"
8 "net/mail"
9 "time"
10 "unicode/utf8"
11
12 family "amadan.net/rastrillo/rastrillo/crypto"
13)
14
15// PersonResolver uses the supplied transaction connection and the credential
16// role's normal blind index. False means existing-only resolution found nobody.
17type PersonResolver func(context.Context, *sql.Conn, string, bool, time.Time) (string, bool, error)
18
19type OwnerInput struct {
20 InputBase
21 Directory string `json:"directory"`
22 TeamID int64 `json:"team_id"`
23 TeamSQID string `json:"team_sqid"`
24 Address string `json:"address"`
25 ExistingOnly bool `json:"existing_only"`
26}
27type Credential struct {
28 engine *Engine
29 directory string
30 signer *family.Keypair
31 resolve PersonResolver
32}
33
34func NewCredential(engine *Engine, directory string, signer *family.Keypair, browserPublicKeys [][]byte, resolve PersonResolver) (*Credential, error) {
35 if engine == nil || engine.verifier.pins.Role != "credential" || !Origin(directory) || directory == engine.verifier.pins.Origin || !signingKey(signer) || resolve == nil || len(browserPublicKeys) == 0 || len(browserPublicKeys) > 8 || bytes.Equal(signer.SignPub(), engine.verifier.key) {
36 return nil, ErrRefused
37 }
38 for _, key := range browserPublicKeys {
39 if _, e := public(base64.RawURLEncoding.EncodeToString(key)); e != nil {
40 return nil, ErrRefused
41 }
42 if bytes.Equal(key, signer.SignPub()) || bytes.Equal(key, engine.verifier.key) {
43 return nil, ErrRefused
44 }
45 }
46 return &Credential{engine, directory, signer, resolve}, nil
47}
48func (c *Credential) Execute(ctx context.Context, raw []byte) ([]byte, error) {
49 return c.engine.Execute(ctx, raw, func(ctx context.Context, conn *sql.Conn, r Request) ([]byte, error) {
50 var in OwnerInput
51 if DecodeInput(r, &in) != nil || !in.matches(r) || r.authorization.Action != "credential.owner-seed" || in.Directory != c.directory || !validTeam(in.TeamID, in.TeamSQID) || len(in.Address) == 0 || len(in.Address) > 320 || !utf8.ValidString(in.Address) {
52 return nil, ErrRefused
53 }
54 address, e := mail.ParseAddress(in.Address)
55 if e != nil || address.Address != in.Address || address.Name != "" {
56 return nil, ErrRefused
57 }
58 now := c.engine.now()
59 subject, found, e := c.resolve(ctx, conn, in.Address, in.ExistingOnly, now)
60 if e != nil {
61 return nil, ErrUnavailable
62 }
63 if !found || !PersonRef(subject) {
64 return nil, ErrRefused
65 }
66 receipt, e := SignOwner(c.signer, OwnerReceipt{1, "owner-seed", r.authorization.OperationID, r.authorization.Origin, c.directory, in.TeamID, in.TeamSQID, subject, "owner", now.Unix(), now.Unix() + 300})
67 if e != nil {
68 return nil, e
69 }
70 result := resultFor(r)
71 result.Receipt = receipt
72 return encode(result)
73 })
74}
75