Added: commons/sandbox/privilizer/trunk/maven-plugin/src/main/java/org/apache/commons/privilizer/maven/WeaveMojo.java URL: http://svn.apache.org/viewvc/commons/sandbox/privilizer/trunk/maven-plugin/src/main/java/org/apache/commons/privilizer/maven/WeaveMojo.java?rev=1411058&view=auto ============================================================================== --- commons/sandbox/privilizer/trunk/maven-plugin/src/main/java/org/apache/commons/privilizer/maven/WeaveMojo.java (added) +++ commons/sandbox/privilizer/trunk/maven-plugin/src/main/java/org/apache/commons/privilizer/maven/WeaveMojo.java Mon Nov 19 03:54:31 2012 @@ -0,0 +1,40 @@ +/* + * Copyright the original author or authors. + * + * Licensed 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.commons.privilizer.maven; + +import org.apache.commons.privilizer.Privileged; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.ResolutionScope; + + +/** + * Goal to weave classes with {@link SecurityManager} handling code for methods marked with + * the {@link Privileged} annotation. + */ +@Mojo(name = "weave", defaultPhase = LifecyclePhase.PROCESS_CLASSES, requiresDependencyCollection = ResolutionScope.COMPILE) +public class WeaveMojo extends PrivilegedMojo { + + @Override + public void execute() throws MojoFailureException { + try { + createWeaver().weaveAll(); + } catch (Exception e) { + throw new MojoFailureException("failed", e); + } + } +}
Propchange: commons/sandbox/privilizer/trunk/maven-plugin/src/main/java/org/apache/commons/privilizer/maven/WeaveMojo.java ------------------------------------------------------------------------------ svn:eol-style = native Added: commons/sandbox/privilizer/trunk/pom.xml URL: http://svn.apache.org/viewvc/commons/sandbox/privilizer/trunk/pom.xml?rev=1411058&view=auto ============================================================================== --- commons/sandbox/privilizer/trunk/pom.xml (added) +++ commons/sandbox/privilizer/trunk/pom.xml Mon Nov 19 03:54:31 2012 @@ -0,0 +1,173 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Copyright the original author or authors. + + Licensed 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. + + --><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.apache.commons</groupId> + <artifactId>commons-sandbox-parent</artifactId> + <version>10-SNAPSHOT</version> + </parent> + + <artifactId>commons-privilizer-parent</artifactId> + <version>1.0-SNAPSHOT</version> + <packaging>pom</packaging> + + <name>Commons Privilizer Parent POM</name> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <maven.compile.source>1.5</maven.compile.source> + <maven.compile.target>1.5</maven.compile.target> + <commons.componentid>privilizer</commons.componentid> + <commons.release.version>1.0</commons.release.version> + <commons.rc.version>RC1</commons.rc.version> + <!--commons.jira.id>PRIVILIZER</commons.jira.id> + <commons.jira.pid>666</commons.jira.pid--> + </properties> + + <developers> + <developer> + <id>mbenson</id> + <name>Matt Benson</name> + <email>mbenson AT apache DOT org</email> + <organization>Apache</organization> + </developer> + </developers> + + <scm> + <connection>scm:svn:http://svn.apache.org/repos/asf/commons/sandbox/privilizer/trunk/</connection> + <developerConnection>scm:svn:https://svn.apache.org/repos/asf/commons/sandbox/privilizer/trunk/</developerConnection> + <url>http://svn.apache.org/viewvc/commons/sandbox/privilizer/trunk/</url> + </scm> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-privilizer-api</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-privilizer</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.10</version> + <scope>test</scope> + </dependency> + </dependencies> + </dependencyManagement> + + <build> + <pluginManagement> + <plugins> + <plugin> + <groupId>org.apache.commons</groupId> + <artifactId>commons-privilizer-maven-plugin</artifactId> + <version>${project.version}</version> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>2.5.1</version> + <configuration> + <source>1.6</source> + <target>1.6</target> + <encoding>UTF-8</encoding> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>2.12.4</version> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-report-plugin</artifactId> + <version>2.12.4</version> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-source-plugin</artifactId> + <version>2.2.1</version> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-dependency-plugin</artifactId> + <version>2.5.1</version> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-antrun-plugin</artifactId> + <version>1.7</version> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>buildhelper-maven-plugin</artifactId> + <version>1.7</version> + </plugin> + </plugins> + </pluginManagement> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-source-plugin</artifactId> + <executions> + <execution> + <id>attach-sources</id> + <phase>verify</phase> + <goals> + <goal>jar-no-fork</goal> + </goals> + </execution> + <execution> + <id>attach-test-sources</id> + <phase>verify</phase> + <goals> + <goal>test-jar-no-fork</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-site-plugin</artifactId> + <version>3.2</version> + <dependencies> + <dependency> + <groupId>org.apache.maven.doxia</groupId> + <artifactId>doxia-module-markdown</artifactId> + <version>1.3</version> + </dependency> + </dependencies> + </plugin> + </plugins> + </build> + + <modules> + <module>api</module> + <module>weaver</module> + <module>maven-plugin</module> + <module>example</module> + <module>ant</module> + </modules> +</project> Propchange: commons/sandbox/privilizer/trunk/pom.xml ------------------------------------------------------------------------------ svn:eol-style = native Added: commons/sandbox/privilizer/trunk/src/site/markdown/index.md URL: http://svn.apache.org/viewvc/commons/sandbox/privilizer/trunk/src/site/markdown/index.md?rev=1411058&view=auto ============================================================================== --- commons/sandbox/privilizer/trunk/src/site/markdown/index.md (added) +++ commons/sandbox/privilizer/trunk/src/site/markdown/index.md Mon Nov 19 03:54:31 2012 @@ -0,0 +1,49 @@ +# Commons Privilizer + +Provides machinery to automate the handling of Java Security access +controls in code. This involves wrapping calls that may trigger +`java.lang.SecurityException`s in `PrivilegedAction` objects. +Unfortunately this is quite an expensive operation and slows code +down considerably; when executed in an environment that has no +`SecurityManager` activated it is an utter waste. +The typical pattern to cope with this is: + +```java +if (System.getSecurityManager() != null) { + AccessController.doPrivileged(new PrivilegedAction<Void>() { + public Void run() { + doSomethingThatRequiresPermissions(); + return null; + } + }); +} else { + doSomethingThatRequiresPermissions(); +} +``` + +This becomes tedious in short order. The immediate response of a +typical developer: relegate the repetitive code to a set of +utility methods. In the case of Java security, however, this +approach is considered risky. The purpose of the Privilizer, then, +is to instrument compiled methods originally annotated with our +`@Privileged` annotation. This annotation is retained in the +classfiles, but not available at runtime, and there are no runtime +dependencies. + +### With Privilizer + +```java + +@Privileged +private void doSomethingThatRequiresPermissions() { + ... +} +``` + +Commons Privilizer provides both a Maven plugin and an Antlib for +weaving your compiled, annotated classes. You can control the weaving +behavior by parameterizing the Maven goals and Ant task with +the [Policy][policy] and [AccessLevel][accessLevel] `enum`s. + +[policy]: apidocs/org/apache/commons/privilizer/weave/Privilizer.Policy.html +[accessLevel]: apidocs/org/apache/commons/privilizer/weave/AccessLevel.html Added: commons/sandbox/privilizer/trunk/src/site/resources/images/privilizer-logo-white.png URL: http://svn.apache.org/viewvc/commons/sandbox/privilizer/trunk/src/site/resources/images/privilizer-logo-white.png?rev=1411058&view=auto ============================================================================== Binary file - no diff available. Propchange: commons/sandbox/privilizer/trunk/src/site/resources/images/privilizer-logo-white.png ------------------------------------------------------------------------------ svn:mime-type = image/png Added: commons/sandbox/privilizer/trunk/src/site/resources/images/privilizer-logo-white.xcf URL: http://svn.apache.org/viewvc/commons/sandbox/privilizer/trunk/src/site/resources/images/privilizer-logo-white.xcf?rev=1411058&view=auto ============================================================================== Binary file - no diff available. Propchange: commons/sandbox/privilizer/trunk/src/site/resources/images/privilizer-logo-white.xcf ------------------------------------------------------------------------------ svn:mime-type = application/octet-stream Added: commons/sandbox/privilizer/trunk/src/site/site.xml URL: http://svn.apache.org/viewvc/commons/sandbox/privilizer/trunk/src/site/site.xml?rev=1411058&view=auto ============================================================================== --- commons/sandbox/privilizer/trunk/src/site/site.xml (added) +++ commons/sandbox/privilizer/trunk/src/site/site.xml Mon Nov 19 03:54:31 2012 @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<!-- + 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. +--> +<project name="Commons Privilizer"> + <bannerRight> + <name>Commons Privilizer</name> + <src>/images/privilizer-logo-white.png</src> + <href>/index.html</href> + </bannerRight> + <body> + <menu name="Commons Privilizer"> + <item name="Overview" href="/index.html"/> + <item name="Javadoc" href="apidocs/index.html"/> + <item name="Mailing lists" href="/mail-lists.html"/> + <item name="Issue Tracking" href="/issue-tracking.html"/> + </menu> + <menu name="Development"> + <item name="Team" href="/team-list.html"/> + <item name="SVN" href="/source-repository.html"/> + </menu> + </body> +</project> Propchange: commons/sandbox/privilizer/trunk/src/site/site.xml ------------------------------------------------------------------------------ svn:eol-style = native Added: commons/sandbox/privilizer/trunk/weaver/pom.xml URL: http://svn.apache.org/viewvc/commons/sandbox/privilizer/trunk/weaver/pom.xml?rev=1411058&view=auto ============================================================================== --- commons/sandbox/privilizer/trunk/weaver/pom.xml (added) +++ commons/sandbox/privilizer/trunk/weaver/pom.xml Mon Nov 19 03:54:31 2012 @@ -0,0 +1,57 @@ +<?xml version="1.0"?> +<!-- + + Copyright the original author or authors. + + Licensed 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. + + --> +<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.commons</groupId> + <artifactId>commons-privilizer-parent</artifactId> + <version>1.0-SNAPSHOT</version> + </parent> + <artifactId>commons-privilizer</artifactId> + <name>Commons Privilizer</name> + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + <dependencies> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-privilizer-api</artifactId> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + <version>3.1</version> + </dependency> + <dependency> + <groupId>org.apache.xbean</groupId> + <artifactId>xbean-finder-shaded</artifactId> + <version>3.12</version> + </dependency> + <dependency> + <groupId>org.javassist</groupId> + <artifactId>javassist</artifactId> + <version>3.16.1-GA</version> + </dependency> + <dependency> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> + <version>2.4</version> + </dependency> + </dependencies> +</project> Propchange: commons/sandbox/privilizer/trunk/weaver/pom.xml ------------------------------------------------------------------------------ svn:eol-style = native Added: commons/sandbox/privilizer/trunk/weaver/src/main/java/org/apache/commons/privilizer/weave/AccessLevel.java URL: http://svn.apache.org/viewvc/commons/sandbox/privilizer/trunk/weaver/src/main/java/org/apache/commons/privilizer/weave/AccessLevel.java?rev=1411058&view=auto ============================================================================== --- commons/sandbox/privilizer/trunk/weaver/src/main/java/org/apache/commons/privilizer/weave/AccessLevel.java (added) +++ commons/sandbox/privilizer/trunk/weaver/src/main/java/org/apache/commons/privilizer/weave/AccessLevel.java Mon Nov 19 03:54:31 2012 @@ -0,0 +1,56 @@ +/* + * Copyright the original author or authors. + * + * Licensed 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.commons.privilizer.weave; + +import java.lang.reflect.Modifier; +import java.util.EnumSet; +import java.util.Locale; + +public enum AccessLevel { + PUBLIC(Modifier.PUBLIC), PROTECTED(Modifier.PROTECTED), PACKAGE(0), PRIVATE(Modifier.PRIVATE); + + private final int flag; + + private AccessLevel(int flag) { + this.flag = flag; + } + + public static AccessLevel of(int mod) { + if (Modifier.isPublic(mod)) { + return PUBLIC; + } + if (Modifier.isProtected(mod)) { + return PROTECTED; + } + if (Modifier.isPrivate(mod)) { + return PRIVATE; + } + return PACKAGE; + } + + public int merge(int mod) { + int remove = 0; + for (AccessLevel accessLevel : EnumSet.complementOf(EnumSet.of(this))) { + remove |= accessLevel.flag; + } + return mod & ~remove | flag; + } + + @Override + public String toString() { + return name().toLowerCase(Locale.US); + } +} Propchange: commons/sandbox/privilizer/trunk/weaver/src/main/java/org/apache/commons/privilizer/weave/AccessLevel.java ------------------------------------------------------------------------------ svn:eol-style = native Added: commons/sandbox/privilizer/trunk/weaver/src/main/java/org/apache/commons/privilizer/weave/AlreadyWovenException.java URL: http://svn.apache.org/viewvc/commons/sandbox/privilizer/trunk/weaver/src/main/java/org/apache/commons/privilizer/weave/AlreadyWovenException.java?rev=1411058&view=auto ============================================================================== --- commons/sandbox/privilizer/trunk/weaver/src/main/java/org/apache/commons/privilizer/weave/AlreadyWovenException.java (added) +++ commons/sandbox/privilizer/trunk/weaver/src/main/java/org/apache/commons/privilizer/weave/AlreadyWovenException.java Mon Nov 19 03:54:31 2012 @@ -0,0 +1,41 @@ +/* + * Copyright the original author or authors. + * + * Licensed 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.commons.privilizer.weave; + +import org.apache.commons.privilizer.weave.Privilizer.Policy; + +public class AlreadyWovenException extends IllegalStateException { + private static final long serialVersionUID = 1L; + private static final String MESSAGE = "%s already woven with policy %s"; + + private final String classname; + private final Policy policy; + + public AlreadyWovenException(String classname, Policy policy) { + super(String.format(MESSAGE, classname, policy)); + this.classname = classname; + this.policy = policy; + } + + public String getClassname() { + return classname; + } + + public Policy getPolicy() { + return policy; + } + +} Propchange: commons/sandbox/privilizer/trunk/weaver/src/main/java/org/apache/commons/privilizer/weave/AlreadyWovenException.java ------------------------------------------------------------------------------ svn:eol-style = native Added: commons/sandbox/privilizer/trunk/weaver/src/main/java/org/apache/commons/privilizer/weave/Body.java URL: http://svn.apache.org/viewvc/commons/sandbox/privilizer/trunk/weaver/src/main/java/org/apache/commons/privilizer/weave/Body.java?rev=1411058&view=auto ============================================================================== --- commons/sandbox/privilizer/trunk/weaver/src/main/java/org/apache/commons/privilizer/weave/Body.java (added) +++ commons/sandbox/privilizer/trunk/weaver/src/main/java/org/apache/commons/privilizer/weave/Body.java Mon Nov 19 03:54:31 2012 @@ -0,0 +1,107 @@ +/* + * Copyright the original author or authors. + * + * Licensed 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.commons.privilizer.weave; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; +import org.apache.commons.lang3.text.StrBuilder; + +class Body implements CharSequence { + private final String indent = " "; + private final StrBuilder content = new StrBuilder(); + private int level = 0; + private boolean lineStarted; + + { + startBlock(); + } + + public char charAt(int index) { + return content.charAt(index); + } + + public int length() { + return content.length(); + } + + public CharSequence subSequence(int start, int end) { + return content.subSequence(start, end); + } + + @Override + public String toString() { + return content.toString(); + } + + Body append(char c) { + content.append(c); + return this; + } + + Body append(String format, Object... args) { + prepare(); + content.append(String.format(format, args)); + return this; + } + + Body appendLine(String format, Object... args) { + return append(format, args).appendNewLine(); + } + + Body appendNewLine() { + content.appendNewLine(); + lineStarted = false; + return this; + } + + Body complete() { + try { + return endBlock(); + } finally { + Validate.validState(level == 0); + } + } + + Body endBlock() { + if (level < 1) { + throw new IllegalStateException(); + } + level--; + prepare(); + append('}').appendNewLine(); + return this; + } + + Body startBlock() { + if (lineStarted) { + append(' '); + } + level++; + return append('{').appendNewLine(); + } + + Body startBlock(String format, Object... args) { + append(format, args); + return startBlock(); + } + + private void prepare() { + if (!lineStarted) { + lineStarted = true; + content.append(StringUtils.repeat(indent, level)); + } + } +} \ No newline at end of file Propchange: commons/sandbox/privilizer/trunk/weaver/src/main/java/org/apache/commons/privilizer/weave/Body.java ------------------------------------------------------------------------------ svn:eol-style = native Added: commons/sandbox/privilizer/trunk/weaver/src/main/java/org/apache/commons/privilizer/weave/FilesystemPrivilizer.java URL: http://svn.apache.org/viewvc/commons/sandbox/privilizer/trunk/weaver/src/main/java/org/apache/commons/privilizer/weave/FilesystemPrivilizer.java?rev=1411058&view=auto ============================================================================== --- commons/sandbox/privilizer/trunk/weaver/src/main/java/org/apache/commons/privilizer/weave/FilesystemPrivilizer.java (added) +++ commons/sandbox/privilizer/trunk/weaver/src/main/java/org/apache/commons/privilizer/weave/FilesystemPrivilizer.java Mon Nov 19 03:54:31 2012 @@ -0,0 +1,166 @@ +/* + * Copyright the original author or authors. + * + * Licensed 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.commons.privilizer.weave; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import javassist.CannotCompileException; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.LoaderClassPath; +import javassist.NotFoundException; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.filefilter.RegexFileFilter; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; +import org.apache.commons.privilizer.Privileged; +import org.apache.xbean.finder.AnnotationFinder; +import org.apache.xbean.finder.archive.FileArchive; + + +/** + * Handles weaving of methods annotated with {@link Privileged}. + */ +public class FilesystemPrivilizer extends Privilizer<FilesystemPrivilizer> { + private static ClassPool createClassPool(ClassLoader classpath, File target) { + final ClassPool result = new ClassPool(); + try { + result.appendClassPath(validTarget(target).getAbsolutePath()); + result.appendClassPath(new LoaderClassPath(classpath)); + result.appendPathList(System.getProperty("java.class.path")); + } catch (final NotFoundException e) { + throw new RuntimeException(e); + } + return result; + } + + private static Set<Class<?>> getDeclaringClasses(Iterable<Method> methods) { + final Set<Class<?>> declaringClasses = new HashSet<Class<?>>(); + for (final Method method : methods) { + declaringClasses.add(method.getDeclaringClass()); + } + return declaringClasses; + } + + private static Class<?> getOutermost(Class<?> type) { + Class<?> enclosing = type.getEnclosingClass(); + return enclosing == null ? type : getOutermost(enclosing); + } + + private static File validTarget(File target) { + Validate.notNull(target, "target"); + Validate.isTrue(target.isDirectory(), "not a directory"); + return target; + } + + private final ClassLoader classpath; + + private final File target; + + private final ClassFileWriter classFileWriter = new ClassFileWriter() { + @Override + public void write(CtClass type) throws CannotCompileException, IOException { + type.writeFile(target.getAbsolutePath()); + } + }; + + public FilesystemPrivilizer(ClassLoader classpath, File target) { + super(createClassPool(classpath, target)); + this.classpath = classpath; + this.target = target; + } + + public FilesystemPrivilizer(Policy policy, ClassLoader classpath, File target) { + super(policy, createClassPool(classpath, target)); + this.classpath = classpath; + this.target = target; + } + + /** + * Clear the way by deleting classfiles woven with a different + * {@link Policy}. + * + * @throws NotFoundException + */ + public void prepare() throws NotFoundException { + info("preparing %s; policy = %s", target, policy); + final Set<File> toDelete = new TreeSet<File>(); + for (final Class<?> type : getDeclaringClasses(findPrivilegedMethods())) { + final CtClass ctClass = classPool.get(type.getName()); + final String policyValue = toString(ctClass.getAttribute(generateName(POLICY_NAME))); + if (policyValue == null || policyValue.equals(policy.name())) { + continue; + } + debug("class %s previously woven with policy %s", type.getName(), policyValue); + final File packageDir = + new File(target, StringUtils.replaceChars(ctClass.getPackageName(), '.', File.separatorChar)); + + // simple classname of outermost class, plus any inner classes: + final String pattern = + new StringBuilder(getOutermost(type).getSimpleName()).append("(\\$.+)??\\.class").toString(); + + debug("searching %s for pattern '%s'", packageDir.getAbsolutePath(), pattern); + toDelete.addAll(FileUtils.listFiles(packageDir, new RegexFileFilter(pattern), null)); + } + if (toDelete.isEmpty()) { + return; + } + info("Deleting %s files...", toDelete.size()); + debug(toDelete.toString()); + for (File f : toDelete) { + if (!f.delete()) { + debug("Failed to delete %s", f); + } + } + } + + /** + * Weave all {@link Privileged} methods found. + * + * @throws NotFoundException + * @throws IOException + * @throws CannotCompileException + * @throws ClassNotFoundException + */ + public void weaveAll() throws NotFoundException, IOException, CannotCompileException, ClassNotFoundException { + int woven = 0; + for (final Class<?> type : getDeclaringClasses(findPrivilegedMethods())) { + if (weave(classPool.get(type.getName()))) { + woven++; + } + } + if (woven > 0) { + info("Wove %s classes.", woven); + } + } + + @Override + protected ClassFileWriter getClassFileWriter() { + return classFileWriter; + } + + private List<Method> findPrivilegedMethods() { + final AnnotationFinder annotationFinder = new AnnotationFinder(new FileArchive(classpath, target), false); + return annotationFinder.findAnnotatedMethods(Privileged.class); + } +} Propchange: commons/sandbox/privilizer/trunk/weaver/src/main/java/org/apache/commons/privilizer/weave/FilesystemPrivilizer.java ------------------------------------------------------------------------------ svn:eol-style = native Added: commons/sandbox/privilizer/trunk/weaver/src/main/java/org/apache/commons/privilizer/weave/Privilizer.java URL: http://svn.apache.org/viewvc/commons/sandbox/privilizer/trunk/weaver/src/main/java/org/apache/commons/privilizer/weave/Privilizer.java?rev=1411058&view=auto ============================================================================== --- commons/sandbox/privilizer/trunk/weaver/src/main/java/org/apache/commons/privilizer/weave/Privilizer.java (added) +++ commons/sandbox/privilizer/trunk/weaver/src/main/java/org/apache/commons/privilizer/weave/Privilizer.java Mon Nov 19 03:54:31 2012 @@ -0,0 +1,490 @@ +/* + * Copyright the original author or authors. + * + * Licensed 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.commons.privilizer.weave; + +import java.io.IOException; +import java.lang.reflect.Modifier; +import java.nio.charset.Charset; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; +import java.util.logging.Logger; + +import javassist.CannotCompileException; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtField; +import javassist.CtMethod; +import javassist.CtNewConstructor; +import javassist.CtNewMethod; +import javassist.CtPrimitiveType; +import javassist.NotFoundException; +import javassist.bytecode.Descriptor; + +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; +import org.apache.commons.lang3.text.StrBuilder; +import org.apache.commons.privilizer.Privileged; + + +/** + * Handles weaving of methods annotated with {@link Privileged}. + */ +public abstract class Privilizer<SELF extends Privilizer<SELF>> { + public interface ClassFileWriter { + void write(CtClass type) throws CannotCompileException, IOException; + } + + public interface Log { + void debug(String message); + + void verbose(String message); + + void error(String message); + + void info(String message); + + void warn(String message); + } + + /** + * Weaving policy: when to use {@link PrivilegedAction}s. + */ + public enum Policy { + /** + * Disables weaving. + */ + NEVER, + + /** + * Weaves such that the check for an active {@link SecurityManager} is + * done once only. + */ + ON_INIT(generateName("hasSecurityManager")), + + /** + * Weaves such that the check for an active {@link SecurityManager} is + * done for each {@link Privileged} method execution. + */ + DYNAMIC(HAS_SECURITY_MANAGER_CONDITION), + + /** + * Weaves such that {@link Privileged} methods are always executed as + * such. + */ + ALWAYS; + + private final String condition; + + private Policy() { + this(null); + } + + private Policy(String condition) { + this.condition = condition; + } + + private boolean isConditional() { + return condition != null; + } + } + + protected static final String POLICY_NAME = "policyName"; + + private static final String ACTION_SUFFIX = "_ACTION"; + + private static final String GENERATE_NAME = "__privileged_%s"; + private static final String HAS_SECURITY_MANAGER_CONDITION = "System.getSecurityManager() != null"; + + protected static String generateName(String simple) { + return String.format(GENERATE_NAME, simple); + } + + protected static String toString(byte[] b) { + return b == null ? null : new String(b, Charset.forName("UTF-8")); + } + + protected final Policy policy; + + protected final ClassPool classPool; + + private boolean settingsReported; + + private Log log = new Log() { + final Logger logger = Logger.getLogger(Privilizer.class.getName()); + + @Override + public void debug(String message) { + logger.finer(message); + } + + @Override + public void verbose(String message) { + logger.fine(message); + } + + @Override + public void error(String message) { + logger.severe(message); + } + + @Override + public void info(String message) { + logger.info(message); + } + + @Override + public void warn(String message) { + logger.warning(message); + } + + }; + + private static final Comparator<CtMethod> CTMETHOD_COMPARATOR = new Comparator<CtMethod>() { + + @Override + public int compare(CtMethod arg0, CtMethod arg1) { + if (ObjectUtils.equals(arg0, arg1)) { + return 0; + } + if (arg0 == null) { + return -1; + } + if (arg1 == null) { + return 1; + } + final int result = ObjectUtils.compare(arg0.getName(), arg1.getName()); + return result == 0 ? ObjectUtils.compare(arg0.getSignature(), arg1.getSignature()) : result; + } + }; + + private static Set<CtMethod> getPrivilegedMethods(CtClass type) throws ClassNotFoundException { + final TreeSet<CtMethod> result = new TreeSet<CtMethod>(CTMETHOD_COMPARATOR); + for (final CtMethod m : type.getDeclaredMethods()) { + if (Modifier.isAbstract(m.getModifiers()) || m.getAnnotation(Privileged.class) == null) { + continue; + } + result.add(m); + } + return result; + } + + public Privilizer(ClassPool classPool) { + this(Policy.DYNAMIC, classPool); + } + + public Privilizer(Policy policy, ClassPool classPool) { + this.policy = Validate.notNull(policy, "policy"); + this.classPool = Validate.notNull(classPool, "classPool"); + } + + public SELF loggingTo(Log log) { + this.log = Validate.notNull(log); + settingsReported = false; + @SuppressWarnings("unchecked") + final SELF self = (SELF) this; + return self; + } + + /** + * Weave the specified class. + * + * @param type + * @return whether any work was done + * @throws NotFoundException + * @throws IOException + * @throws CannotCompileException + * @throws ClassNotFoundException + */ + public boolean weave(CtClass type) throws NotFoundException, IOException, CannotCompileException, + ClassNotFoundException { + reportSettings(); + final String policyName = generateName(POLICY_NAME); + final String policyValue = toString(type.getAttribute(policyName)); + if (policyValue != null) { + verbose("%s already woven with policy %s", type.getName(), policyValue); + if (!policy.name().equals(policyValue)) { + throw new AlreadyWovenException(type.getName(), Policy.valueOf(policyValue)); + } + return false; + } + boolean result = false; + if (policy.compareTo(Policy.NEVER) > 0) { + if (policy == Policy.ON_INIT) { + debug("Initializing field %s to %s", policy.condition, HAS_SECURITY_MANAGER_CONDITION); + type.addField(new CtField(CtClass.booleanType, policy.condition, type), + CtField.Initializer.byExpr(HAS_SECURITY_MANAGER_CONDITION)); + } + for (final CtMethod m : getPrivilegedMethods(type)) { + result |= weave(type, m); + } + if (result) { + type.setAttribute(policyName, policy.name().getBytes(Charset.forName("UTF-8"))); + getClassFileWriter().write(type); + } + } + log.verbose(String.format(result ? "Wove class %s" : "Nothing to do for class %s", type.getName())); + return result; + } + + protected void debug(String message, Object... args) { + log.debug(String.format(message, args)); + } + + protected void verbose(String message, Object... args) { + log.verbose(String.format(message, args)); + } + + protected void warn(String message, Object... args) { + log.warn(String.format(message, args)); + } + + protected abstract ClassFileWriter getClassFileWriter(); + + protected void info(String message, Object... args) { + log.info(String.format(message, args)); + } + + protected boolean permitMethodWeaving(AccessLevel accessLevel) { + return true; + } + + private CtClass createAction(CtClass type, CtMethod impl, Class<?> iface) throws NotFoundException, + CannotCompileException, IOException { + final boolean exc = impl.getExceptionTypes().length > 0; + + final CtClass actionType = classPool.get(iface.getName()); + + final String simpleName = generateActionClassname(impl); + debug("Creating action type %s for method %s", simpleName, toString(impl)); + final CtClass result = type.makeNestedClass(simpleName, true); + result.addInterface(actionType); + + final CtField owner; + if (Modifier.isStatic(impl.getModifiers())) { + owner = null; + } else { + owner = new CtField(type, generateName("owner"), result); + owner.setModifiers(Modifier.PRIVATE | Modifier.FINAL); + debug("Adding owner field %s to %s", owner.getName(), simpleName); + result.addField(owner); + } + + final List<String> propagatedParameters = new ArrayList<String>(); + int index = -1; + for (final CtClass param : impl.getParameterTypes()) { + final String f = String.format("arg%s", Integer.valueOf(++index)); + final CtField fld = new CtField(param, f, result); + fld.setModifiers(Modifier.PRIVATE | Modifier.FINAL); + debug("Copying parameter %s from %s to %s.%s", index, toString(impl), simpleName, f); + result.addField(fld); + propagatedParameters.add(f); + } + { + final StrBuilder constructor = new StrBuilder(simpleName).append('('); + boolean sep = false; + final Body body = new Body(); + + for (final CtField fld : result.getDeclaredFields()) { + if (sep) { + constructor.append(", "); + } else { + sep = true; + } + constructor.append(fld.getType().getName()).append(' ').append(fld.getName()); + body.appendLine("this.%1$s = %1$s;", fld.getName()); + } + constructor.append(") ").append(body.complete()); + + final String c = constructor.toString(); + debug("Creating action constructor:"); + debug(c); + result.addConstructor(CtNewConstructor.make(c, result)); + } + { + final StrBuilder run = new StrBuilder("public Object run() "); + if (exc) { + run.append("throws Exception "); + } + final Body body = new Body(); + final CtClass rt = impl.getReturnType(); + final boolean isVoid = rt.equals(CtClass.voidType); + if (!isVoid) { + body.append("return "); + } + final String deref = Modifier.isStatic(impl.getModifiers()) ? type.getName() : owner.getName(); + final String call = + String.format("%s.%s(%s)", deref, impl.getName(), StringUtils.join(propagatedParameters, ", ")); + + if (!isVoid && rt.isPrimitive()) { + body.appendLine("%2$s.valueOf(%1$s);", call, ((CtPrimitiveType) rt).getWrapperName()); + } else { + body.append(call).append(';').appendNewLine(); + + if (isVoid) { + body.appendLine("return null;"); + } + } + + run.append(body.complete()); + + final String r = run.toString(); + debug("Creating run method:"); + debug(r); + result.addMethod(CtNewMethod.make(r, result)); + } + getClassFileWriter().write(result); + debug("Returning action type %s", result); + return result; + } + + private String generateActionClassname(CtMethod m) throws NotFoundException { + final StringBuilder b = new StringBuilder(m.getName()); + if (m.getParameterTypes().length > 0) { + b.append("$$").append( + StringUtils.strip(Descriptor.getParamDescriptor(m.getSignature()), "(;)").replace("[", "ARRAYOF_") + .replace('/', '_').replace(';', '$')); + } + return b.append(ACTION_SUFFIX).toString(); + } + + private String toString(CtMethod m) { + return String.format("%s%s", m.getName(), m.getSignature()); + } + + private boolean weave(CtClass type, CtMethod method) throws ClassNotFoundException, CannotCompileException, + NotFoundException, IOException { + final AccessLevel accessLevel = AccessLevel.of(method.getModifiers()); + if (!permitMethodWeaving(accessLevel)) { + warn("Ignoring %s method %s.%s", accessLevel, type.getName(), toString(method)); + return false; + } + if (AccessLevel.PACKAGE.compareTo(accessLevel) > 0) { + warn("Possible security leak: granting privileges to %s method %s.%s", accessLevel, type.getName(), + toString(method)); + } + final String implName = generateName(method.getName()); + + final CtMethod impl = CtNewMethod.copy(method, implName, type, null); + impl.setModifiers(AccessLevel.PRIVATE.merge(method.getModifiers())); + type.addMethod(impl); + debug("Copied %2$s %1$s.%3$s to %4$s %1$s.%5$s", type.getName(), accessLevel, toString(method), + AccessLevel.PRIVATE, toString(impl)); + + final Body body = new Body(); + if (policy.isConditional()) { + body.startBlock("if (%s)", policy.condition); + } + + final boolean exc = method.getExceptionTypes().length > 0; + + if (exc) { + body.startBlock("try"); + } + + final Class<?> iface = exc ? PrivilegedExceptionAction.class : PrivilegedAction.class; + final CtClass actionType = createAction(type, impl, iface); + final String action = generateName("action"); + + body.append("final %s %s = new %s(", iface.getName(), action, actionType.getName()); + boolean firstParam; + if (Modifier.isStatic(impl.getModifiers())) { + firstParam = true; + } else { + body.append("$0"); + firstParam = false; + } + for (int i = 1, sz = impl.getParameterTypes().length; i <= sz; i++) { + if (firstParam) { + firstParam = false; + } else { + body.append(", "); + } + body.append('$').append(Integer.toString(i)); + } + body.appendLine(");"); + + final CtClass rt = method.getReturnType(); + final boolean isVoid = rt.equals(CtClass.voidType); + + final String doPrivileged = String.format("%1$s.doPrivileged(%2$s)", AccessController.class.getName(), action); + if (isVoid) { + body.append(doPrivileged).append(';').appendNewLine(); + if (policy.isConditional()) { + body.appendLine("return;"); + } + } else { + final String cast = rt.isPrimitive() ? ((CtPrimitiveType) rt).getWrapperName() : rt.getName(); + // don't worry about wrapper NPEs because we should be simply + // passing back an autoboxed value, then unboxing again + final String result = generateName("result"); + body.appendLine("final %1$s %3$s = (%1$s) %2$s;", cast, doPrivileged, result); + body.append("return %s", result); + if (rt.isPrimitive()) { + body.append(".%sValue()", rt.getName()); + } + body.append(';').appendNewLine(); + } + + if (exc) { + body.endBlock(); + final String e = generateName("e"); + body.startBlock("catch (%1$s %2$s)", PrivilegedActionException.class.getName(), e).appendNewLine(); + + final String wrapped = generateName("wrapped"); + + body.appendLine("final Exception %1$s = %2$s.getCause();", wrapped, e); + for (final CtClass thrown : method.getExceptionTypes()) { + body.startBlock("if (%1$s instanceof %2$s)", wrapped, thrown.getName()); + body.appendLine("throw (%2$s) %1$s;", wrapped, thrown.getName()); + body.endBlock(); + } + body.appendLine( + "throw %1$s instanceof RuntimeException ? (RuntimeException) %1$s : new RuntimeException(%1$s);", + wrapped); + body.endBlock(); + } + + if (policy.isConditional()) { + // close if block we opened before: + body.endBlock(); + // no security manager=> just call impl: + if (!isVoid) { + body.append("return "); + } + body.appendLine("%s($$);", impl.getName()); + } + + final String block = body.complete().toString(); + debug("Setting body of %s to:\n%s", toString(method), block); + method.setBody(block); + return true; + } + + private void reportSettings() { + if (!settingsReported) { + settingsReported = true; + info("Weave policy == %s", policy); + } + } +} Propchange: commons/sandbox/privilizer/trunk/weaver/src/main/java/org/apache/commons/privilizer/weave/Privilizer.java ------------------------------------------------------------------------------ svn:eol-style = native Added: commons/sandbox/privilizer/trunk/weaver/src/main/java/org/apache/commons/privilizer/weave/URLArray.java URL: http://svn.apache.org/viewvc/commons/sandbox/privilizer/trunk/weaver/src/main/java/org/apache/commons/privilizer/weave/URLArray.java?rev=1411058&view=auto ============================================================================== --- commons/sandbox/privilizer/trunk/weaver/src/main/java/org/apache/commons/privilizer/weave/URLArray.java (added) +++ commons/sandbox/privilizer/trunk/weaver/src/main/java/org/apache/commons/privilizer/weave/URLArray.java Mon Nov 19 03:54:31 2012 @@ -0,0 +1,83 @@ +/* + * Copyright the original author or authors. + * + * Licensed 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.commons.privilizer.weave; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Iterator; + +/** + * {@link URL} Array utilities. + */ +public abstract class URLArray { + private URLArray() { + } + + /** + * Convert an {@link Iterable} of filesystem paths. + * + * @param files + * @return URL[] + */ + public static URL[] fromPaths(final Iterable<String> files) { + return fromFiles(new Iterable<File>() { + + public Iterator<File> iterator() { + final Iterator<String> path = files.iterator(); + return new Iterator<File>() { + + public boolean hasNext() { + return path.hasNext(); + } + + public File next() { + final String p = path.next(); + return p == null ? null : new File(p); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }); + } + + /** + * Convert an {@link Iterable} of {@link File}s. + * + * @param files + * @return URL[] + */ + public static URL[] fromFiles(Iterable<File> files) { + final ArrayList<URL> result = new ArrayList<URL>(); + for (File f : files) { + if (f == null) { + result.add(null); + continue; + } + try { + result.add(f.toURI().toURL()); + } catch (MalformedURLException e) { + // this shouldn't happen + throw new RuntimeException(e); + } + } + return result.toArray(new URL[result.size()]); + } +} Propchange: commons/sandbox/privilizer/trunk/weaver/src/main/java/org/apache/commons/privilizer/weave/URLArray.java ------------------------------------------------------------------------------ svn:eol-style = native