/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.felix.framework.security.condpermadmin; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.StringTokenizer; import org.apache.felix.framework.security.util.Permissions; import org.osgi.service.condpermadmin.ConditionInfo; import org.osgi.service.condpermadmin.ConditionalPermissionInfo; import org.osgi.service.permissionadmin.PermissionInfo; /** * Simple storage class for condperminfos. Additionally, this class can be used * to encode and decode infos. */ public final class ConditionalPermissionInfoImpl implements ConditionalPermissionInfo { private static final Random RANDOM = new Random(); static final ConditionInfo[] CONDITION_INFO = new ConditionInfo[0]; static final PermissionInfo[] PERMISSION_INFO = new PermissionInfo[0]; private final Object m_lock = new Object(); private final String m_name; private final boolean m_allow; private volatile ConditionalPermissionAdminImpl m_cpai; private ConditionInfo[] m_conditions; private PermissionInfo[] m_permissions; private int parseConditionInfo(char[] encoded, int idx, List conditions) { String type; String[] args; try { int pos = idx; /* skip whitespace */ while (Character.isWhitespace(encoded[pos])) { pos++; } /* the first character must be '[' */ if (encoded[pos] != '[') { throw new IllegalArgumentException("expecting open bracket"); } pos++; /* skip whitespace */ while (Character.isWhitespace(encoded[pos])) { pos++; } /* type is not quoted or encoded */ int begin = pos; while (!Character.isWhitespace(encoded[pos]) && (encoded[pos] != ']')) { pos++; } if (pos == begin || encoded[begin] == '"') { throw new IllegalArgumentException("expecting type"); } type = new String(encoded, begin, pos - begin); /* skip whitespace */ while (Character.isWhitespace(encoded[pos])) { pos++; } /* type may be followed by args which are quoted and encoded */ ArrayList argsList = new ArrayList(); while (encoded[pos] == '"') { pos++; begin = pos; while (encoded[pos] != '"') { if (encoded[pos] == '\\') { pos++; } pos++; } argsList.add(unescapeString(encoded, begin, pos)); pos++; if (Character.isWhitespace(encoded[pos])) { /* skip whitespace */ while (Character.isWhitespace(encoded[pos])) { pos++; } } } args = (String[]) argsList .toArray(new String[argsList.size()]); /* the final character must be ']' */ char c = encoded[pos++]; if (c != ']') { throw new IllegalArgumentException("expecting close bracket"); } conditions.add(new ConditionInfo(type, args)); return pos; } catch (ArrayIndexOutOfBoundsException e) { throw new IllegalArgumentException("parsing terminated abruptly"); } } private int parsePermissionInfo(char[] encoded, int idx, List permissions) { String parsedType = null; String parsedName = null; String parsedActions = null; try { int pos = idx; /* skip whitespace */ while (Character.isWhitespace(encoded[pos])) { pos++; } /* the first character must be '(' */ if (encoded[pos] != '(') { throw new IllegalArgumentException("expecting open parenthesis"); } pos++; /* skip whitespace */ while (Character.isWhitespace(encoded[pos])) { pos++; } /* type is not quoted or encoded */ int begin = pos; while (!Character.isWhitespace(encoded[pos]) && (encoded[pos] != ')')) { pos++; } if (pos == begin || encoded[begin] == '"') { throw new IllegalArgumentException("expecting type"); } parsedType = new String(encoded, begin, pos - begin); /* skip whitespace */ while (Character.isWhitespace(encoded[pos])) { pos++; } /* type may be followed by name which is quoted and encoded */ if (encoded[pos] == '"') { pos++; begin = pos; while (encoded[pos] != '"') { if (encoded[pos] == '\\') { pos++; } pos++; } parsedName = unescapeString(encoded, begin, pos); pos++; if (Character.isWhitespace(encoded[pos])) { /* skip whitespace */ while (Character.isWhitespace(encoded[pos])) { pos++; } /* * name may be followed by actions which is quoted and * encoded */ if (encoded[pos] == '"') { pos++; begin = pos; while (encoded[pos] != '"') { if (encoded[pos] == '\\') { pos++; } pos++; } parsedActions = unescapeString(encoded, begin, pos); pos++; /* skip whitespace */ while (Character.isWhitespace(encoded[pos])) { pos++; } } } } /* the final character must be ')' */ char c = encoded[pos++]; if (c != ')') { throw new IllegalArgumentException( "expecting close parenthesis"); } permissions.add(new PermissionInfo(parsedType,parsedName, parsedActions)); return pos; } catch (ArrayIndexOutOfBoundsException e) { throw new IllegalArgumentException("parsing terminated abruptly"); } } /** * Takes an encoded character array and decodes it into a new String. */ private static String unescapeString(char[] str, int begin, int end) { StringBuffer output = new StringBuffer(end - begin); for (int i = begin; i < end; i++) { char c = str[i]; if (c == '\\') { i++; if (i < end) { c = str[i]; switch (c) { case '"' : case '\\' : break; case 'r' : c = '\r'; break; case 'n' : c = '\n'; break; default : c = '\\'; i--; break; } } } output.append(c); } return output.toString(); } public ConditionalPermissionInfoImpl(String encoded) { encoded = encoded.trim(); String toUpper = encoded.toUpperCase(); if (!(toUpper.startsWith("ALLOW {") || toUpper.startsWith("DENY {"))) { throw new IllegalArgumentException(); } m_allow = toUpper.startsWith("ALLOW {"); m_cpai = null; List conditions = new ArrayList(); List permissions = new ArrayList(); try { char[] chars = encoded.substring((m_allow ? "ALLOW {".length() : "DENY {".length())).toCharArray(); int idx = 0; while (idx < chars.length) { if (Character.isWhitespace(chars[idx])) { idx++; } else if (chars[idx] == '[') { idx = parseConditionInfo(chars, idx, conditions); } else if (chars[idx] == '(') { idx = parsePermissionInfo(chars, idx, permissions); } else { if (chars[idx] != '}') { throw new IllegalArgumentException("Expected } but was: " + chars[idx]); } idx++; break; } } while (Character.isWhitespace(chars[idx])) { idx++; } if (chars[idx] == '"') { idx++; int begin = idx; while (chars[idx] != '"') { if (chars[idx] == '\\') { idx++; } idx++; } m_name = unescapeString(chars, begin, idx); } else { m_name = Long.toString(RANDOM.nextLong() ^ System.currentTimeMillis()); } } catch (ArrayIndexOutOfBoundsException ex) { ex.printStackTrace(); throw new IllegalArgumentException("Unable to parse conditional permission info: " + ex.getMessage()); } m_conditions = conditions.isEmpty() ? CONDITION_INFO : (ConditionInfo[]) conditions.toArray(new ConditionInfo[conditions .size()]); m_permissions = permissions.isEmpty() ? PERMISSION_INFO : (PermissionInfo[]) permissions .toArray(new PermissionInfo[permissions.size()]); } public ConditionalPermissionInfoImpl(ConditionalPermissionAdminImpl cpai, String name, boolean access) { m_allow = access; m_name = name; m_cpai = cpai; m_conditions = CONDITION_INFO; m_permissions = PERMISSION_INFO; } public ConditionalPermissionInfoImpl(ConditionInfo[] conditions, PermissionInfo[] permisions, ConditionalPermissionAdminImpl cpai, boolean access) { m_allow = access; m_name = Long.toString(RANDOM.nextLong() ^ System.currentTimeMillis()); m_cpai = cpai; m_conditions = conditions == null ? CONDITION_INFO : conditions; m_permissions = permisions == null ? PERMISSION_INFO : permisions; } public ConditionalPermissionInfoImpl(String name, ConditionInfo[] conditions, PermissionInfo[] permisions, ConditionalPermissionAdminImpl cpai, boolean access) { m_allow = access; m_name = (name != null) ? name : Long.toString(RANDOM.nextLong() ^ System.currentTimeMillis()); m_conditions = conditions == null ? CONDITION_INFO : conditions; m_permissions = permisions == null ? PERMISSION_INFO : permisions; m_cpai = cpai; } public void delete() { Object sm = System.getSecurityManager(); if (sm != null) { ((SecurityManager) sm).checkPermission(Permissions.ALL_PERMISSION); } synchronized (m_lock) { m_cpai.write(m_name, null); m_conditions = CONDITION_INFO; m_permissions = PERMISSION_INFO; } } public ConditionInfo[] getConditionInfos() { synchronized (m_lock) { return (ConditionInfo[]) m_conditions.clone(); } } ConditionInfo[] _getConditionInfos() { synchronized (m_lock) { return m_conditions; } } void setConditionsAndPermissions(ConditionInfo[] conditions, PermissionInfo[] permissions) { synchronized (m_lock) { m_conditions = conditions; m_permissions = permissions; } } public String getName() { return m_name; } public PermissionInfo[] getPermissionInfos() { synchronized (m_lock) { return (PermissionInfo[]) m_permissions.clone(); } } PermissionInfo[] _getPermissionInfos() { synchronized (m_lock) { return m_permissions; } } public String getEncoded() { StringBuffer buffer = new StringBuffer(); buffer.append(m_allow ? "ALLOW " : "DENY "); buffer.append('{'); buffer.append(' '); synchronized (m_lock) { writeTo(m_conditions, buffer); writeTo(m_permissions, buffer); } buffer.append('}'); buffer.append(' '); buffer.append('"'); escapeString(m_name, buffer); buffer.append('"'); return buffer.toString(); } /** * This escapes the quotes, backslashes, \n, and \r in the string using a * backslash and appends the newly escaped string to a StringBuffer. */ private static void escapeString(String str, StringBuffer output) { int len = str.length(); for (int i = 0; i < len; i++) { char c = str.charAt(i); switch (c) { case '"' : case '\\' : output.append('\\'); output.append(c); break; case '\r' : output.append("\\r"); break; case '\n' : output.append("\\n"); break; default : output.append(c); break; } } } private void writeTo(Object[] elements, StringBuffer buffer) { for (int i = 0; i < elements.length; i++) { buffer.append(elements[i]); buffer.append(' '); } } public String toString() { return getEncoded(); } public String getAccessDecision() { return m_allow ? ConditionalPermissionInfo.ALLOW : ConditionalPermissionInfo.DENY; } public boolean isAllow() { return m_allow; } }