KissValidator.java

/*******************************************************************************
 * Copyright (c) 2019, RISE AB
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions 
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, 
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, 
 *    this list of conditions and the following disclaimer in the documentation 
 *    and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *******************************************************************************/
package se.sics.ace.examples;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.upokecenter.cbor.CBORObject;
import com.upokecenter.cbor.CBORType;

import se.sics.ace.AceException;
import se.sics.ace.rs.AudienceValidator;
import se.sics.ace.rs.ScopeValidator;

/**
 * Simple audience and scope validator for testing purposes.
 * This validator expects the scopes to be Strings as in OAuth 2.0.
 * 
 * The actions are expected to be integers corresponding to the 
 * values for RESTful actions in <code>Constants</code>.
 * 
 * @author Ludwig Seitz and Marco Tiloca
 *
 */
public class KissValidator implements AudienceValidator, ScopeValidator {

    /**
     * The audiences we recognize
     */
	private Set<String> myAudiences;
	
	/**
	 * Maps the scopes to a map that maps the scope's resources to the actions 
	 * allowed on that resource
	 */
	private Map<String, Map<String, Set<Short>>> myScopes;  
	
	/**
	 * Constructor.
	 * 
	 * @param myAudiences  the audiences that this validator should accept
	 * @param myScopes  the scopes that this validator should accept
	 */
	public KissValidator(Set<String> myAudiences, 
	        Map<String, Map<String, Set<Short>>> myScopes) {
		this.myAudiences = new HashSet<>();
		this.myScopes = new HashMap<>();
		if (myAudiences != null) {
		    this.myAudiences.addAll(myAudiences);
		} else {
		    this.myAudiences = Collections.emptySet();
		}
		if (myScopes != null) {
		    this.myScopes.putAll(myScopes);
		} else {
		    this.myScopes = Collections.emptyMap();
		}
	}
	
	@Override
	public boolean match(String aud) {
		return this.myAudiences.contains(aud);
	}

    @Override
    public boolean scopeMatch(CBORObject scope, String resourceId, Object actionId)
            throws AceException {
        if (!scope.getType().equals(CBORType.TextString)) {
            throw new AceException("Scope must be a String in KissValidator");
        }
        String[] scopes = scope.AsString().split(" ");
        for (String subscope : scopes) {
            Map<String, Set<Short>> resources = this.myScopes.get(subscope);
            if (resources == null) {
                continue;
            }
            if (resources.containsKey(resourceId)) {
                if (resources.get(resourceId).contains(actionId)) {
                    return true;
                }
            }
        }
        return false;
    }

    @Override
    public boolean scopeMatchResource(CBORObject scope, String resourceId)
            throws AceException {
        if (!scope.getType().equals(CBORType.TextString)) {
            throw new AceException("Scope must be a String in KissValidator");
        }
        String[] scopes = scope.AsString().split(" ");
        for (String subscope : scopes) {
            Map<String, Set<Short>> resources = this.myScopes.get(subscope);
            if (resources.containsKey(resourceId)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean isScopeMeaningful(CBORObject scope) throws AceException {
        if (!scope.getType().equals(CBORType.TextString)) {
            throw new AceException("Scope must be a String in KissValidator");
        }
        String[] scopes = scope.AsString().split(" ");
        for (String subscope : scopes) {
            if (!this.myScopes.containsKey(subscope))
                    return false;
        }
        return true;
    }

    @Override
    public CBORObject getScope(String resource, short action) {
        Iterator<Entry<String, Map<String, Set<Short>>>> it = this.myScopes.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry<String, Map<String, Set<Short>>> e = it.next();
            Iterator<Entry<String, Set<Short>>> it2 = e.getValue().entrySet().iterator();
            while (it2.hasNext()) {
                Map.Entry<String, Set<Short>> e2 = it2.next();
                if (e2.getKey().equals(resource)) {//Found resource
                //Check if action matches
                    if (e2.getValue().contains(action)) {
                        return CBORObject.FromObject(e.getKey());
                    }
                }   
            }
        }
        return null; //No scope found
    }
    
    // This method performs as isScopeMeaningful(CBORObject scope) for this Validator
    @Override
    public boolean isScopeMeaningful(CBORObject scope, String aud) throws AceException {
        return isScopeMeaningful(scope);
    }
}