Class: ApiAuthentication
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- ApiAuthentication
- Defined in:
- app/models/api_authentication.rb
Overview
== Schema Information
Table name: api_authentications
Database name: primary
id :integer not null, primary key
api_authentication_token :string not null
expires_at :datetime not null
last_used_at :datetime
name :string
permitted_services :text is an Array
revoked_at :datetime
created_at :datetime
account_id :integer not null
Indexes
index_api_authentications_on_account_id (account_id)
index_api_authentications_on_api_authentication_token (api_authentication_token)
index_api_authentications_on_expires_at (expires_at)
index_api_authentications_on_revoked_at (revoked_at)
Constant Summary collapse
- EXPIRATION_OPTIONS =
Common expiration options for UI
{ '1 hour' => 1.hour, '1 day' => 1.day, '1 week' => 1.week, '1 month' => 1.month, '3 months' => 3.months, '6 months' => 6.months, '1 year' => 1.year, 'Never' => 100.years }.freeze
- UPSTREAM_SERVICES =
MCP gateway services that tokens can grant access to.
The gateway provides content tools only. All other services
(PostgreSQL, Ahrefs, AppSignal, Google Analytics, etc.) are
connected directly as standalone MCP servers in each developer's
Cursor config via script/setup_mcp_servers.sh.Content-type permissions control access to sensitive embeddings:
- call_recordings: Search own call recordings (employee's calls only)
- call_recordings_all: Search ALL call recordings (managers/admins)
{ 'content' => { label: 'Content Tools', description: 'Search, posts, FAQs, products, showcases, images, videos, reviews' }, 'support_cases' => { label: 'Support Cases', description: 'Search support cases, activity notes, and communications for customer issues and resolutions' }, 'call_recordings' => { label: 'My Call Recordings', description: 'Search your own call recording transcripts', sensitive: true }, 'call_recordings_all' => { label: 'All Call Recordings', description: 'Search all employee call recording transcripts (managers only)', sensitive: true }, 'google_analytics' => { label: 'Google Analytics', description: 'GA4 page views, sessions, engagement, traffic sources' }, 'search_console' => { label: 'Search Console', description: 'Google search clicks, impressions, CTR, keyword positions' }, 'google_ads' => { label: 'Google Ads', description: 'Keyword search volume, GAQL queries, campaign data' }, 'ahrefs' => { label: 'Ahrefs SEO', description: 'Backlinks, organic traffic, keyword rankings' }, 'basecamp' => { label: 'Basecamp', description: 'Projects, todos, search, and team collaboration' } }.freeze
- DEFAULT_SERVICES =
Default services.
%w[content].freeze
- SENSITIVE_SERVICES =
Services that require explicit permission -- never granted by default or
by the "all services" fallback for OAuth tokens without scoping. UPSTREAM_SERVICES.select { |_, v| v[:sensitive] }.keys.freeze
Constants included from Models::Schedulable
Models::Schedulable::SIMPLE_FORM_OPTIONS
Instance Attribute Summary collapse
- #api_authentication_token ⇒ String readonly
- #expires_at ⇒ ActiveSupport::TimeWithZone readonly
-
#expires_in ⇒ Object
Returns the value of attribute expires_in.
-
#is_guest ⇒ Object
Returns the value of attribute is_guest.
Belongs to collapse
Class Method Summary collapse
-
.active ⇒ ActiveRecord::Relation<ApiAuthentication>
A relation of ApiAuthentications that are active.
-
.expired ⇒ ActiveRecord::Relation<ApiAuthentication>
A relation of ApiAuthentications that are expired.
-
.revoked ⇒ ActiveRecord::Relation<ApiAuthentication>
A relation of ApiAuthentications that are revoked.
Instance Method Summary collapse
- #active? ⇒ Boolean
-
#can_access_service?(service_key) ⇒ Boolean
Check if this token has access to a given upstream service.
-
#effective_services ⇒ Object
Returns the effective services list (with 'content' always included).
- #expired? ⇒ Boolean
-
#masked_token ⇒ Object
Masked token for display (only show first/last 4 chars).
- #revoke! ⇒ void
- #revoked? ⇒ Boolean
-
#service_labels ⇒ Object
Human-readable labels for the permitted services.
- #status ⇒ void
- #status_badge_class ⇒ void
-
#time_until_expiration ⇒ Object
Time until expiration in human-readable format.
Methods inherited from ApplicationRecord
ransackable_associations, ransackable_attributes, ransackable_scopes, ransortable_attributes, #to_relation
Methods included from Models::Schedulable
Methods included from Models::AfterCommittable
Methods included from Models::EventPublishable
Instance Attribute Details
#api_authentication_token ⇒ String (readonly)
Validations:
- Uniqueness ({ scope: :account_id })
75 |
# File 'app/models/api_authentication.rb', line 75 validates :api_authentication_token, presence: true |
#expires_at ⇒ ActiveSupport::TimeWithZone (readonly)
77 |
# File 'app/models/api_authentication.rb', line 77 validates :expires_at, presence: true |
#expires_in ⇒ Object
Returns the value of attribute expires_in.
85 86 87 |
# File 'app/models/api_authentication.rb', line 85 def expires_in @expires_in end |
#is_guest ⇒ Object
Returns the value of attribute is_guest.
85 86 87 |
# File 'app/models/api_authentication.rb', line 85 def is_guest @is_guest end |
Class Method Details
.active ⇒ ActiveRecord::Relation<ApiAuthentication>
A relation of ApiAuthentications that are active. Active Record Scope
81 |
# File 'app/models/api_authentication.rb', line 81 scope :active, -> { where(revoked_at: nil).where('expires_at > ?', Time.current) } |
.expired ⇒ ActiveRecord::Relation<ApiAuthentication>
A relation of ApiAuthentications that are expired. Active Record Scope
83 |
# File 'app/models/api_authentication.rb', line 83 scope :expired, -> { where(expires_at: ..Time.current) } |
.revoked ⇒ ActiveRecord::Relation<ApiAuthentication>
A relation of ApiAuthentications that are revoked. Active Record Scope
82 |
# File 'app/models/api_authentication.rb', line 82 scope :revoked, -> { where.not(revoked_at: nil) } |
Instance Method Details
#active? ⇒ Boolean
98 99 100 |
# File 'app/models/api_authentication.rb', line 98 def active? !expired? && !revoked? end |
#can_access_service?(service_key) ⇒ Boolean
Check if this token has access to a given upstream service
144 145 146 |
# File 'app/models/api_authentication.rb', line 144 def can_access_service?(service_key) effective_services.include?(service_key.to_s) end |
#effective_services ⇒ Object
Returns the effective services list (with 'content' always included)
149 150 151 152 |
# File 'app/models/api_authentication.rb', line 149 def effective_services services = permitted_services.presence || DEFAULT_SERVICES (services | DEFAULT_SERVICES).sort end |
#expired? ⇒ Boolean
88 89 90 |
# File 'app/models/api_authentication.rb', line 88 def expired? expires_at <= Time.current end |
#masked_token ⇒ Object
Masked token for display (only show first/last 4 chars)
125 126 127 128 129 130 131 132 |
# File 'app/models/api_authentication.rb', line 125 def masked_token return nil if api_authentication_token.blank? token = api_authentication_token return token if token.length <= 12 "#{token[0..3]}...#{token[-4..]}" end |
#revoke! ⇒ void
This method returns an undefined value.
103 104 105 |
# File 'app/models/api_authentication.rb', line 103 def revoke! update!(revoked_at: Time.current) end |
#revoked? ⇒ Boolean
93 94 95 |
# File 'app/models/api_authentication.rb', line 93 def revoked? revoked_at.present? end |
#service_labels ⇒ Object
Human-readable labels for the permitted services
155 156 157 |
# File 'app/models/api_authentication.rb', line 155 def service_labels effective_services.filter_map { |key| UPSTREAM_SERVICES.dig(key, :label) } end |
#status ⇒ void
This method returns an undefined value.
108 109 110 111 112 113 |
# File 'app/models/api_authentication.rb', line 108 def status return 'revoked' if revoked? return 'expired' if expired? 'active' end |
#status_badge_class ⇒ void
This method returns an undefined value.
116 117 118 119 120 121 122 |
# File 'app/models/api_authentication.rb', line 116 def status_badge_class case status when 'active' then 'bg-success' when 'expired' then 'bg-warning' when 'revoked' then 'bg-danger' end end |
#time_until_expiration ⇒ Object
Time until expiration in human-readable format
135 136 137 138 139 |
# File 'app/models/api_authentication.rb', line 135 def time_until_expiration return 'Expired' if expired? distance_of_time_in_words(Time.current, expires_at) end |