scimaenaga 0.6.2 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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