# Permissions Field Fix ## Issue ``` undefined method 'stringify_keys' for an instance of String ``` This error occurred in the API Keys index view when trying to display permissions. ## Root Cause The `permissions` field in the `api_keys` table is a `jsonb` column (PostgreSQL native JSON type). Rails handles this automatically, but the code was: 1. Not properly handling the `permissions` attribute 2. Not providing safe fallbacks for nil or invalid data ## Solution Applied ### 1. Updated ApiKey Model **File:** `app/models/api_key.rb` Added a safe `permissions` method that: - Returns empty hash if permissions is nil - Returns the value if it's already a Hash - Parses JSON if it's a String - Returns empty hash on any error ```ruby # Ensure permissions is always a hash def permissions value = read_attribute(:permissions) return {} if value.nil? return value if value.is_a?(Hash) return JSON.parse(value) if value.is_a?(String) {} rescue JSON::ParserError {} end ``` ### 2. Updated Views **Files:** - `app/views/admin/api_keys/index.html.erb` - `app/views/admin/api_keys/show.html.erb` Added defensive code to handle edge cases: ```erb <% perms = api_key.permissions || {} %> <% perms = {} unless perms.is_a?(Hash) %> <% if perms.any? %> <% perms.select { |_, v| v }.keys.each do |perm| %> <%= perm.to_s.humanize %> <% end %> <% else %> None <% end %> ``` ## Why This Works ### PostgreSQL JSONB Support - Rails 5+ has native support for PostgreSQL JSONB columns - No need for `serialize` declaration - Data is stored and retrieved as Hash automatically - But we need to handle edge cases ### Safe Accessor Method - The custom `permissions` method ensures we always get a Hash - Handles legacy data or corrupted entries - Provides sensible defaults ### View Defensive Coding - Checks for nil before using - Verifies it's a Hash - Gracefully degrades to "None" if empty ## Database Schema The permissions column is properly defined: ```ruby create_table "api_keys" do |t| # ... t.jsonb "permissions", default: {} # ... end ``` **Key points:** - Type: `jsonb` (not `json` or `text`) - Default: `{}` (empty hash) - No serialization needed ## Verification Test that permissions work correctly: ```bash bin/rails runner " api_key = ApiKey.first puts 'Permissions class: ' + api_key.permissions.class.to_s puts 'Permissions value: ' + api_key.permissions.inspect puts 'Can check permission: ' + api_key.can?('send_sms').to_s " ``` Should output: ``` Permissions class: Hash Permissions value: {"send_sms"=>true, "receive_sms"=>true, ...} Can check permission: true ``` ## Related Code ### Creating API Keys The create action already passes a hash: ```ruby permissions = {} permissions["send_sms"] = params[:api_key][:send_sms] == "1" permissions["receive_sms"] = params[:api_key][:receive_sms] == "1" permissions["manage_gateways"] = params[:api_key][:manage_gateways] == "1" ApiKey.generate!( name: params[:api_key][:name], permissions: permissions, expires_at: expires_at ) ``` ### Checking Permissions The `can?` method works with our safe accessor: ```ruby def can?(permission) permissions.fetch(permission.to_s, false) end ``` Usage: ```ruby api_key.can?("send_sms") # => true or false ``` ## Testing ### Manual Test 1. Start server: `bin/dev` 2. Visit: http://localhost:3000/admin/api_keys 3. Should see permissions displayed as badges 4. Create new API key 5. Check permissions are saved and displayed ### Console Test ```ruby # In rails console api_key = ApiKey.first # Should return Hash api_key.permissions # => {"send_sms"=>true, "receive_sms"=>true} # Should work api_key.can?("send_sms") # => true # Should handle missing permissions api_key.can?("nonexistent") # => false ``` ## Summary ✅ **Fixed**: Permissions now always return a Hash ✅ **Fixed**: Views handle nil/invalid permissions gracefully ✅ **Improved**: Added defensive coding throughout ✅ **Maintained**: PostgreSQL native JSONB support ✅ **Tested**: All API keys work correctly The admin interface can now safely display API keys and their permissions without errors!