scimaenaga 0.6.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3959060e28dd1554d42df95f07f0bf311ce1f59da0f0d9bce9a3467193874d9b
4
- data.tar.gz: c6ffc092a2584e829db8011b76d7c0e8f73c3ebe2422d993682409db90e59439
3
+ metadata.gz: 6d2d55daa03a9b1e4771b1f2e1287a75075514d57faeb0075ea8784a7dd8cc9e
4
+ data.tar.gz: b4a38c2c629cce871703a2a1874c3be93e34d55f0d5eacadae8da9b8c5e5c438
5
5
  SHA512:
6
- metadata.gz: 013aba54f061b96a3ebe964f285a0d26bfd41062270ca50572c7633bb1a0a7e40c6668d7a5709da9b80c4883e14b07f75f14bb2e1a18dc44d5c90bcb80abe868
7
- data.tar.gz: f31c1577377e803693ba5932ff1c64f794e2de44b5d55fd2ad1e89219a9d020e499002ef8e83a97ce81316e3d267abd44b1bd57639f69060234eeecf301bb730
6
+ metadata.gz: 43e2ec416e2f9bad5c185042aff3e6fd576437d3d9bff5ac6ad5d1f930246fa0b59b12e1940a5498e386c8c8f385a245d531907e7ab5850cc8ebc5a7c35df607
7
+ data.tar.gz: 284bead26bc660ca0d4cc229dd5f13102501904b7658e3e780f5ea5657eb367c52cd5399d4a29dcd20af66eee19ce4592af7defa62c2f182bb7b23f76ebe6a31
@@ -7,6 +7,9 @@ module ScimRails
7
7
  class InvalidCredentials < StandardError
8
8
  end
9
9
 
10
+ class InvalidRequest < StandardError
11
+ end
12
+
10
13
  class InvalidQuery < StandardError
11
14
  end
12
15
 
@@ -16,6 +19,12 @@ module ScimRails
16
19
  class UnsupportedDeleteRequest < StandardError
17
20
  end
18
21
 
22
+ class InvalidConfiguration < StandardError
23
+ end
24
+
25
+ class UnexpectedError < StandardError
26
+ end
27
+
19
28
  included do
20
29
  if Rails.env.production?
21
30
  rescue_from StandardError do |exception|
@@ -47,6 +56,17 @@ module ScimRails
47
56
  )
48
57
  end
49
58
 
59
+ rescue_from ScimRails::ExceptionHandler::InvalidRequest do |e|
60
+ json_response(
61
+ {
62
+ schemas: ["urn:ietf:params:scim:api:messages:2.0:Error"],
63
+ detail: "Invalid request. #{e.message}",
64
+ status: "400"
65
+ },
66
+ :bad_request
67
+ )
68
+ end
69
+
50
70
  rescue_from ScimRails::ExceptionHandler::InvalidQuery do
