一键导入
grant-security-features
Grant ORM security features including encrypted attributes, secure tokens, signed IDs, token generators, and data normalization.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Grant ORM security features including encrypted attributes, secure tokens, signed IDs, token generators, and data normalization.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Grant ORM advanced features including enum attributes, dirty tracking, serialized columns, value objects, horizontal sharding, and async operations.
Grant ORM associations including belongs_to, has_one, has_many, has_many through, polymorphic, dependent options, counter_cache, eager loading, and nested attributes.
Grant ORM lifecycle callbacks, transaction blocks, nested transactions with savepoints, isolation levels, optimistic and pessimistic locking.
Create, read, update, and delete operations in Grant ORM including bulk operations, batch processing, upserts, and convenience methods.
Generate a new Grant ORM model file with proper structure, column definitions, validations, associations, and callbacks.
Defining Grant ORM models with columns, types, primary keys, timestamps, connections, converters, and serialization.
| name | grant-security-features |
| description | Grant ORM security features including encrypted attributes, secure tokens, signed IDs, token generators, and data normalization. |
| user-invocable | false |
Grant provides transparent encryption using AES-256-CBC with HMAC-SHA256 (Encrypt-then-MAC).
Grant::Encryption.configure do |config|
config.primary_key = ENV["GRANT_ENCRYPTION_PRIMARY_KEY"]
config.deterministic_key = ENV["GRANT_ENCRYPTION_DETERMINISTIC_KEY"]
config.key_derivation_salt = ENV["GRANT_ENCRYPTION_SALT"]
config.support_unencrypted_data = true # Useful during migration
end
Generate secure keys:
key = Grant::Encryption::Config.generate_key
puts "GRANT_ENCRYPTION_PRIMARY_KEY=#{key}"
class User < Grant::Base
connection sqlite
table users
column id : Int64, primary: true
column email : String
# Non-deterministic (default) -- each encryption produces different ciphertext
encrypts :ssn
encrypts :credit_card_number
# Deterministic -- same plaintext always produces same ciphertext (queryable)
encrypts :phone_number, deterministic: true
end
user = User.new(email: "john@example.com", ssn: "123-45-6789", phone_number: "+1-555-0123")
user.save!
user.ssn # => "123-45-6789" (automatically decrypted)
# Update encrypted data
user.ssn = "987-65-4321"
user.save!
# Nil values store nil, no encryption
user.ssn = nil
user.save!
Deterministic fields can be queried:
User.where(phone_number: "+1-555-0123").first
User.find_by(phone_number: "+1-555-0123")
User.where_encrypted(phone_number: "+1-555-0123", status: "active")
Non-deterministic fields cannot be queried directly (load and filter in memory):
users = User.all.select { |u| u.ssn == "123-45-6789" }
| Type | Use When | Security | Queryable |
|---|---|---|---|
| Non-deterministic (default) | SSN, credit cards, medical records | Maximum | No |
| Deterministic | Email, phone, account numbers you must search | Trade-off (reveals patterns) | Yes |
Grant::Encryption.configure do |config|
config.primary_key = new_key
config.deterministic_key = new_det_key
end
Grant::Encryption::MigrationHelpers.rotate_encryption(
User, :ssn,
old_keys: { primary: old_key, deterministic: old_det_key },
batch_size: 1000
)
# Encrypt existing plaintext data
Grant::Encryption::MigrationHelpers.encrypt_column(User, :ssn, batch_size: 1000, progress: true)
# Decrypt back to plaintext
Grant::Encryption::MigrationHelpers.decrypt_column(User, :ssn, target_column: :ssn_plain)
The Grant::SecureToken module provides automatic generation of cryptographically secure random tokens.
class User < Grant::Base
include Grant::SecureToken
has_secure_token :auth_token
has_secure_token :password_reset_token, length: 36
has_secure_token :api_key, alphabet: :hex
end
| Option | Description | Default |
|---|---|---|
length: | Token length | 24 |
alphabet: | Character set | :base58 |
Alphabet choices:
:base58 (default) -- Bitcoin-style, no confusing characters (0/O, I/l):hex -- Hexadecimal:base64 -- URL-safe Base64user = User.create(name: "John")
user.auth_token # => "pX27zsMN2ViQKta1bGfLmVJE" (auto-generated on create)
# Regenerate
user.regenerate_auth_token
user.save
The Grant::SignedId module provides tamper-proof, optionally expiring IDs for secure URL generation. Uses HMAC-SHA256 with your application secret.
class User < Grant::Base
include Grant::SignedId
end
# Generate with purpose (prevents token reuse across contexts)
signed_id = user.signed_id(purpose: :password_reset)
# With expiration
signed_id = user.signed_id(purpose: :password_reset, expires_in: 15.minutes)
# Permanent (no expiration)
signed_id = user.signed_id(purpose: :login_token)
# Find by signed ID
user = User.find_signed(signed_id, purpose: :password_reset)
# Returns nil if expired, tampered, or wrong purpose
Set GRANT_SIGNING_SECRET environment variable for the signing key:
export GRANT_SIGNING_SECRET="your-secret-key-here"
The Grant::TokenFor module generates tokens that automatically invalidate when specific data changes.
class User < Grant::Base
include Grant::TokenFor
generates_token_for :password_reset, expires_in: 15.minutes do
password_salt # Token invalidates when password_salt changes
end
generates_token_for :email_confirmation, expires_in: 24.hours do
email # Token invalidates when email changes
end
end
# Generate
token = user.generate_token_for(:password_reset)
reset_url = "https://example.com/reset?token=#{token}"
# Find (returns nil if expired or data changed)
user = User.find_by_token_for(:password_reset, token)
class User < Grant::Base
connection sqlite
table users
include Grant::SecureToken
include Grant::SignedId
include Grant::TokenFor
column id : Int64, primary: true
column name : String
column email : String
column password_digest : String
column password_salt : String
# Encrypted attributes
encrypts :ssn
encrypts :phone_number, deterministic: true
# Auto-generated tokens
has_secure_token :auth_token
has_secure_token :api_key, alphabet: :hex, length: 32
# Invalidating token generators
generates_token_for :password_reset, expires_in: 15.minutes do
password_salt
end
generates_token_for :email_confirmation, expires_in: 24.hours do
email
end
end
The Grant::Normalization module provides automatic data normalization that runs before validation.
class User < Grant::Base
include Grant::Normalization
column id : Int64, primary: true
column email : String?
column phone : String?
normalizes :email do |value|
value.downcase.strip
end
normalizes :phone do |value|
value.gsub(/\D/, "")
end
end
Normalizations run during validation (via before_validation), NOT at assignment time:
user = User.new
user.email = " JOHN@EXAMPLE.COM "
user.email # => " JOHN@EXAMPLE.COM " (unchanged)
user.valid?
user.email # => "john@example.com" (normalized after validation)
normalizes :website, if: :website_present? do |value|
value.starts_with?("http") ? value : "https://#{value}"
end
user.valid?(skip_normalization: true)
If normalization returns the value to its original state, the attribute is NOT considered changed:
user = User.create(email: "test@example.com")
user.email = " TEST@EXAMPLE.COM "
user.email_changed? # => true
user.valid?
user.email # => "test@example.com" (normalized back)
user.email_changed? # => false