Module: ApiKeys::Models::Concerns::HasApiKeys
- Extended by:
- ActiveSupport::Concern
- Defined in:
- lib/api_keys/models/concerns/has_api_keys.rb
Overview
Concern to add API key capabilities to an owner model (e.g., User, Organization).
This module provides the has_api_keys class method when extended onto ActiveRecord::Base.
Defined Under Namespace
Modules: ClassMethods Classes: DslProvider
Instance Method Summary collapse
-
#available_api_key_scopes ⇒ Array<String>
Returns the available scopes for API keys on this owner.
-
#can_create_api_key?(key_type: nil, environment: nil) ⇒ Boolean
Checks if this owner can create an API key of the given type.
-
#create_api_key!(name: nil, scopes: nil, expires_at: nil, expires_at_preset: nil, metadata: nil, key_type: nil, environment: nil) ⇒ ApiKeys::ApiKey
Creates a new API key for this owner instance and returns the ApiKey instance.
Instance Method Details
#available_api_key_scopes ⇒ Array<String>
Returns the available scopes for API keys on this owner. Uses owner-specific settings if defined, otherwise falls back to global config. Useful for populating scope checkboxes in forms.
103 104 105 106 |
# File 'lib/api_keys/models/concerns/has_api_keys.rb', line 103 def available_api_key_scopes owner_settings = self.class.api_keys_settings owner_settings&.[](:default_scopes) || ApiKeys.configuration.default_scopes || [] end |
#can_create_api_key?(key_type: nil, environment: nil) ⇒ Boolean
Checks if this owner can create an API key of the given type. Returns false if the limit for this key type/environment is reached. Useful for conditional UI (e.g., hiding "Create" button when at limit).
Note: This is a best-effort check for UI purposes without locking. Concurrent requests could see stale data. The actual limit is enforced with pessimistic locking in create_api_key!, so this is safe to use for UI decisions (worst case: button shown but creation fails validation).
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/api_keys/models/concerns/has_api_keys.rb', line 126 def can_create_api_key?(key_type: nil, environment: nil) config = ApiKeys.configuration # If no key_type specified, check global quota only return within_global_quota? if key_type.nil? # Resolve environment resolved_environment = resolve_environment(environment, key_type, config) # Get type-specific limit type_config = config.key_types&.dig(key_type.to_sym) return within_global_quota? unless type_config limit = type_config[:limit] return within_global_quota? unless limit # Count existing keys of this type/environment existing_count = api_keys .active .where(key_type: key_type.to_s) .where(environment: resolved_environment.to_s) .count existing_count < limit && within_global_quota? end |
#create_api_key!(name: nil, scopes: nil, expires_at: nil, expires_at_preset: nil, metadata: nil, key_type: nil, environment: nil) ⇒ ApiKeys::ApiKey
Creates a new API key for this owner instance and returns the ApiKey instance. Raises ActiveRecord::RecordInvalid if creation fails.
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
# File 'lib/api_keys/models/concerns/has_api_keys.rb', line 170 def create_api_key!(name: nil, scopes: nil, expires_at: nil, expires_at_preset: nil, metadata: nil, key_type: nil, environment: nil) config = ApiKeys.configuration # Parse expires_at_preset if provided (takes precedence over expires_at) if expires_at_preset.present? expires_at = ApiKeys::Helpers::ExpirationOptions.parse(expires_at_preset) end # Auto-clean scopes: remove blank values from arrays (common when using form checkboxes) if scopes.is_a?(Array) scopes = scopes.reject { |s| s.blank? } end # Check for missing columns if key_types feature is enabled if key_types_feature_enabled?(config) check_required_columns! end # Use default_key_type if not specified and key_types feature is enabled resolved_key_type = key_type if resolved_key_type.nil? && key_types_feature_enabled?(config) && config.default_key_type.present? resolved_key_type = config.default_key_type end # Validate key_type if provided and key_types feature is enabled if resolved_key_type.present? validate_key_type!(resolved_key_type, config) end # Determine environment: use provided, or default from config resolved_environment = resolve_environment(environment, resolved_key_type, config) # Validate environment if key_types feature is enabled if resolved_environment.present? && key_types_feature_enabled?(config) validate_environment!(resolved_environment, config) end # Fetch default scopes from this owner class's settings, falling back to global config. owner_settings = self.class.api_keys_settings default_scopes = owner_settings&.[](:default_scopes) || config.default_scopes || [] # Use provided scopes if given, otherwise use the calculated defaults. key_scopes = scopes.nil? ? default_scopes : Array(scopes) # Filter scopes based on key type permissions ceiling if resolved_key_type.present? key_scopes = (key_scopes, resolved_key_type, config) end # Create the key using the association, letting AR handle owner_id/type. api_key = self.api_keys.create!( name: name, scopes: key_scopes, expires_at: expires_at, metadata: || {}, # Ensure metadata is at least an empty hash key_type: resolved_key_type&.to_s, environment: resolved_environment&.to_s # prefix, token_digest, digest_algorithm are set by ApiKey callbacks ) # Return the ApiKey instance itself. # The plaintext token is available via `api_key.token` immediately after this. api_key end |