412 lines
12 KiB
Go
412 lines
12 KiB
Go
package keybase
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
type HandlerFunc func(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error)
|
|
|
|
type resourceHandlers map[string]HandlerFunc
|
|
|
|
var handlers = map[string]resourceHandlers{
|
|
"accounts": {
|
|
"list": handleAccountList,
|
|
"get": handleAccountGet,
|
|
"sign": handleAccountSign,
|
|
},
|
|
"credentials": {
|
|
"list": handleCredentialList,
|
|
"get": handleCredentialGet,
|
|
},
|
|
"sessions": {
|
|
"list": handleSessionList,
|
|
"revoke": handleSessionRevoke,
|
|
},
|
|
"grants": {
|
|
"list": handleGrantList,
|
|
"revoke": handleGrantRevoke,
|
|
},
|
|
"enclaves": {
|
|
"list": handleEnclaveList,
|
|
"get": handleEnclaveGet,
|
|
"sign": handleEnclaveSign,
|
|
"rotate": handleEnclaveRotate,
|
|
"archive": handleEnclaveArchive,
|
|
"delete": handleEnclaveDelete,
|
|
},
|
|
"delegations": {
|
|
"list": handleDelegationList,
|
|
"list_received": handleDelegationListReceived,
|
|
"list_command": handleDelegationListCommand,
|
|
"get": handleDelegationGet,
|
|
"revoke": handleDelegationRevoke,
|
|
"verify": handleDelegationVerify,
|
|
"cleanup": handleDelegationCleanup,
|
|
},
|
|
"ucans": {
|
|
"list": handleDelegationList,
|
|
"get": handleDelegationGet,
|
|
"revoke": handleDelegationRevoke,
|
|
"verify": handleDelegationVerify,
|
|
"cleanup": handleDelegationCleanup,
|
|
},
|
|
"verification_methods": {
|
|
"list": handleVerificationMethodList,
|
|
"get": handleVerificationMethodGet,
|
|
"delete": handleVerificationMethodDelete,
|
|
},
|
|
"services": {
|
|
"list": handleServiceList,
|
|
"get": handleServiceGet,
|
|
"get_by_id": handleServiceGetByID,
|
|
},
|
|
}
|
|
|
|
func Exec(ctx context.Context, resource, action, subject string) (json.RawMessage, error) {
|
|
am, err := NewActionManager()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("action manager: %w", err)
|
|
}
|
|
|
|
resHandlers, ok := handlers[resource]
|
|
if !ok {
|
|
return nil, fmt.Errorf("unknown resource: %s", resource)
|
|
}
|
|
|
|
handler, ok := resHandlers[action]
|
|
if !ok {
|
|
return nil, fmt.Errorf("unknown action for %s: %s", resource, action)
|
|
}
|
|
|
|
return handler(ctx, am, subject)
|
|
}
|
|
|
|
func handleAccountList(ctx context.Context, am *ActionManager, _ string) (json.RawMessage, error) {
|
|
accounts, err := am.ListAccounts(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(accounts)
|
|
}
|
|
|
|
func handleAccountGet(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
|
|
if subject == "" {
|
|
return nil, errors.New("subject (address) required for get action")
|
|
}
|
|
account, err := am.GetAccountByAddress(ctx, subject)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(account)
|
|
}
|
|
|
|
func handleAccountSign(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
|
|
if subject == "" {
|
|
return nil, errors.New("subject (hex-encoded data) required for sign action")
|
|
}
|
|
|
|
enclaves, err := am.ListEnclaves(ctx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("list enclaves: %w", err)
|
|
}
|
|
if len(enclaves) == 0 {
|
|
return nil, errors.New("no enclave available for signing")
|
|
}
|
|
|
|
enc := enclaves[0]
|
|
signature, err := am.SignWithEnclave(ctx, enc.EnclaveID, []byte(subject))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("sign: %w", err)
|
|
}
|
|
|
|
return json.Marshal(map[string]string{
|
|
"signature": fmt.Sprintf("%x", signature),
|
|
"enclave_id": enc.EnclaveID,
|
|
"public_key": enc.PublicKeyHex,
|
|
})
|
|
}
|
|
|
|
func handleCredentialList(ctx context.Context, am *ActionManager, _ string) (json.RawMessage, error) {
|
|
credentials, err := am.ListCredentials(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(credentials)
|
|
}
|
|
|
|
func handleCredentialGet(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
|
|
if subject == "" {
|
|
return nil, errors.New("subject (credential_id) required for get action")
|
|
}
|
|
credential, err := am.GetCredentialByID(ctx, subject)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(credential)
|
|
}
|
|
|
|
func handleSessionList(ctx context.Context, am *ActionManager, _ string) (json.RawMessage, error) {
|
|
sessions, err := am.ListSessions(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(sessions)
|
|
}
|
|
|
|
func handleSessionRevoke(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
|
|
if subject == "" {
|
|
return nil, errors.New("subject (session_id) required for revoke action")
|
|
}
|
|
if err := am.RevokeSession(ctx, subject); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(map[string]bool{"revoked": true})
|
|
}
|
|
|
|
func handleGrantList(ctx context.Context, am *ActionManager, _ string) (json.RawMessage, error) {
|
|
grants, err := am.ListGrants(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(grants)
|
|
}
|
|
|
|
func handleGrantRevoke(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
|
|
if subject == "" {
|
|
return nil, errors.New("subject (grant_id) required for revoke action")
|
|
}
|
|
var grantID int64
|
|
if _, err := fmt.Sscanf(subject, "%d", &grantID); err != nil {
|
|
return nil, fmt.Errorf("invalid grant_id: %w", err)
|
|
}
|
|
if err := am.RevokeGrant(ctx, grantID); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(map[string]bool{"revoked": true})
|
|
}
|
|
|
|
func handleEnclaveList(ctx context.Context, am *ActionManager, _ string) (json.RawMessage, error) {
|
|
enclaves, err := am.ListEnclaves(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(enclaves)
|
|
}
|
|
|
|
func handleEnclaveGet(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
|
|
if subject == "" {
|
|
return nil, errors.New("subject (enclave_id) required for get action")
|
|
}
|
|
enc, err := am.GetEnclaveByID(ctx, subject)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(enc)
|
|
}
|
|
|
|
func handleEnclaveSign(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
|
|
if subject == "" {
|
|
return nil, errors.New("subject (enclave_id:hex_data) required for sign action")
|
|
}
|
|
|
|
parts := strings.SplitN(subject, ":", 2)
|
|
if len(parts) != 2 {
|
|
return nil, errors.New("subject must be enclave_id:hex_data format")
|
|
}
|
|
|
|
enclaveID := parts[0]
|
|
data := []byte(parts[1])
|
|
|
|
signature, err := am.SignWithEnclave(ctx, enclaveID, data)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
enc, _ := am.GetEnclaveByID(ctx, enclaveID)
|
|
pubKeyHex := ""
|
|
if enc != nil {
|
|
pubKeyHex = enc.PublicKeyHex
|
|
}
|
|
|
|
return json.Marshal(map[string]string{
|
|
"signature": fmt.Sprintf("%x", signature),
|
|
"enclave_id": enclaveID,
|
|
"public_key": pubKeyHex,
|
|
})
|
|
}
|
|
|
|
func handleEnclaveRotate(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
|
|
if subject == "" {
|
|
return nil, errors.New("subject (enclave_id) required for rotate action")
|
|
}
|
|
if err := am.RotateEnclave(ctx, subject); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(map[string]bool{"rotated": true})
|
|
}
|
|
|
|
func handleEnclaveArchive(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
|
|
if subject == "" {
|
|
return nil, errors.New("subject (enclave_id) required for archive action")
|
|
}
|
|
if err := am.ArchiveEnclave(ctx, subject); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(map[string]bool{"archived": true})
|
|
}
|
|
|
|
func handleEnclaveDelete(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
|
|
if subject == "" {
|
|
return nil, errors.New("subject (enclave_id) required for delete action")
|
|
}
|
|
if err := am.DeleteEnclave(ctx, subject); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(map[string]bool{"deleted": true})
|
|
}
|
|
|
|
func handleDelegationList(ctx context.Context, am *ActionManager, _ string) (json.RawMessage, error) {
|
|
delegations, err := am.ListDelegations(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(delegations)
|
|
}
|
|
|
|
func handleDelegationListReceived(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
|
|
if subject == "" {
|
|
return nil, errors.New("subject (audience DID) required for list_received action")
|
|
}
|
|
delegations, err := am.ListDelegationsByAudience(ctx, subject)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(delegations)
|
|
}
|
|
|
|
func handleDelegationListCommand(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
|
|
if subject == "" {
|
|
return nil, errors.New("subject (command) required for list_command action")
|
|
}
|
|
delegations, err := am.ListDelegationsForCommand(ctx, subject)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(delegations)
|
|
}
|
|
|
|
func handleDelegationGet(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
|
|
if subject == "" {
|
|
return nil, errors.New("subject (cid) required for get action")
|
|
}
|
|
delegation, err := am.GetDelegationByCID(ctx, subject)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(delegation)
|
|
}
|
|
|
|
func handleDelegationRevoke(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
|
|
if subject == "" {
|
|
return nil, errors.New("subject (cid) required for revoke action")
|
|
}
|
|
kb := Get()
|
|
revokedBy := ""
|
|
if kb != nil {
|
|
revokedBy = kb.DID()
|
|
}
|
|
if err := am.RevokeDelegation(ctx, RevokeDelegationParams{
|
|
DelegationCID: subject,
|
|
RevokedBy: revokedBy,
|
|
Reason: "user revoked",
|
|
}); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(map[string]bool{"revoked": true})
|
|
}
|
|
|
|
func handleDelegationVerify(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
|
|
if subject == "" {
|
|
return nil, errors.New("subject (cid) required for verify action")
|
|
}
|
|
revoked, err := am.IsDelegationRevoked(ctx, subject)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(map[string]bool{"valid": !revoked, "revoked": revoked})
|
|
}
|
|
|
|
func handleDelegationCleanup(ctx context.Context, am *ActionManager, _ string) (json.RawMessage, error) {
|
|
if err := am.CleanExpiredDelegations(ctx); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(map[string]bool{"cleaned": true})
|
|
}
|
|
|
|
func handleVerificationMethodList(ctx context.Context, am *ActionManager, _ string) (json.RawMessage, error) {
|
|
vms, err := am.ListVerificationMethodsFull(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(vms)
|
|
}
|
|
|
|
func handleVerificationMethodGet(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
|
|
if subject == "" {
|
|
return nil, errors.New("subject (method_id) required for get action")
|
|
}
|
|
vm, err := am.GetVerificationMethod(ctx, subject)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(vm)
|
|
}
|
|
|
|
func handleVerificationMethodDelete(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
|
|
if subject == "" {
|
|
return nil, errors.New("subject (method_id) required for delete action")
|
|
}
|
|
if err := am.DeleteVerificationMethod(ctx, subject); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(map[string]bool{"deleted": true})
|
|
}
|
|
|
|
func handleServiceList(ctx context.Context, am *ActionManager, _ string) (json.RawMessage, error) {
|
|
services, err := am.ListVerifiedServices(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(services)
|
|
}
|
|
|
|
func handleServiceGet(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
|
|
if subject == "" {
|
|
return nil, errors.New("subject (origin) required for get action")
|
|
}
|
|
svc, err := am.GetServiceByOrigin(ctx, subject)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(svc)
|
|
}
|
|
|
|
func handleServiceGetByID(ctx context.Context, am *ActionManager, subject string) (json.RawMessage, error) {
|
|
if subject == "" {
|
|
return nil, errors.New("subject (service_id) required for get_by_id action")
|
|
}
|
|
var serviceID int64
|
|
if _, err := fmt.Sscanf(subject, "%d", &serviceID); err != nil {
|
|
return nil, fmt.Errorf("invalid service_id: %w", err)
|
|
}
|
|
svc, err := am.GetServiceByID(ctx, serviceID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(svc)
|
|
}
|