Comparing changes
v0.1.0
→
v0.2.0
14 commits
17 files changed
Commits
Changed files (17)
lib
scim
kit
v2
spec
scim
lib/scim/kit/v2/templates/authentication_scheme.json.jbuilder
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+json.key_format! camelize: :lower
+json.name name
+json.description description
+json.spec_uri spec_uri
+json.documentation_uri documentation_uri
+json.type type
+json.primary primary if primary
lib/scim/kit/v2/templates/meta.json.jbuilder
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+json.key_format! camelize: :lower
+json.location location
+json.resource_type resource_type
+json.created created.iso8601 if created
+json.last_modified last_modified.iso8601 if last_modified
+json.version version.to_i if version
lib/scim/kit/v2/templates/nil_class.json.jbuilder
lib/scim/kit/v2/templates/resource_type.json.jbuilder
@@ -2,8 +2,7 @@
json.key_format! camelize: :lower
json.meta do
- json.resource_type 'ResourceType'
- json.location location
+ render meta, json: json
end
json.schemas [Scim::Kit::V2::Schema::RESOURCE_TYPE]
json.id id
lib/scim/kit/v2/templates/service_provider_configuration.json.jbuilder
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+json.key_format! camelize: :lower
+json.schemas [Scim::Kit::V2::Schema::SERVICE_PROVIDER_CONFIGURATION]
+json.documentation_uri documentation_uri
+json.patch do
+ render patch, json: json
+end
+json.bulk do
+ render bulk, json: json
+end
+json.filter do
+ render filter, json: json
+end
+json.change_password do
+ render change_password, json: json
+end
+json.sort do
+ render sort, json: json
+end
+json.etag do
+ render etag, json: json
+end
+json.authentication_schemes authentication_schemes do |authentication_scheme|
+ render authentication_scheme, json: json
+end
+json.meta do
+ render meta, json: json
+end
lib/scim/kit/v2/templates/supportable.json.jbuilder
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+json.key_format! camelize: :lower
+json.supported supported
+@dynamic_attributes.each do |key, value|
+ json.set! key.to_s.delete('='), value
+end
lib/scim/kit/v2/authentication_scheme.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+module Scim
+ module Kit
+ module V2
+ # Represents the available Authentication Schemes.
+ class AuthenticationScheme
+ DEFAULTS = {
+ httpbasic: {
+ description: 'Authentication scheme using the HTTP Basic Standard',
+ documentation_uri: 'http://example.com/help/httpBasic.html',
+ name: 'HTTP Basic',
+ spec_uri: 'http://www.rfc-editor.org/info/rfc2617'
+ },
+ oauthbearertoken: {
+ description:
+ 'Authentication scheme using the OAuth Bearer Token Standard',
+ documentation_uri: 'http://example.com/help/oauth.html',
+ name: 'OAuth Bearer Token',
+ spec_uri: 'http://www.rfc-editor.org/info/rfc6750'
+ }
+ }.freeze
+ include Templatable
+ attr_accessor :name
+ attr_accessor :description
+ attr_accessor :documentation_uri
+ attr_accessor :spec_uri
+ attr_accessor :type
+ attr_accessor :primary
+
+ def initialize
+ yield self if block_given?
+ end
+
+ def self.build_for(type, primary: nil)
+ defaults = DEFAULTS[type.to_sym] || {}
+ new do |x|
+ x.type = type
+ x.primary = primary
+ x.description = defaults[:description]
+ x.documentation_uri = defaults[:documentation_uri]
+ x.name = defaults[:name]
+ x.spec_uri = defaults[:spec_uri]
+ end
+ end
+ end
+ end
+ end
+end
lib/scim/kit/v2/meta.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Scim
+ module Kit
+ module V2
+ # Represents a meta section
+ class Meta
+ include Templatable
+
+ attr_accessor :created, :last_modified, :version
+ attr_reader :location
+ attr_reader :resource_type
+
+ def initialize(resource_type, location)
+ @resource_type = resource_type
+ @location = location
+ @version = @created = @last_modified = Time.now
+ end
+ end
+ end
+ end
+end
lib/scim/kit/v2/resource_type.rb
@@ -12,10 +12,11 @@ module Scim
attr_accessor :description
attr_accessor :endpoint
attr_accessor :schema
- attr_reader :location
+ attr_reader :meta
def initialize(location:)
- @location = location
+ @meta = Meta.new('ResourceType', location)
+ @meta.version = @meta.created = @meta.last_modified = nil
end
def self.build(*args)
lib/scim/kit/v2/schema.rb
@@ -6,10 +6,13 @@ module Scim
# Represents a SCIM Schema
class Schema
include Templatable
+
+ CORE = 'urn:ietf:params:scim:schemas:core:2.0'
ERROR = 'urn:ietf:params:scim:api:messages:2.0:Error'
- GROUP = 'urn:ietf:params:scim:schemas:core:2.0:Group'
- RESOURCE_TYPE = 'urn:ietf:params:scim:schemas:core:2.0:ResourceType'
- USER = 'urn:ietf:params:scim:schemas:core:2.0:User'
+ GROUP = "#{CORE}:Group"
+ RESOURCE_TYPE = "#{CORE}:ResourceType"
+ SERVICE_PROVIDER_CONFIGURATION = "#{CORE}:ServiceProviderConfig"
+ USER = "#{CORE}:User"
attr_reader :id, :name, :location, :attributes
attr_accessor :description
lib/scim/kit/v2/service_provider_configuration.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+module Scim
+ module Kit
+ module V2
+ # Represents a scim Service Provider Configuration
+ class ServiceProviderConfiguration
+ include Templatable
+ attr_reader :location
+ attr_accessor :documentation_uri
+ attr_reader :authentication_schemes
+ attr_reader :etag, :sort, :change_password, :patch
+ attr_reader :bulk, :filter, :meta
+
+ def initialize(location:)
+ @meta = Meta.new('ServiceProviderConfig', location)
+ @authentication_schemes = []
+ @etag = Supportable.new
+ @sort = Supportable.new
+ @change_password = Supportable.new
+ @patch = Supportable.new
+ @bulk = Supportable.new(:max_operations, :max_payload_size)
+ @filter = Supportable.new(:max_results)
+ end
+
+ def add_authentication(type, primary: nil)
+ scheme = AuthenticationScheme.build_for(type, primary: primary)
+ yield scheme if block_given?
+ @authentication_schemes << scheme
+ end
+ end
+ end
+ end
+end
lib/scim/kit/v2/supportable.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Scim
+ module Kit
+ module V2
+ # Represents a Feature
+ class Supportable
+ include Templatable
+ include DynamicAttributes
+
+ attr_accessor :supported
+
+ def initialize(*dynamic_attributes)
+ @dynamic_attributes = Hash[
+ dynamic_attributes.map { |x| ["#{x}=".to_sym, nil] }
+ ]
+ @supported = false
+ end
+ end
+ end
+ end
+end
lib/scim/kit/dynamic_attributes.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module Scim
+ module Kit
+ # Allows dynamic assignment of attributes.
+ module DynamicAttributes
+ def method_missing(method, *args)
+ return super unless respond_to_missing?(method)
+
+ @dynamic_attributes[method] = args[0]
+ end
+
+ def respond_to_missing?(method, _include_private = false)
+ @dynamic_attributes.key?(method) || super
+ end
+ end
+ end
+end
lib/scim/kit/templatable.rb
@@ -8,7 +8,7 @@ module Scim
render(self, options)
end
- def as_json(options = nil)
+ def as_json(_options = nil)
to_h
end
lib/scim/kit/version.rb
@@ -2,6 +2,6 @@
module Scim
module Kit
- VERSION = '0.1.0'
+ VERSION = '0.2.0'
end
end
lib/scim/kit.rb
@@ -3,15 +3,20 @@
require 'tilt'
require 'tilt/jbuilder'
+require 'scim/kit/dynamic_attributes'
require 'scim/kit/templatable'
require 'scim/kit/template'
require 'scim/kit/version'
require 'scim/kit/v2/attribute_type'
+require 'scim/kit/v2/authentication_scheme'
+require 'scim/kit/v2/meta'
require 'scim/kit/v2/mutability'
require 'scim/kit/v2/resource_type'
require 'scim/kit/v2/returned'
require 'scim/kit/v2/schema'
+require 'scim/kit/v2/service_provider_configuration'
+require 'scim/kit/v2/supportable'
require 'scim/kit/v2/uniqueness'
module Scim
spec/scim/kit/v2/service_provider_configuration_spec.rb
@@ -0,0 +1,138 @@
+# frozen_string_literal: true
+
+RSpec.describe Scim::Kit::V2::ServiceProviderConfiguration do
+ subject { described_class.new(location: location) }
+
+ let(:location) { FFaker::Internet.uri('https') }
+ let(:now) { Time.now }
+
+ describe '#to_json' do
+ let(:result) { JSON.parse(subject.to_json, symbolize_names: true) }
+
+ specify { expect(result[:schemas]).to match_array(['urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig']) }
+ specify { expect(result[:documentationUri]).to be_blank }
+ specify { expect(result[:patch][:supported]).to be(false) }
+ specify { expect(result[:bulk][:supported]).to be(false) }
+ specify { expect(result[:filter][:supported]).to be(false) }
+ specify { expect(result[:changePassword][:supported]).to be(false) }
+ specify { expect(result[:sort][:supported]).to be(false) }
+ specify { expect(result[:etag][:supported]).to be(false) }
+ specify { expect(result[:authenticationSchemes]).to be_empty }
+ specify { expect(result[:meta][:location]).to eql(location) }
+ specify { expect(result[:meta][:resourceType]).to eql('ServiceProviderConfig') }
+ specify { expect(result[:meta][:created]).to eql(now.iso8601) }
+ specify { expect(result[:meta][:lastModified]).to eql(now.iso8601) }
+ specify { expect(result[:meta][:version]).not_to be_nil }
+
+ context 'with documentation uri' do
+ before do
+ subject.documentation_uri = FFaker::Internet.uri('https')
+ end
+
+ specify { expect(result[:documentationUri]).to eql(subject.documentation_uri) }
+ end
+
+ context 'with OAuth Bearer Token' do
+ before { subject.add_authentication(:oauthbearertoken) }
+
+ specify { expect(result[:authenticationSchemes][0][:name]).to eql('OAuth Bearer Token') }
+ specify { expect(result[:authenticationSchemes][0][:description]).to eql('Authentication scheme using the OAuth Bearer Token Standard') }
+ specify { expect(result[:authenticationSchemes][0][:specUri]).to eql('http://www.rfc-editor.org/info/rfc6750') }
+ specify { expect(result[:authenticationSchemes][0][:documentationUri]).to eql('http://example.com/help/oauth.html') }
+ specify { expect(result[:authenticationSchemes][0][:type]).to eql('oauthbearertoken') }
+ end
+
+ context 'with http basic' do
+ before { subject.add_authentication(:httpbasic) }
+
+ specify { expect(result[:authenticationSchemes][0][:name]).to eql('HTTP Basic') }
+ specify { expect(result[:authenticationSchemes][0][:description]).to eql('Authentication scheme using the HTTP Basic Standard') }
+ specify { expect(result[:authenticationSchemes][0][:specUri]).to eql('http://www.rfc-editor.org/info/rfc2617') }
+ specify { expect(result[:authenticationSchemes][0][:documentationUri]).to eql('http://example.com/help/httpBasic.html') }
+ specify { expect(result[:authenticationSchemes][0][:type]).to eql('httpbasic') }
+ end
+
+ context 'with multiple schemes' do
+ before do
+ subject.add_authentication(:oauthbearertoken, primary: true)
+ subject.add_authentication(:httpbasic)
+ end
+
+ specify { expect(result[:authenticationSchemes][0][:name]).to eql('OAuth Bearer Token') }
+ specify { expect(result[:authenticationSchemes][0][:description]).to eql('Authentication scheme using the OAuth Bearer Token Standard') }
+ specify { expect(result[:authenticationSchemes][0][:specUri]).to eql('http://www.rfc-editor.org/info/rfc6750') }
+ specify { expect(result[:authenticationSchemes][0][:documentationUri]).to eql('http://example.com/help/oauth.html') }
+ specify { expect(result[:authenticationSchemes][0][:type]).to eql('oauthbearertoken') }
+ specify { expect(result[:authenticationSchemes][0][:primary]).to be(true) }
+
+ specify { expect(result[:authenticationSchemes][1][:name]).to eql('HTTP Basic') }
+ specify { expect(result[:authenticationSchemes][1][:description]).to eql('Authentication scheme using the HTTP Basic Standard') }
+ specify { expect(result[:authenticationSchemes][1][:specUri]).to eql('http://www.rfc-editor.org/info/rfc2617') }
+ specify { expect(result[:authenticationSchemes][1][:documentationUri]).to eql('http://example.com/help/httpBasic.html') }
+ specify { expect(result[:authenticationSchemes][1][:type]).to eql('httpbasic') }
+ end
+
+ context 'with custom scheme' do
+ before do
+ subject.add_authentication(:custom) do |x|
+ x.name = 'custom'
+ x.description = 'custom'
+ x.spec_uri = 'http://www.rfc-editor.org/info/rfcXXXX'
+ x.documentation_uri = 'http://example.com/help/custom.html'
+ end
+ end
+
+ specify { expect(result[:authenticationSchemes][0][:name]).to eql('custom') }
+ specify { expect(result[:authenticationSchemes][0][:description]).to eql('custom') }
+ specify { expect(result[:authenticationSchemes][0][:specUri]).to eql('http://www.rfc-editor.org/info/rfcXXXX') }
+ specify { expect(result[:authenticationSchemes][0][:documentationUri]).to eql('http://example.com/help/custom.html') }
+ specify { expect(result[:authenticationSchemes][0][:type]).to eql('custom') }
+ end
+
+ context 'with etag support' do
+ before { subject.etag.supported = true }
+
+ specify { expect(result[:etag][:supported]).to be(true) }
+ end
+
+ context 'with sort support' do
+ before { subject.sort.supported = true }
+
+ specify { expect(result[:sort][:supported]).to be(true) }
+ end
+
+ context 'with change_password support' do
+ before { subject.change_password.supported = true }
+
+ specify { expect(result[:changePassword][:supported]).to be(true) }
+ end
+
+ context 'with patch support' do
+ before { subject.patch.supported = true }
+
+ specify { expect(result[:patch][:supported]).to be(true) }
+ end
+
+ context 'with bulk support' do
+ before do
+ subject.bulk.supported = true
+ subject.bulk.max_operations = 1000
+ subject.bulk.max_payload_size = 1_048_576
+ end
+
+ specify { expect(result[:bulk][:supported]).to be(true) }
+ specify { expect(result[:bulk][:maxOperations]).to be(1000) }
+ specify { expect(result[:bulk][:maxPayloadSize]).to be(1_048_576) }
+ end
+
+ context 'with filter support' do
+ before do
+ subject.filter.supported = true
+ subject.filter.max_results = 200
+ end
+
+ specify { expect(result[:filter][:supported]).to be(true) }
+ specify { expect(result[:filter][:maxResults]).to be(200) }
+ end
+ end
+end