diff --git a/app/abilities/api_key_ability.rb b/app/abilities/api_key_ability.rb
new file mode 100644
index 00000000..57af40e7
--- /dev/null
+++ b/app/abilities/api_key_ability.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+class ApiKeyAbility
+ include CanCan::Ability
+ def initialize(api_key)
+ return if api_key.blank?
+
+ can :read, :all
+ end
+end
diff --git a/app/models/ability.rb b/app/abilities/user_ability.rb
similarity index 96%
rename from app/models/ability.rb
rename to app/abilities/user_ability.rb
index c8c19ad7..95120bda 100644
--- a/app/models/ability.rb
+++ b/app/abilities/user_ability.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-class Ability
+class UserAbility
include CanCan::Ability
def initialize(user)
diff --git a/app/controllers/api_keys_controller.rb b/app/controllers/api_keys_controller.rb
new file mode 100644
index 00000000..c37b53ee
--- /dev/null
+++ b/app/controllers/api_keys_controller.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+class ApiKeysController < ApplicationController
+ include ApiKeyAuthenticatable
+ include SessionsHelper
+
+ # Require token authentication for index
+
+ def index
+ @api_keys = ApiKey.accessible_by(current_ability)
+ authorize! :index, @api_keys
+ end
+
+ def new
+ @api_key = ApiKey.new
+ authorize! :new, @api_key
+ end
+
+ def create
+ @api_key = ApiKey.new(api_key_params)
+ authorize! :create, @api_key
+ respond_to do |format|
+ if @api_key.save
+ format.html do
+ flash[:success] = "ApiKey added! It is #{@api_key.key}"
+ redirect_to api_keys_url
+ end
+ else
+ format.html { render 'new', status: :unprocessable_entity }
+ end
+ end
+ end
+
+ def destroy
+ @api_key = ApiKey.find(params[:id])
+ authorize! :destroy, @api_key
+ @api_key.destroy
+ flash[:success] = 'ApiKey deleted!'
+ redirect_to api_keys_url
+ end
+
+ private
+
+ def api_key_params
+ params.require(:api_key).permit(:bearer_name)
+ end
+end
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index f61825df..879252f8 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -2,12 +2,32 @@
class ApplicationController < ActionController::Base
include SessionsHelper
+ include ApiKeyAuthenticatable
before_action :still_authenticated?
+ before_action :api_auth
+
+ def current_ability
+ @current_ability ||= if !session[:api_key_id].nil?
+
+ ApiKeyAbility.new(current_bearer)
+ elsif !session[:user_id].nil?
+ UserAbility.new(current_user)
+ else
+ UserAbility.new(nil)
+ end
+ end
private
def still_authenticated?
log_out if should_log_out?
end
+
+ def api_auth
+ return unless request.path[0, 5] == '/api/'
+
+ current_bearer = authenticate_or_request_with_http_token { |token, _options| authenticator(token) }
+ log_in_api current_bearer
+ end
end
diff --git a/app/controllers/concerns/api_key_authenticatable.rb b/app/controllers/concerns/api_key_authenticatable.rb
new file mode 100644
index 00000000..7ae5f253
--- /dev/null
+++ b/app/controllers/concerns/api_key_authenticatable.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module ApiKeyAuthenticatable
+ extend ActiveSupport::Concern
+
+ include ActionController::HttpAuthentication::Basic::ControllerMethods
+ include ActionController::HttpAuthentication::Token::ControllerMethods
+
+ attr_reader :current_api_key
+
+ private
+
+ attr_writer :current_api_key
+
+ def authenticator(http_token)
+ @current_api_key = ApiKey.authenticate_by_token! http_token
+
+ current_api_key
+ end
+end
diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb
index 75c5394d..27613cc6 100644
--- a/app/controllers/sessions_controller.rb
+++ b/app/controllers/sessions_controller.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
class SessionsController < ApplicationController
+ include ApiKeyAuthenticatable
def create
user = User.upsert_from_auth_hash(request.env['omniauth.auth'])
log_in user
diff --git a/app/helpers/api_keys_helper.rb b/app/helpers/api_keys_helper.rb
new file mode 100644
index 00000000..fa0342fd
--- /dev/null
+++ b/app/helpers/api_keys_helper.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+module ApiKeysHelper
+end
diff --git a/app/helpers/sessions_helper.rb b/app/helpers/sessions_helper.rb
index c4c33079..077643d6 100644
--- a/app/helpers/sessions_helper.rb
+++ b/app/helpers/sessions_helper.rb
@@ -9,8 +9,15 @@ def current_user
@current_user
end
+ def current_bearer
+ return nil if session[:api_key_id].nil?
+
+ @current_bearer = ApiKey.find(session[:api_key_id])
+ @current_bearer
+ end
+
def logged_in?
- !current_user.nil?
+ !current_user.nil? || !current_bearer.nil?
end
def log_in(user)
@@ -20,6 +27,12 @@ def log_in(user)
session[:groups] = user.groups
end
+ def log_in_api(bearer)
+ reset_session # For security reasons, we clear the session data before login
+ session[:api_key_id] = bearer.id
+ session[:expires_at] = Time.current
+ end
+
# TODO: also logout of sso
def log_out
reset_session
diff --git a/app/models/api_key.rb b/app/models/api_key.rb
new file mode 100644
index 00000000..5ca7ae61
--- /dev/null
+++ b/app/models/api_key.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+class ApiKey < ApplicationRecord
+ HMAC_SECRET_KEY = Rails.application.credentials.api_key_hmac_secret_key!
+
+ validates :bearer_name, presence: true, allow_blank: false
+
+ before_create :generate_token_hmac_digest
+
+ attr_accessor :key
+
+ def self.authenticate_by_token!(key)
+ digest = OpenSSL::HMAC.hexdigest 'SHA256', HMAC_SECRET_KEY, key
+
+ find_by! api_key: digest
+ end
+
+ private
+
+ def generate_token_hmac_digest
+ @key = SecureRandom.hex(32)
+
+ digest = OpenSSL::HMAC.hexdigest 'SHA256', HMAC_SECRET_KEY, @key
+ self.api_key = digest
+ end
+end
diff --git a/app/views/api_keys/_api_key.html.erb b/app/views/api_keys/_api_key.html.erb
new file mode 100644
index 00000000..712d301d
--- /dev/null
+++ b/app/views/api_keys/_api_key.html.erb
@@ -0,0 +1,11 @@
+<%# locals: (api_key:) -%>
+
+
+ <%= api_key.bearer_name %>
+ <%=api_key.created_at %>
+ <% if can?(:destroy, api_key) %>
+ <%= button_to(api_key,method: :delete, data: { turbo_confirm: "Are you sure ?" }, 'aria-label': 'Delete this api key') do %>
+ <%= svg_icon_tag 'icon_delete' %>
+ <% end %>
+ <% end %>
+
diff --git a/app/views/api_keys/_form.html.erb b/app/views/api_keys/_form.html.erb
new file mode 100644
index 00000000..7f0a3b61
--- /dev/null
+++ b/app/views/api_keys/_form.html.erb
@@ -0,0 +1,11 @@
+<%# locals: () -%>
+
+<%= form_with(model: @api_key, class: 'form') do |f| %>
+
+ <%= f.label :bearer_name %>
+ <%= f.text_field :bearer_name, required: true %>
+
+
+ <%= f.submit yield(:button_text) %>
+
+<% end %>
diff --git a/app/views/api_keys/index.html.erb b/app/views/api_keys/index.html.erb
new file mode 100644
index 00000000..1a25ca21
--- /dev/null
+++ b/app/views/api_keys/index.html.erb
@@ -0,0 +1,30 @@
+<%# locals: () -%>
+
+
+
+
+
+
+
+
+ <%# Need to pass an instance for an ability with block https://github.com/CanCanCommunity/cancancan/blob/develop/docs/define_abilities_with_blocks.md %>
+ <% if can?(:create, ApiKey) %>
+ <%= render "components/buttons/button_primary_create_api", text: "new api key", path: new_api_key_path, aria_label: "Add a new api key" %>
+ <% end %>
+
+
+
+
+
+
+
+ <%= render(@api_keys) || "No bearers of an api key" %>
+
+
+
+
\ No newline at end of file
diff --git a/app/views/api_keys/index.json.jbuilder b/app/views/api_keys/index.json.jbuilder
new file mode 100644
index 00000000..0f983e97
--- /dev/null
+++ b/app/views/api_keys/index.json.jbuilder
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+# locals: ()
+
+json.array! @api_keys, :id, :bearer_name, :created_at, :updated_at
diff --git a/app/views/api_keys/new.html.erb b/app/views/api_keys/new.html.erb
new file mode 100644
index 00000000..680f8644
--- /dev/null
+++ b/app/views/api_keys/new.html.erb
@@ -0,0 +1,7 @@
+<%# locals: () -%>
+
+<% provide :button_text, "Create" %>
+
+ApiKey#new
+
+<%= render 'form' %>
\ No newline at end of file
diff --git a/app/views/components/buttons/_button_primary_create_api.html.erb b/app/views/components/buttons/_button_primary_create_api.html.erb
new file mode 100644
index 00000000..4934c8dc
--- /dev/null
+++ b/app/views/components/buttons/_button_primary_create_api.html.erb
@@ -0,0 +1,6 @@
+<%# locals: (aria_label:, path:, text:) -%>
+
+<%= button_to(path, method: :get, class: "button-primary", 'aria-label': aria_label) do %>
+ <%= text %>
+ <%= svg_icon_tag 'icon_plus' %>
+<% end %>
diff --git a/app/views/layouts/_header.html.erb b/app/views/layouts/_header.html.erb
index cae3c4a1..2798a96a 100644
--- a/app/views/layouts/_header.html.erb
+++ b/app/views/layouts/_header.html.erb
@@ -10,6 +10,9 @@
<%= link_to "Users", users_path %>
+
+ <%= link_to "Api Keys", api_keys_url %>
+
<% end %>
diff --git a/config/credentials.yml.enc b/config/credentials.yml.enc
index 69782882..7859e17b 100644
--- a/config/credentials.yml.enc
+++ b/config/credentials.yml.enc
@@ -1 +1 @@
-VfC7hNP55EjMyi4vt8jK8lAaRmOkql+wsyAFxSVsQdVT8tbZk1l0dgyELsvoVfHwU9kqzI+wMIFyuH6MoXBhcghZfa6m5g683FM06BDkYPwwDflEVeox0DvWmgGji4qzk3oFe57T9qQT736mz3dWfeQTzvjHnaUpq27gYQTpQHOuELjYwKsMXbFRFiNNMmG5phiG2k0Asc7dqZ8CRPwmhJYPm5aJBc9Bzrz3ebBnhxmZ+JzZ8AsZnnvnAFnylm2jRwgN91kJE9l3fkWVTlF6rm0EoCJ9r3neibTeDu2PtNnkYISp3O7chrBJKo6FS9GFAcOxgkxB8QPiX2TV2s3jPiNyxffxLqUe+jziPP4dDiHdZvKrONIltblTJV2WTzur7/82fCEhQUf2oM7uwavq/yvQNOlay4nUD9TyBGZrs1fyFuulT4Ik2/w4T7usUC6am1xIzB2ITF8IuAgolkT/5EPsIs45gJEpJy9Fqlg1PAv01NN9hhViEl+A--tARvrsUmwyVJ9/XI--ytpULZnEcYeOSqfUYhHAnA==
\ No newline at end of file
+Ctjn6rzrT8lN8apnt2Pd02BtoVstxzsZTgu7DACFn3A6pXt5rbEb1Z79Tcx5R6t2lB9MbLauB/DwUyS9JdzjzKeQQnGvGDHo0j9/vR1I1N3m3+sEXOuPNihRzzCLgePkh4jYtqNZ8NK1MdjmeG6kfPvB4fYNgLz+LWHudMLDhW98Ee3U6yso/edEMlXolmXyp3C4LeyqW/4qVPqaonUU4LJpYeG+UZndxjifEU8vpIhY4dW10mVpdO5JSnteNbtRBc/ZojuVla69hLEgYyr1uiyoy23ArtztfpyXhZEc6cmuJoXi7gpJd9sIDOZf2/xwasRW1EkI3NgpREj4Gy1t7lSwWLf8vp4j3qpC3/ifnGUUNAoJfBjax18WVOfzf2z051Q1TRN0tMhTknihNcUjAer54eT+JYHv7PLbxACcHs4evI/mnuglBOvI9DggODsUIe/7BqM0oQ0j1lj0/m7FHnexdc8FFHrrZ5IXN3VvZ6E0dAEMXNY8gS66mNjNEf2jfXKRicG/eu0FHYRiR00QgtVKsrggUy5IQnLc5Nj3lAq74Bi53UFDQI/AnaIA6uGQtf2lG3XTyfaadwMA6hOFKSQtVd1/OVJAptFIyJkkMiBiJAcU--rNRzrZK7K/fPFPxc--fNlBrjqZoGt/U748IRtjng==
\ No newline at end of file
diff --git a/config/credentials/production.yml.enc b/config/credentials/production.yml.enc
index 54ee9261..a9ab69d6 100644
--- a/config/credentials/production.yml.enc
+++ b/config/credentials/production.yml.enc
@@ -1 +1 @@
-QF2+HIDkGNTPjx81hqlGjQAZyIzZVAp9c7J+dWPqlggPt9u/LWhxWWi6vY7KXGYe3opGafwsWlOgrO0BXtzbOg+RK+Bq9a8rUgsKchqHi44B487Y9/rTvS5bcCTJj5c78AJFdZCsEdOXqUv6khBGHB2i8fGQ5LZyh/SFJhmyt3QqGuL6Ig4MsuD/275o2M94CjrlXT2nNGe6BPl94GurtQv7s67bbgrSy3N2f0Kc+VR4VfrIhTwgkpXxEBv7vk/ok/1WvYpqRbFNuyPhiqkZ6Rj+0oK7JCxtFDL15tBtrIRbdpNuBeWO8BQwf/CfvcgBsJnOGg//LU/AJe+ndMCdUzdfVpuB1QKG4A62lmrLNOEwlw/K3JdPsWqPEh6rSVIr5nAzl4oaxMo0NZs7VK7ZhNpGxkvdNVQEZDxchZkaanQOIFU02240w3nMHYx5aed1MEj2ZpuR26Jg+W85m3K0BvsCDcXAouLTvMCB8kGZ8H6SOWExIdbIhmM3--x6H3t+K9w244qMLp--x+vXvS3cObYjuhqsAb4HYA==
\ No newline at end of file
+Zmnkjz2oYau2HVmSP0GPQ/fzdTezrNhjFyIiP7okk4UZoOATR8X1mbTwulJF0/1vmtmoCr8jTAPWLfRY+0SwZAidd7yh16vhWCy6mpd5/OMeupQencSQCl3T0wgkkMtIa/J64DbfRav8BPhPBHi4St+1x62FM+WRw0udfj+xA2AUPxtIy/FZXIm+WUHcj6wQKXvIY2xJ1YcpA2WAsmow+1d1Aps1UWzMm0pwysm4SsUCDVS3nloaNTQ25g5+0HZ49biY7nbU6E1IM6dsgwK2/yRrrPxgDpv22L/r3BNp1wv0u4XP5LG+/yfaRfcdaO34WegffGsGEbuBf+hJ/fmxEJ3kq3Uk7DVlbFft94kIfzhHPOU/fi6EX4pWwrS4hExJ43X6/nEMtUY2b6ZXQDLazHv6rI2FcVzsSP+CRQwFc3VU2sjiJbHoe9hh1Jz27d/8rc+BjAwiwwpzZyTsAUlFy92qLZIENJskOhiowZewErJIUmV31L1ImRw/NCCC/Qt+hE8VvTkYmlWFU++9v4wddd5u2GUHzmHh7h7QFUCgKYFeqZiZbi58ihfIn/j7mpdJVk6lc73C+zAMI52rN65bZ6pGySU1/IJMGkhPc181hVC8tdiryw==--ss5nXaT6eOm/wEgh--oHW8aumKNzcCbzU7XFQyXg==
\ No newline at end of file
diff --git a/config/credentials/test.key b/config/credentials/test.key
new file mode 100644
index 00000000..3684503d
--- /dev/null
+++ b/config/credentials/test.key
@@ -0,0 +1 @@
+151fe6f76326223cfb5f7cef5369c504
\ No newline at end of file
diff --git a/config/credentials/test.yml.enc b/config/credentials/test.yml.enc
new file mode 100644
index 00000000..ed715a92
--- /dev/null
+++ b/config/credentials/test.yml.enc
@@ -0,0 +1 @@
+mrjWeVMW0ZzGnTS1Yw+YRq/mJUTEyb7vTGp781GRYBv7s4XMK4eRkNrcG3jlb6StbugQFUa3yKSSUac1E/LHrtRgDS8FO7lkN3m3gnD/apzZ6QFQ+ub/Gzhgg9htCB2OSTwKwHINeCuog/wlxeGd3VGon0nNltGlYKv8Fet2W8KyMaOBNSFTwf6zv0jPrke1LRGiqAlNuaA=--w7kZ8Fb41WHlHnQB--mk0Xq4zeXvm7j1VYpZ39jQ==
\ No newline at end of file
diff --git a/config/initializers/_constants.rb b/config/initializers/_constants.rb
index 8008454c..0b0d27d6 100644
--- a/config/initializers/_constants.rb
+++ b/config/initializers/_constants.rb
@@ -1,5 +1,8 @@
# frozen_string_literal: true
+# Path to login via api key
+AUTH_API_PATH = '/auth/api'
+
# Path to login via SSO authentication
AUTH_PATH = '/auth/keycloak'
diff --git a/config/routes.rb b/config/routes.rb
index 767c301a..a40ac210 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -13,6 +13,14 @@
resources :free_accesses, shallow: true, except: [:index, :show]
end
+ resources :api_keys
+
+ scope 'api' do
+ resources :users, controller: :users, as: 'api_users'
+ resources :api_keys, controller: :api_keys, as: 'api_api_keys'
+ resources :machines, controller: :machines, as: 'api_machines'
+ end
+
get '/search', as: 'search', to: 'search#search'
# Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
diff --git a/db/migrate/20240531163901_api_keys.rb b/db/migrate/20240531163901_api_keys.rb
new file mode 100644
index 00000000..38373cf1
--- /dev/null
+++ b/db/migrate/20240531163901_api_keys.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+class ApiKeys < ActiveRecord::Migration[7.0]
+ def change
+ create_table :api_keys do |t|
+ t.integer :bearer_id, null: false, index: { unique: true }
+ t.string :bearer_name, null: false
+ t.string :api_key, null: false, index: { unique: true }
+ t.datetime :api_key_start_at, index: { unique: true }
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/migrate/20240602124750_fix_api_key.rb b/db/migrate/20240602124750_fix_api_key.rb
new file mode 100644
index 00000000..54fd225d
--- /dev/null
+++ b/db/migrate/20240602124750_fix_api_key.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+class FixApiKey < ActiveRecord::Migration[7.0]
+ def change
+ change_table :api_keys do |t|
+ t.remove :api_key_start_at,
+ type: :datetime
+ end
+ end
+end
diff --git a/db/migrate/20240603090057_fix_api_key_bearer_id.rb b/db/migrate/20240603090057_fix_api_key_bearer_id.rb
new file mode 100644
index 00000000..55bf2157
--- /dev/null
+++ b/db/migrate/20240603090057_fix_api_key_bearer_id.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+class FixApiKeyBearerId < ActiveRecord::Migration[7.0]
+ def change
+ change_table :api_keys do |t|
+ t.remove :bearer_id,
+ type: :integer
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 727a9128..545e0ca4 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,10 +10,18 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[7.0].define(version: 2024_05_31_195758) do
+ActiveRecord::Schema[7.1].define(version: 2024_06_03_090057) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
+ create_table "api_keys", force: :cascade do |t|
+ t.string "bearer_name", null: false
+ t.string "api_key", null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.index ["api_key"], name: "index_api_keys_on_api_key", unique: true
+ end
+
create_table "free_accesses", force: :cascade do |t|
t.bigint "user_id", null: false
t.datetime "start_at", null: false
@@ -48,8 +56,8 @@
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "user_id", null: false
- t.datetime "start_at", null: false
- t.datetime "end_at", null: false
+ t.datetime "start_at", precision: nil, null: false
+ t.datetime "end_at", precision: nil, null: false
t.virtual "duration", type: :integer, comment: "Duration in months", as: "((EXTRACT(year FROM age(date_trunc('months'::text, end_at), date_trunc('months'::text, start_at))) * (12)::numeric) + EXTRACT(month FROM age(date_trunc('months'::text, end_at), date_trunc('months'::text, start_at))))", stored: true
t.index ["user_id"], name: "index_subscriptions_on_user_id"
end
diff --git a/docs/features/api_authentication.md b/docs/features/api_authentication.md
new file mode 100644
index 00000000..bdc42bcf
--- /dev/null
+++ b/docs/features/api_authentication.md
@@ -0,0 +1,12 @@
+# API Authentication
+
+It is possible to authenticate using an API Key. To do so, an admin must generate an API key for a given entity.
+A GET request must be made to the /api endpoint and the api key must be given in the *Authorization* header.
+
+## Example using curl
+
+```
+curl GET http://lea5.fr/api/users.json -H 'Authorization: Bearer EnterApiKeyHere'
+```
+
+You will get the related json
\ No newline at end of file
diff --git a/test/controllers/api_keys_controller_right_test.rb b/test/controllers/api_keys_controller_right_test.rb
new file mode 100644
index 00000000..4c2e25fc
--- /dev/null
+++ b/test/controllers/api_keys_controller_right_test.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require 'test_helper'
+
+class ApiKeysControllerApiKeyRight < ActionDispatch::IntegrationTest
+ def setup
+ @bearer = api_keys(:FakeRadius)
+ @real_key = '5fcdb374f0a70e9ff0675a0ce4acbdf6d21225fe74483319c2766074732d6d80'
+ end
+
+ test 'api key bearers should be able to read api keys index' do
+ get '/api/api_keys.json', headers: { 'Authorization' => "Bearer #{@real_key}" }
+ result = @response.body
+ json = JSON(result)
+ assert_not_empty json
+ end
+end
diff --git a/test/controllers/api_keys_controller_test.rb b/test/controllers/api_keys_controller_test.rb
new file mode 100644
index 00000000..7d31694a
--- /dev/null
+++ b/test/controllers/api_keys_controller_test.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+require 'test_helper'
+
+class ApiKeysControllerTest < ActionDispatch::IntegrationTest
+ def setup
+ @user = users(:pepper)
+
+ @admin = users(:ironman)
+ sign_in_as @admin, ['rezoleo']
+
+ @api_key = api_keys(:FakeRadius)
+ @real_key = '5fcdb374f0a70e9ff0675a0ce4acbdf6d21225fe74483319c2766074732d6d80'
+ end
+
+ test 'should get index' do
+ get api_keys_path
+ assert_template 'api_keys/index'
+ end
+
+ test 'should get new' do
+ get new_api_key_path
+ assert_response :success
+ end
+
+ test 'should create api key and redirect if api key is valid in html' do
+ assert_difference 'ApiKey.count', 1 do
+ post api_keys_url(format: :html), params: {
+ api_key: {
+ bearer_name: 'Ultron'
+ }
+ }
+ end
+ end
+
+ test 'should destroy an api key and redirect to index in html' do
+ assert_difference 'ApiKey.count', -1 do
+ delete api_key_url(@api_key, format: :html)
+ end
+ assert_redirected_to api_keys_url
+ end
+
+ test 'should re-render new if bearer is invalid' do
+ post api_keys_url(format: :html), params: {
+ api_key: {
+ bearer_name: ''
+ }
+ }
+ assert_template 'api_keys/new'
+ end
+
+ test 'should send error if bearer is invalid' do
+ post api_keys_url(format: :html), params: {
+ api_key: {
+ bearer_name: ''
+ }
+ }
+ assert_response :unprocessable_entity
+ end
+end
diff --git a/test/controllers/sessions_controller_test.rb b/test/controllers/sessions_controller_test.rb
index c5209f26..534b75ae 100644
--- a/test/controllers/sessions_controller_test.rb
+++ b/test/controllers/sessions_controller_test.rb
@@ -4,6 +4,9 @@
class SessionsControllerTest < ActionDispatch::IntegrationTest
def setup
+ @api_key = api_keys(:FakeRadius)
+ @real_key = '5fcdb374f0a70e9ff0675a0ce4acbdf6d21225fe74483319c2766074732d6d80'
+
OmniAuth.config.add_mock(:keycloak, { provider: 'keycloak',
uid: '11111111-1111-1111-1111-111111111111',
info: { first_name: 'John',
diff --git a/test/controllers/users_controller_user_right_test.rb b/test/controllers/users_controller_user_right_test.rb
index 90257e60..1c6b7eff 100644
--- a/test/controllers/users_controller_user_right_test.rb
+++ b/test/controllers/users_controller_user_right_test.rb
@@ -51,4 +51,10 @@ def setup
delete user_path @user
end
end
+
+ test 'non-admin cannot access api keys' do
+ assert_raises CanCan::AccessDenied do
+ get api_keys_path
+ end
+ end
end
diff --git a/test/fixtures/api_keys.yml b/test/fixtures/api_keys.yml
new file mode 100644
index 00000000..a42d3feb
--- /dev/null
+++ b/test/fixtures/api_keys.yml
@@ -0,0 +1,4 @@
+FakeRadius:
+ id: 1
+ bearer_name: "FakeRadius"
+ api_key: ed7ba36706197624e8e4c9f3007ac1caffe1b61fd8b9315ab4a9d4a81e30b440
diff --git a/test/models/ability_test.rb b/test/models/ability_test.rb
index 7b735c59..0b6c1b40 100644
--- a/test/models/ability_test.rb
+++ b/test/models/ability_test.rb
@@ -6,17 +6,20 @@ class AbilityTest < ActiveSupport::TestCase
# rubocop:disable Metrics/AbcSize
def setup
@user = users(:pepper)
- @user_ability = Ability.new(@user)
+ @user_ability = UserAbility.new(@user)
@user_machine = @user.machines.first
@user_subscription = @user.subscriptions.first
@user_free_access = @user.free_accesses.first
@admin = users(:ironman)
@admin.groups = ['rezoleo'] # runtime value, cannot be set in fixture
- @admin_ability = Ability.new(@admin)
+ @admin_ability = UserAbility.new(@admin)
@admin_machine = @admin.machines.first
@admin_subscription = @admin.subscriptions.first
@admin_free_access = @admin.free_accesses.first
+
+ @api_key = api_keys(:FakeRadius)
+ @api_key_ability = ApiKeyAbility.new(@api_key)
end
# rubocop:enable Metrics/AbcSize
@@ -108,4 +111,8 @@ def setup
test 'admin can do everything' do
assert @admin_ability.can?(:manage, :all)
end
+
+ test 'api key bearer can read everything' do
+ assert @api_key_ability.can?(:read, :all)
+ end
end