/** * Copyright 2013-2015 Seagate Technology LLC. * * This Source Code Form is subject to the terms of the Mozilla * Public License, v. 2.0. If a copy of the MPL was not * distributed with this file, You can obtain one at * https://mozilla.org/MP:/2.0/. * * This program is distributed in the hope that it will be useful, * but is provided AS-IS, WITHOUT ANY WARRANTY; including without * the implied warranty of MERCHANTABILITY, NON-INFRINGEMENT or * FITNESS FOR A PARTICULAR PURPOSE. See the Mozilla Public * License for more details. * * See www.openkinetic.org for more project information */ package com.seagate.kinetic.simulator.internal; import java.util.Arrays; import java.util.Map; import java.util.logging.Logger; import com.google.protobuf.ByteString; import com.seagate.kinetic.proto.Kinetic.Command.Security.ACL; import com.seagate.kinetic.proto.Kinetic.Command.Security.ACL.Permission; import com.seagate.kinetic.proto.Kinetic.Command.Security.ACL.Scope; /** * Prototype. * * @author chiaming * */ public class Authorizer { private final static Logger logger = Logger.getLogger(Authorizer.class .getName()); /** * Check permission. * * @param aclmap * userId/ACL map * @param user * userId associated with its Hmac key. * @param role * operation request role (command). */ public static void checkPermission(Map<Long, ACL> aclmap, long user, Permission role) throws KVSecurityException { // check if there is an ACL entry for the userId. ACL acl = aclmap.get(user); if (acl == null) { throw new KVSecurityException("permission denied."); } // chiaming: fix this - only support one domain for (Scope domain : acl.getScopeList()) { // check if the request has the role (permission) to perform the op if (domain.getPermissionList().contains(role) == false) { throw new KVSecurityException("permission denied."); } } logger.fine("check operation passed: " + role); return; } /** * Check permission. * * @param aclmap * userId/ACL map * @param user * userId associated with its Hmac key. * @param role * operation request role (command). * @param key * from the request. */ public static void checkPermission(Map<Long, ACL> aclmap, long user, Permission role, ByteString key) throws KVSecurityException { if (!hasPermission(aclmap, user, role, key)) { throw new KVSecurityException("permission denied"); } logger.fine("check operation passed: " + role); return; } /** * Returns true iff the user has the given role on the given key * * @param aclmap * @param user * @param role * @param key * @return * @throws KVSecurityException for unexpected configuration problems related to security parameters */ public static boolean hasPermission(Map<Long, ACL> aclmap, long user, Permission role, ByteString key) throws KVSecurityException { if (null == key) { throw new KVSecurityException( "permission denied. the parameter of key is invalid."); } // check if there is an ACL entry for the userId. ACL acl = aclmap.get(user); if (acl == null) { throw new KVSecurityException("permission denied. ACL is null"); } /** * check if key is within the defined scope */ boolean hasRightRole = false; for (Scope scope : acl.getScopeList()) { long offset = scope.getOffset(); if (0 > offset) { throw new KVSecurityException( "permission denied. domain offset is invalid."); } ByteString currentScopeValue = scope.getValue(); if (currentScopeValue.size() > key.size()) { throw new KVSecurityException( "permission denied. domain value size is bigger than key size."); } int startValueIndex = (int) offset; int endValueIndex = startValueIndex + currentScopeValue.size(); ByteString scopeValueFromKey = key.substring(startValueIndex, endValueIndex); if (equals(currentScopeValue.toByteArray(), scopeValueFromKey.toByteArray())) { if (scope.getPermissionList().contains(role) == true) { hasRightRole = true; break; } } } return hasRightRole; } private static boolean equals(byte[] byteArray1, byte[] byteArray2) { if (null == byteArray1 && null == byteArray2) { return true; } else if (null == byteArray1 || null == byteArray2) { return false; } else { if (byteArray1.length != byteArray2.length) { return false; } else { for (int i = 0; i < byteArray1.length; i++) { if (byteArray1[i] != byteArray2[i]) { return false; } } } } return true; } /** * Returns true if the user has the given role on the given key range. * * 1. Start key and end key must be with in the same scope and has Range * role for the scope. The start key and end key must have the same prefix: * * The byte array value of (0, offset+scopeValue.size()) must be equal for * start and end keys. * * 2. See the protocol definition for ACL for boundary scenarios. * * https://github.com/Seagate/kinetic-protocol/blob/master/kinetic.proto * * * @param aclmap * @param user * @param role * @param startKey * @return * @throws KVSecurityException * for unexpected configuration problems related to security * parameters */ public static boolean hasRangePermission(Map<Long, ACL> aclmap, long user, Permission role, ByteString startKey, ByteString endKey) throws KVSecurityException { if (null == startKey || null == endKey) { throw new KVSecurityException( "permission denied. start key and end key cannot be null"); } // check if there is an ACL entry for the userId. ACL acl = aclmap.get(user); if (acl == null) { throw new KVSecurityException("permission denied. ACL is null"); } /** * check if key is within the defined scope */ boolean hasRightRole = false; for (Scope scope : acl.getScopeList()) { long offset = scope.getOffset(); if (0 > offset) { throw new KVSecurityException( "permission denied. domain offset is invalid."); } ByteString currentScopeValue = scope.getValue(); if (currentScopeValue.size() > startKey.size()) { throw new KVSecurityException( "permission denied. domain value size is bigger than key size."); } int startValueIndex = (int) offset; int endValueIndex = startValueIndex + currentScopeValue.size(); // start key scope ByteString scopeValueFromStartKey = startKey.substring( startValueIndex, endValueIndex); byte[] currentScope = currentScopeValue.toByteArray(); if (equals(currentScope, scopeValueFromStartKey.toByteArray())) { if (scope.getPermissionList().contains(role) == true) { /** * at this point, start key is within the permitted scope. * if no offset and vale is defined, the user has ALL range * for the current scope. */ if (offset == 0 && currentScopeValue.isEmpty()) { return true; } /** * if endkey == "", use max value for the end key. */ if (endKey.isEmpty()) { byte[] maxKey = new byte[4096]; Arrays.fill(maxKey, (byte) 0XFF); endKey = ByteString.copyFrom(maxKey); } // end key scope value ByteString scopeValueFromEndKey = endKey.substring( startValueIndex, endValueIndex); /** * start and end key must be within the same scope and same * prefix */ if (equals(currentScope, scopeValueFromEndKey.toByteArray())) { // start key prefix ByteString startKeyRangeScope = startKey.substring(0, endValueIndex); // end key prefix ByteString endKeyRangeScope = endKey.substring(0, endValueIndex); // compare start and end key's (prefix + scope) if (equals(startKeyRangeScope.toByteArray(), endKeyRangeScope.toByteArray())) { hasRightRole = true; break; } } } } } return hasRightRole; } }