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: () -%> + +
+
+
+
+ +
+ + + +

Api Keys

+
+ + <%# 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