- commit
- 48dadd1795339ccf4ed7ba616053d65fae17c399
- parent
- 6f435aa87994c6b89a4e470bdf26587c7703f097
- author
- Dami <dami.etoile@gmail.com>
| M | internal/ | 53 | ++++++++++++++++++++++++++++++ |
| M | internal/ | 39 | ++++++++++++++++++++++- |
@@ -255,3 +255,56 @@ func TestAgentManagementMethodsAndPermissions(t *testing.T) {
255255 }256256 }257257 }258+259+func TestBrowseAgentReturnsGrantAndRecentEventsForOperators(t *testing.T) {260+ _, tenant := testTenant(t)261+ ctx := context.Background()262+ owner := tenant.Policy().Owner263+ agent, _ := event.PublicKey(testAgentSecret)264+ member, _ := event.PublicKey(testMemberSecret)265+ moderator, _ := event.PublicKey(testModSecret)266+ now := time.Now().Unix()267+ for _, target := range []struct {268+ pubkey, role string269+ }{{member, "member"}, {moderator, "moderator"}} {270+ raw, _ := json.Marshal(map[string]string{"role": target.role})271+ if _, err := tenant.Execute(ctx, owner, "setmember", []json.RawMessage{json.RawMessage(strconv.Quote(target.pubkey)), raw}); err != nil {272+ t.Fatal(err)273+ }274+ }275+ if err := publishAs(t, tenant, agentGrantEvent(t, agent, now, now+3600, []string{"k", "1"}, []string{"room", "build"})); err != nil {276+ t.Fatal(err)277+ }278+ for i := int64(0); i < 12; i++ {279+ if err := publishAs(t, tenant, signedEvent(t, testAgentSecret, 1, now-20+i, nil, "note "+strconv.FormatInt(i, 10))); err != nil {280+ t.Fatal(err)281+ }282+ }283+ params := []json.RawMessage{json.RawMessage(`{"agent":"` + agent + `"}`)}284+ for _, actor := range []string{member, agent, ""} {285+ if _, err := tenant.Execute(ctx, actor, "browseagent", params); err == nil || !strings.HasPrefix(err.Error(), "restricted:") {286+ t.Fatalf("browseagent by %q: %v", actor, err)287+ }288+ }289+ for _, actor := range []string{owner, moderator} {290+ result, err := tenant.Execute(ctx, actor, "browseagent", params)291+ if err != nil {292+ t.Fatalf("browseagent by %s: %v", actor, err)293+ }294+ detail := result.(map[string]any)295+ summary := detail["agent"].(community.AgentSummary)296+ events := detail["events"].([]event.Event)297+ if summary.Agent != agent || summary.Name != "helper" || summary.Scope.Rooms[0] != "build" || summary.LastEvent != now-9 {298+ t.Fatalf("browseagent summary = %+v", summary)299+ }300+ if len(events) != 10 || events[0].CreatedAt != now-9 || events[9].CreatedAt != now-18 {301+ t.Fatalf("browseagent events = %d newest %d", len(events), events[0].CreatedAt)302+ }303+ }304+ if _, err := tenant.Execute(ctx, owner, "browseagent", []json.RawMessage{json.RawMessage(`{"agent":"` + strings.Repeat("e", 64) + `"}`)}); err == nil || !strings.HasPrefix(err.Error(), "not found:") {305+ t.Fatalf("unknown agent: %v", err)306+ }307+ if _, err := tenant.Execute(ctx, owner, "browseagent", []json.RawMessage{json.RawMessage(`{"agent":"nope"}`)}); err == nil || !strings.HasPrefix(err.Error(), "invalid:") {308+ t.Fatalf("bad agent key: %v", err)309+ }310+}@@ -10,12 +10,15 @@ import (
1010 "net/url"1111 "sort"1212 "strings"13+ "time"1314 "unicode/utf8"1415 1516 "github.com/FelineStateMachine/tinyrelay/internal/blob"17+ "github.com/FelineStateMachine/tinyrelay/internal/community"1618 "github.com/FelineStateMachine/tinyrelay/internal/event"1719 "github.com/FelineStateMachine/tinyrelay/internal/gitrelay"1820 "github.com/FelineStateMachine/tinyrelay/internal/relay"21+ "github.com/FelineStateMachine/tinyrelay/internal/storage"1922 )2023 2124 type clientBrowseRequest struct {@@ -27,10 +30,11 @@ type clientBrowseRequest struct {
2730 ID string `json:"id"`2831 State string `json:"state"`2932 Label string `json:"label"`33+ Agent string `json:"agent"`3034 }3135 3236 func clientBrowseMethod(method string) bool {33- return wikiBrowseMethod(method) || containsString([]string{"browserepos", "browserepo", "browsefiles", "browsefile", "browsestatus", "browseissues", "browsepulls", "browseissue", "browsepull"}, method)37+ return wikiBrowseMethod(method) || containsString([]string{"browserepos", "browserepo", "browsefiles", "browsefile", "browsestatus", "browseissues", "browsepulls", "browseissue", "browsepull", "browseagent"}, method)3438 }3539 3640 func (t *Tenant) browseRead(ctx context.Context, actor string) error {@@ -77,6 +81,8 @@ func (t *Tenant) executeBrowse(ctx context.Context, actor, method string, params
7781 return t.browseFile(ctx, actor, q.Hash)7882 case "browsestatus":7983 return t.browseStatus(ctx, actor)84+ case "browseagent":85+ return t.browseAgent(ctx, actor, q.Agent)8086 case "browseissues":8187 return t.browseCollaboration(ctx, actor, q, event.KIND_GIT_ISSUE)8288 case "browsepulls":@@ -356,6 +362,37 @@ func (t *Tenant) browseFile(ctx context.Context, actor, hash string) (any, error
356362 return result, nil357363 }358364 365+// browseAgent returns one agent grant with the agent's newest events for the366+// Manage > Agents page. Like listagents, it is for the owner and moderators.367+func (t *Tenant) browseAgent(ctx context.Context, actor, agent string) (any, error) {368+ role, err := t.community.Role(ctx, actor)369+ if err != nil {370+ return nil, err371+ }372+ if role != "owner" && role != "moderator" {373+ return nil, errors.New("restricted: agent details")374+ }375+ if len(agent) != 64 || strings.Trim(agent, "0123456789abcdef") != "" {376+ return nil, errors.New("invalid: agent public key required")377+ }378+ grant, ok, err := t.community.AgentGrant(ctx, agent)379+ if err != nil {380+ return nil, err381+ }382+ if !ok {383+ return nil, errors.New("not found: no agent grant for this pubkey")384+ }385+ rows, err := t.store.Query(ctx, event.Filter{Authors: []string{agent}, Tags: map[string][]string{}}, storage.QueryOptions{Now: time.Now().Unix(), Access: storage.Access{All: true}, Limit: 10})386+ if err != nil {387+ return nil, err388+ }389+ summary := community.AgentSummary{AgentGrant: grant}390+ if len(rows.Events) > 0 {391+ summary.LastEvent = rows.Events[0].CreatedAt392+ }393+ return map[string]any{"agent": summary, "events": rows.Events}, nil394+}395+359396 func (t *Tenant) browseStatus(ctx context.Context, actor string) (any, error) {360397 role, err := t.community.Role(ctx, actor)361398 if err != nil {