70 lines
1.4 KiB
Go
70 lines
1.4 KiB
Go
package elevate
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
// Resolution is the outcome of a pending request.
|
|
type Resolution int
|
|
|
|
const (
|
|
ResolvePending Resolution = iota
|
|
ResolveApprove
|
|
ResolveDeny
|
|
ResolvePersist // approve + add to session policy
|
|
ResolveTimeout
|
|
)
|
|
|
|
func (r Resolution) String() string {
|
|
switch r {
|
|
case ResolveApprove:
|
|
return "approve"
|
|
case ResolveDeny:
|
|
return "deny"
|
|
case ResolvePersist:
|
|
return "persist"
|
|
case ResolveTimeout:
|
|
return "timeout"
|
|
default:
|
|
return "pending"
|
|
}
|
|
}
|
|
|
|
// Request represents a pending elevation request.
|
|
type Request struct {
|
|
ID string
|
|
Cmd string
|
|
Cwd string
|
|
Env map[string]string
|
|
SessionID string // ollie session that made the request (from SO_PEERCRED mapping)
|
|
CreatedAt time.Time
|
|
|
|
// Resolution channel — exactly one value sent when resolved.
|
|
resolved chan Resolution
|
|
conn net.Conn // the elevate client connection (held open until resolved)
|
|
}
|
|
|
|
// Resolved returns the channel that receives the resolution.
|
|
func (r *Request) Resolved() <-chan Resolution {
|
|
return r.resolved
|
|
}
|
|
|
|
// Summary returns a human-readable description for notifications.
|
|
func (r *Request) Summary() string {
|
|
sess := r.SessionID
|
|
if sess == "" {
|
|
sess = "unknown"
|
|
}
|
|
return fmt.Sprintf("[%s] %s\ncwd: %s", sess, r.Cmd, r.Cwd)
|
|
}
|
|
|
|
var requestCounter atomic.Uint64
|
|
|
|
func nextRequestID() string {
|
|
n := requestCounter.Add(1)
|
|
return fmt.Sprintf("%d", n)
|
|
}
|