190 lines
6.2 KiB
Go
190 lines
6.2 KiB
Go
// SPDX-License-Identifier: EUPL-1.2
|
|
|
|
package agentic
|
|
|
|
import (
|
|
"context"
|
|
|
|
core "dappco.re/go/core"
|
|
)
|
|
|
|
// key := agentic.AgentApiKey{ID: 7, Name: "codex local", Prefix: "ak_abcd", IPRestrictions: []string{"10.0.0.0/8"}, RateLimit: 60}
|
|
type AgentApiKey struct {
|
|
ID int `json:"id"`
|
|
WorkspaceID int `json:"workspace_id,omitempty"`
|
|
Name string `json:"name,omitempty"`
|
|
Key string `json:"key,omitempty"`
|
|
Prefix string `json:"prefix,omitempty"`
|
|
Permissions []string `json:"permissions,omitempty"`
|
|
IPRestrictions []string `json:"ip_restrictions,omitempty"`
|
|
RateLimit int `json:"rate_limit,omitempty"`
|
|
CallCount int `json:"call_count,omitempty"`
|
|
LastUsedAt string `json:"last_used_at,omitempty"`
|
|
ExpiresAt string `json:"expires_at,omitempty"`
|
|
RevokedAt string `json:"revoked_at,omitempty"`
|
|
CreatedAt string `json:"created_at,omitempty"`
|
|
}
|
|
|
|
// input := agentic.AuthProvisionInput{OAuthUserID: "user-42", Permissions: []string{"plans:read"}, IPRestrictions: []string{"10.0.0.0/8"}}
|
|
type AuthProvisionInput struct {
|
|
OAuthUserID string `json:"oauth_user_id"`
|
|
Name string `json:"name,omitempty"`
|
|
Permissions []string `json:"permissions,omitempty"`
|
|
IPRestrictions []string `json:"ip_restrictions,omitempty"`
|
|
RateLimit int `json:"rate_limit,omitempty"`
|
|
ExpiresAt string `json:"expires_at,omitempty"`
|
|
}
|
|
|
|
// out := agentic.AuthProvisionOutput{Success: true, Key: agentic.AgentApiKey{Prefix: "ak_abcd"}}
|
|
type AuthProvisionOutput struct {
|
|
Success bool `json:"success"`
|
|
Key AgentApiKey `json:"key"`
|
|
}
|
|
|
|
// input := agentic.AuthRevokeInput{KeyID: "7"}
|
|
type AuthRevokeInput struct {
|
|
KeyID string `json:"key_id"`
|
|
}
|
|
|
|
// out := agentic.AuthRevokeOutput{Success: true, KeyID: "7", Revoked: true}
|
|
type AuthRevokeOutput struct {
|
|
Success bool `json:"success"`
|
|
KeyID string `json:"key_id"`
|
|
Revoked bool `json:"revoked"`
|
|
}
|
|
|
|
// result := c.Action("agentic.auth.provision").Run(ctx, core.NewOptions(
|
|
//
|
|
// core.Option{Key: "oauth_user_id", Value: "user-42"},
|
|
// core.Option{Key: "permissions", Value: "plans:read,plans:write"},
|
|
//
|
|
// ))
|
|
func (s *PrepSubsystem) handleAuthProvision(ctx context.Context, options core.Options) core.Result {
|
|
input := AuthProvisionInput{
|
|
OAuthUserID: optionStringValue(options, "oauth_user_id", "oauth-user-id", "user_id", "user-id", "_arg"),
|
|
Name: optionStringValue(options, "name"),
|
|
Permissions: optionStringSliceValue(options, "permissions"),
|
|
IPRestrictions: optionStringSliceValue(options, "ip_restrictions", "ip-restrictions", "allowed_ips", "allowed-ips"),
|
|
RateLimit: optionIntValue(options, "rate_limit", "rate-limit"),
|
|
ExpiresAt: optionStringValue(options, "expires_at", "expires-at"),
|
|
}
|
|
if input.OAuthUserID == "" {
|
|
return core.Result{Value: core.E("agentic.auth.provision", "oauth_user_id is required", nil), OK: false}
|
|
}
|
|
|
|
body := map[string]any{
|
|
"oauth_user_id": input.OAuthUserID,
|
|
}
|
|
if input.Name != "" {
|
|
body["name"] = input.Name
|
|
}
|
|
if len(input.Permissions) > 0 {
|
|
body["permissions"] = input.Permissions
|
|
}
|
|
if len(input.IPRestrictions) > 0 {
|
|
body["ip_restrictions"] = input.IPRestrictions
|
|
}
|
|
if input.RateLimit > 0 {
|
|
body["rate_limit"] = input.RateLimit
|
|
}
|
|
if input.ExpiresAt != "" {
|
|
body["expires_at"] = input.ExpiresAt
|
|
}
|
|
|
|
result := s.platformPayload(ctx, "agentic.auth.provision", "POST", "/v1/agent/auth/provision", body)
|
|
if !result.OK {
|
|
return result
|
|
}
|
|
|
|
return core.Result{Value: AuthProvisionOutput{
|
|
Success: true,
|
|
Key: parseAgentApiKey(payloadResourceMap(result.Value.(map[string]any), "key", "api_key", "agent_api_key")),
|
|
}, OK: true}
|
|
}
|
|
|
|
// result := c.Action("agentic.auth.revoke").Run(ctx, core.NewOptions(core.Option{Key: "key_id", Value: "7"}))
|
|
func (s *PrepSubsystem) handleAuthRevoke(ctx context.Context, options core.Options) core.Result {
|
|
keyID := optionStringValue(options, "key_id", "key-id", "_arg")
|
|
if keyID == "" {
|
|
return core.Result{Value: core.E("agentic.auth.revoke", "key_id is required", nil), OK: false}
|
|
}
|
|
|
|
path := core.Concat("/v1/agent/auth/revoke/", keyID)
|
|
result := s.platformPayload(ctx, "agentic.auth.revoke", "DELETE", path, nil)
|
|
if !result.OK {
|
|
return result
|
|
}
|
|
|
|
output := AuthRevokeOutput{
|
|
Success: true,
|
|
KeyID: keyID,
|
|
Revoked: true,
|
|
}
|
|
|
|
payload, ok := result.Value.(map[string]any)
|
|
if !ok {
|
|
return core.Result{Value: output, OK: true}
|
|
}
|
|
|
|
if data := payloadResourceMap(payload, "result", "revocation"); len(data) > 0 {
|
|
if value := stringValue(data["key_id"]); value != "" {
|
|
output.KeyID = value
|
|
}
|
|
if value, ok := boolValueOK(data["revoked"]); ok {
|
|
output.Revoked = value
|
|
}
|
|
if value, ok := boolValueOK(data["success"]); ok {
|
|
output.Success = value
|
|
}
|
|
return core.Result{Value: output, OK: output.Success && output.Revoked}
|
|
}
|
|
|
|
if data, exists := payload["data"]; exists {
|
|
if value, ok := boolValueOK(data); ok {
|
|
output.Revoked = value
|
|
return core.Result{Value: output, OK: output.Success && output.Revoked}
|
|
}
|
|
}
|
|
|
|
return core.Result{Value: output, OK: true}
|
|
}
|
|
|
|
func parseAgentApiKey(values map[string]any) AgentApiKey {
|
|
return AgentApiKey{
|
|
ID: intValue(values["id"]),
|
|
WorkspaceID: intValue(values["workspace_id"]),
|
|
Name: stringValue(values["name"]),
|
|
Key: stringValue(values["key"]),
|
|
Prefix: stringValue(values["prefix"]),
|
|
Permissions: listValue(values["permissions"]),
|
|
IPRestrictions: listValue(values["ip_restrictions"]),
|
|
RateLimit: intValue(values["rate_limit"]),
|
|
CallCount: intValue(values["call_count"]),
|
|
LastUsedAt: stringValue(values["last_used_at"]),
|
|
ExpiresAt: stringValue(values["expires_at"]),
|
|
RevokedAt: stringValue(values["revoked_at"]),
|
|
CreatedAt: stringValue(values["created_at"]),
|
|
}
|
|
}
|
|
|
|
func boolValueOK(value any) (bool, bool) {
|
|
switch typed := value.(type) {
|
|
case bool:
|
|
return typed, true
|
|
case string:
|
|
trimmed := core.Lower(core.Trim(typed))
|
|
switch trimmed {
|
|
case "true", "1", "yes":
|
|
return true, true
|
|
case "false", "0", "no":
|
|
return false, true
|
|
}
|
|
case int:
|
|
return typed != 0, true
|
|
case int64:
|
|
return typed != 0, true
|
|
case float64:
|
|
return typed != 0, true
|
|
}
|
|
return false, false
|
|
}
|