blob: 102ace18a1fd9fe18c11554e88ddcad8bbd6c42c [file] [log] [blame]
Karl Pauls36407322008-03-07 00:37:30 +00001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19package org.apache.felix.framework.security.condpermadmin;
20
21import java.util.ArrayList;
22import java.util.List;
23import java.util.Random;
24import java.util.StringTokenizer;
25
26import org.apache.felix.framework.security.util.Permissions;
27import org.osgi.service.condpermadmin.ConditionInfo;
28import org.osgi.service.condpermadmin.ConditionalPermissionInfo;
29import org.osgi.service.permissionadmin.PermissionInfo;
30
31/**
32 * Simple storage class for condperminfos. Additionally, this class can be used
Karl Pauls23287bd2010-01-10 22:11:27 +000033 * to encode and decode infos.
Karl Pauls36407322008-03-07 00:37:30 +000034 */
Karl Pauls23287bd2010-01-10 22:11:27 +000035public final class ConditionalPermissionInfoImpl implements
36 ConditionalPermissionInfo
Karl Pauls36407322008-03-07 00:37:30 +000037{
38 private static final Random RANDOM = new Random();
39 static final ConditionInfo[] CONDITION_INFO = new ConditionInfo[0];
40 static final PermissionInfo[] PERMISSION_INFO = new PermissionInfo[0];
41 private final Object m_lock = new Object();
42 private final String m_name;
Karl Pauls23287bd2010-01-10 22:11:27 +000043 private final boolean m_allow;
Karl Pauls36407322008-03-07 00:37:30 +000044 private volatile ConditionalPermissionAdminImpl m_cpai;
45 private ConditionInfo[] m_conditions;
46 private PermissionInfo[] m_permissions;
Karl Pauls9ac328a2010-06-21 22:36:50 +000047
48 private int parseConditionInfo(char[] encoded, int idx, List conditions) {
49 String type;
50 String[] args;
51 try {
52 int pos = idx;
53
54 /* skip whitespace */
55 while (Character.isWhitespace(encoded[pos])) {
56 pos++;
57 }
58
59 /* the first character must be '[' */
60 if (encoded[pos] != '[') {
61 throw new IllegalArgumentException("expecting open bracket");
62 }
63 pos++;
64
65 /* skip whitespace */
66 while (Character.isWhitespace(encoded[pos])) {
67 pos++;
68 }
69
70 /* type is not quoted or encoded */
71 int begin = pos;
72 while (!Character.isWhitespace(encoded[pos])
73 && (encoded[pos] != ']')) {
74 pos++;
75 }
76 if (pos == begin || encoded[begin] == '"') {
77 throw new IllegalArgumentException("expecting type");
78 }
79 type = new String(encoded, begin, pos - begin);
80
81 /* skip whitespace */
82 while (Character.isWhitespace(encoded[pos])) {
83 pos++;
84 }
85
86 /* type may be followed by args which are quoted and encoded */
87 ArrayList argsList = new ArrayList();
88 while (encoded[pos] == '"') {
89 pos++;
90 begin = pos;
91 while (encoded[pos] != '"') {
92 if (encoded[pos] == '\\') {
93 pos++;
94 }
95 pos++;
96 }
97 argsList.add(unescapeString(encoded, begin, pos));
98 pos++;
99
100 if (Character.isWhitespace(encoded[pos])) {
101 /* skip whitespace */
102 while (Character.isWhitespace(encoded[pos])) {
103 pos++;
104 }
105 }
106 }
107 args = (String[]) argsList
108 .toArray(new String[argsList.size()]);
109
110 /* the final character must be ']' */
111 char c = encoded[pos++];
112 if (c != ']') {
113 throw new IllegalArgumentException("expecting close bracket");
114 }
115 conditions.add(new ConditionInfo(type, args));
116 return pos;
117 }
118 catch (ArrayIndexOutOfBoundsException e) {
119 throw new IllegalArgumentException("parsing terminated abruptly");
120 }
121 }
122
123 private int parsePermissionInfo(char[] encoded, int idx, List permissions)
124 {
125 String parsedType = null;
126 String parsedName = null;
127 String parsedActions = null;
128 try {
129 int pos = idx;
130
131 /* skip whitespace */
132 while (Character.isWhitespace(encoded[pos])) {
133 pos++;
134 }
135
136 /* the first character must be '(' */
137 if (encoded[pos] != '(') {
138 throw new IllegalArgumentException("expecting open parenthesis");
139 }
140 pos++;
141
142 /* skip whitespace */
143 while (Character.isWhitespace(encoded[pos])) {
144 pos++;
145 }
146
147 /* type is not quoted or encoded */
148 int begin = pos;
149 while (!Character.isWhitespace(encoded[pos])
150 && (encoded[pos] != ')')) {
151 pos++;
152 }
153 if (pos == begin || encoded[begin] == '"') {
154 throw new IllegalArgumentException("expecting type");
155 }
156 parsedType = new String(encoded, begin, pos - begin);
157
158 /* skip whitespace */
159 while (Character.isWhitespace(encoded[pos])) {
160 pos++;
161 }
162
163 /* type may be followed by name which is quoted and encoded */
164 if (encoded[pos] == '"') {
165 pos++;
166 begin = pos;
167 while (encoded[pos] != '"') {
168 if (encoded[pos] == '\\') {
169 pos++;
170 }
171 pos++;
172 }
173 parsedName = unescapeString(encoded, begin, pos);
174 pos++;
175
176 if (Character.isWhitespace(encoded[pos])) {
177 /* skip whitespace */
178 while (Character.isWhitespace(encoded[pos])) {
179 pos++;
180 }
181
182 /*
183 * name may be followed by actions which is quoted and
184 * encoded
185 */
186 if (encoded[pos] == '"') {
187 pos++;
188 begin = pos;
189 while (encoded[pos] != '"') {
190 if (encoded[pos] == '\\') {
191 pos++;
192 }
193 pos++;
194 }
195 parsedActions = unescapeString(encoded, begin, pos);
196 pos++;
197
198 /* skip whitespace */
199 while (Character.isWhitespace(encoded[pos])) {
200 pos++;
201 }
202 }
203 }
204 }
205
206 /* the final character must be ')' */
207 char c = encoded[pos++];
208 if (c != ')') {
209 throw new IllegalArgumentException(
210 "expecting close parenthesis");
211 }
212 permissions.add(new PermissionInfo(parsedType,parsedName, parsedActions));
213 return pos;
214 }
215 catch (ArrayIndexOutOfBoundsException e) {
216 throw new IllegalArgumentException("parsing terminated abruptly");
217 }
218 }
219 /**
220 * Takes an encoded character array and decodes it into a new String.
221 */
222 private static String unescapeString(char[] str, int begin, int end) {
223 StringBuffer output = new StringBuffer(end - begin);
224 for (int i = begin; i < end; i++) {
225 char c = str[i];
226 if (c == '\\') {
227 i++;
228 if (i < end) {
229 c = str[i];
230 switch (c) {
231 case '"' :
232 case '\\' :
233 break;
234 case 'r' :
235 c = '\r';
236 break;
237 case 'n' :
238 c = '\n';
239 break;
240 default :
241 c = '\\';
242 i--;
243 break;
244 }
245 }
246 }
247 output.append(c);
248 }
249
250 return output.toString();
251 }
Karl Pauls36407322008-03-07 00:37:30 +0000252
253 public ConditionalPermissionInfoImpl(String encoded)
254 {
Karl Pauls9ac328a2010-06-21 22:36:50 +0000255 encoded = encoded.trim();
Karl Pauls58beafb2011-09-12 10:12:16 +0000256 String toUpper = encoded.toUpperCase();
257 if (!(toUpper.startsWith("ALLOW {") || toUpper.startsWith("DENY {")))
Karl Pauls36407322008-03-07 00:37:30 +0000258 {
259 throw new IllegalArgumentException();
260 }
Karl Pauls58beafb2011-09-12 10:12:16 +0000261 m_allow = toUpper.startsWith("ALLOW {");
Karl Pauls36407322008-03-07 00:37:30 +0000262 m_cpai = null;
Karl Pauls36407322008-03-07 00:37:30 +0000263 List conditions = new ArrayList();
264 List permissions = new ArrayList();
Karl Pauls9ac328a2010-06-21 22:36:50 +0000265 try {
266 char[] chars = encoded.substring((m_allow ? "ALLOW {".length() : "DENY {".length())).toCharArray();
267 int idx = 0;
268 while (idx < chars.length)
Karl Pauls36407322008-03-07 00:37:30 +0000269 {
Karl Pauls9ac328a2010-06-21 22:36:50 +0000270 if (Character.isWhitespace(chars[idx])) {
271 idx++;
Karl Pauls36407322008-03-07 00:37:30 +0000272 }
Karl Pauls9ac328a2010-06-21 22:36:50 +0000273 else if (chars[idx] == '[')
Karl Pauls36407322008-03-07 00:37:30 +0000274 {
Karl Pauls9ac328a2010-06-21 22:36:50 +0000275 idx = parseConditionInfo(chars, idx, conditions);
Karl Pauls36407322008-03-07 00:37:30 +0000276 }
Karl Pauls9ac328a2010-06-21 22:36:50 +0000277 else if (chars[idx] == '(')
Karl Pauls36407322008-03-07 00:37:30 +0000278 {
Karl Pauls9ac328a2010-06-21 22:36:50 +0000279 idx = parsePermissionInfo(chars, idx, permissions);
Karl Pauls36407322008-03-07 00:37:30 +0000280 }
281 else
282 {
Karl Pauls9ac328a2010-06-21 22:36:50 +0000283 if (chars[idx] != '}')
Karl Pauls36407322008-03-07 00:37:30 +0000284 {
Karl Pauls9ac328a2010-06-21 22:36:50 +0000285 throw new IllegalArgumentException("Expected } but was: " + chars[idx]);
Karl Pauls36407322008-03-07 00:37:30 +0000286 }
Karl Pauls9ac328a2010-06-21 22:36:50 +0000287 idx++;
288 break;
Karl Pauls36407322008-03-07 00:37:30 +0000289 }
290 }
Karl Pauls9ac328a2010-06-21 22:36:50 +0000291 while (Character.isWhitespace(chars[idx])) {
292 idx++;
293 }
294 if (chars[idx] == '"') {
295 idx++;
296 int begin = idx;
297 while (chars[idx] != '"') {
298 if (chars[idx] == '\\') {
299 idx++;
300 }
301 idx++;
302 }
303 m_name = unescapeString(chars, begin, idx);
304 }
305 else {
Karl Pauls58beafb2011-09-12 10:12:16 +0000306 m_name = Long.toString(RANDOM.nextLong() ^ System.currentTimeMillis());
Karl Pauls9ac328a2010-06-21 22:36:50 +0000307 }
308 } catch (ArrayIndexOutOfBoundsException ex) {
309 ex.printStackTrace();
310 throw new IllegalArgumentException("Unable to parse conditional permission info: " + ex.getMessage());
311 }
Karl Pauls23287bd2010-01-10 22:11:27 +0000312 m_conditions = conditions.isEmpty() ? CONDITION_INFO
313 : (ConditionInfo[]) conditions.toArray(new ConditionInfo[conditions
314 .size()]);
315 m_permissions = permissions.isEmpty() ? PERMISSION_INFO
316 : (PermissionInfo[]) permissions
317 .toArray(new PermissionInfo[permissions.size()]);
Karl Pauls36407322008-03-07 00:37:30 +0000318 }
319
320 public ConditionalPermissionInfoImpl(ConditionalPermissionAdminImpl cpai,
Karl Pauls23287bd2010-01-10 22:11:27 +0000321 String name, boolean access)
Karl Pauls36407322008-03-07 00:37:30 +0000322 {
Karl Pauls23287bd2010-01-10 22:11:27 +0000323 m_allow = access;
Karl Pauls36407322008-03-07 00:37:30 +0000324 m_name = name;
325 m_cpai = cpai;
326 m_conditions = CONDITION_INFO;
327 m_permissions = PERMISSION_INFO;
328 }
329
330 public ConditionalPermissionInfoImpl(ConditionInfo[] conditions,
Karl Pauls23287bd2010-01-10 22:11:27 +0000331 PermissionInfo[] permisions, ConditionalPermissionAdminImpl cpai,
332 boolean access)
Karl Pauls36407322008-03-07 00:37:30 +0000333 {
Karl Pauls23287bd2010-01-10 22:11:27 +0000334 m_allow = access;
Karl Pauls36407322008-03-07 00:37:30 +0000335 m_name = Long.toString(RANDOM.nextLong() ^ System.currentTimeMillis());
336 m_cpai = cpai;
Karl Pauls58beafb2011-09-12 10:12:16 +0000337 m_conditions = conditions == null ? CONDITION_INFO : conditions;
338 m_permissions = permisions == null ? PERMISSION_INFO : permisions;
Karl Pauls36407322008-03-07 00:37:30 +0000339 }
340
341 public ConditionalPermissionInfoImpl(String name,
342 ConditionInfo[] conditions, PermissionInfo[] permisions,
Karl Pauls23287bd2010-01-10 22:11:27 +0000343 ConditionalPermissionAdminImpl cpai, boolean access)
Karl Pauls36407322008-03-07 00:37:30 +0000344 {
Karl Pauls23287bd2010-01-10 22:11:27 +0000345 m_allow = access;
346 m_name = (name != null) ? name : Long.toString(RANDOM.nextLong()
347 ^ System.currentTimeMillis());
Karl Pauls58beafb2011-09-12 10:12:16 +0000348 m_conditions = conditions == null ? CONDITION_INFO : conditions;
349 m_permissions = permisions == null ? PERMISSION_INFO : permisions;
Karl Pauls36407322008-03-07 00:37:30 +0000350 m_cpai = cpai;
351 }
352
353 public void delete()
354 {
355 Object sm = System.getSecurityManager();
356 if (sm != null)
357 {
358 ((SecurityManager) sm).checkPermission(Permissions.ALL_PERMISSION);
359 }
360
361 synchronized (m_lock)
362 {
363 m_cpai.write(m_name, null);
364 m_conditions = CONDITION_INFO;
365 m_permissions = PERMISSION_INFO;
366 }
367 }
368
369 public ConditionInfo[] getConditionInfos()
370 {
371 synchronized (m_lock)
372 {
373 return (ConditionInfo[]) m_conditions.clone();
374 }
375 }
376
377 ConditionInfo[] _getConditionInfos()
378 {
379 synchronized (m_lock)
380 {
381 return m_conditions;
382 }
383 }
384
385 void setConditionsAndPermissions(ConditionInfo[] conditions,
386 PermissionInfo[] permissions)
387 {
388 synchronized (m_lock)
389 {
390 m_conditions = conditions;
391 m_permissions = permissions;
392 }
393 }
394
395 public String getName()
396 {
397 return m_name;
398 }
399
400 public PermissionInfo[] getPermissionInfos()
401 {
402 synchronized (m_lock)
403 {
404 return (PermissionInfo[]) m_permissions.clone();
405 }
406 }
407
408 PermissionInfo[] _getPermissionInfos()
409 {
410 synchronized (m_lock)
411 {
412 return m_permissions;
413 }
414 }
415
416 public String getEncoded()
417 {
418 StringBuffer buffer = new StringBuffer();
Karl Pauls23287bd2010-01-10 22:11:27 +0000419 buffer.append(m_allow ? "ALLOW " : "DENY ");
Karl Pauls36407322008-03-07 00:37:30 +0000420 buffer.append('{');
Karl Pauls9ac328a2010-06-21 22:36:50 +0000421 buffer.append(' ');
Karl Pauls36407322008-03-07 00:37:30 +0000422 synchronized (m_lock)
423 {
424 writeTo(m_conditions, buffer);
425 writeTo(m_permissions, buffer);
426 }
427 buffer.append('}');
Karl Pauls9ac328a2010-06-21 22:36:50 +0000428 buffer.append(' ');
429 buffer.append('"');
430 escapeString(m_name, buffer);
431 buffer.append('"');
Karl Pauls36407322008-03-07 00:37:30 +0000432 return buffer.toString();
433 }
Karl Pauls9ac328a2010-06-21 22:36:50 +0000434
435 /**
436 * This escapes the quotes, backslashes, \n, and \r in the string using a
437 * backslash and appends the newly escaped string to a StringBuffer.
438 */
439 private static void escapeString(String str, StringBuffer output) {
440 int len = str.length();
441 for (int i = 0; i < len; i++) {
442 char c = str.charAt(i);
443 switch (c) {
444 case '"' :
445 case '\\' :
446 output.append('\\');
447 output.append(c);
448 break;
449 case '\r' :
450 output.append("\\r");
451 break;
452 case '\n' :
453 output.append("\\n");
454 break;
455 default :
456 output.append(c);
457 break;
458 }
459 }
460 }
Karl Pauls36407322008-03-07 00:37:30 +0000461
462 private void writeTo(Object[] elements, StringBuffer buffer)
463 {
464 for (int i = 0; i < elements.length; i++)
465 {
466 buffer.append(elements[i]);
Karl Pauls9ac328a2010-06-21 22:36:50 +0000467 buffer.append(' ');
Karl Pauls36407322008-03-07 00:37:30 +0000468 }
469 }
470
471 public String toString()
472 {
473 return getEncoded();
474 }
Karl Pauls23287bd2010-01-10 22:11:27 +0000475
476 public String getAccessDecision()
477 {
478 return m_allow ? ConditionalPermissionInfo.ALLOW
479 : ConditionalPermissionInfo.DENY;
480 }
481
482 public boolean isAllow()
483 {
484 return m_allow;
485 }
Karl Pauls36407322008-03-07 00:37:30 +0000486}