Commit Graph

36 Commits

Author SHA1 Message Date
Alphaeus Mote 63701dd086 feat: real restore, portable secret key, multi-arch image, real CSRF
Addresses the gaps identified in the last audit.

Restore (was a stub returning "not yet implemented"). Every repository shares
one connection pool, so the database cannot be swapped underneath a live
server. Restore is therefore two-phase: RestoreBackup validates the file and
stages it beside the database; db.New applies it before the pool is opened,
which is the only safe moment. The database being replaced is preserved as
<db>.replaced-<timestamp>, and stale -wal/-shm are removed so SQLite cannot
replay the old journal over the restored file. Validation is strict — SQLite
integrity_check plus a schema probe — because applying an unrelated file
would destroy the install. GET/DELETE /api/v1/backups/restore inspect and
cancel a staged restore. The CLI does both phases at once, since it runs
standalone; `orchestrad backup` was also a stub and now works.

Secret key. With nothing configured the key is generated once and persisted
to <data>/secret.key, so restarts reuse it and moving the stack to another
server is a matter of copying the data directory. Upgrades are handled: if a
database already exists the install was silently running on the legacy
built-in default, so that value is adopted and written out rather than
replaced — generating a fresh key there would make every stored credential
undecryptable. The file is owner-only (ACL-restricted on Windows).

Multi-arch image: buildx now emits linux/amd64 + linux/arm64, matching the
architectures the release binaries already covered. The Dockerfile
cross-compiles via TARGETARCH rather than emulating, so arm64 costs little.

CSRF: the middleware previously checked only that a header was *present* and
was never wired up, and /auth/csrf returned "csrf-token-placeholder". Tokens
are now nonce + HMAC-SHA256 signed with the application secret, validated
properly, and the middleware is mounted on /api/v1. Bearer and API-key
requests are not CSRF-reachable and pass through untouched, so this is
transparent to the SPA and to API clients.

Also: the Windows store import drops CRYPT_EXPORTABLE (the store copy is not
the source of truth — <data>/tls holds the key, so portability is unaffected
and a non-exportable server key is the better posture), the PFX password is
written to server.pfx.password beside the bundle so an operator importing it
by hand does not have to hunt for a password they never chose, and the
"renewed" log line now reflects whether a leaf was actually issued instead of
guessing from its age.

Verified live: backup -> stage -> restart applies and preserves the previous
database; secret key generated, adopted, and read back across restarts with
the credential check confirming decryptability; CSRF endpoint issues real
signed tokens.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-03 13:57:15 -04:00
Alphaeus Mote e0b002975f feat(api-docs): require auth for docs and add filterable route discovery
The OpenAPI spec and Swagger UI were public. Put them behind the same
authentication as the rest of the API, and add a compact route list so a
client can ask "what can I call?" without opening dev tools.

Access:
- /api/openapi.json and /api/routes require a bearer token or API key.
- /api/docs additionally accepts a session cookie set at login, so a
  signed-in operator can open the docs in a new tab; an anonymous browser
  is redirected to /login?redirect=... and returned afterwards.
