Commit ce367f7
Changed files (9)
app
controllers
scim
models
spec
requests
app/controllers/scim/v2/groups_controller.rb
@@ -7,7 +7,7 @@ module Scim
def index
render json: {
schemas: [Scim::Shady::Messages::LIST_RESPONSE],
- totalResults: 0,
+ totalResults: User.count,
Resources: resources,
}.to_json, status: :ok
end
app/controllers/scim/controller.rb
@@ -8,7 +8,15 @@ module Scim
private
- def authenticate!; end
+ def current_user
+ @current_user ||= authenticate_with_http_token do |token|
+ User.authenticate_token(token)
+ end
+ end
+
+ def authenticate!
+ render plain: "Unauthorized", status: :unauthorized unless current_user?
+ end
def not_found
render json: {
app/models/bearer_token.rb
@@ -13,7 +13,8 @@ class BearerToken
def decode(token)
decoded = JWT.decode(token, public_key, true, algorithm: 'RS256')[0]
decoded.with_indifferent_access
- rescue StandardError
+ rescue StandardError => error
+ Rails.logger.error(error)
{}
end
@@ -24,7 +25,7 @@ class BearerToken
def defaults
issued_at = Time.current.to_i
{
- exp: 1.hour.from_now,
+ exp: 1.hour.from_now.to_i,
iat: issued_at,
iss: Saml::Kit.configuration.entity_id,
nbf: issued_at,
app/models/user.rb
@@ -29,12 +29,18 @@ class User < ApplicationRecord
nil
end
- private
+ def self.authenticate_token(token)
+ token = BearerToken.new.decode(token)
+ return if token.empty?
+ User.find_by(uuid: token[:sub])
+ end
def access_token(audience)
BearerToken.new.encode(sub: uuid, aud: audience)
end
+ private
+
def trusted_attributes_for(request)
{
id: uuid,
spec/requests/scim/v2/groups_spec.rb
@@ -1,18 +1,18 @@
require 'rails_helper'
describe "/scim/v2/groups" do
- let(:token) { SecureRandom.uuid }
- let(:headers) do
- {
- 'Authorization' => "Bearer #{token}",
- 'Accept' => 'application/scim+json',
- 'Content-Type' => 'application/scim+json',
- }
- end
+ context "when authenticated" do
+ let(:user) { create(:user) }
+ let(:token) { user.access_token('unknown') }
+ let(:headers) do
+ {
+ 'Authorization' => "Bearer #{token}",
+ 'Accept' => 'application/scim+json',
+ 'Content-Type' => 'application/scim+json',
+ }
+ end
- describe "GET /scim/v2/groups" do
- context "when retrieving all groups" do
- let!(:user) { create(:user) }
+ describe "GET /scim/v2/groups" do
before { get '/scim/v2/groups', headers: headers }
specify { expect(response).to have_http_status(:ok) }
@@ -24,4 +24,17 @@ describe "/scim/v2/groups" do
specify { expect(json[:Resources]).to match_array([id: user.uuid, userName: user.email]) }
end
end
+
+ context "when the authentication token is invalid" do
+ let(:bad_headers) do
+ {
+ 'Authorization' => "Bearer #{SecureRandom.uuid}",
+ 'Accept' => 'application/scim+json',
+ 'Content-Type' => 'application/scim+json',
+ }
+ end
+ before { get '/scim/v2/groups', headers: bad_headers }
+
+ specify { expect(response).to have_http_status(:unauthorized) }
+ end
end
spec/requests/scim/v2/search_spec.rb
@@ -1,7 +1,8 @@
require 'rails_helper'
describe '/scim/v1/.search' do
- let(:token) { SecureRandom.uuid }
+ let(:user) { create(:user) }
+ let(:token) { user.access_token('rspec') }
let(:headers) do
{
'Authorization' => "Bearer #{token}",
spec/requests/scim/v2/service_provider_config_spec.rb
@@ -1,8 +1,18 @@
require 'rails_helper'
describe "/ServiceProviderConfig" do
+ let(:user) { create(:user) }
+ let(:token) { user.access_token('rspec') }
+ let(:headers) do
+ {
+ 'Authorization' => "Bearer #{token}",
+ 'Accept' => 'application/scim+json',
+ 'Content-Type' => 'application/scim+json',
+ }
+ end
+
it 'returns a 200' do
- get '/scim/v2/ServiceProviderConfig'
+ get '/scim/v2/ServiceProviderConfig', headers: headers
expect(response).to have_http_status(:ok)
expect(response.body).to be_present
spec/requests/scim/v2/users_spec.rb
@@ -1,7 +1,8 @@
require 'rails_helper'
describe '/scim/v2/users' do
- let(:token) { SecureRandom.uuid }
+ let(:user) { create(:user) }
+ let(:token) { user.access_token("rspec") }
let(:headers) do
{
'Authorization' => "Bearer #{token}",
@@ -100,18 +101,18 @@ describe '/scim/v2/users' do
end
describe "DELETE /scim/v2/users/:id" do
- let(:user) { create(:user) }
+ let(:other_user) { create(:user) }
it 'deletes the user' do
- delete "/scim/v2/users/#{user.uuid}", headers: headers
+ delete "/scim/v2/users/#{other_user.uuid}", headers: headers
expect(response).to have_http_status(:no_content)
- get "/scim/v2/users/#{user.uuid}", headers: headers
+ get "/scim/v2/users/#{other_user.uuid}", headers: headers
expect(response).to have_http_status(:not_found)
expect(response.body).to be_present
json = JSON.parse(response.body, symbolize_names: true)
expect(json[:schemas]).to match_array([Scim::Shady::Messages::ERROR])
- expect(json[:detail]).to eql("Resource #{user.uuid} not found")
+ expect(json[:detail]).to eql("Resource #{other_user.uuid} not found")
expect(json[:status]).to eql("404")
end
end
spec/factories.rb
@@ -1,7 +1,7 @@
FactoryBot.define do
factory :user do
- email FFaker::Internet.email
- uuid SecureRandom.uuid
- password FFaker::Internet.password
+ email { FFaker::Internet.email }
+ uuid { SecureRandom.uuid }
+ password { FFaker::Internet.password }
end
end