001/* 002 * Copyright 2015-2024 Ping Identity Corporation 003 * 004 * This program is free software; you can redistribute it and/or modify 005 * it under the terms of the GNU General Public License (GPLv2 only) 006 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 007 * as published by the Free Software Foundation. 008 * 009 * This program is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 012 * GNU General Public License for more details. 013 * 014 * You should have received a copy of the GNU General Public License 015 * along with this program; if not, see <http://www.gnu.org/licenses>. 016 */ 017 018package com.unboundid.scim2.server.utils; 019 020import com.unboundid.scim2.common.Path; 021import com.unboundid.scim2.common.annotations.NotNull; 022import com.unboundid.scim2.common.types.AttributeDefinition; 023 024import java.util.Set; 025 026 027 028/** 029 * A resource trimmer implementing the SCIM standard for returning attributes. 030 */ 031public class ScimResourceTrimmer extends ResourceTrimmer 032{ 033 @NotNull 034 private final ResourceTypeDefinition resourceType; 035 036 @NotNull 037 private final Set<Path> requestAttributes; 038 039 @NotNull 040 private final Set<Path> queryAttributes; 041 042 private final boolean excluded; 043 044 045 046 /** 047 * Create a new SCIMResourceTrimmer. 048 * 049 * @param resourceType The resource type definition for resources to 050 * trim. 051 * @param requestAttributes The attributes in the request object or 052 * {@code null} for 053 * other requests. 054 * @param queryAttributes The attributes from the 'attributes' or 055 * 'excludedAttributes' query parameter. 056 * @param excluded {@code true} if the queryAttributes came from 057 * the excludedAttributes query parameter. 058 */ 059 public ScimResourceTrimmer(@NotNull final ResourceTypeDefinition resourceType, 060 @NotNull final Set<Path> requestAttributes, 061 @NotNull final Set<Path> queryAttributes, 062 final boolean excluded) 063 { 064 this.resourceType = resourceType; 065 this.requestAttributes = requestAttributes; 066 this.queryAttributes = queryAttributes; 067 this.excluded = excluded; 068 } 069 070 071 072 /** 073 * {@inheritDoc} 074 */ 075 @Override 076 public boolean shouldReturn(@NotNull final Path path) 077 { 078 AttributeDefinition attributeDefinition = 079 resourceType.getAttributeDefinition(path); 080 AttributeDefinition.Returned returned = attributeDefinition == null ? 081 AttributeDefinition.Returned.DEFAULT : 082 attributeDefinition.getReturned(); 083 084 switch(returned) 085 { 086 case ALWAYS: 087 return true; 088 case NEVER: 089 return false; 090 case REQUEST: 091 // Return only if it was one of the request attributes or if there are 092 // no request attributes, then only if it was one of the override query 093 // attributes. 094 return pathContains(requestAttributes, path) || 095 (requestAttributes.isEmpty() && !excluded && 096 pathContains(queryAttributes, path)); 097 default: 098 // Return if it is not one of the excluded query attributes and no 099 // override query attributes are provided. If override query attributes 100 // are provided, only return if it is one of them. 101 if(excluded) 102 { 103 return !pathContains(queryAttributes, path); 104 } 105 else 106 { 107 return queryAttributes.isEmpty() || 108 pathContains(queryAttributes, path); 109 } 110 } 111 } 112 113 private boolean pathContains(@NotNull final Set<Path> paths, 114 @NotNull final Path path) 115 { 116 // Exact path match 117 if (paths.contains(path)) 118 { 119 return true; 120 } 121 122 if (!excluded) 123 { 124 // See if a sub-attribute of the given path is included in the list 125 // ie. include name if name.givenName is in the list. 126 for (Path p : paths) 127 { 128 if (p.size() > path.size() && path.equals(p.subPath(path.size()))) 129 { 130 return true; 131 } 132 } 133 } 134 135 // See if the parent attribute of the given path is included in the list 136 // ie. include name.{anything} if name is in the list. 137 for (Path p = path; p.size() > 0; p = p.subPath(p.size() - 1)) 138 { 139 if (paths.contains(p)) 140 { 141 return true; 142 } 143 } 144 145 return false; 146 } 147}