diff --git a/server.go b/server.go index 44545fe..b3d0732 100644 --- a/server.go +++ b/server.go @@ -279,6 +279,8 @@ func New(sink *olog.Sink, opts ...ServerOption) *Server { return storeWrite(s.transcriptStore, name, data) }, OnRename: func(oldID, newID string) { + s.RemoveGroup("agent", oldID) + s.AddGroup("agent", newID) oldPrefix := "/s/" + oldID newPrefix := "/s/" + newID for _, c := range s.conns { @@ -326,15 +328,14 @@ func (s *Server) InGroup(group, user string) bool { } // fileOwnerGroup returns the uid and gid for a given path. -// Session files are owned by the session ID with group "agent". +// Session files are owned by the session's principal with group "agent". // Everything else is owned by "ollie" with group "ollie". func (s *Server) fileOwnerGroup(path string) (uid, gid string) { if strings.HasPrefix(path, "/s/") { parts := strings.SplitN(strings.TrimPrefix(path, "/s/"), "/", 2) if len(parts) >= 1 && parts[0] != "new" { - // Check if parts[0] is a session (not a script like sh, bfg, etc.) if sess := s.sessionStore.Session(parts[0]); sess != nil { - return parts[0], "agent" + return sess.Uname(), "agent" } } } @@ -771,9 +772,9 @@ func (s *Server) attach(cs *connState, fc *plan9.Fcall) *plan9.Fcall { qid := plan9.Qid{Type: QTDir, Path: 0} cs.fids[fc.Fid] = &fid{path: "/", qid: qid} s.log.Debug("Tattach uname=%q", fc.Uname) - // Assign group membership based on whether uname is a session ID. + // Assign group membership based on whether uname is a session principal. if fc.Uname != "" { - if sess := s.sessionStore.Session(fc.Uname); sess != nil { + if s.sessionStore.SessionByUname(fc.Uname) != nil { s.AddGroup("agent", fc.Uname) } else { s.AddGroup("user", fc.Uname) diff --git a/store/session.go b/store/session.go index c7ef325..87a0a61 100644 --- a/store/session.go +++ b/store/session.go @@ -7,6 +7,7 @@ import ( "sort" "strings" "sync" + "sync/atomic" "ollie/pkg/agent" "ollie/pkg/backend" @@ -21,6 +22,7 @@ import ( type Session struct { mu sync.RWMutex id string + uname string // immutable user principal (numeric UID), set at creation Core agent.Core Ctx context.Context cancel context.CancelFunc @@ -37,6 +39,7 @@ func NewSession(id string, core agent.Core, ctx context.Context, cancel context. } func (sess *Session) RunnableID() string { return sess.id } +func (sess *Session) Uname() string { return sess.uname } func (sess *Session) Cancel() { sess.cancel() @@ -120,9 +123,15 @@ type SessionStoreConfig struct { // SessionStore implements Store for session management. type SessionStore struct { *storeConfig - cfg SessionStoreConfig - mu sync.RWMutex - sessions map[string]*Session + cfg SessionStoreConfig + mu sync.RWMutex + sessions map[string]*Session + nextUID atomic.Uint32 // incrementing principal counter +} + +// nextUname generates the next uname atomically. +func (s *SessionStore) nextUname() string { + return fmt.Sprintf("%d", s.nextUID.Add(1)) } func NewSessionStore(cfg SessionStoreConfig) *SessionStore { @@ -136,6 +145,7 @@ func NewSessionStore(cfg SessionStoreConfig) *SessionStore { cfg: cfg, sessions: make(map[string]*Session), } + ss.nextUID.Store(9999) ss.storeConfig = &storeConfig{ StatFn: ss.stat, ListFn: ss.list, @@ -236,6 +246,18 @@ func (s *SessionStore) Session(id string) *Session { return s.sessions[id] } +// SessionByUname returns the session with the given uname (principal), or nil. +func (s *SessionStore) SessionByUname(uname string) *Session { + s.mu.RLock() + defer s.mu.RUnlock() + for _, sess := range s.sessions { + if sess.uname == uname { + return sess + } + } + return nil +} + // OpenStore returns a RunnableStore for the given session ID. func (s *SessionStore) OpenStore(id string) (RunnableStore, error) { if sess := s.Session(id); sess != nil { @@ -358,6 +380,7 @@ func (s *SessionStore) createSession(args []string) error { var core agent.Core var allowTools []string + uname := s.nextUname() if s.cfg.NewCore != nil { var err error core, err = s.cfg.NewCore(sessID, agentName, cwd) @@ -393,6 +416,7 @@ func (s *SessionStore) createSession(args []string) error { } var execOpts []execute.Option + execOpts = append(execOpts, execute.WithMount()) if s.cfg.Strict { execOpts = append(execOpts, execute.WithStrict()) } @@ -409,7 +433,7 @@ func (s *SessionStore) createSession(args []string) error { "execute": execute.Decl(cwd, execOpts...), }) - env := agent.BuildAgentEnv(cfg, newDisp(), cwd, []string{"OLLIE_SESSION_ID=" + sessID}) + env := agent.BuildAgentEnv(cfg, newDisp(), cwd, []string{"OLLIE_SESSION_ID=" + sessID, "OLLIE_UNAME=" + uname}) core = agent.NewAgentCore(agent.AgentCoreConfig{ Backend: be, @@ -417,6 +441,7 @@ func (s *SessionStore) createSession(args []string) error { AgentsDir: s.cfg.AgentsDir, SessionsDir: s.cfg.SessionsDir, SessionID: sessID, + Uname: uname, CWD: cwd, Env: env, NewDispatcher: newDisp, @@ -429,6 +454,7 @@ func (s *SessionStore) createSession(args []string) error { sess.AllowTools = allowTools s.mu.Lock() + sess.uname = uname s.sessions[sessID] = sess s.mu.Unlock()