@@ -26,7 +26,7 @@ public static function getAuthUserClass()
2626 *
2727 * @param unknown $object
2828 */
29- public static function prepareReturn (Arrayable $ object , ResourceType $ resourceType = null , array $ attributes = [])
29+ public static function prepareReturn (Arrayable $ object , ResourceType $ resourceType = null , array $ attributes = [], array $ excludedAttributes = [] )
3030 {
3131 $ result = null ;
3232
@@ -35,7 +35,7 @@ public static function prepareReturn(Arrayable $object, ResourceType $resourceTy
3535 $ result = [];
3636
3737 foreach ($ object as $ key => $ value ) {
38- $ result [] = self ::objectToSCIMArray ($ value , $ resourceType , $ attributes );
38+ $ result [] = self ::objectToSCIMArray ($ value , $ resourceType , $ attributes, $ excludedAttributes );
3939 }
4040 }
4141 }
@@ -44,18 +44,33 @@ public static function prepareReturn(Arrayable $object, ResourceType $resourceTy
4444 $ result = $ object ;
4545 }
4646
47+ if (is_array ($ result ) && !empty ($ excludedAttributes )) {
48+ $ defaultSchema = $ resourceType ?->getMapping()->getDefaultSchema ();
49+ $ result = self ::applyExcludedAttributes ($ result , $ excludedAttributes , $ defaultSchema );
50+ }
51+
4752 return $ result ;
4853 }
4954
50- public static function objectToSCIMArray ($ object , ResourceType $ resourceType = null , array $ attributes = [])
55+ public static function objectToSCIMArray ($ object , ResourceType $ resourceType = null , array $ attributes = [], array $ excludedAttributes = [] )
5156 {
5257 if ($ resourceType == null ){
53- return $ object instanceof Arrayable ? $ object ->toArray () : $ object ;
58+ $ result = $ object instanceof Arrayable ? $ object ->toArray () : $ object ;
59+
60+ if (is_array ($ result ) && !empty ($ excludedAttributes )) {
61+ $ result = self ::applyExcludedAttributes ($ result , $ excludedAttributes );
62+ }
63+
64+ return $ result ;
5465 }
5566
5667 $ mapping = $ resourceType ->getMapping ();
5768 $ result = $ mapping ->read ($ object , $ attributes )->value ;
5869
70+ if (!empty ($ excludedAttributes )) {
71+ $ result = self ::applyExcludedAttributes ($ result , $ excludedAttributes , $ mapping ->getDefaultSchema ());
72+ }
73+
5974 if (config ('scim.omit_main_schema_in_return ' )) {
6075 $ defaultSchema = collect ($ mapping ->getDefaultSchema ())->first ();
6176
@@ -90,9 +105,39 @@ public static function getResourceObjectVersion($object)
90105 * @param unknown $object
91106 * @param ResourceType $resourceType
92107 */
93- public static function objectToSCIMResponse (Model $ object , ResourceType $ resourceType = null )
108+ public static function objectToSCIMResponse (Model $ object , ResourceType $ resourceType = null , array $ attributes = [], array $ excludedAttributes = [] )
94109 {
95- return response (self ::objectToSCIMArray ($ object , $ resourceType ))->header ('ETag ' , self ::getResourceObjectVersion ($ object ));
110+ $ response = response (self ::objectToSCIMArray ($ object , $ resourceType , $ attributes , $ excludedAttributes ))
111+ ->header ('ETag ' , self ::getResourceObjectVersion ($ object ));
112+
113+ if ($ resourceType !== null ) {
114+ $ resourceTypeName = $ resourceType ->getName ();
115+
116+ if ($ resourceTypeName === null ) {
117+ $ routeResourceType = request ()?->route('resourceType ' );
118+
119+ if ($ routeResourceType instanceof ResourceType) {
120+ $ resourceTypeName = $ routeResourceType ->getName ();
121+ } elseif (is_string ($ routeResourceType )) {
122+ $ resourceTypeName = $ routeResourceType ;
123+ }
124+ }
125+
126+ if ($ resourceTypeName !== null ) {
127+ $ response ->header (
128+ 'Location ' ,
129+ route (
130+ 'scim.resource ' ,
131+ [
132+ 'resourceType ' => $ resourceTypeName ,
133+ 'resourceObject ' => $ object ->getKey (),
134+ ]
135+ )
136+ );
137+ }
138+ }
139+
140+ return $ response ;
96141 }
97142
98143 /**
@@ -132,6 +177,125 @@ function ($query) use ($term, $resourceType) {
132177 }
133178 }
134179
180+ protected static function applyExcludedAttributes (array $ resource , array $ excludedAttributes , $ defaultSchema = null ): array
181+ {
182+ foreach ($ excludedAttributes as $ reference ) {
183+ $ reference = trim ($ reference );
184+
185+ if ($ reference === '' ) {
186+ continue ;
187+ }
188+
189+ [$ schema , $ segments ] = self ::splitAttributeReference ($ reference , $ defaultSchema );
190+
191+ if (empty ($ segments )) {
192+ continue ;
193+ }
194+
195+ if ($ schema !== null ) {
196+ if (!isset ($ resource [$ schema ]) || !is_array ($ resource [$ schema ])) {
197+ continue ;
198+ }
199+
200+ self ::removeAttributePath ($ resource [$ schema ], $ segments );
201+
202+ if (is_array ($ resource [$ schema ]) && empty ($ resource [$ schema ])) {
203+ unset($ resource [$ schema ]);
204+ }
205+
206+ continue ;
207+ }
208+
209+ self ::removeAttributePath ($ resource , $ segments );
210+ }
211+
212+ return $ resource ;
213+ }
214+
215+ protected static function splitAttributeReference (string $ reference , $ defaultSchema = null ): array
216+ {
217+ $ schema = null ;
218+ $ attributePart = $ reference ;
219+
220+ if (str_starts_with ($ reference , 'urn: ' )) {
221+ $ lastColon = strrpos ($ reference , ': ' );
222+
223+ if ($ lastColon !== false ) {
224+ $ schema = substr ($ reference , 0 , $ lastColon );
225+ $ attributePart = substr ($ reference , $ lastColon + 1 );
226+ }
227+ }
228+
229+ $ attributePart = trim ($ attributePart );
230+
231+ if ($ schema === null ) {
232+ $ firstSegment = $ attributePart ;
233+
234+ if (($ dotPosition = strpos ($ attributePart , '. ' )) !== false ) {
235+ $ firstSegment = substr ($ attributePart , 0 , $ dotPosition );
236+ }
237+
238+ if (!in_array ($ firstSegment , ['schemas ' , 'meta ' , 'id ' ], true ) && $ defaultSchema !== null ) {
239+ $ schema = $ defaultSchema ;
240+ }
241+ }
242+
243+ $ segments = $ attributePart === '' ? [] : explode ('. ' , $ attributePart );
244+
245+ return [$ schema , $ segments ];
246+ }
247+
248+ protected static function removeAttributePath (&$ node , array $ segments , int $ depth = 0 ): void
249+ {
250+ if (!is_array ($ node )) {
251+ return ;
252+ }
253+
254+ if (self ::isList ($ node )) {
255+ foreach ($ node as &$ element ) {
256+ self ::removeAttributePath ($ element , $ segments , $ depth );
257+ }
258+
259+ return ;
260+ }
261+
262+ $ key = $ segments [$ depth ] ?? null ;
263+
264+ if ($ key === null || !array_key_exists ($ key , $ node )) {
265+ return ;
266+ }
267+
268+ if ($ depth === count ($ segments ) - 1 ) {
269+ unset($ node [$ key ]);
270+ return ;
271+ }
272+
273+ self ::removeAttributePath ($ node [$ key ], $ segments , $ depth + 1 );
274+
275+ if (is_array ($ node [$ key ]) && empty ($ node [$ key ])) {
276+ unset($ node [$ key ]);
277+ }
278+ }
279+
280+ protected static function isList (array $ value ): bool
281+ {
282+ if (function_exists ('array_is_list ' )) {
283+ return array_is_list ($ value );
284+ }
285+
286+ $ expectedKey = 0 ;
287+
288+ foreach ($ value as $ key => $ unused ) {
289+ if ($ key !== $ expectedKey ) {
290+ return false ;
291+ }
292+
293+ $ expectedKey ++;
294+ }
295+
296+ return true ;
297+ }
298+
135299 public static function getFlattenKey ($ parts , $ schemas )
136300 {
137301 $ result = "" ;
0 commit comments