Class: KairosMcp::Admin::Router

Inherits:
Object
  • Object
show all
Includes:
Helpers
Defined in:
lib/kairos_mcp/admin/router.rb

Overview

Router: Handles all /admin/* requests for the admin UI

Provides lightweight session-based authentication (using existing Bearer tokens), CSRF protection, and delegates to page-specific handlers.

Pages:

GET  /admin/login   - Login form
POST /admin/login   - Authenticate with Bearer token
GET  /admin/logout  - Clear session
GET  /admin         - Dashboard
GET  /admin/tokens  - Token management
POST /admin/tokens/* - Token operations (htmx)
GET  /admin/chain   - Chain explorer
POST /admin/chain/* - Chain operations (htmx)
GET  /admin/skills  - Skills viewer
GET  /admin/knowledge - Knowledge viewer
GET  /admin/config  - Config viewer
GET  /admin/static/* - Static files (CSS, JS)

Constant Summary

Constants included from Helpers

Helpers::SESSION_COOKIE, Helpers::SESSION_SECRET, Helpers::STATIC_DIR, Helpers::VIEWS_DIR

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Helpers

#clear_session_cookie, #decode_session, #encode_session, #generate_csrf_token, #get_session, #h, #html_response, #parse_cookies, #parse_form_body, #parse_query, #redirect, #render, #render_layout, #render_partial, #serve_static, #session_cookie, #valid_csrf?

Constructor Details

#initialize(token_store:, authenticator:) ⇒ Router

Returns a new instance of Router.



34
35
36
37
38
# File 'lib/kairos_mcp/admin/router.rb', line 34

def initialize(token_store:, authenticator:)
  @token_store = token_store
  @authenticator = authenticator
  @flash = nil
end

Instance Attribute Details

#authenticatorObject (readonly)

Returns the value of attribute authenticator.



32
33
34
# File 'lib/kairos_mcp/admin/router.rb', line 32

def authenticator
  @authenticator
end

#token_storeObject (readonly)

Returns the value of attribute token_store.



32
33
34
# File 'lib/kairos_mcp/admin/router.rb', line 32

def token_store
  @token_store
end

Instance Method Details

#call(env) ⇒ Array

Main entry point: route an admin request

Parameters:

  • env (Hash)

    Rack environment

Returns:

  • (Array)

    Rack response triple [status, headers, body]



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/kairos_mcp/admin/router.rb', line 44

def call(env)
  method = env['REQUEST_METHOD']
  path = env['PATH_INFO']

  # Static files: no auth required
  if path.start_with?('/admin/static/')
    filename = path.sub('/admin/static/', '')
    return serve_static(filename)
  end

  # Login page: no auth required
  case [method, path]
  when ['GET', '/admin/login']
    return (env)
  when ['POST', '/admin/login']
    return (env)
  end

  # All other routes require authentication
  session = get_session(env)
  unless session && session[:token]
    return redirect('/admin/login')
  end

  # Verify token is still valid
   = @token_store.verify(session[:token])
  unless 
    return redirect_with_flash('/admin/login', 'Session expired. Please login again.')
  end

  # Verify owner role
  unless [:role] == 'owner'
    return redirect_with_flash('/admin/login', 'Admin access requires owner role.')
  end

  @current_user = Protocol.apply_all_filters()
  @csrf_token = session[:csrf_token]

  # CSRF check for POST requests
  if method == 'POST' && !valid_csrf?(env, session)
    return html_response(403, render('layout', content: '<p>CSRF validation failed. Please try again.</p>'))
  end

  # Authenticated routes — set thread-scoped user context for PgBackend
  Thread.current[:kairos_user_context] = @current_user
  route(method, path, env)
rescue StandardError => e
  $stderr.puts "[ADMIN ERROR] #{e.message}"
  $stderr.puts e.backtrace.first(5).join("\n")
  html_response(500, render('_error', layout: true, error: e.message))
ensure
  Thread.current[:kairos_user_context] = nil
end