- The cookie is HttpOnly and path-scoped to /api/docs, so it is never sent
  to /api/v1/* and cannot authenticate an API call (no CSRF surface).
  Verified: cookie-only request to /api/v1/rules returns 401.

Discovery: both the spec and GET /api/routes accept ?method=get,post and
?path=<substring> (comma-separated, case-insensitive). The route list
returns method, path, summary, tag, public, and `allowed` — false when a
read-scoped API key cannot invoke that route. /api/docs passes the same
query through to the spec it loads.

UI: a </> icon in the header (both layouts) and an Administration → API
Docs menu entry, opened in a new tab via a new `external` nav-item flag.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-03 10:44:54 -04:00
Alphaeus Mote 6fdf9a9d54 feat(users): assign RBAC roles to users; OIDC default-role dropdown + clearer SSO config
Roles:
- Add GET /api/v1/roles (built-in SuperAdmin/Admin/Operator/Viewer) and
  wire role assignment into user create/update (UserRepository.SetRoles /
  GetRoleNames / ListRoles). User responses now include roles.
- User dialog gains a roles multi-select; the users list shows role chips.

OIDC / SSO config UI:
- Default role is now a dropdown populated from /roles.
- Issuer URL shows real provider examples (Entra/Okta/Google).
- Replace the confusing manual "Redirect URL" field with a read-only,
  auto-derived callback URL (from the browser origin / public URL) plus a
  copy button — the exact value to register at the IdP. The backend still
  auto-derives the callback and honors an env override.

Verified: /roles lists the four roles; creating/updating a user with roles
round-trips through GET.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-02 21:25:59 -04:00
Alphaeus Mote c74dd63281 feat(api): OpenAPI/Swagger docs generated from the router + PowerShell example
- GET /api/docs serves a Swagger UI and GET /api/openapi.json serves an
  OpenAPI 3 spec built by walking the live chi router, so documented paths
  always match what the build serves. A small registry adds rich detail
  (request bodies, params, schemas) for the automation-critical operations
  (auth login, rule create/update/preview, connection introspection);
  RuleInput and friends are defined as reusable component schemas.
- Add docs/examples/Create-OrchestrADRule.ps1: a no-alias PowerShell sample
  that builds headers/body as typed dictionaries, serializes with
  ConvertTo-Json, logs in, creates a SyncGroupMembership rule, and runs it.
- README: new "API & Automation" section (Swagger + PowerShell), and a
  "Logging & Retention" section documenting log rotation and the new
  database maintenance/retention knobs.

Tests cover spec generation from a router and registry well-formedness.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-02 18:33:53 -04:00
Alphaeus Mote 1f77473d8f feat(server): trust local proxies by default, seed built-in schedules
Proxy / base-URL:
- ORCHESTRAD_TRUSTED_PROXIES now defaults to "local", trusting reverse
  proxies in loopback + RFC1918 + link-local/ULA ranges out of the box, so
  X-Forwarded-* (client IP, scheme, host) is honored behind an edge proxy
  without extra config. New keywords: local/private, all/any, none.
- OIDC redirect URI derivation now uses the trust-gated request base URL
  instead of reading X-Forwarded-Proto directly, and audit client IP now
  trusts the middleware-rewritten RemoteAddr rather than the raw (spoofable)
  X-Forwarded-For header. Both honor forwarded values only from trusted
  peers.

Schedules:
- Seed eight built-in schedules on startup (every 5/15/30 min, hourly,
  every 6/12h, daily, weekly), idempotent by name, so operators have
  ready-made cadences in the Schedules page and the rule editor's schedule
  dropdown without hand-building one.

Test covers the trusted-proxy keyword expansion.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-02 17:14:49 -04:00
Alphaeus Mote dc3c2c9811 feat(connections): schema attribute + distinct-value lookups for filter building
Add two directory introspection endpoints the rule filter builder uses:

- GET /ad-connections/{id}/attributes?objectType=&q= returns the schema
  attributes that APPLY to the given object type (User/Computer/Group). The
  applicable set is derived by walking the classSchema hierarchy from the
  object's class up through subClassOf to top, plus auxiliary classes,
  unioning each class's may/must-contain attributes; results are filtered by
  substring and returned with adminDescription. The per-(connection,object
  type) set is cached for 10 minutes so the walk is not repeated per
  keystroke. (Computer inherits user attributes, since AD's computer class
  subclasses user — reflected correctly.)
- GET /ad-connections/{id}/attribute-values?attribute=&objectType=&q= samples
  objects and returns the distinct values present for one attribute, so the
  value field can suggest real directory values.

Backed by a new bounded ldap Client.SearchWithLimit that tolerates the
server's size-limit response. Attribute names are validated against the LDAP
descriptor charset before use in a filter.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-02 15:57:52 -04:00
Alphaeus Mote 5920b690d1 feat(rules): dynamic-group reconciliation + editor-facing APIs
Turns rules into Adaxes/Active-Roles style dynamic groups. The core new
capability is a set-level SyncGroupMembership action that reconciles a
target group's membership against the matched object set in one pass
instead of the old add-only, per-object behaviour.

Engine / reconciliation:
- New ActionSyncGroupMembership runs once per target group after the
  match: resolve (and optionally create) the group, read its current
  members, diff against the matched set, and apply the adds/removes.
- Three per-rule sync modes (types.SyncMode): FullSync (membership ==
  matched set; removes stale members incl. manual adds), ManagedAdd (adds
  matches, removes only members this rule added), AddOnly (never removes).
- Managed ownership tracked in a new managed_group_members table
  (migration 005) + repository, wired into the runner's engine so
  ManagedAdd removes only what it added.
- Adds/removes are recorded as AddToGroup / RemoveFromGroupIfNoLongerMatched
  run-actions so the activity feed categorises them as syncs/removals.
- Preview now computes an accurate, non-mutating diff for sync actions
  (+add / -remove / already-in-sync counts and per-member entries).
- memberOf and memberOf-recursive (LDAP_MATCHING_RULE_IN_CHAIN) operators;
  Regex no longer silently degrades to equals.
- Canonical group targets: CanonicalToLeafDN / NormalizeGroupTarget so a
  target group can be given as domain.com/OU/Group as well as a DN.

Editor-facing APIs (backend-first; UI comes next):
- Rule create/update now accept conditionGroups + actions and persist them
  via RuleRepository.ReplaceLogic (soft-delete + insert, preserving the
  rule_run_actions FK). Omitting them leaves existing logic untouched.
- POST /api/v1/rules/preview evaluates an unsaved draft (live match panel).
- GET /api/v1/rules/metadata serves the operator vocabulary (object types,
  operators, action types, sync modes, common attributes) so UI dropdowns
  stay in lock-step with the backend.
- GET /api/v1/ad-connections/{id}/directory searches groups/OUs for the
  target pickers.

Tests: reconciliation across all three modes + create-if-missing and the
missing-group error path (fake directory client); canonical leaf-DN
conversion; ReplaceLogic round-trip. Full suite green.

Note: RuleRepository.GetByID nests a query (getConditionGroups holds a
cursor while calling getConditions); safe under the production pool (25)
but a follow-up should flatten it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-02 14:57:30 -04:00
Alphaeus Mote e74cb454c8 feat(activity): add action-ledger intelligence and drill-in feed
Turn the rule_run_actions ledger into operator intelligence. A new
ActivityService rolls the ledger up by time window (24h / 7d / all-time)
into syncs, operations and removals with success/failure counts and the
number of distinct objects affected, plus all-time totals per action type
and a most-active-rules ranking. A companion filtered, paginated feed
answers the operational questions directly: what action ran, what
happened, to which object, triggered by whom, and when.

Backend:
- ActivityService.Summary() and ListActions(filter) over the joined
  rule_run_actions / rule_runs / rules tables, categorising each action
  type as Sync (AddToGroup/AddGroupToGroup), Removal
  (RemoveFromGroupIfNoLongerMatched) or Operation (everything else).
- GET /api/v1/activity/summary and GET /api/v1/activity (category,
  actionType, status, ruleId, search, pagination).
- Table-driven test seeding a run with mixed action types/statuses and
  asserting window roll-ups, distinct-object counts, top rules, and the
  category/status feed filters.

Frontend:
- New Activity page: window summary cards, by-action-type and
  most-active-rules tables, and a drill-in feed with category/result/
  object filters and a detail dialog that pretty-prints the action's
  details JSON and error.
- Wired into the vertical sidebar and horizontal navbar under Automation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-02 14:09:04 -04:00
Alphaeus Mote 06b1c860d4 feat(auth): OIDC/SSO login with UI-configurable, env-overriding settings
Add backend OIDC: /api/v1/auth/oidc/status|login|callback (authorization code +
PKCE, state+nonce cookies, ID-token verification via go-oidc) and admin
/auth/oidc/config (GET/PUT, validates the issuer via discovery on enable).
Config resolves app_settings (UI) > env > default, so SSO can be configured from
the UI while env still seeds it. LoginOIDC provisions/links federated users by
the stable (issuer, subject) pair, refusing to reuse a username held by a
different account. Verified: discovery + real authorize redirect + PKCE cookies;
config API with DB-over-env precedence; provisioning unit-tested.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-02 12:01:48 -04:00
Alphaeus Mote a4698ee9f9 feat(tls): certificate modes (auto/provided/windows-store) + config API + richer SANs
Add runtime TLS modes resolved via settings (UI wins over env): auto
(self-managed), provided (bring-your-own upload), and windows-store (Windows
cert store, enumeration/serving scaffolded). New /api/v1/tls endpoints: status,
mode, certificate upload, and windows-store list; changes reload the live
manager without a restart, falling back to the self-managed cert if a configured
source is unavailable. Auto SANs now cover the hostname (CN), localhost, every
non-loopback IP, and host.<suffix> + the suffix for each detected DNS suffix
(Windows registry / resolv.conf). Bring-your-own is validated and unit-tested.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-02 11:21:00 -04:00
Alphaeus Mote 300866d5cf feat(tls): auto-managed HTTPS with renewal; disable for edge termination
Add internal/tlsmgr: on startup, load-or-generate the CA, issue/reuse the leaf,
export the chain under <data>/tls, and hot-swap the cert on renewal via
GetCertificate. The server listens HTTPS when a manager is present. TLS is on by
default for the standalone binary/service (tls.enabled setting or
ORCHESTRAD_TLS_ENABLED, UI wins over env); the container image defaults it off so
it runs plain HTTP behind a TLS-terminating proxy. Startup logs a clickable URL.
Verified end to end: HTTPS serves, plain HTTP rejected, disable serves HTTP.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-02 11:06:42 -04:00
Alphaeus Mote 1a87030e42 fix(server): answer HEAD on /health so container probes pass
The Docker HEALTHCHECK uses 'wget --spider' (a HEAD request), but the health
routes were GET-only and chi returned 405, which would mark the container
unhealthy. Register HEAD alongside GET on /health and /api/health.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-02 08:36:52 -04:00
Alphaeus Mote 7f824245d3 test: add config loader and health/version smoke tests
Cover the environment-driven config loader (defaults, overrides, secret
from file) and the unauthenticated /health and /api/v1/version handlers
the Docker HEALTHCHECK and bootstrap flow depend on.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-02 00:13:35 -04:00
GraceSolutions 611f736088 feat(auth): default admin/admin bootstrap with forced first-login password change
- Add password_reset_required column (migration 004) + repository support
- auth.Service.ChangePassword verifies current, hashes new, clears flag,
  emits PasswordChange audit events for success and failure
- Bootstrap: when no ORCHESTRAD_BOOTSTRAP_PASSWORD[_FILE] is set, seed
  admin/admin with password_reset_required=true and log a one-time warn
  banner; env/file-supplied passwords keep the flag clear
- Expose passwordResetRequired in UserInfo / /auth/me / login response
- POST /api/v1/auth/change-password behind the authenticated group
- Frontend: /change-password page + ChangePasswordForm, AuthLogin and
  RequireAuth bounce any other route to it while the flag is set
- Docs: DesignSpecification 8.6/8.8 and Template 7.6/7.7 rewritten,
  Trusted Proxy renumbered to 7.8 in the template, acceptance items
  updated to match the new default-credential behavior
2026-04-23 17:36:23 -04:00
GraceSolutions 1a24ed5276 feat(server): trusted-proxy middleware and gate admin routes behind auth
Two server-layer changes that were developed together because they
share the same routing rewrite:

- proxy.go: TrustedProxy middleware inspects RemoteAddr against the
  operator-configured CIDR list (ORCHESTRAD_TRUSTED_PROXIES) and, only
  when the peer matches, rewrites r.RemoteAddr and r.URL.Scheme from
  X-Forwarded-For / X-Forwarded-Proto. Untrusted peers are ignored so
  downstream handlers and the audit log see the direct connection
  address, preventing header spoofing.

- server.go: /api/v1 is split into a public surface (version, health,
  auth login/logout/csrf) and an authenticated group that now wraps
  /users, /credentials, /ad-connections, /schedules, /rules, /rule-runs,
  /backups, /api-keys, /audit, /settings, /dashboard, and /config with
  AuthMiddleware. Previously only /auth/me and two /api-keys routes
  were protected; every other administrative endpoint was reachable
  without a token, which was a real security bug.
2026-04-23 16:40:36 -04:00
GraceSolutions 2dde5af6cb feat(ui-embed): ship the web UI inside the Go binary
Switch Next.js to static export (output: 'export', trailingSlash: true) and add a new backend/internal/webui package that embeds frontend/out/ via //go:embed. The backend now serves the UI from its own HTTP server with SPA fallback, long-lived cache headers on _next/static/*, and no dependency on a separate Node.js runtime or static host.

- frontend/next.config.mjs: output='export', trailingSlash=true, images.unoptimized.

- backend/internal/webui: Handler() with SPA fallback, resolve() mirroring Next trailing-slash behavior, IsBuilt() helper, and a placeholder dist/index.html so go build works without a prior frontend build. Top-level .gitignore tracks only the placeholder and its own .gitignore; the copied static export is ignored.

- backend/internal/server/server.go: mount webui.Handler() as the chi NotFound handler so /api/v1/* and /health continue to be served by their handlers while all other routes resolve through the embedded UI with SPA deep-link support.

- scripts/build.ps1: build the frontend (npm install if needed, then npm run build) before any Go build, stage frontend/out into backend/internal/webui/dist, and fail loudly if the UI output is missing or empty. Adds -SkipFrontend for developer iteration.

- Remove Spike demo content that prevented static export: api/* route handlers (dynamic POST/PUT/DELETE), apps/{blog,ecommerce,invoice} dynamic [slug]/[id] detail/edit pages, and frontend-pages/blog/[slug].

- Fix TypeScript errors surfaced by enabling the production build: ApiMeta.totalCount (not total) in audit and rule-runs pages; MUI v7 Grid uses size={{...}} instead of item+xs+md in config page.

Binary size grows from ~8 MB to ~37 MB, reflecting the embedded UI. The produced single binary is now sufficient to run the full product; no separate web server is required.
2026-04-23 14:48:59 -04:00
GraceSolutions 014003775b feat(lifecycle): add enable/disable endpoints for credentials, connections, and schedules
Add UpdateEnabled methods on CredentialRepository, ConnectionRepository, and ScheduleRepository that toggle the is_enabled flag while preserving updated_utc. Expose corresponding Enable/Disable handlers and wire POST /{id}/enable and POST /{id}/disable routes under /api/v1/credentials, /api/v1/ad-connections, and /api/v1/schedules. Each toggle emits an audit event. Also finalizes CredentialService.Test and TestCredentialInput so the existing /credentials/{id}/test handler compiles and runs against an LDAP host using the stored username and decrypted secret.
2026-04-23 13:35:08 -04:00
GraceSolutions b05858b40d feat(config): add configuration export/import API
Introduce ConfigService and /api/v1/config endpoints for system portability.

- Export: emits a versioned JSON document covering credentials (encrypted secrets preserved), connections, schedules, rules (with nested condition groups, conditions, and actions), and non-sensitive app settings.
- Import: validates the format version and upserts each entity by ID. Supports a dryRun mode that plans the import without writing, and emits per-entity warnings for secrets that need re-entry.
- Audit: Export and Import actions emit ConfigChange audit events with entity counts.
- Wiring: add ConfigService to server.Dependencies and instantiate it in cli.RunForeground.
2026-04-23 13:28:51 -04:00
GraceSolutions cb2e9b1307 feat(api-keys): add enable/disable lifecycle endpoints
APIKeyService.SetEnabled toggles the is_enabled flag, refusing to re-enable revoked keys. New POST /api/v1/api-keys/{id}/enable and /disable handlers expose the lifecycle transitions and emit Update audit events with the resulting state.
2026-04-23 13:23:08 -04:00
GraceSolutions 409ae42bc7 feat(dashboard): add aggregated dashboard summary endpoint
New DashboardService aggregates entity counts (rules, connections, credentials, schedules, users, api keys), rolling rule-run statistics (last 24h and 7d), the 10 most recent rule runs, and per-connection health based on last_tested_utc/last_test_result. Exposed via GET /api/v1/dashboard/summary and wired through server.Dependencies + cli.RunForeground.
2026-04-23 13:22:04 -04:00
GraceSolutions 99a0589a2c feat(settings): add CRUD API for the app_settings key-value store
New SettingsService provides List/Get/Upsert/Delete over the app_settings table. SettingsHandler exposes GET /api/v1/settings, GET/PUT/DELETE /api/v1/settings/{key} and redacts sensitive values in responses. Changes are recorded as ConfigChange audit events. The service is wired into server.Dependencies and constructed in cli.RunForeground.
2026-04-23 13:19:18 -04:00
GraceSolutions e8556b3c16 feat(audit): expose read-only audit event query API
Add Filter-based List and GetByID methods to audit.Service backed by a shared rowScanner so both sql.Row and sql.Rows can be decoded into an Event. Add a new AuditHandler with GET /api/v1/audit and GET /api/v1/audit/{id} wired into the router. Filters accepted via query string: eventType, userId, username, resourceType, resourceId, action, success, startUtc, endUtc.
2026-04-23 13:17:50 -04:00
GraceSolutions 2a6df8e874 feat(audit): integrate audit logging across management API handlers
Introduce a shared audit_helpers.emitAudit utility that extracts user identity, IP, and User-Agent from the request context and forwards events to audit.Service. Wire an auditService dependency into all management handlers (auth, credentials, connections, schedules, rules, backups, api-keys, users) and emit audit events for login, CRUD, test, preview, run, backup, restore, API key create/revoke, and user lifecycle operations. Both successful and failed paths log with appropriate event types, resource IDs, and sanitized detail maps.
2026-04-23 12:52:20 -04:00
GraceSolutions d8f45fe863 feat(connections): add LDAP query preview endpoint
- New ConnectionService.QueryPreview runs an ad-hoc LDAP search against an
  existing AD connection. Caller supplies filter, optional baseDn (defaults
  to the connection's rootDn), scope, attributes, and limit (capped at 500,
  defaulting to 100) and gets back the matched entries as DN + attribute map
  along with a truncated flag.
- Scope strings are mapped through goldap.Scope* constants so base, one,
  and sub values all work.
- Wire POST /api/v1/ad-connections/{id}/query-preview to
  ConnectionsHandler.QueryPreview and require a non-empty filter.
2026-04-23 12:41:49 -04:00
GraceSolutions 958f2bf501 feat(credentials): add credential test endpoint
- New CredentialService.Test method that decrypts the stored secret and
  performs an LDAP bind against a user-supplied host/port/TLS target.
- Persists the outcome on the credential via UpdateTestResult so the
  last_tested_utc and last_test_result columns stay current.
- Wire POST /api/v1/credentials/{id}/test to CredentialsHandler.Test,
  which accepts the connection parameters in the request body and maps
  'credential not found' to 404.
2026-04-23 12:39:06 -04:00
GraceSolutions 29a8a0b503 feat(backups): add database backup and restore endpoints
- New BackupsHandler exposing list, create, and restore over
  /api/v1/backups, backed by services.BackupService.
- Create records who triggered the backup (username from context or
  'api') and defaults the backup type to 'manual'.
- Restore resolves the {id} path parameter against the list of available
  backups so clients can reference backups by filename, or pass an
  explicit filePath in the body.
- Inject BackupService via server.Dependencies and initialize it in
  cli.RunForeground under <DataPath>/backups with retention 10.
2026-04-23 12:37:31 -04:00
GraceSolutions 76469eb401 feat(api-keys): add API key administration endpoints
- New APIKeysHandler exposing list, create, revoke, and delete over
  /api/v1/api-keys, backed by services.APIKeyService.
- Create returns the full plaintext key once; list/get responses only
  surface the prefix and metadata.
- When userId is omitted, the handler falls back to the authenticated
  principal pulled from context so callers can self-service tokens.
- Inject APIKeyService via server.Dependencies and initialize it in
  cli.RunForeground.
2026-04-23 12:36:06 -04:00
GraceSolutions 8b3cf48bba feat(users): add user administration CRUD endpoints
- Add UserRepository.List with pagination and non-deleted filtering.
- New UsersHandler with List/Get/Create/Update/Delete; Create hashes
  passwords via crypto.HashPassword and Update re-hashes on rotation.
- Responses omit password material; username conflict returns 409.
- Inject UserRepo via server.Dependencies and wire /api/v1/users routes.
2026-04-23 12:34:19 -04:00
GraceSolutions a4473ba77e feat(api): wire live auth and credentials handlers
- Extend server.Dependencies with AuthService and CredService, and
  initialize them in cli.RunForeground using the derived AES key.
- Replace the handleNotImplemented stubs on /auth and /credentials with
  the existing AuthHandler and CredentialsHandler routes.
- Guard /auth/me behind api.AuthMiddleware so it only resolves when a
  valid session token is presented.
2026-04-23 12:32:13 -04:00
GraceSolutions 6a044dcb1b feat(rule-runs): expose rule execution history endpoints
- Add RuleRunRepository.List for paging across runs regardless of rule.
- New RuleRunsHandler with List/Get/ListByRule, returning run summaries
  and per-action detail via RuleRunDetailResponse.
- Register GET /api/v1/rule-runs, GET /api/v1/rule-runs/{runId}, and
  GET /api/v1/rules/{id}/runs.
2026-04-23 12:30:40 -04:00
GraceSolutions 1c50828b9c feat(schedules): add full CRUD endpoints for schedule resource
- New SchedulesHandler backed by the existing ScheduleRepository, with
  request/response DTOs matching the schedules model (kind, easy/cron,
  timezone mode).
- Register List/Get/Create/Update/Delete routes on /api/v1/schedules.
- Extend server.Dependencies with ScheduleRepo and wire it from
  cli.RunForeground.
2026-04-23 12:29:19 -04:00
GraceSolutions 6af0ffff9d feat(connections): wire full CRUD endpoints for AD connections
- Add Create/Update/Delete handlers on ConnectionsHandler backed by the
  existing ConnectionRepository (soft-delete preserves audit history).
- Register List/Get/Create/Update/Delete/Test routes on /ad-connections
  so the resource is no longer stubbed out.
2026-04-23 12:27:59 -04:00
GraceSolutions c117d6d546 feat(rules): complete CRUD endpoints for rules resource
- Extend RuleRepository with List/Update/SoftDelete/UpdateEnabled and
  upsert helpers for condition groups, conditions, and actions.
- Extend RuleService with Create/GetByID/List/Update/Delete/Enable/Disable.
- Add List/Get/Create/Update/Delete/Enable/Disable handlers on RulesHandler
  with request/response DTOs that decouple the API from storage models.
- Register the full CRUD route set on /api/v1/rules.
- Inject RuleService into server.Dependencies from cli.RunForeground.
2026-04-23 12:26:50 -04:00
GraceSolutions 807b95e92a feat(api): add rule preview and run endpoints
Adds RulesHandler exposing POST /api/v1/rules/{id}/preview and
POST /api/v1/rules/{id}/run. Preview returns the generated LDAP filter,
matched objects, and planned actions. Run triggers an immediate execution
via the Runner using the X-Triggered-By header (defaults to 'api').

Also adds Runner.PreviewRule so the handler can delegate without having to
load the rule, connection, and LDAP client itself.
2026-04-23 12:16:37 -04:00
GraceSolutions 2376daf91c feat(startup): wire runner, engine, and scheduler into foreground mode
Derives a 32-byte AES key from the configured secret via SHA-256, constructs
the ConnectionService, Runner, and Engine, and starts the Scheduler so
enabled rules fire automatically. Injects the shared services into the HTTP
server via a new Dependencies struct so API handlers can reuse them.
2026-04-23 12:14:50 -04:00
GraceSolutions 09a09d4a68 feat: Initial backend scaffold - Go service, database, CLI, API structure
Phase 1 foundations:
- Go backend with Chi router framework
- SQLite database with WAL mode and foreign keys
- Database migrations for users, roles, credentials, AD connections, schedules, rules, and audit
- CLI commands: init, run, install, uninstall, start, stop, migrate, backup, restore, doctor
- Configuration loading from environment variables
- Centralized logging with file rotation (lumberjack)
- Crypto package for Argon2id password hashing and AES-GCM encryption
- Auth service with session management
- Audit service for event logging
- Scheduler with 6-field cron support
- REST API routes scaffolded for all major resources
- CORS support with localhost defaults for development
- Docker support with Dockerfile and docker-compose.yml
- Multi-platform build script (PowerShell)
- Project structure per design specification

Version format: yyyy.MM.dd.HHmm
All PKs are UUIDv4, all timestamps UTC
2026-04-19 10:07:21 -04:00