Files
motr-enclave/internal/keybase/actions_ucan.go

206 lines
5.1 KiB
Go

package keybase
import (
"context"
"encoding/json"
"fmt"
)
type UCANResult struct {
ID int64 `json:"id"`
CID string `json:"cid"`
Issuer string `json:"issuer"`
Audience string `json:"audience"`
Subject string `json:"subject,omitempty"`
Capabilities json.RawMessage `json:"capabilities"`
NotBefore string `json:"not_before,omitempty"`
ExpiresAt string `json:"expires_at"`
IsRevoked bool `json:"is_revoked"`
CreatedAt string `json:"created_at"`
}
type NewUCANInput struct {
CID string `json:"cid"`
Issuer string `json:"issuer"`
Audience string `json:"audience"`
Subject string `json:"subject,omitempty"`
Capabilities json.RawMessage `json:"capabilities"`
ProofChain json.RawMessage `json:"proof_chain,omitempty"`
NotBefore string `json:"not_before,omitempty"`
ExpiresAt string `json:"expires_at"`
Nonce string `json:"nonce,omitempty"`
Facts json.RawMessage `json:"facts,omitempty"`
Signature string `json:"signature"`
RawToken string `json:"raw_token"`
}
func (am *ActionManager) CreateUCAN(ctx context.Context, params NewUCANInput) (*UCANResult, error) {
am.kb.mu.Lock()
defer am.kb.mu.Unlock()
if am.kb.didID == 0 {
return nil, fmt.Errorf("DID not initialized")
}
var subject, notBefore, nonce *string
if params.Subject != "" {
subject = &params.Subject
}
if params.NotBefore != "" {
notBefore = &params.NotBefore
}
if params.Nonce != "" {
nonce = &params.Nonce
}
proofChain := params.ProofChain
if proofChain == nil {
proofChain = json.RawMessage(`[]`)
}
facts := params.Facts
if facts == nil {
facts = json.RawMessage(`{}`)
}
ucan, err := am.kb.queries.CreateUCAN(ctx, CreateUCANParams{
DidID: am.kb.didID,
Cid: params.CID,
Issuer: params.Issuer,
Audience: params.Audience,
Subject: subject,
Capabilities: params.Capabilities,
ProofChain: proofChain,
NotBefore: notBefore,
ExpiresAt: params.ExpiresAt,
Nonce: nonce,
Facts: facts,
Signature: params.Signature,
RawToken: params.RawToken,
})
if err != nil {
return nil, fmt.Errorf("create ucan: %w", err)
}
return ucanToResult(&ucan), nil
}
func (am *ActionManager) ListUCANs(ctx context.Context) ([]UCANResult, error) {
am.kb.mu.RLock()
defer am.kb.mu.RUnlock()
if am.kb.didID == 0 {
return []UCANResult{}, nil
}
ucans, err := am.kb.queries.ListUCANsByDID(ctx, am.kb.didID)
if err != nil {
return nil, fmt.Errorf("list ucans: %w", err)
}
results := make([]UCANResult, len(ucans))
for i, u := range ucans {
results[i] = *ucanToResult(&u)
}
return results, nil
}
func (am *ActionManager) GetUCANByCID(ctx context.Context, cid string) (*UCANResult, error) {
am.kb.mu.RLock()
defer am.kb.mu.RUnlock()
ucan, err := am.kb.queries.GetUCANByCID(ctx, cid)
if err != nil {
return nil, fmt.Errorf("get ucan: %w", err)
}
return ucanToResult(&ucan), nil
}
func (am *ActionManager) ListUCANsByAudience(ctx context.Context, audience string) ([]UCANResult, error) {
am.kb.mu.RLock()
defer am.kb.mu.RUnlock()
ucans, err := am.kb.queries.ListUCANsByAudience(ctx, audience)
if err != nil {
return nil, fmt.Errorf("list ucans by audience: %w", err)
}
results := make([]UCANResult, len(ucans))
for i, u := range ucans {
results[i] = *ucanToResult(&u)
}
return results, nil
}
func (am *ActionManager) RevokeUCAN(ctx context.Context, cid string) error {
am.kb.mu.Lock()
defer am.kb.mu.Unlock()
return am.kb.queries.RevokeUCAN(ctx, cid)
}
func (am *ActionManager) IsUCANRevoked(ctx context.Context, cid string) (bool, error) {
am.kb.mu.RLock()
defer am.kb.mu.RUnlock()
revoked, err := am.kb.queries.IsUCANRevoked(ctx, cid)
if err != nil {
return false, fmt.Errorf("check ucan revocation: %w", err)
}
return revoked == 1, nil
}
func (am *ActionManager) CreateRevocation(ctx context.Context, ucanCID string, revokedBy string, reason string) error {
am.kb.mu.Lock()
defer am.kb.mu.Unlock()
var reasonPtr *string
if reason != "" {
reasonPtr = &reason
}
if err := am.kb.queries.RevokeUCAN(ctx, ucanCID); err != nil {
return fmt.Errorf("revoke ucan token: %w", err)
}
return am.kb.queries.CreateRevocation(ctx, CreateRevocationParams{
UcanCid: ucanCID,
RevokedBy: revokedBy,
Reason: reasonPtr,
})
}
func (am *ActionManager) CleanExpiredUCANs(ctx context.Context) error {
am.kb.mu.Lock()
defer am.kb.mu.Unlock()
return am.kb.queries.CleanExpiredUCANs(ctx)
}
func ucanToResult(u *UcanToken) *UCANResult {
subject := ""
if u.Subject != nil {
subject = *u.Subject
}
notBefore := ""
if u.NotBefore != nil {
notBefore = *u.NotBefore
}
return &UCANResult{
ID: u.ID,
CID: u.Cid,
Issuer: u.Issuer,
Audience: u.Audience,
Subject: subject,
Capabilities: u.Capabilities,
NotBefore: notBefore,
ExpiresAt: u.ExpiresAt,
IsRevoked: u.IsRevoked == 1,
CreatedAt: u.CreatedAt,
}
}