Module: Apartment::TenantNameValidator
- Defined in:
- lib/apartment/tenant_name_validator.rb
Class Method Summary collapse
-
.validate!(name, strategy:, adapter_name: nil) ⇒ Object
Validate a tenant name against common and engine-specific rules.
-
.validate_common!(name) ⇒ Object
— Common rules (all engines) —.
-
.validate_for_adapter!(name, adapter_name) ⇒ Object
— Dispatcher for :database_name strategy —.
-
.validate_mysql_database_name!(name) ⇒ Object
— MySQL database names — Max 64 chars, allowed: [a-zA-Z0-9_$-], no leading digit, no trailing dot.
-
.validate_postgresql_identifier!(name) ⇒ Object
— PostgreSQL identifiers (schema names, database names) — Hyphens are allowed — our adapters quote via quote_table_name.
-
.validate_sqlite_path!(name) ⇒ Object
— SQLite file paths — No path traversal, filesystem-safe characters.
Class Method Details
.validate!(name, strategy:, adapter_name: nil) ⇒ Object
Validate a tenant name against common and engine-specific rules. Raises ConfigurationError on invalid names. Pure in-memory check — no IO.
9 10 11 12 13 14 15 16 17 18 |
# File 'lib/apartment/tenant_name_validator.rb', line 9 def validate!(name, strategy:, adapter_name: nil) validate_common!(name) case strategy when :schema validate_postgresql_identifier!(name) when :database_name validate_for_adapter!(name, adapter_name) end # :shard and :database_config use common validation only (not yet implemented). end |
.validate_common!(name) ⇒ Object
— Common rules (all engines) —
22 23 24 25 26 27 28 29 30 31 |
# File 'lib/apartment/tenant_name_validator.rb', line 22 def validate_common!(name) raise(ConfigurationError, 'Tenant name must be a String') unless name.is_a?(String) raise(ConfigurationError, 'Tenant name cannot be empty') if name.empty? raise(ConfigurationError, "Tenant name contains NUL byte: #{name.inspect}") if name.include?("\x00") raise(ConfigurationError, "Tenant name contains whitespace: #{name.inspect}") if name.match?(/\s/) raise(ConfigurationError, "Tenant name contains colon: #{name.inspect}") if name.include?(':') return unless name.length > 255 raise(ConfigurationError, "Tenant name too long (#{name.length} chars, max 255): #{name.inspect}") end |
.validate_for_adapter!(name, adapter_name) ⇒ Object
— Dispatcher for :database_name strategy —
79 80 81 82 83 84 85 |
# File 'lib/apartment/tenant_name_validator.rb', line 79 def validate_for_adapter!(name, adapter_name) case adapter_name when /mysql/i, /trilogy/i then validate_mysql_database_name!(name) when /postgresql/i, /postgis/i then validate_postgresql_identifier!(name) when /sqlite/i then validate_sqlite_path!(name) end end |
.validate_mysql_database_name!(name) ⇒ Object
— MySQL database names — Max 64 chars, allowed: [a-zA-Z0-9_$-], no leading digit, no trailing dot.
54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/apartment/tenant_name_validator.rb', line 54 def validate_mysql_database_name!(name) if name.length > 64 raise(ConfigurationError, "MySQL database name too long (#{name.length} chars, max 64): #{name.inspect}") end raise(ConfigurationError, "MySQL database name cannot start with a digit: #{name.inspect}") if name.match?(/\A\d/) raise(ConfigurationError, "MySQL database name cannot end with a period: #{name.inspect}") if name.end_with?('.') return unless name.match?(/[^a-zA-Z0-9_$-]/) raise(ConfigurationError, "Invalid MySQL database name: #{name.inspect}. " \ 'Allowed characters: letters, digits, underscore, dollar sign, hyphen') end |
.validate_postgresql_identifier!(name) ⇒ Object
— PostgreSQL identifiers (schema names, database names) — Hyphens are allowed — our adapters quote via quote_table_name. Cannot start with pg_ (reserved prefix).
37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/apartment/tenant_name_validator.rb', line 37 def validate_postgresql_identifier!(name) if name.length > 63 raise(ConfigurationError, "PostgreSQL identifier too long (#{name.length} chars, max 63): #{name.inspect}") end unless name.match?(/\A[a-zA-Z_][a-zA-Z0-9_-]*\z/) raise(ConfigurationError, "Invalid PostgreSQL identifier: #{name.inspect}. " \ 'Must start with letter/underscore, contain only letters, digits, underscores, hyphens') end return unless name.start_with?('pg_') raise(ConfigurationError, "Tenant name cannot start with 'pg_' (reserved prefix): #{name.inspect}") end |
.validate_sqlite_path!(name) ⇒ Object
— SQLite file paths — No path traversal, filesystem-safe characters.
70 71 72 73 74 75 |
# File 'lib/apartment/tenant_name_validator.rb', line 70 def validate_sqlite_path!(name) raise(ConfigurationError, "SQLite tenant name contains path traversal: #{name.inspect}") if name.include?('..') return unless name.match?(%r{[/\\]}) raise(ConfigurationError, "SQLite tenant name contains path separators: #{name.inspect}") end |