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) }