51
71
  json_response(
52
72
  {
@@ -63,7 +83,7 @@ module ScimRails
63
83
  json_response(
64
84
  {
65
85
  schemas: ["urn:ietf:params:scim:api:messages:2.0:Error"],
66
- detail: "Invalid PATCH request. This PATCH endpoint only supports deprovisioning and reprovisioning records.",
86
+ detail: "Invalid PATCH request.",
67
87
  status: "422"
68
88
  },
69
89
  :unprocessable_entity
@@ -81,6 +101,28 @@ module ScimRails
81
101
  )
82
102
  end
83
103
 
104
+ rescue_from ScimRails::ExceptionHandler::InvalidConfiguration do |e|
105
+ json_response(
106
+ {
107
+ schemas: ["urn:ietf:params:scim:api:messages:2.0:Error"],
108
+ detail: "Invalid configuration. #{e.message}",
109
+ status: "500"
110
+ },
111
+ :internal_server_error
112
+ )
113
+ end
114
+
115
+ rescue_from ScimRails::ExceptionHandler::UnexpectedError do |e|
116
+ json_response(
117
+ {
118
+ schemas: ["urn:ietf:params:scim:api:messages:2.0:Error"],
119
+ detail: "Unexpected Error. #{e.message}",
120
+ status: "500"
121
+ },
122
+ :internal_server_error
123
+ )
124
+ end
125
+
84
126
  rescue_from ActiveRecord::RecordNotFound do |e|
85
127
  json_response(
86
128
  {
@@ -68,13 +68,24 @@ module ScimRails
68
68
 
69
69
  def destroy
70
70
  unless ScimRails.config.group_destroy_method
71
- raise ScimRails::ExceptionHandler::UnsupportedDeleteRequest
71
+ raise ScimRails::ExceptionHandler::InvalidConfiguration
72
72
  end
73
73
 
74
74
  group = @company
75
75
  .public_send(ScimRails.config.scim_groups_scope)
76
76
  .find(params[:id])
77
- group.public_send(ScimRails.config.group_destroy_method)
77
+ raise ActiveRecord::RecordNotFound unless group
78
+
79
+ begin
80
+ group.public_send(ScimRails.config.group_destroy_method)
81
+ rescue NoMethodError => e
82
+ raise ScimRails::ExceptionHandler::InvalidConfiguration, e.message
83
+ rescue ActiveRecord::RecordNotDestroyed => e
84
+ raise ScimRails::ExceptionHandler::InvalidRequest, e.message
85
+ rescue => e
86
+ raise ScimRails::ExceptionHandler::UnexpectedError, e.message
87
+ end
88
+
78
89
  head :no_content
79
90
  end
80
91
 
@@ -71,6 +71,27 @@ module ScimRails
71
71
  json_scim_response(object: user)
72
72
  end
73
73
 
74
+ def destroy
75
+ unless ScimRails.config.user_destroy_method
76
+ raise ScimRails::ExceptionHandler::InvalidConfiguration
77
+ end
78
+
79
+ user = @company.public_send(ScimRails.config.scim_users_scope).find(params[:id])
80
+ raise ActiveRecord::RecordNotFound unless user
81
+
82
+ begin
83
+ user.public_send(ScimRails.config.user_destroy_method)
84
+ rescue NoMethodError => e
85
+ raise ScimRails::ExceptionHandler::InvalidConfiguration, e.message
86
+ rescue ActiveRecord::RecordNotDestroyed => e
87
+ raise ScimRails::ExceptionHandler::InvalidRequest, e.message
88
+ rescue => e
89
+ raise ScimRails::ExceptionHandler::UnexpectedError, e.message
90
+ end
91
+
92
+ head :no_content
93
+ end
94
+
74
95
  private
75
96
 
76
97
  def permitted_user_params
@@ -5,9 +5,8 @@ class ScimPatch
5
5
  attr_accessor :operations
6
6
 
7
7
  def initialize(params, mutable_attributes_schema)
8
- # FIXME: raise proper error.
9
8
  unless params['schemas'] == ['urn:ietf:params:scim:api:messages:2.0:PatchOp']
10
- raise StandardError
9
+ raise ScimRails::ExceptionHandler::UnsupportedPatchRequest
11
10
  end
12
11
  if params['Operations'].nil?
13
12
  raise ScimRails::ExceptionHandler::UnsupportedPatchRequest
@@ -2,57 +2,126 @@
2
2
 
3
3
  # Parse One of "Operations" in PATCH request
4
4
  class ScimPatchOperation
5
- attr_accessor :op, :path_scim, :path_sp, :value
5
+
6
+ # 1 Operation means 1 attribute change
7
+ # If 1 value is specified, @operations has 1 "Operation" struct.
8
+ # If 3 value is specified, @operations has 3 "Operation" struct.
9
+ attr_accessor :operations
10
+
11
+ Operation = Struct.new('Operation', :op, :path_scim, :path_sp, :value)
6
12
 
7
13
  def initialize(op, path, value, mutable_attributes_schema)
8
- # FIXME: Raise proper Error
9
- raise StandardError unless op.downcase.in? %w[add replace remove]
10
-
11
- # No path is not supported.
12
- # FIXME: Raise proper Error
13
- raise ScimRails::ExceptionHandler::UnsupportedPatchRequest if path.nil?
14
- raise ScimRails::ExceptionHandler::UnsupportedPatchRequest if value.nil?
15
-
16
- @op = op.downcase.to_sym
17
- @path_scim = path
18
- @path_sp = convert_path(path, mutable_attributes_schema)
19
- @value = convert_bool_if_string(value, @path_scim)
14
+ op_downcase = op.downcase
15
+
16
+ unless op_downcase.in? %w[add replace remove]
17
+ raise ScimRails::ExceptionHandler::UnsupportedPatchRequest
18
+ end
19
+
20
+ @operations = []
21
+
22
+ # To handle request pattern A and B in the same way,
23
+ # convert complex-value to path + single-value
24
+ #
25
+ # pattern A
26
+ # {
27
+ # "op": "replace",
28
+ # "value": {
29
+ # "displayName": "Suzuki Taro",
30
+ # "name.givenName": "taro"
31
+ # }
32
+ # }
33
+ # => [{path: displayName, value: "Suzuki Taro"}, {path: name.givenName, value: "taro"}]
34
+ #
35
+ # pattern B
36
+ # [
37
+ # {
38
+ # "op": "replace",
39
+ # "path": "displayName"
40
+ # "value": "Suzuki Taro",
41
+ # },
42
+ # {
43
+ # "op": "replace",
44
+ # "path": "name.givenNAme"
45
+ # "value": "taro",
46
+ # },
47
+ # ]
48
+ if value.instance_of?(Hash) || value.instance_of?(ActionController::Parameters)
49
+ create_multiple_operations(op_downcase, path, value, mutable_attributes_schema)
50
+ else
51
+ create_operation(op_downcase, path, value, mutable_attributes_schema)
52
+ end
20
53
  end
21
54
 
22
55
  def save(model)
23
- if @path_scim == 'members' # Only members are supported for value is an array
24
- update_member_ids = @value.map do |v|
25
- v[ScimRails.config.group_member_relation_schema.keys.first].to_s
56
+ @operations.each do |operation|
57
+ apply_operation(model, operation)
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def apply_operation(model, operation)
64
+ if operation.path_scim == 'members' # Only members are supported for value is an array
65
+ update_member_ids = operation.value.map do |v|
66
+ v[ScimRails.config.group_member_relation_schema.keys.first].to_s
67
+ end
68
+
69
+ current_member_ids = model.public_send(
70
+ ScimRails.config.group_member_relation_attribute
71
+ ).map(&:to_s)
72
+ case operation.op
73
+ when :add
74
+ member_ids = current_member_ids.concat(update_member_ids)
75
+ when :replace
76
+ member_ids = current_member_ids.concat(update_member_ids)
77
+ when :remove
78
+ member_ids = current_member_ids - update_member_ids
79
+ end
80
+
81
+ # Only the member addition process is saved by each ids
82
+ model.public_send("#{ScimRails.config.group_member_relation_attribute}=",
83
+ member_ids.uniq)
84
+ return
26
85
  end
27
86
 
28
- current_member_ids = model
29
- .public_send(ScimRails.config.group_member_relation_attribute).map(&:to_s)
30
- case @op
31
- when :add
32
- member_ids = current_member_ids.concat(update_member_ids)
33
- when :replace
34
- member_ids = current_member_ids.concat(update_member_ids)
87
+ case operation.op
88
+ when :add, :replace
89
+ model.attributes = { operation.path_sp => operation.value }
35
90
  when :remove
36
- member_ids = current_member_ids - update_member_ids
91
+ model.attributes = { operation.path_sp => nil }
37
92
  end
38
-
39
- # Only the member addition process is saved by each ids
40
- model.public_send("#{ScimRails.config.group_member_relation_attribute}=",
41
- member_ids.uniq)
42
- return
43
93
  end
44
94
 
45
- case @op
46
- when :add, :replace
47
- model.attributes = { @path_sp => @value }
48
- when :remove
49
- model.attributes = { @path_sp => nil }
95
+ def create_operation(op, path_scim, value, mutable_attributes_schema)
96
+ path_sp = convert_path(path_scim, mutable_attributes_schema)
97
+ value = convert_bool_if_string(value, path_scim)
98
+ @operations << Operation.new(op.to_sym, path_scim, path_sp, value)
50
99
  end
51
- end
52
100
 
53
- private
101
+ # convert hash value to 1 path + 1 value
102
+ # each path is created by path_scim_base + key of value
103
+ def create_multiple_operations(op, path_scim_base, hash_value, mutable_attributes_schema)
104
+ hash_value.each do |k, v|
105
+ # Typical request is path_scim_base = nil and value = complex-value:
106
+ # {
107
+ # "op": "replace",
108
+ # "value": {
109
+ # "displayName": "Taro Suzuki",
110
+ # "name.givenName": "taro"
111
+ # }
112
+ # }
113
+ path_scim = if path_scim_base.present?
114
+ "#{path_scim_base}.#{k}"
115
+ else
116
+ k
117
+ end
118
+ create_operation(op, path_scim, v, mutable_attributes_schema)
119
+ end
120
+ end
54
121
 
55
122
  def convert_path(path, mutable_attributes_schema)
123
+ return nil if path.nil?
124
+
56
125
  # For now, library does not support Multi-Valued Attributes properly.
57
126
  # examle:
58
127
  # path = 'emails[type eq "work"].value'
data/config/routes.rb CHANGED
@@ -4,6 +4,7 @@ ScimRails::Engine.routes.draw do
4
4
  get 'scim/v2/Users/:id', action: :show, controller: 'scim_users'
5
5
  put 'scim/v2/Users/:id', action: :put_update, controller: 'scim_users'
6
6
  patch 'scim/v2/Users/:id', action: :patch_update, controller: 'scim_users'
7
+ delete 'scim/v2/Users/:id', action: :destroy, controller: 'scim_users'
7
8
  get 'scim/v2/Groups', action: :index, controller: 'scim_groups'
8
9
  post 'scim/v2/Groups', action: :create, controller: 'scim_groups'
9
10
  get 'scim/v2/Groups/:id', action: :show, controller: 'scim_groups'
@@ -44,6 +44,7 @@ module ScimRails
44
44
  :user_attributes,
45
45
  :user_schema,
46
46
  :group_schema,
47
+ :user_destroy_method,
47
48
  :group_destroy_method
48
49
 
49
50
  def initialize
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ScimRails
4
- VERSION = '0.6.2'
4
+ VERSION = '0.7.0'
5
5
  end
@@ -505,12 +505,6 @@ RSpec.describe ScimRails::ScimGroupsController, type: :controller do
505
505
  end
506
506
 
507
507
  context 'when Group destroy method is configured' do
508
- before do
509
- allow(ScimRails.config).to(
510
- receive(:group_destroy_method).and_return(:destroy!)
511
- )
512
- end
513
-
514
508
  it 'returns empty response' do
515
509
  delete :destroy, params: { id: 1 }, as: :json
516
510
 
@@ -557,7 +551,31 @@ RSpec.describe ScimRails::ScimGroupsController, type: :controller do
557
551
  delete :destroy, params: { id: 1 }, as: :json
558
552
  end.not_to change { company.groups.reload.count }.from(1)
559
553
 
560
- expect(response.status).to eq 501
554
+ expect(response.status).to eq 500
555
+ end
556
+ end
557
+
558
+ context 'when Group destroy method is invalid' do
559
+ it 'does not delete Group' do
560
+ allow(ScimRails.config).to(
561
+ receive(:group_destroy_method).and_return('destory!')
562
+ )
563
+
564
+ expect do
565
+ delete :destroy, params: { id: 1 }, as: :json
566
+ end.not_to change { company.groups.reload.count }.from(1)
567
+
568
+ expect(response.status).to eq 500
569
+ end
570
+ end
571
+
572
+ context 'whenr target Group is not found' do
573
+ it 'return 404 not found' do
574
+ expect do
575
+ delete :destroy, params: { id: 999999 }, as: :json
576
+ end.not_to change { company.groups.reload.count }.from(1)
577
+
578
+ expect(response.status).to eq 404
561
579
  end
562
580
  end
563
581
  end