All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.unboundid.scim.sdk.SCIMAttribute Maven / Gradle / Ivy

Go to download

The UnboundID SCIM SDK is a library that may be used to interact with various types of SCIM-enabled endpoints (such as the UnboundID server products) to perform lightweight, cloud-based identity management via the SCIM Protocol. See http://www.simplecloud.info for more information.

There is a newer version: 1.8.26
Show newest version
/*
 * Copyright 2011-2019 Ping Identity Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License (GPLv2 only)
 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see .
 */

package com.unboundid.scim.sdk;


import com.unboundid.scim.schema.AttributeDescriptor;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.xml.bind.DatatypeConverter;


/**
 * This class represents a System for Cross-Domain Identity Management (SCIM)
 * attribute. Attributes are categorized as either single-valued or
 * multi-valued. This class allows for the following kinds of attributes.
 *
 * 
    *
  1. Simple type (String, Boolean, DateTime, Integer or Binary). * An example is the 'userName' attribute in the core schema.
  2. * *
  3. Complex type. An example is the 'name' attribute in the core * schema.
  4. * *
  5. Multi-valued simple type. Represented using multi-valued complex values, * because the values may have 'type' and 'primary' sub-attributes to * distinguish each primitive value. Examples of this are the 'emails' and * 'photos' attributes in the core schema.
  6. * *
  7. Multi-valued complex type. An examples is the 'addresses' attribute in * the core schema.
  8. *
* */ public final class SCIMAttribute { /** * The mapping descriptor of this attribute. */ private final AttributeDescriptor attributeDescriptor; /** * The value(s) of this attribute. */ private final SCIMAttributeValue[] values; /** * Create a new instance of an attribute. * * @param descriptor The mapping descriptor of this value. * @param values The value(s) of this attribute. */ private SCIMAttribute(final AttributeDescriptor descriptor, final SCIMAttributeValue ... values) { this.attributeDescriptor = descriptor; this.values = values; } /** * Create an attribute. * * @param descriptor The mapping descriptor for this attribute. * @param values The value(s) of this attribute. * * @return A new attribute. */ public static SCIMAttribute create( final AttributeDescriptor descriptor, final SCIMAttributeValue... values) { // Make sure all values of a multi-valued attributes are represented using // the special MultiValuedSCIMAttributeValue implementation. SCIMAttributeValue[] valuesCopy; if(values != null) { valuesCopy = new SCIMAttributeValue[values.length]; if(descriptor.isMultiValued()) { int i = 0; for(SCIMAttributeValue value : values) { if(!(value instanceof MultiValuedSCIMAttributeValue)) { if(value.isComplex()) { valuesCopy[i++] = new MultiValuedSCIMAttributeValue( value.getAttributes()); } else { // Try to convert to complex value by using the value // sub-attribute. SCIMAttribute valueSubAttribute; try { valueSubAttribute = new SCIMAttribute(descriptor.getSubAttribute("value"), value); } catch (InvalidResourceException e) { // The multi-valued attribute did not define a value // sub-attribute. Throw error. throw new IllegalArgumentException("Values of multi-valued " + "attributes must be complex when the normative value " + "sub-attribute is not defined"); } valuesCopy[i++] = new MultiValuedSCIMAttributeValue( Collections.singletonMap("value", valueSubAttribute)); } } else { valuesCopy[i++] = value; } } } else { System.arraycopy(values, 0, valuesCopy, 0, values.length); } } else { valuesCopy = new SCIMAttributeValue[0]; } return new SCIMAttribute(descriptor, valuesCopy); } /** * Retrieve the name of the schema to which this attribute belongs. * * @return The name of the schema to which this attribute belongs. */ public String getSchema() { return this.attributeDescriptor.getSchema(); } /** * Retrieve the name of this attribute. The name does not indicate which * schema the attribute belongs to. * * @return The name of this attribute. */ public String getName() { return this.attributeDescriptor.getName(); } /** * Retrieves the value of this attribute. This method should only be * called if the attribute is single valued. * * @return The value of this attribute. */ public SCIMAttributeValue getValue() { return values[0]; } /** * Retrieves the values of this attribute. This method should only be * called if the attribute is multi-valued. * * @return The values of this attribute. */ public SCIMAttributeValue[] getValues() { return values; } /** * Retrieves the SCIM attribute mapping of this this attribute. * * @return The attribute descriptor */ public AttributeDescriptor getAttributeDescriptor() { return attributeDescriptor; } /** * Determine whether this attribute matches the provided filter parameters. * * @param filter The filter parameters to be compared against this attribute. * * @return {@code true} if this attribute matches the provided filter, and * {@code false} otherwise. */ public boolean matchesFilter(final SCIMFilter filter) { final SCIMFilterType type = filter.getFilterType(); final List components = filter.getFilterComponents(); switch(type) { case AND: for(SCIMFilter component : components) { if(!matchesFilter(component)) { return false; } } return true; case OR: for(SCIMFilter component : components) { if(matchesFilter(component)) { return true; } } return false; } final String schema = filter.getFilterAttribute().getAttributeSchema(); if (!schema.equalsIgnoreCase(getSchema())) { return false; } final String attributeName = filter.getFilterAttribute().getAttributeName(); String subAttributeName = filter.getFilterAttribute().getSubAttributeName(); if (subAttributeName == null) { subAttributeName = "value"; } if (!attributeName.equalsIgnoreCase(getName())) { return false; } if (attributeDescriptor.isMultiValued()) { for (final SCIMAttributeValue v : getValues()) { if (v.isComplex()) { final Collection descriptors = attributeDescriptor.getSubAttributes(); for (AttributeDescriptor descriptor : descriptors) { final SCIMAttribute a = v.getAttribute(descriptor.getName()); if (a != null) { // This is done because the client specifies 'emails' rather // than 'emails.email'. final AttributePath childPath = new AttributePath(schema, a.getName(), subAttributeName); if (a.matchesFilter(new SCIMFilter(type, childPath, filter.getFilterValue(), filter.isQuoteFilterValue(), filter.getFilterComponents()))) { return true; } } } } else { AttributeDescriptor singularDescriptor = AttributeDescriptor.createAttribute(getName(), attributeDescriptor.getDataType(), attributeDescriptor.getDescription(), getSchema(), attributeDescriptor.isReadOnly(), attributeDescriptor.isRequired(), attributeDescriptor.isCaseExact()); final SCIMAttribute singularAttr = create(singularDescriptor, v); if (singularAttr.matchesFilter(filter)) { return true; } } } } else { final SCIMAttributeValue v = getValue(); if (v.isComplex()) { if (subAttributeName != null) { final SCIMAttribute a = v.getAttribute(subAttributeName); if (a != null) { final AttributePath childPath = new AttributePath(schema, subAttributeName, null); return a.matchesFilter( new SCIMFilter(type, childPath, filter.getFilterValue(), filter.isQuoteFilterValue(), filter.getFilterComponents())); } } } else { if (type == SCIMFilterType.PRESENCE) { return true; } final AttributeDescriptor.DataType dataType = attributeDescriptor.getDataType(); String stringValue = null; Double doubleValue = null; Long longValue = null; Date dateValue = null; Boolean boolValue = null; byte[] binValue = null; switch(dataType) { case BINARY: binValue = v.getBinaryValue(); if(binValue == null) { return false; } break; case BOOLEAN: boolValue = v.getBooleanValue(); if(boolValue == null) { return false; } break; case DATETIME: dateValue = v.getDateValue(); if(dateValue == null) { return false; } break; case DECIMAL: doubleValue = v.getDecimalValue(); if(doubleValue == null) { return false; } break; case INTEGER: longValue = v.getIntegerValue(); if(longValue == null) { return false; } break; case STRING: stringValue = v.getStringValue(); if(stringValue == null) { return false; } break; default: throw new RuntimeException( "Invalid attribute data type: " + dataType); } final String filterValue = filter.getFilterValue(); // TODO support caseExact attributes //Note: The code below explicitly unboxes the objects before comparing // to avoid auto-unboxing and make it clear that it is just // primitives being compared. switch (type) { case EQUALITY: if(stringValue != null) { return stringValue.equalsIgnoreCase(filterValue); } else if(doubleValue != null) { try { double filterValueDouble = Double.parseDouble(filterValue); return doubleValue.doubleValue() == filterValueDouble; } catch(NumberFormatException e) { return false; } } else if(longValue != null) { try { long filterValueLong = Long.parseLong(filterValue); return longValue.longValue() == filterValueLong; } catch(NumberFormatException e) { return false; } } else if(boolValue != null) { return boolValue.booleanValue() == Boolean.parseBoolean(filterValue); } else if(dateValue != null) { try { SimpleValue filterValueDate = new SimpleValue(filterValue); return dateValue.equals(filterValueDate.getDateValue()); } catch(IllegalArgumentException e) { return false; } } else if(binValue != null) { //TODO: It's debatable whether this ought to just check whether // the base-64 encoded string is equal, rather than checking // if the bytes are equal. This seems more correct. try { byte[] filterValueBytes = DatatypeConverter.parseBase64Binary(filterValue); return Arrays.equals(binValue, filterValueBytes); } catch(IllegalArgumentException e) { return false; } } return false; case CONTAINS: if(stringValue != null) { return StaticUtils.toLowerCase(stringValue).contains( StaticUtils.toLowerCase(filterValue)); } else if(doubleValue != null) { try { double filterValueDouble = Double.parseDouble(filterValue); return doubleValue.doubleValue() == filterValueDouble; } catch(NumberFormatException e) { return false; } } else if(longValue != null) { try { long filterValueLong = Long.parseLong(filterValue); return longValue.longValue() == filterValueLong; } catch(NumberFormatException e) { return false; } } else if(boolValue != null) { return boolValue.booleanValue() == Boolean.parseBoolean(filterValue); } else if(dateValue != null) { try { SimpleValue filterValueDate = new SimpleValue(filterValue); return dateValue.equals(filterValueDate.getDateValue()); } catch(IllegalArgumentException e) { return false; } } else if(binValue != null) { try { byte[] filterValueBytes = DatatypeConverter.parseBase64Binary(filterValue); boolean contains = false; for(int i = 0; i < binValue.length; i++) { if(binValue[i] == filterValueBytes[0]) { contains = true; for(int j = 1; j < filterValueBytes.length; j++) { if(i+j >= binValue.length || binValue[i+j] != filterValueBytes[j]) { contains = false; break; } } if(contains) { break; } } } return contains; } catch(IllegalArgumentException e) { return false; } } return false; case STARTS_WITH: if(stringValue != null) { return StaticUtils.toLowerCase(stringValue).startsWith( StaticUtils.toLowerCase(filterValue)); } else if(doubleValue != null) { return false; } else if(longValue != null) { return false; } else if(boolValue != null) { return false; } else if(dateValue != null) { return false; } else if(binValue != null) { try { byte[] filterValueBytes = DatatypeConverter.parseBase64Binary(filterValue); for(int i = 0; i < filterValueBytes.length; i++) { if(binValue[i] != filterValueBytes[i]) { return false; } } return true; } catch(IllegalArgumentException e) { return false; } } return false; case GREATER_THAN: if(stringValue != null) { return stringValue.compareToIgnoreCase(filterValue) > 0; } else if(doubleValue != null) { try { double filterValueDouble = Double.parseDouble(filterValue); return doubleValue.doubleValue() > filterValueDouble; } catch(NumberFormatException e) { return false; } } else if(longValue != null) { try { long filterValueLong = Long.parseLong(filterValue); return longValue.longValue() > filterValueLong; } catch(NumberFormatException e) { return false; } } else if(boolValue != null) { return false; } else if(dateValue != null) { try { SimpleValue filterValueDate = new SimpleValue(filterValue); return dateValue.after(filterValueDate.getDateValue()); } catch(IllegalArgumentException e) { return false; } } else if(binValue != null) { return false; } return false; case GREATER_OR_EQUAL: if(stringValue != null) { return stringValue.compareToIgnoreCase(filterValue) >= 0; } else if(doubleValue != null) { try { double filterValueDouble = Double.parseDouble(filterValue); return doubleValue.doubleValue() >= filterValueDouble; } catch(NumberFormatException e) { return false; } } else if(longValue != null) { try { long filterValueLong = Long.parseLong(filterValue); return longValue.longValue() >= filterValueLong; } catch(NumberFormatException e) { return false; } } else if(boolValue != null) { return false; } else if(dateValue != null) { try { SimpleValue filterValueDate = new SimpleValue(filterValue); return dateValue.after(filterValueDate.getDateValue()) || dateValue.equals(filterValueDate.getDateValue()); } catch(IllegalArgumentException e) { return false; } } else if(binValue != null) { return false; } return false; case LESS_THAN: if(stringValue != null) { return stringValue.compareToIgnoreCase(filterValue) < 0; } else if(doubleValue != null) { try { double filterValueDouble = Double.parseDouble(filterValue); return doubleValue.doubleValue() < filterValueDouble; } catch(NumberFormatException e) { return false; } } else if(longValue != null) { try { long filterValueLong = Long.parseLong(filterValue); return longValue.longValue() < filterValueLong; } catch(NumberFormatException e) { return false; } } else if(boolValue != null) { return false; } else if(dateValue != null) { try { SimpleValue filterValueDate = new SimpleValue(filterValue); return dateValue.before(filterValueDate.getDateValue()); } catch(IllegalArgumentException e) { return false; } } else if(binValue != null) { return false; } return false; case LESS_OR_EQUAL: if(stringValue != null) { return stringValue.compareToIgnoreCase(filterValue) <= 0; } else if(doubleValue != null) { try { double filterValueDouble = Double.parseDouble(filterValue); return doubleValue.doubleValue() <= filterValueDouble; } catch(NumberFormatException e) { return false; } } else if(longValue != null) { try { long filterValueLong = Long.parseLong(filterValue); return longValue.longValue() <= filterValueLong; } catch(NumberFormatException e) { return false; } } else if(boolValue != null) { return false; } else if(dateValue != null) { try { SimpleValue filterValueDate = new SimpleValue(filterValue); return dateValue.before(filterValueDate.getDateValue()) || dateValue.equals(filterValueDate.getDateValue()); } catch(IllegalArgumentException e) { return false; } } else if(binValue != null) { return false; } return false; } } } return false; } @Override public boolean equals(final Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } SCIMAttribute that = (SCIMAttribute) o; //Convert the value arrays into Sets so that the order of the attributes //doesn't matter. Set valueSet1 = new HashSet(Arrays.asList(values)); Set valueSet2 = new HashSet(Arrays.asList(that.values)); return attributeDescriptor.equals(that.attributeDescriptor) && valueSet1.equals(valueSet2); } @Override public int hashCode() { int result = attributeDescriptor.hashCode(); result = 31 * result + (values != null ? Arrays.hashCode(values) : 0); return result; } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append("SCIMAttribute"); sb.append("{attribute=").append(attributeDescriptor.getSchema()); sb.append(SCIMConstants.SEPARATOR_CHAR_QUALIFIED_ATTRIBUTE); sb.append(attributeDescriptor.getName()); sb.append(", values=").append(values == null ? "null" : Arrays.asList(values).toString()); sb.append('}'); return sb.toString(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy