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' }, '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 =
%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
Instance Attribute Summary collapse
- #api_authentication_token ⇒ Object readonly
- #expires_at ⇒ Object 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! ⇒ Object
- #revoked? ⇒ Boolean
-
#service_labels ⇒ Object
Human-readable labels for the permitted services.
- #status ⇒ Object
- #status_badge_class ⇒ Object
-
#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::EventPublishable
Instance Attribute Details
#api_authentication_token ⇒ Object (readonly)
70 |
# File 'app/models/api_authentication.rb', line 70 validates :api_authentication_token, presence: true |
#expires_at ⇒ Object (readonly)
71 |
# File 'app/models/api_authentication.rb', line 71 validates :expires_at, presence: true |
#expires_in ⇒ Object
Returns the value of attribute expires_in.
79 80 81 |
# File 'app/models/api_authentication.rb', line 79 def expires_in @expires_in end |
#is_guest ⇒ Object
Returns the value of attribute is_guest.
79 80 81 |
# File 'app/models/api_authentication.rb', line 79 def is_guest @is_guest end |
Class Method Details
.active ⇒ ActiveRecord::Relation<ApiAuthentication>
A relation of ApiAuthentications that are active. Active Record Scope
75 |
# File 'app/models/api_authentication.rb', line 75 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
77 |
# File 'app/models/api_authentication.rb', line 77 scope :expired, -> { where('expires_at <= ?', Time.current) } |
.revoked ⇒ ActiveRecord::Relation<ApiAuthentication>
A relation of ApiAuthentications that are revoked. Active Record Scope
76 |
# File 'app/models/api_authentication.rb', line 76 scope :revoked, -> { where.not(revoked_at: nil) } |
Instance Method Details
#active? ⇒ Boolean
89 90 91 |
# File 'app/models/api_authentication.rb', line 89 def active? !expired? && !revoked? end |
#can_access_service?(service_key) ⇒ Boolean
Check if this token has access to a given upstream service
130 131 132 |
# File 'app/models/api_authentication.rb', line 130 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)
135 136 137 138 |
# File 'app/models/api_authentication.rb', line 135 def effective_services services = permitted_services.presence || DEFAULT_SERVICES (services | DEFAULT_SERVICES).sort end |
#expired? ⇒ Boolean
81 82 83 |
# File 'app/models/api_authentication.rb', line 81 def expired? expires_at <= Time.current end |
#masked_token ⇒ Object
Masked token for display (only show first/last 4 chars)
113 114 115 116 117 118 119 120 |
# File 'app/models/api_authentication.rb', line 113 def masked_token return nil unless api_authentication_token.present? token = api_authentication_token return token if token.length <= 12 "#{token[0..3]}...#{token[-4..]}" end |
#revoke! ⇒ Object
93 94 95 |
# File 'app/models/api_authentication.rb', line 93 def revoke! update!(revoked_at: Time.current) end |
#revoked? ⇒ Boolean
85 86 87 |
# File 'app/models/api_authentication.rb', line 85 def revoked? revoked_at.present? end |
#service_labels ⇒ Object
Human-readable labels for the permitted services
141 142 143 |
# File 'app/models/api_authentication.rb', line 141 def service_labels effective_services.filter_map { |key| UPSTREAM_SERVICES.dig(key, :label) } end |
#status ⇒ Object
97 98 99 100 101 102 |
# File 'app/models/api_authentication.rb', line 97 def status return 'revoked' if revoked? return 'expired' if expired? 'active' end |
#status_badge_class ⇒ Object
104 105 106 107 108 109 110 |
# File 'app/models/api_authentication.rb', line 104 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
123 124 125 126 127 |
# File 'app/models/api_authentication.rb', line 123 def time_until_expiration return 'Expired' if expired? distance_of_time_in_words(Time.current, expires_at) end |