diff options
Diffstat (limited to 'cadi')
341 files changed, 43733 insertions, 0 deletions
diff --git a/cadi/.gitignore b/cadi/.gitignore new file mode 100644 index 00000000..2699d451 --- /dev/null +++ b/cadi/.gitignore @@ -0,0 +1,3 @@ +/.project +/.settings/ +/target/ diff --git a/cadi/aaf/.gitignore b/cadi/aaf/.gitignore new file mode 100644 index 00000000..6028f0a5 --- /dev/null +++ b/cadi/aaf/.gitignore @@ -0,0 +1,4 @@ +/.classpath +/.settings/ +/target/ +/.project diff --git a/cadi/aaf/jenkins-pom.xml b/cadi/aaf/jenkins-pom.xml new file mode 100644 index 00000000..026f9ee5 --- /dev/null +++ b/cadi/aaf/jenkins-pom.xml @@ -0,0 +1,245 @@ +<!-- + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * +--> +<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"> + <parent> + <groupId>com.att.cadi</groupId> + <artifactId>parent</artifactId> + <version>1.2.2</version> + <relativePath>..</relativePath> + </parent> + + <modelVersion>4.0.0</modelVersion> + <name>CADI AAF (Application Authorization Framework) LUR</name> + <packaging>jar</packaging> + <artifactId>cadi-aaf</artifactId> + + <dependencies> + <dependency> + <groupId>com.att.authz</groupId> + <artifactId>authz-client</artifactId> + </dependency> + + <dependency> + <groupId>com.att.cadi</groupId> + <artifactId>cadi-client</artifactId> + </dependency> + + <dependency> + <groupId>com.att.aft</groupId> + <artifactId>dme2</artifactId> + <exclusions> + <exclusion> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-log4j12</artifactId> + </exclusion> + <exclusion> + <groupId>log4j</groupId> + <artifactId>log4j</artifactId> + </exclusion> + </exclusions> + </dependency> + + <!-- dependency> + <groupId>org.apache.cassandra</groupId> + <artifactId>cassandra-all</artifactId> + <version>2.1.2</version> + <scope>compile</scope> + <exclusions> + <exclusion> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-log4j12</artifactId> + </exclusion> + <exclusion> + <groupId>log4j</groupId> + <artifactId>log4j</artifactId> + </exclusion> + </exclusions> + </dependency--> + + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>jaxb2-maven-plugin</artifactId> + <version>1.3</version> + <executions> + <execution> + <phase>generate-sources</phase> + <goals> + <goal>xjc</goal> + </goals> + </execution> + </executions> + <configuration> + <schemaDirectory>src/main/xsd</schemaDirectory> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <configuration> + <archive> + <manifestEntries> + <Sealed>true</Sealed> + </manifestEntries> + </archive> + + </configuration> + <executions> + <execution> + <id>test-jar</id> + <phase>package</phase> + <goals> + <goal>test-jar</goal> + </goals> + </execution> + </executions> + + </plugin> + + <!-- We want to create a Jar with Rosetta built in (since I don't want + a separate deployment at this time Use this one as the jar to put in SWM + packages --> + <plugin> + <artifactId>maven-assembly-plugin</artifactId> + <version>2.4</version> + <configuration> + <classifier>tests</classifier> + <archive> + <manifestEntries> + <Sealed>true</Sealed> + </manifestEntries> + </archive> + </configuration> + <executions> + <execution> + <id>full</id> + <phase>package</phase> + <goals> + <goal>single</goal> + </goals> + <configuration> + <descriptors> + <descriptor>src/assemble/cadi-aaf.xml</descriptor> + </descriptors> + </configuration> + </execution> + </executions> + </plugin> + + <plugin> + <!-- Must put this in to turn on Signing, but Configuration itself is + in Parent --> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jarsigner-plugin</artifactId> + <configuration> + <skip>false</skip> + </configuration> + <executions> + <execution> + <id>sign-full</id> + <goals> + <goal>sign</goal> + </goals> + <configuration> + <archive>target/${project.artifactId}-${project.version}-full.jar</archive> + </configuration> + </execution> + <execution> + <id>verify-full</id> + <goals> + <goal>verify</goal> + </goals> + <configuration> + <archive>target/${project.artifactId}-${project.version}-full.jar</archive> + </configuration> + </execution> + </executions> + </plugin> + + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>jaxb2-maven-plugin</artifactId> + <version>1.3</version> + <executions> + <execution> + <phase>generate-sources</phase> + <goals> + <goal>xjc</goal> + </goals> + </execution> + </executions> + <configuration> + <schemaDirectory>src/main/xsd</schemaDirectory> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>2.6</version> + <configuration> + <skip>false</skip> + </configuration> + </plugin> + </plugins> + + <pluginManagement> + <plugins> + <!--This plugin's configuration is used to store Eclipse m2e settings + only. It has no influence on the Maven build itself. --> + <plugin> + <groupId>org.eclipse.m2e</groupId> + <artifactId>lifecycle-mapping</artifactId> + <version>1.0.0</version> + <configuration> + <lifecycleMappingMetadata> + <pluginExecutions> + <pluginExecution> + <pluginExecutionFilter> + <groupId> + org.codehaus.mojo + </groupId> + <artifactId> + jaxb2-maven-plugin + </artifactId> + <versionRange> + [1.3,) + </versionRange> + <goals> + <goal>xjc</goal> + </goals> + </pluginExecutionFilter> + <action> + <ignore></ignore> + </action> + </pluginExecution> + </pluginExecutions> + </lifecycleMappingMetadata> + </configuration> + </plugin> + </plugins> + </pluginManagement> + </build> + + +</project> diff --git a/cadi/aaf/pom.xml b/cadi/aaf/pom.xml new file mode 100644 index 00000000..9c57e3c4 --- /dev/null +++ b/cadi/aaf/pom.xml @@ -0,0 +1,238 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * +--> +<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"> + <parent> + <groupId>org.onap.aaf.authz</groupId> + <artifactId>cadiparent</artifactId> + <version>2.1.0-SNAPSHOT</version> + <relativePath>..</relativePath> + </parent> + + <modelVersion>4.0.0</modelVersion> + <artifactId>aaf-cadi-aaf</artifactId> + <name>AAF CADI AAF Connection Library</name> + <packaging>jar</packaging> + + <properties> + <!-- SONAR --> + <!-- <sonar.skip>true</sonar.skip> --> + <jacoco.version>0.7.7.201606060606</jacoco.version> + <sonar-jacoco-listeners.version>3.2</sonar-jacoco-listeners.version> + <sonar.core.codeCoveragePlugin>jacoco</sonar.core.codeCoveragePlugin> + <!-- Default Sonar configuration --> + <sonar.jacoco.reportPaths>target/code-coverage/jacoco-ut.exec</sonar.jacoco.reportPaths> + <sonar.jacoco.itReportPaths>target/code-coverage/jacoco-it.exec</sonar.jacoco.itReportPaths> + <!-- Note: This list should match jacoco-maven-plugin's exclusion list below --> + <sonar.exclusions>**/gen/**,**/generated-sources/**,**/yang-gen**,**/pax/**</sonar.exclusions> + <nexusproxy>https://nexus.onap.org</nexusproxy> + <snapshotNexusPath>/content/repositories/snapshots/</snapshotNexusPath> + <releaseNexusPath>/content/repositories/releases/</releaseNexusPath> + <stagingNexusPath>/content/repositories/staging/</stagingNexusPath> + <sitePath>/content/sites/site/org/onap/aaf/authz/${project.artifactId}/${project.version}</sitePath> + </properties> + <developers> + <developer> + <name>Jonathan Gathman</name> + <email>jonathan.gathman@att.com</email> + <organization>ATT</organization> + <roles> + <role>Architect</role> + <role>Lead Developer</role> + </roles> + </developer> + <developer> + <name>Gabe Maurer</name> + <email>gabe.maurer@att.com</email> + <organization>ATT</organization> + <roles> + <role>Developer</role> + </roles> + </developer> + <developer> + <name>Ian Howell</name> + <email>ian.howell@att.com</email> + <organization>ATT</organization> + <roles> + <role>Developer</role> + </roles> + </developer> + <developer> + <name>Sai Gandham</name> + <email>sai.gandham@att.com</email> + <organization>ATT</organization> + <roles> + <role>Developer</role> + </roles> + </developer> + </developers> + + + <dependencies> + <dependency> + <groupId>org.onap.aaf.authz</groupId> + <artifactId>aaf-auth-client</artifactId> + </dependency> + + <dependency> + <groupId>org.onap.aaf.authz</groupId> + <artifactId>aaf-cadi-client</artifactId> + </dependency> + + <dependency> + <groupId>org.onap.aaf.authz</groupId> + <artifactId>aaf-misc-env</artifactId> + </dependency> + + <dependency> + <groupId>org.onap.aaf.authz</groupId> + <artifactId>aaf-cadi-core</artifactId> + </dependency> + + <dependency> + <groupId>javax.servlet</groupId> + <artifactId>javax.servlet-api</artifactId> + <scope>provided</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.sonatype.plugins</groupId> + <artifactId>nexus-staging-maven-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <nexusUrl>${nexusproxy}</nexusUrl> + <stagingProfileId>176c31dfe190a</stagingProfileId> + <serverId>ecomp-staging</serverId> + </configuration> + </plugin> + <plugin> + <groupId>org.jacoco</groupId> + <artifactId>jacoco-maven-plugin</artifactId> + <configuration> + <excludes> + <exclude>**/gen/**</exclude> + <exclude>**/generated-sources/**</exclude> + <exclude>**/yang-gen/**</exclude> + <exclude>**/pax/**</exclude> + </excludes> + </configuration> + <executions> + <execution> + <id>pre-unit-test</id> + <goals> + <goal>prepare-agent</goal> + </goals> + <configuration> + <destFile>${project.build.directory}/code-coverage/jacoco-ut.exec</destFile> + <propertyName>surefireArgLine</propertyName> + </configuration> + </execution> + <execution> + <id>post-unit-test</id> + <phase>test</phase> + <goals> + <goal>report</goal> + </goals> + <configuration> + <dataFile>${project.build.directory}/code-coverage/jacoco-ut.exec</dataFile> + <outputDirectory>${project.reporting.outputDirectory}/jacoco-ut</outputDirectory> + </configuration> + </execution> + <execution> + <id>pre-integration-test</id> + <phase>pre-integration-test</phase> + <goals> + <goal>prepare-agent</goal> + </goals> + <configuration> + <destFile>${project.build.directory}/code-coverage/jacoco-it.exec</destFile> + <propertyName>failsafeArgLine</propertyName> + </configuration> + </execution> + <execution> + <id>post-integration-test</id> + <phase>post-integration-test</phase> + <goals> + <goal>report</goal> + </goals> + <configuration> + <dataFile>${project.build.directory}/code-coverage/jacoco-it.exec</dataFile> + <outputDirectory>${project.reporting.outputDirectory}/jacoco-it</outputDirectory> + </configuration> + </execution> + </executions> + </plugin> + + <plugin> + <artifactId>maven-assembly-plugin</artifactId> + <configuration> + <classifier>tests</classifier> + <archive> + <manifest> + <mainClass>org.onap.aaf.cadi.cm.CmAgent</mainClass> + </manifest> + <manifestEntries> + <Sealed>true</Sealed> + </manifestEntries> + </archive> + </configuration> + <executions> + <execution> + <id>full</id> + <phase>package</phase> + <goals> + <goal>single</goal> + </goals> + <configuration> + <descriptors> + <descriptor>src/assemble/cadi-aaf.xml</descriptor> + </descriptors> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + + <distributionManagement> + <repository> + <id>ecomp-releases</id> + <name>AAF Release Repository</name> + <url>${nexusproxy}${releaseNexusPath}</url> + </repository> + <snapshotRepository> + <id>ecomp-snapshots</id> + <name>AAF Snapshot Repository</name> + <url>${nexusproxy}${snapshotNexusPath}</url> + </snapshotRepository> + <site> + <id>ecomp-site</id> + <url>dav:${nexusproxy}${sitePath}</url> + </site> + </distributionManagement> +</project> diff --git a/cadi/aaf/src/assemble/cadi-aaf.xml b/cadi/aaf/src/assemble/cadi-aaf.xml new file mode 100644 index 00000000..0a485b83 --- /dev/null +++ b/cadi/aaf/src/assemble/cadi-aaf.xml @@ -0,0 +1,31 @@ +<?xml version='1.0' encoding='utf-8'?> +<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd"> + + <id>full</id> + <formats> + <format>jar</format> + </formats> + + <includeBaseDirectory>false</includeBaseDirectory> + <dependencySets> + <dependencySet> + <unpack>true</unpack> + <scope>compile</scope> + <includes> + <include>org.onap.aaf.authz:aaf-auth-client</include> + <include>org.onap.aaf.authz:aaf-cadi-aaf</include> + <include>org.onap.aaf.authz:aaf-cadi-core</include> + <include>org.onap.aaf.authz:aaf-cadi-client</include> + <include>org.onap.aaf.authz:aaf-misc-env</include> + <include>org.onap.aaf.authz:aaf-misc-rosetta</include> + </includes> + </dependencySet> + + </dependencySets> + <fileSets> + <fileSet> + <directory>src/main/xsd</directory> + </fileSet> + </fileSets> +</assembly>
\ No newline at end of file diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/AAFPermission.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/AAFPermission.java new file mode 100644 index 00000000..e586d991 --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/AAFPermission.java @@ -0,0 +1,129 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.aaf; + +import java.util.ArrayList; +import java.util.List; + +import org.onap.aaf.cadi.Permission; + +/** + * A Class that understands the AAF format of Permission (name/type/action) + * or String "name|type|action" + * + * @author Jonathan + * + */ +public class AAFPermission implements Permission { + private static final List<String> NO_ROLES; + protected String type,instance,action,key; + private List<String> roles; + + static { + NO_ROLES = new ArrayList<String>(); + } + + protected AAFPermission() {roles=NO_ROLES;} + + public AAFPermission(String type, String instance, String action) { + this.type = type; + this.instance = instance; + this.action = action; + key = type + '|' + instance + '|' + action; + this.roles = NO_ROLES; + + } + public AAFPermission(String type, String instance, String action, List<String> roles) { + this.type = type; + this.instance = instance; + this.action = action; + key = type + '|' + instance + '|' + action; + this.roles = roles==null?NO_ROLES:roles; + } + + /** + * Match a Permission + * if Permission is Fielded type "Permission", we use the fields + * otherwise, we split the Permission with '|' + * + * when the type or action starts with REGEX indicator character ( ! ), + * then it is evaluated as a regular expression. + * + * If you want a simple field comparison, it is faster without REGEX + */ + public boolean match(Permission p) { + String aafType; + String aafInstance; + String aafAction; + if(p instanceof AAFPermission) { + AAFPermission ap = (AAFPermission)p; + // Note: In AAF > 1.0, Accepting "*" from name would violate multi-tenancy + // Current solution is only allow direct match on Type. + // 8/28/2014 Jonathan - added REGEX ability + aafType = ap.getName(); + aafInstance = ap.getInstance(); + aafAction = ap.getAction(); + } else { + // Permission is concatenated together: separated by | + String[] aaf = p.getKey().split("[\\s]*\\|[\\s]*",3); + aafType = aaf[0]; + aafInstance = (aaf.length > 1) ? aaf[1] : "*"; + aafAction = (aaf.length > 2) ? aaf[2] : "*"; + } + return ((type.equals(aafType)) && + (PermEval.evalInstance(instance, aafInstance)) && + (PermEval.evalAction(action, aafAction))); + } + + public String getName() { + return type; + } + + public String getInstance() { + return instance; + } + + public String getAction() { + return action; + } + + public String getKey() { + return key; + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.Permission#permType() + */ + public String permType() { + return "AAF"; + } + + public List<String> roles() { + return roles; + } + public String toString() { + return "AAFPermission:\n\tType: " + type + + "\n\tInstance: " + instance + + "\n\tAction: " + action + + "\n\tKey: " + key; + } +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/PermEval.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/PermEval.java new file mode 100644 index 00000000..75df4eab --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/PermEval.java @@ -0,0 +1,140 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.aaf; + +import org.onap.aaf.misc.env.util.Split; + + +public class PermEval { + public static final char START_REGEX_CHAR = '!'; + public static final char START_INST_KEY_CHAR=':'; + public static final char ALT_START_INST_KEY_CHAR='/'; + + public static final char LIST_SEP = ','; + public static final String INST_KEY_REGEX = new StringBuilder().append(START_INST_KEY_CHAR).toString(); + public static final String ASTERIX = "*"; + + /** + * Evaluate Instance + * + * Instance can be more complex. It can be a string, a Regular Expression, or a ":" separated Key + * who's parts can also be a String, Regular Expression. + * + * sInst = Server's Instance + * In order to prevent false matches, keys must be the same length to count as equal + * Changing this will break existing users, like Cassandra. Jonathan 9-4-2015 + */ + public static boolean evalInstance(String sInst, String pInst) { + if(sInst == null || pInst == null) { + return false; + } + if (sInst == "" || pInst == "") { + return false; + } + if(ASTERIX.equals(sInst)) { + return true; // If Server's String is "*", then it accepts every Instance + } + char firstChar = pInst.charAt(0); + char startChar = firstChar==ALT_START_INST_KEY_CHAR?ALT_START_INST_KEY_CHAR:START_INST_KEY_CHAR; + switch(pInst.charAt(0)) { // First char + case START_REGEX_CHAR: // Evaluate as Regular Expression + String pItem = pInst.substring(1); + String first = Split.split(LIST_SEP,sInst)[0]; // allow for "," definition in Action + return first.matches(pItem); + + case START_INST_KEY_CHAR: // Evaluate a special Key field, i.e.:xyz:*:!df.* + case ALT_START_INST_KEY_CHAR: // Also allow '/' as special Key Field, i.e. /xyz/*/!.* + if(sInst.charAt(0)==startChar) { // To compare key-to-key, both strings must be keys + String[] skeys=Split.split(startChar,sInst); + String[] pkeys=Split.split(startChar,pInst); + if(skeys.length!=pkeys.length) return false; + + boolean pass = true; + for(int i=1;pass && i<skeys.length;++i) { // We start at 1, because the first one, being ":" is always "" + if(ASTERIX.equals(skeys[i]))continue; // Server data accepts all for this key spot + pass = false; + for(String sItem : Split.split(LIST_SEP,skeys[i])) { // allow for "," definition in Action + if(pkeys[i].length()==0) { + if(pass=sItem.length()==0) { + break; // Both Empty, keep checking + } + } else if(sItem.charAt(0)==START_REGEX_CHAR) { // Check Server side when wildcarding like * + if(pass=pkeys[i].matches(sItem.substring(1))) { + break; // Matches, keep checking + } + } else if(skeys[i].endsWith(ASTERIX)) { + if(pass=endAsterixCompare(skeys[i],pkeys[i])) { + break; + } + } else if(pass=sItem.equals(pkeys[i])) { + break; // Equal, keep checking + } + } + } + return pass; // return whether passed all key checks + } + return false; // if first chars aren't the same, further String compare not necessary + default: // Evaluate as String Compare + for(String sItem : Split.split(LIST_SEP,sInst)) { // allow for "," separator //TODO is this only for actions? + if((sItem.endsWith(ASTERIX)) && (endAsterixCompare(sInst, pInst))) { + return true; + } else if(sItem.equals(pInst)) { + return true; + } + } + return false; + } + } + + private static boolean endAsterixCompare(String sInst, String pInst) { + final int len = sInst.length()-1; + if(pInst.length()<len) { + return false; + } + for(int j=0;j<len;++j) { + if(pInst.charAt(j)!=sInst.charAt(j)) { + return false; + } + } + return true; + } + + /** + * Evaluate Action + * + * sAction = Stored Action... + * pAction = Present Action... the Permission to validate against. + * Action is not quite as complex. But we write it in this function so it can be consistent + */ + public static boolean evalAction(String sAction,String pAction) { + if(ASTERIX.equals(sAction))return true; // If Server's String is "*", then it accepts every Action + if(pAction == "") return false; + for(String sItem : Split.split(LIST_SEP,sAction)) { // allow for "," definition in Action + if (pAction.charAt(0)==START_REGEX_CHAR? // First char + sItem.matches(pAction.substring(1)): // Evaluate as Regular Expression + sItem.equals(pAction)) // Evaluate as String Compare + return true; + } + return false; + } + +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/TestConnectivity.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/TestConnectivity.java new file mode 100644 index 00000000..243b3a6a --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/TestConnectivity.java @@ -0,0 +1,309 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.aaf; + +import java.io.IOException; +import java.io.PrintStream; +import java.net.HttpURLConnection; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.URI; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.Locator; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.SecuritySetter; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.Locator.Item; +import org.onap.aaf.cadi.aaf.v2_0.AAFLocator; +import org.onap.aaf.cadi.client.Future; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.config.SecurityInfoC; +import org.onap.aaf.cadi.http.HBasicAuthSS; +import org.onap.aaf.cadi.http.HClient; +import org.onap.aaf.cadi.http.HX509SS; +import org.onap.aaf.cadi.oauth.HRenewingTokenSS; +import org.onap.aaf.misc.env.APIException; + +public class TestConnectivity { + + public static void main(String[] args) { + if(args.length<1) { + System.out.println("Usage: ConnectivityTester <cadi_prop_files> [<AAF FQDN (i.e. aaf.dev.att.com)>]"); + } else { + print(true,"START OF CONNECTIVITY TESTS",new Date().toString(),System.getProperty("user.name"), + "Note: All API Calls are /authz/perms/user/<MechID/Alias of the caller>"); + + if(!args[0].contains(Config.CADI_PROP_FILES+'=')) { + args[0]=Config.CADI_PROP_FILES+'='+args[0]; + } + + PropAccess access = new PropAccess(args); + String aaflocate; + if(args.length>1) { + aaflocate = "https://" + args[1] + "/locate"; + access.setProperty(Config.AAF_LOCATE_URL, "https://" + args[1]); + } else { + aaflocate = access.getProperty(Config.AAF_LOCATE_URL); + if(aaflocate==null) { + print(true,"Properties must contain ",Config.AAF_LOCATE_URL); + } else if (!aaflocate.endsWith("/locate")) { + aaflocate += "/locate"; + } + } + + try { + SecurityInfoC<HttpURLConnection> si = SecurityInfoC.instance(access, HttpURLConnection.class); + + List<SecuritySetter<HttpURLConnection>> lss = loadSetters(access,si); + ///////// + print(true,"Test Connections driven by AAFLocator"); + URI serviceURI = new URI(aaflocate+"/AAF_NS.service/2.0"); + + for(URI uri : new URI[] { + serviceURI, + new URI(aaflocate+"/AAF_NS.service:2.0"), + new URI(aaflocate+"/AAF_NS.service"), + new URI(aaflocate+"/AAF_NS.gw:2.0"), + new URI(aaflocate+"/AAF_NS.token:2.0"), + new URI(aaflocate+"/AAF_NS.certman:2.0"), + new URI(aaflocate+"/AAF_NS.hello") + }) { + Locator<URI> locator = new AAFLocator(si, uri); + try { + connectTest(locator, uri); + } catch (Exception e) { + e.printStackTrace(); + System.err.flush(); + } + } + + ///////// + print(true,"Test Service driven by AAFLocator"); + Locator<URI> locator = new AAFLocator(si,new URI(aaflocate+"/AAF_NS.service:2.0")); + for(SecuritySetter<HttpURLConnection> ss : lss) { + permTest(locator,ss); + } + + ///////// + // Removed for ONAP +// print(true,"Test Proxy Access driven by AAFLocator"); +// locator = new AAFLocator(si, new URI(aaflocate+"/AAF_NS.gw:2.0/proxy")); +// for(SecuritySetter<HttpURLConnection> ss : lss) { +// permTest(locator,ss); +// } + + ////////// + print(true,"Test essential BasicAuth Service call, driven by AAFLocator"); + for(SecuritySetter<HttpURLConnection> ss : lss) { + if(ss instanceof HBasicAuthSS) { + basicAuthTest(new AAFLocator(si, new URI(aaflocate+"/AAF_NS.service:2.0")),ss); + } + } + + } catch(Exception e) { + e.printStackTrace(System.err); + } finally { + print(true,"END OF TESTS"); + } + } + } + + private static List<SecuritySetter<HttpURLConnection>> loadSetters(PropAccess access, SecurityInfoC<HttpURLConnection> si) { + print(true,"Load Security Setters from Configuration Information"); + String user = access.getProperty(Config.AAF_APPID); + + ArrayList<SecuritySetter<HttpURLConnection>> lss = new ArrayList<SecuritySetter<HttpURLConnection>>(); + + + try { + HBasicAuthSS hbass = new HBasicAuthSS(si,true); + if(hbass==null || hbass.getID()==null) { + access.log(Level.INFO, "BasicAuth Information is not available in configuration, BasicAuth tests will not be conducted... Continuing"); + } else { + access.log(Level.INFO, "BasicAuth Information found with ID",hbass.getID(),". BasicAuth tests will be performed."); + lss.add(hbass); + } + } catch (Exception e) { + access.log(Level.INFO, "BasicAuth Security Setter constructor threw exception: \"",e.getMessage(),"\". BasicAuth tests will not be performed"); + } + + try { + HX509SS hxss = new HX509SS(user,si); + if(hxss==null || hxss.getID()==null) { + access.log(Level.INFO, "X509 (Client certificate) Information is not available in configuration, X509 tests will not be conducted... Continuing"); + } else { + access.log(Level.INFO, "X509 (Client certificate) Information found with ID",hxss.getID(),". X509 tests will be performed."); + lss.add(hxss); + } + } catch (Exception e) { + access.log(Level.INFO, "X509 (Client certificate) Security Setter constructor threw exception: \"",e.getMessage(),"\". X509 tests will not be performed"); + } + + String tokenURL = access.getProperty(Config.AAF_OAUTH2_TOKEN_URL); + String locateURL=access.getProperty(Config.AAF_LOCATE_URL); + if(tokenURL==null || (tokenURL.contains("/locate/") && locateURL!=null)) { + tokenURL=locateURL+"/locate/AAF_NS.token:2.0/token"; + } + + try { + HRenewingTokenSS hrtss = new HRenewingTokenSS(access, tokenURL); + access.log(Level.INFO, "AAF OAUTH2 Information found with ID",hrtss.getID(),". AAF OAUTH2 tests will be performed."); + lss.add(hrtss); + } catch (Exception e) { + access.log(Level.INFO, "AAF OAUTH2 Security Setter constructor threw exception: \"",e.getMessage(),"\". AAF OAUTH2 tests will not be conducted... Continuing"); + } + + tokenURL = access.getProperty(Config.AAF_ALT_OAUTH2_TOKEN_URL); + if(tokenURL==null) { + access.log(Level.INFO, "AAF Alternative OAUTH2 requires",Config.AAF_ALT_OAUTH2_TOKEN_URL, "OAuth2 tests to", tokenURL, "will not be conducted... Continuing"); + } else { + try { + HRenewingTokenSS hrtss = new HRenewingTokenSS(access, tokenURL); + access.log(Level.INFO, "ALT OAUTH2 Information found with ID",hrtss.getID(),". ALT OAUTH2 tests will be performed."); + lss.add(hrtss); + } catch (Exception e) { + access.log(Level.INFO, "ALT OAUTH2 Security Setter constructor threw exception: \"",e.getMessage(),"\". ALT OAuth2 tests to", tokenURL, " will not be conducted... Continuing"); + } + } + + return lss; + } + + private static void print(Boolean strong, String ... args) { + PrintStream out = System.out; + out.println(); + if(strong) { + for(int i=0;i<70;++i) { + out.print('='); + } + out.println(); + } + for(String s : args) { + out.print(strong?"== ":"------ "); + out.print(s); + if(!strong) { + out.print(" ------"); + } + out.println(); + } + if(strong) { + for(int i=0;i<70;++i) { + out.print('='); + } + } + out.println(); + } + + private static void connectTest(Locator<URI> dl, URI locatorURI) throws LocatorException { + URI uri; + Socket socket; + print(false,"TCP/IP Connect test to all Located Services for " + locatorURI.toString() ); + for(Item li = dl.first();li!=null;li=dl.next(li)) { + if((uri = dl.get(li)) == null) { + System.out.println("Locator Item empty"); + } else { + socket = new Socket(); + try { + try { + socket.connect(new InetSocketAddress(uri.getHost(), uri.getPort()),3000); + System.out.printf("Can Connect a Socket to %s %d\n",uri.getHost(),uri.getPort()); + } catch (IOException e) { + System.out.printf("Cannot Connect a Socket to %s %d: %s\n",uri.getHost(),uri.getPort(),e.getMessage()); + } + } finally { + try { + socket.close(); + } catch (IOException e1) { + System.out.printf("Could not close Socket Connection: %s\n",e1.getMessage()); + } + } + } + } + } + + private static void permTest(Locator<URI> dl, SecuritySetter<HttpURLConnection> ss) { + try { + URI uri = dl.get(dl.best()); + if(uri==null) { + System.out.print("No URI available using " + ss.getClass().getSimpleName()); + System.out.println(); + return; + } else { + System.out.print("Resolved to: " + uri + " using " + ss.getClass().getSimpleName()); + } + if(ss instanceof HRenewingTokenSS) { + System.out.println(" " + ((HRenewingTokenSS)ss).tokenURL()); + } else { + System.out.println(); + } + HClient client = new HClient(ss, uri, 3000); + client.setMethod("GET"); + String user = ss.getID(); + if(user.indexOf('@')<0) { + user+="@isam.att.com"; + } + client.setPathInfo("/authz/perms/user/"+user); + client.send(); + Future<String> future = client.futureReadString(); + if(future.get(7000)) { + System.out.println(future.body()); + } else { + if(future.code()==401 && ss instanceof HX509SS) { + System.out.println(" Authentication denied with 401 for Certificate.\n\t" + + "This means Certificate isn't valid for this environment, and has attempted another method of Authentication"); + } else { + System.out.println(future.code() + ":" + future.body()); + } + } + } catch (CadiException | LocatorException | APIException e) { + e.printStackTrace(); + } + } + + + private static void basicAuthTest(Locator<URI> dl, SecuritySetter<HttpURLConnection> ss) { + try { + URI uri = dl.get(dl.best()); + System.out.println("Resolved to: " + uri); + HClient client = new HClient(ss, uri, 3000); + client.setMethod("GET"); + client.setPathInfo("/authn/basicAuth"); + client.addHeader("Accept", "text/plain"); + client.send(); + + + Future<String> future = client.futureReadString(); + if(future.get(7000)) { + System.out.println("BasicAuth Validated"); + } else { + System.out.println("Failure " + future.code() + ":" + future.body()); + } + } catch (CadiException | LocatorException | APIException e) { + e.printStackTrace(); + } + } +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/cert/AAFListedCertIdentity.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/cert/AAFListedCertIdentity.java new file mode 100644 index 00000000..e336042a --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/cert/AAFListedCertIdentity.java @@ -0,0 +1,176 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.aaf.cert; + + +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; +import java.util.TreeMap; + +import javax.servlet.http.HttpServletRequest; +import javax.xml.datatype.XMLGregorianCalendar; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.Hash; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.aaf.v2_0.AAFCon; +import org.onap.aaf.cadi.client.Future; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.principal.TaggedPrincipal; +import org.onap.aaf.cadi.principal.X509Principal; +import org.onap.aaf.cadi.taf.cert.CertIdentity; +import org.onap.aaf.cadi.taf.cert.X509Taf; +import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.misc.env.util.Chrono; +import org.onap.aaf.misc.env.util.Split; + +import aaf.v2_0.Certs; +import aaf.v2_0.Certs.Cert; +import aaf.v2_0.Users; +import aaf.v2_0.Users.User; + +public class AAFListedCertIdentity implements CertIdentity { + //TODO should 8 hours be configurable? + private static final long EIGHT_HOURS = 1000*60*60*8L; + + private static Map<ByteArrayHolder,String> certs = null; + + // Did this to add other Trust Mechanisms + // Trust mechanism set by Property: + private static final String[] authMechanisms = new String[] {"tguard","basicAuth","csp"}; + private static String[] certIDs; + + private static Map<String,Set<String>> trusted =null; + + public AAFListedCertIdentity(Access access, AAFCon<?> aafcon) throws APIException { + synchronized(AAFListedCertIdentity.class) { + if(certIDs==null) { + String cip = access.getProperty(Config.AAF_CERT_IDS, null); + if(cip!=null) { + certIDs = Split.split(',',cip); + } + } + if(certIDs!=null && certs==null) { + TimerTask cu = new CertUpdate(aafcon); + cu.run(); // want this to run in this thread first... + new Timer("AAF Identity Refresh Timer",true).scheduleAtFixedRate(cu, EIGHT_HOURS,EIGHT_HOURS); + } + } + } + + public static Set<String> trusted(String authMech) { + return trusted.get(authMech); + } + + public TaggedPrincipal identity(HttpServletRequest req, X509Certificate cert, byte[] certBytes) throws CertificateException { + if(cert==null && certBytes==null)return null; + if(certBytes==null)certBytes = cert.getEncoded(); + byte[] fingerprint = X509Taf.getFingerPrint(certBytes); + String id = certs.get(new ByteArrayHolder(fingerprint)); + if(id!=null) { // Caller is Validated + return new X509Principal(id,cert,certBytes); + } + return null; + } + + private static class ByteArrayHolder implements Comparable<ByteArrayHolder> { + private byte[] ba; + public ByteArrayHolder(byte[] ba) { + this.ba = ba; + } + public int compareTo(ByteArrayHolder b) { + return Hash.compareTo(ba, b.ba); + } + } + + private class CertUpdate extends TimerTask { + + private AAFCon<?> aafcon; + public CertUpdate(AAFCon<?> con) { + aafcon = con; + } + + @Override + public void run() { + try { + TreeMap<ByteArrayHolder, String> newCertsMap = new TreeMap<ByteArrayHolder,String>(); + Map<String,Set<String>> newTrustMap = new TreeMap<String,Set<String>>(); + Set<String> userLookup = new HashSet<String>(); + for(String s : certIDs) { + userLookup.add(s); + } + for(String authMech : authMechanisms) { + Future<Users> fusr = aafcon.client(Config.AAF_DEFAULT_VERSION).read("/authz/users/perm/com.att.aaf.trust/"+authMech+"/authenticate", Users.class, aafcon.usersDF); + if(fusr.get(5000)) { + List<User> users = fusr.value.getUser(); + if(users.isEmpty()) { + aafcon.access.log(Level.WARN, "AAF Lookup-No IDs in Role com.att.aaf.trustForID <> "+authMech); + } else { + aafcon.access.log(Level.INFO,"Loading Trust Authentication Info for",authMech); + Set<String> hsUser = new HashSet<String>(); + for(User u : users) { + userLookup.add(u.getId()); + hsUser.add(u.getId()); + } + newTrustMap.put(authMech,hsUser); + } + } else { + aafcon.access.log(Level.WARN, "Could not get Users in Perm com.att.trust|tguard|authenticate",fusr.code(),fusr.body()); + } + + } + + for(String u : userLookup) { + Future<Certs> fc = aafcon.client(Config.AAF_DEFAULT_VERSION).read("/authn/cert/id/"+u, Certs.class, aafcon.certsDF); + XMLGregorianCalendar now = Chrono.timeStamp(); + if(fc.get(5000)) { + List<Cert> certs = fc.value.getCert(); + if(certs.isEmpty()) { + aafcon.access.log(Level.WARN, "No Cert Associations for",u); + } else { + for(Cert c : fc.value.getCert()) { + XMLGregorianCalendar then =c.getExpires(); + if(then !=null && then.compare(now)>0) { + newCertsMap.put(new ByteArrayHolder(c.getFingerprint()), c.getId()); + aafcon.access.log(Level.INIT,"Associating "+ c.getId() + " expiring " + Chrono.dateOnlyStamp(c.getExpires()) + " with " + c.getX500()); + } + } + } + } else { + aafcon.access.log(Level.WARN, "Could not get Certificates for",u); + } + } + + certs = newCertsMap; + trusted = newTrustMap; + } catch(Exception e) { + aafcon.access.log(e, "Failure to update Certificate Identities from AAF"); + } + } + } +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/client/ErrMessage.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/client/ErrMessage.java new file mode 100644 index 00000000..0fb4d60d --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/client/ErrMessage.java @@ -0,0 +1,96 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.aaf.client; + +import java.io.PrintStream; + +import org.onap.aaf.cadi.client.Future; +import org.onap.aaf.cadi.util.Vars; +import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.misc.env.Data.TYPE; +import org.onap.aaf.misc.rosetta.env.RosettaDF; +import org.onap.aaf.misc.rosetta.env.RosettaEnv; + +import aaf.v2_0.Error; + +public class ErrMessage { + private RosettaDF<Error> errDF; + + public ErrMessage(RosettaEnv env) throws APIException { + errDF = env.newDataFactory(Error.class); + } + + /** + * AT&T Requires a specific Error Format for RESTful Services, which AAF complies with. + * + * This code will create a meaningful string from this format. + * + * @param ps + * @param df + * @param r + * @throws APIException + */ + public void printErr(PrintStream ps, String attErrJson) throws APIException { + StringBuilder sb = new StringBuilder(); + Error err = errDF.newData().in(TYPE.JSON).load(attErrJson).asObject(); + ps.println(toMsg(sb,err)); + } + + /** + * AT&T Requires a specific Error Format for RESTful Services, which AAF complies with. + * + * This code will create a meaningful string from this format. + * + * @param sb + * @param df + * @param r + * @throws APIException + */ + public StringBuilder toMsg(StringBuilder sb, String attErrJson) throws APIException { + return toMsg(sb,errDF.newData().in(TYPE.JSON).load(attErrJson).asObject()); + } + + public StringBuilder toMsg(Future<?> future) { + return toMsg(new StringBuilder(),future); + } + + public StringBuilder toMsg(StringBuilder sb, Future<?> future) { + try { + toMsg(sb,errDF.newData().in(TYPE.JSON).load(future.body()).asObject()); + } catch(Exception e) { + //just print what we can + sb.append(future.code()); + sb.append(": "); + sb.append(future.body()); + } + return sb; + } + + public StringBuilder toMsg(StringBuilder sb, Error err) { + sb.append(err.getMessageId()); + sb.append(' '); + String[] vars = new String[err.getVariables().size()]; + err.getVariables().toArray(vars); + Vars.convert(sb, err.getText(),vars); + return sb; + } +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/client/Examples.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/client/Examples.java new file mode 100644 index 00000000..31f60ee8 --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/client/Examples.java @@ -0,0 +1,443 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.aaf.client; + + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.GregorianCalendar; + +import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.misc.env.Data; +import org.onap.aaf.misc.env.Data.TYPE; +import org.onap.aaf.misc.env.util.Chrono; +import org.onap.aaf.misc.rosetta.env.RosettaDF; +import org.onap.aaf.misc.rosetta.env.RosettaEnv; + +import aaf.v2_0.Approval; +import aaf.v2_0.Approvals; +import aaf.v2_0.CredRequest; +import aaf.v2_0.Keys; +import aaf.v2_0.NsRequest; +import aaf.v2_0.Nss; +import aaf.v2_0.Nss.Ns; +import aaf.v2_0.Perm; +import aaf.v2_0.PermKey; +import aaf.v2_0.PermRequest; +import aaf.v2_0.Perms; +import aaf.v2_0.Pkey; +import aaf.v2_0.Request; +import aaf.v2_0.Role; +import aaf.v2_0.RoleKey; +import aaf.v2_0.RolePermRequest; +import aaf.v2_0.RoleRequest; +import aaf.v2_0.Roles; +import aaf.v2_0.UserRole; +import aaf.v2_0.UserRoleRequest; +import aaf.v2_0.UserRoles; +import aaf.v2_0.Users; +import aaf.v2_0.Users.User; + +public class Examples { + public static <C> String print(RosettaEnv env, String nameOrContentType, boolean optional) throws APIException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException { + // Discover ClassName + String className = null; + String version = null; + TYPE type = TYPE.JSON; // default + if(nameOrContentType.startsWith("application/")) { + for(String ct : nameOrContentType.split("\\s*,\\s*")) { + for(String elem : ct.split("\\s*;\\s*")) { + if(elem.endsWith("+json")) { + type = TYPE.JSON; + className = elem.substring(elem.indexOf('/')+1, elem.length()-5); + } else if(elem.endsWith("+xml")) { + type = TYPE.XML; + className = elem.substring(elem.indexOf('/')+1, elem.length()-4); + } else if(elem.startsWith("version=")) { + version = elem.substring(8); + } + } + if(className!=null && version!=null)break; + } + if(className==null) { + throw new APIException(nameOrContentType + " does not contain Class Information"); + } + } else { + className = nameOrContentType; + } + + // No Void.class in aaf.v2_0 package causing errors when trying to use a newVoidv2_0 + // method similar to others in this class. This makes it work, but is it right? + if ("Void".equals(className)) return ""; + + if("1.1".equals(version)) { + version = "v1_0"; + } else if(version!=null) { + version = "v" + version.replace('.', '_'); + } else { + version = "v2_0"; + } + + Class<?> cls; + try { + cls = Examples.class.getClassLoader().loadClass("aaf."+version+'.'+className); + } catch (ClassNotFoundException e) { + throw new APIException(e); + } + + Method meth; + try { + meth = Examples.class.getDeclaredMethod("new"+cls.getSimpleName()+version,boolean.class); + } catch (Exception e) { + throw new APIException("ERROR: " + cls.getName() + " does not have an Example in Code. Request from AAF Developers"); + } + + RosettaDF<C> df = env.newDataFactory(cls); + df.option(Data.PRETTY); + + Object data = meth.invoke(null,optional); + + @SuppressWarnings("unchecked") + String rv = df.newData().load((C)data).out(type).asString(); +// Object obj = df.newData().in(type).load(rv).asObject(); + return rv; + } + + /* + * Set Base Class Request (easier than coding over and over) + */ + private static void setOptional(Request req) { + GregorianCalendar gc = new GregorianCalendar(); + req.setStart(Chrono.timeStamp(gc)); + gc.add(GregorianCalendar.MONTH, 6); + req.setEnd(Chrono.timeStamp(gc)); +// req.setForce("false"); + + } + + @SuppressWarnings("unused") + private static Request newRequestv2_0(boolean optional) { + Request r = new Request(); + setOptional(r); + return r; + } + @SuppressWarnings("unused") + private static RolePermRequest newRolePermRequestv2_0(boolean optional) { + RolePermRequest rpr = new RolePermRequest(); + Pkey pkey = new Pkey(); + pkey.setType("org.osaaf.myns.mytype"); + pkey.setInstance("myInstance"); + pkey.setAction("myAction"); + rpr.setPerm(pkey); + rpr.setRole("org.osaaf.myns.myrole"); + if(optional)setOptional(rpr); + return rpr; + } + + @SuppressWarnings("unused") + private static Roles newRolesv2_0(boolean optional) { + Role r; + Pkey p; + Roles rs = new Roles(); + rs.getRole().add(r = new Role()); + r.setName("org.osaaf.myns.myRole"); + r.getPerms().add(p = new Pkey()); + p.setType("org.osaaf.myns.myType"); + p.setInstance("myInstance"); + p.setAction("myAction"); + + r.getPerms().add(p = new Pkey()); + p.setType("org.osaaf.myns.myType"); + p.setInstance("myInstance"); + p.setAction("myOtherAction"); + + rs.getRole().add(r = new Role()); + r.setName("org.osaaf.myns.myOtherRole"); + r.getPerms().add(p = new Pkey()); + p.setType("org.osaaf.myns.myOtherType"); + p.setInstance("myInstance"); + p.setAction("myAction"); + + r.getPerms().add(p = new Pkey()); + p.setType("org.osaaf.myns.myOthertype"); + p.setInstance("myInstance"); + p.setAction("myOtherAction"); + + return rs; + } + + + @SuppressWarnings("unused") + private static PermRequest newPermRequestv2_0(boolean optional) { + PermRequest pr = new PermRequest(); + pr.setType("org.osaaf.myns.myType"); + pr.setInstance("myInstance"); + pr.setAction("myAction"); + if(optional) { + pr.setDescription("Short and meaningful verbiage about the Permission"); + + setOptional(pr); + } + return pr; + } + + @SuppressWarnings("unused") + private static Perm newPermv2_0(boolean optional) { + Perm pr = new Perm(); + pr.setType("org.osaaf.myns.myType"); + pr.setInstance("myInstance"); + pr.setAction("myAction"); + pr.getRoles().add("org.osaaf.aaf.myRole"); + pr.getRoles().add("org.osaaf.aaf.myRole2"); + pr.setDescription("This is my description, and I'm sticking with it"); + if(optional) { + pr.setDescription("Short and meaningful verbiage about the Permission"); + } + return pr; + } + + + @SuppressWarnings("unused") + private static PermKey newPermKeyv2_0(boolean optional) { + PermKey pr = new PermKey(); + pr.setType("org.osaaf.myns.myType"); + pr.setInstance("myInstance"); + pr.setAction("myAction"); + return pr; + } + + @SuppressWarnings("unused") + private static Perms newPermsv2_0(boolean optional) { + Perms perms = new Perms(); + Perm p; + perms.getPerm().add(p=new Perm()); + p.setType("org.osaaf.myns.myType"); + p.setInstance("myInstance"); + p.setAction("myAction"); + p.getRoles().add("org.osaaf.myns.myRole"); + p.getRoles().add("org.osaaf.myns.myRole2"); + + + perms.getPerm().add(p=new Perm()); + p.setType("org.osaaf.myns.myOtherType"); + p.setInstance("myInstance"); + p.setAction("myOtherAction"); + p.getRoles().add("org.osaaf.myns.myRole"); + p.getRoles().add("org.osaaf.myns.myRole2"); + + return perms; + + } + + @SuppressWarnings("unused") + private static UserRoleRequest newUserRoleRequestv2_0(boolean optional) { + UserRoleRequest urr = new UserRoleRequest(); + urr.setRole("org.osaaf.myns.myRole"); + urr.setUser("ab1234@csp.att.com"); + if(optional) setOptional(urr); + return urr; + } + + @SuppressWarnings("unused") + private static NsRequest newNsRequestv2_0(boolean optional) { + NsRequest nr = new NsRequest(); + nr.setName("org.osaaf.myns"); + nr.getResponsible().add("ab1234@csp.att.com"); + nr.getResponsible().add("cd5678@csp.att.com"); + nr.getAdmin().add("zy9876@csp.att.com"); + nr.getAdmin().add("xw5432@csp.att.com"); + if(optional) { + nr.setDescription("This is my Namespace to set up"); + nr.setType("APP"); + setOptional(nr); + } + return nr; + } + + + @SuppressWarnings("unused") + private static Nss newNssv2_0(boolean optional) { + Ns ns; + + Nss nss = new Nss(); + nss.getNs().add(ns = new Nss.Ns()); + ns.setName("org.osaaf.myns"); + ns.getResponsible().add("ab1234@csp.att.com"); + ns.getResponsible().add("cd5678@csp.att.com"); + ns.getAdmin().add("zy9876@csp.att.com"); + ns.getAdmin().add("xw5432@csp.att.com"); + ns.setDescription("This is my Namespace to set up"); + + nss.getNs().add(ns = new Nss.Ns()); + ns.setName("org.osaaf.myOtherNs"); + ns.getResponsible().add("ab1234@csp.att.com"); + ns.getResponsible().add("cd5678@csp.att.com"); + ns.getAdmin().add("zy9876@csp.att.com"); + ns.getAdmin().add("xw5432@csp.att.com"); + + return nss; + } + @SuppressWarnings("unused") + private static RoleRequest newRoleRequestv2_0(boolean optional) { + RoleRequest rr = new RoleRequest(); + rr.setName("org.osaaf.myns.myRole"); + if(optional) { + rr.setDescription("This is my Role"); + setOptional(rr); + } + return rr; + } + + @SuppressWarnings("unused") + private static CredRequest newCredRequestv2_0(boolean optional) { + CredRequest cr = new CredRequest(); + cr.setId("myID@fully.qualified.domain"); + if(optional) { + cr.setType(2); + cr.setEntry("0x125AB256344CE"); + } else { + cr.setPassword("This is my provisioned password"); + } + + return cr; + } + + @SuppressWarnings("unused") + private static Users newUsersv2_0(boolean optional) { + User user; + + Users users = new Users(); + users.getUser().add(user = new Users.User()); + user.setId("ab1234@csp.att.com"); + GregorianCalendar gc = new GregorianCalendar(); + user.setExpires(Chrono.timeStamp(gc)); + + users.getUser().add(user = new Users.User()); + user.setId("zy9876@csp.att.com"); + user.setExpires(Chrono.timeStamp(gc)); + + return users; + } + + @SuppressWarnings("unused") + private static Role newRolev2_0(boolean optional) { + Role r = new Role(); + Pkey p; + r.setName("org.osaaf.myns.myRole"); + r.getPerms().add(p = new Pkey()); + p.setType("org.osaaf.myns.myType"); + p.setInstance("myInstance"); + p.setAction("myAction"); + + return r; + } + + @SuppressWarnings("unused") + private static RoleKey newRoleKeyv2_0(boolean optional) { + RoleKey r = new RoleKey(); + Pkey p; + r.setName("org.osaaf.myns.myRole"); + return r; + } + + @SuppressWarnings("unused") + private static Keys newKeysv2_0(boolean optional) { + Keys ks = new Keys(); + ks.getKey().add("Reponse 1"); + ks.getKey().add("Response 2"); + return ks; + } + + @SuppressWarnings("unused") + private static UserRoles newUserRolesv2_0(boolean optional) { + UserRoles urs = new UserRoles(); + UserRole ur = new UserRole(); + ur.setUser("xy1234"); + ur.setRole("com.test.myapp.myRole"); + ur.setExpires(Chrono.timeStamp()); + urs.getUserRole().add(ur); + + ur = new UserRole(); + ur.setUser("yx4321"); + ur.setRole("com.test.yourapp.yourRole"); + ur.setExpires(Chrono.timeStamp()); + urs.getUserRole().add(ur); + return urs; + } + + + @SuppressWarnings("unused") + private static Approvals newApprovalsv2_0(boolean optional) { + Approvals as = new Approvals(); + Approval a = new Approval(); + a.setApprover("MyApprover"); + a.setId("MyID"); + a.setMemo("My memo (and then some)"); + a.setOperation("MyOperation"); + a.setStatus("MyStatus"); + a.setTicket("MyTicket"); + a.setType("MyType"); + a.setUpdated(Chrono.timeStamp()); + a.setUser("MyUser"); + as.getApprovals().add(a); + a = new Approval(); + a.setApprover("MyApprover2"); + a.setId("MyID2"); + a.setMemo("My memo (and then some)2"); + a.setOperation("MyOperation2"); + a.setStatus("MyStatus2"); + a.setTicket("MyTicket2"); + a.setType("MyType2"); + a.setUpdated(Chrono.timeStamp()); + a.setUser("MyUser2"); + as.getApprovals().add(a); + return as; + } + + @SuppressWarnings("unused") + private static Approval newApprovalv2_0(boolean optional) { + Approval a = new Approval(); + a.setApprover("MyApprover"); + a.setId("MyID"); + a.setMemo("My memo (and then some)"); + a.setOperation("MyOperation"); + a.setStatus("MyStatus"); + a.setTicket("MyTicket"); + a.setType("MyType"); + a.setUpdated(Chrono.timeStamp()); + a.setUser("MyUser"); + return a; + } + + + + @SuppressWarnings("unused") + private static aaf.v2_0.Error newErrorv2_0(boolean optional) { + aaf.v2_0.Error err = new aaf.v2_0.Error(); + err.setMessageId("SVC1403"); + err.setText("MyText %s, %s: The last three digits are usually the HTTP Code"); + err.getVariables().add("Variable 1"); + err.getVariables().add("Variable 2"); + return err; + } + +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/marshal/CertMarshal.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/marshal/CertMarshal.java new file mode 100644 index 00000000..5ceb082f --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/marshal/CertMarshal.java @@ -0,0 +1,65 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.aaf.marshal; + +import javax.xml.datatype.XMLGregorianCalendar; + +import org.onap.aaf.misc.rosetta.marshal.FieldDateTime; +import org.onap.aaf.misc.rosetta.marshal.FieldHexBinary; +import org.onap.aaf.misc.rosetta.marshal.FieldString; +import org.onap.aaf.misc.rosetta.marshal.ObjMarshal; + +import aaf.v2_0.Certs.Cert; + +public class CertMarshal extends ObjMarshal<Cert> { + public CertMarshal() { + add(new FieldHexBinary<Cert>("fingerprint") { + @Override + protected byte[] data(Cert t) { + return t.getFingerprint(); + } + }); + + add(new FieldString<Cert>("id") { + @Override + protected String data(Cert t) { + return t.getId(); + } + }); + + add(new FieldString<Cert>("x500") { + @Override + protected String data(Cert t) { + return t.getX500(); + } + }); + + add(new FieldDateTime<Cert>("expires") { + @Override + protected XMLGregorianCalendar data(Cert t) { + return t.getExpires(); + } + }); + + + } +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/marshal/CertsMarshal.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/marshal/CertsMarshal.java new file mode 100644 index 00000000..c6e28408 --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/marshal/CertsMarshal.java @@ -0,0 +1,44 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.aaf.marshal; + +import java.util.List; + +import org.onap.aaf.misc.rosetta.marshal.ObjArray; +import org.onap.aaf.misc.rosetta.marshal.ObjMarshal; + +import aaf.v2_0.Certs; +import aaf.v2_0.Certs.Cert; + +public class CertsMarshal extends ObjMarshal<Certs> { + + public CertsMarshal() { + add(new ObjArray<Certs,Cert>("cert",new CertMarshal()) { + @Override + protected List<Cert> data(Certs t) { + return t.getCert(); + } + }); + } + + +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFAuthn.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFAuthn.java new file mode 100644 index 00000000..3c970bc2 --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFAuthn.java @@ -0,0 +1,169 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.aaf.v2_0; + +import java.io.IOException; + +import org.onap.aaf.cadi.AbsUserCache; +import org.onap.aaf.cadi.CachedPrincipal; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.User; +import org.onap.aaf.cadi.aaf.AAFPermission; +import org.onap.aaf.cadi.client.Future; +import org.onap.aaf.cadi.client.Rcli; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.lur.ConfigPrincipal; + +public class AAFAuthn<CLIENT> extends AbsUserCache<AAFPermission> { + private AAFCon<CLIENT> con; + private String realm; + + /** + * Configure with Standard AAF properties, Stand alone + * @param con + * @throws Exception .. + */ + // Package on purpose + AAFAuthn(AAFCon<CLIENT> con) throws Exception { + super(con.access,con.cleanInterval,con.highCount,con.usageRefreshTriggerCount); + this.con = con; + } + + /** + * Configure with Standard AAF properties, but share the Cache (with AAF Lur) + * @param con + * @throws Exception + */ + // Package on purpose + AAFAuthn(AAFCon<CLIENT> con, AbsUserCache<AAFPermission> cache) { + super(cache); + this.con = con; + } + + /** + * Return Native Realm of AAF Instance. + * + * @return + */ + public String getRealm() { + return realm; + } + + /** + * Returns null if ok, or an Error String; + * + * Convenience function. Passes "null" for State object + */ + public String validate(String user, String password) throws IOException, CadiException { + return validate(user,password,null); + } + + /** + * Returns null if ok, or an Error String; + * + * For State Object, you may put in HTTPServletRequest or AuthzTrans, if available. Otherwise, + * leave null + * + * @param user + * @param password + * @return + * @throws IOException + * @throws CadiException + * @throws Exception + */ + public String validate(String user, String password, Object state) throws IOException, CadiException { + password = access.decrypt(password, false); + byte[] bytes = password.getBytes(); + User<AAFPermission> usr = getUser(user,bytes); + + if(usr != null && !usr.permExpired()) { + if(usr.principal==null) { + return "User already denied"; + } else { + return null; // good + } + } + + AAFCachedPrincipal cp = new AAFCachedPrincipal(this,con.app, user, bytes, con.cleanInterval); + // Since I've relocated the Validation piece in the Principal, just revalidate, then do Switch + // Statement + switch(cp.revalidate(state)) { + case REVALIDATED: + if(usr!=null) { + usr.principal = cp; + } else { + addUser(new User<AAFPermission>(cp,con.timeout)); + } + return null; + case INACCESSIBLE: + return "AAF Inaccessible"; + case UNVALIDATED: + addUser(new User<AAFPermission>(user,bytes,con.timeout)); + return "User/Pass combo invalid for " + user; + case DENIED: + return "AAF denies API for " + user; + default: + return "AAFAuthn doesn't handle Principal " + user; + } + } + + private class AAFCachedPrincipal extends ConfigPrincipal implements CachedPrincipal { + private long expires,timeToLive; + + public AAFCachedPrincipal(AAFAuthn<?> aaf, String app, String name, byte[] pass, int timeToLive) { + super(name,pass); + this.timeToLive = timeToLive; + expires = timeToLive + System.currentTimeMillis(); + } + + public Resp revalidate(Object state) { + try { + Miss missed = missed(getName(),getCred()); + if(missed==null || missed.mayContinue()) { + Rcli<CLIENT> client = con.client(Config.AAF_DEFAULT_VERSION).forUser(con.basicAuth(getName(), new String(getCred()))); + Future<String> fp = client.read( + "/authn/basicAuth", + "text/plain" + ); + if(fp.get(con.timeout)) { + expires = System.currentTimeMillis() + timeToLive; + addUser(new User<AAFPermission>(this, expires)); + return Resp.REVALIDATED; + } else { + addMiss(getName(), getCred()); + return Resp.UNVALIDATED; + } + } else { + return Resp.UNVALIDATED; + } + } catch (Exception e) { + con.access.log(e); + return Resp.INACCESSIBLE; + } + } + + public long expires() { + return expires; + } + }; + +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFCon.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFCon.java new file mode 100644 index 00000000..70b3e766 --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFCon.java @@ -0,0 +1,371 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.aaf.v2_0; + +import java.net.URI; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.onap.aaf.cadi.AbsUserCache; +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.CadiWrap; +import org.onap.aaf.cadi.Connector; +import org.onap.aaf.cadi.Locator; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.Lur; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.SecuritySetter; +import org.onap.aaf.cadi.aaf.AAFPermission; +import org.onap.aaf.cadi.aaf.marshal.CertsMarshal; +import org.onap.aaf.cadi.client.Future; +import org.onap.aaf.cadi.client.Rcli; +import org.onap.aaf.cadi.client.Retryable; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.config.SecurityInfoC; +import org.onap.aaf.cadi.lur.EpiLur; +import org.onap.aaf.cadi.principal.BasicPrincipal; +import org.onap.aaf.cadi.principal.TaggedPrincipal; +import org.onap.aaf.cadi.util.FQI; +import org.onap.aaf.cadi.util.Vars; +import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.misc.env.Data.TYPE; +import org.onap.aaf.misc.rosetta.env.RosettaDF; +import org.onap.aaf.misc.rosetta.env.RosettaEnv; + +import aaf.v2_0.Certs; +import aaf.v2_0.Error; +import aaf.v2_0.Perms; +import aaf.v2_0.Users; + +public abstract class AAFCon<CLIENT> implements Connector { + final public Access access; + // Package access + final public int timeout, cleanInterval, connTimeout; + final public int highCount, userExpires, usageRefreshTriggerCount; + private Map<String,Rcli<CLIENT>> clients = new ConcurrentHashMap<String,Rcli<CLIENT>>(); + final public RosettaDF<Perms> permsDF; + final public RosettaDF<Certs> certsDF; + final public RosettaDF<Users> usersDF; + final public RosettaDF<Error> errDF; + private String realm; + public final String app; + protected SecuritySetter<CLIENT> ss; + protected SecurityInfoC<CLIENT> si; + + private AAFLurPerm lur; + + final public RosettaEnv env; + protected abstract URI initURI(); + protected abstract void setInitURI(String uriString) throws CadiException; + + /** + * Use this call to get the appropriate client based on configuration (HTTP, future) + * + * @param apiVersion + * @return + * @throws CadiException + */ + public Rcli<CLIENT> client(String apiVersion) throws CadiException { + Rcli<CLIENT> client = clients.get(apiVersion); + if(client==null) { + client = rclient(initURI(),ss); + client.apiVersion(apiVersion) + .readTimeout(connTimeout); + clients.put(apiVersion, client); + } + return client; + } + + public Rcli<CLIENT> client(URI uri) throws CadiException { + return rclient(uri,ss).readTimeout(connTimeout); + } + + /** + * Use this API when you have permission to have your call act as the end client's ID. + * + * Your calls will get 403 errors if you do not have this permission. it is a special setup, rarely given. + * + * @param apiVersion + * @param req + * @return + * @throws CadiException + */ + public Rcli<CLIENT> clientAs(String apiVersion, TaggedPrincipal p) throws CadiException { + Rcli<CLIENT> cl = client(apiVersion); + return cl.forUser(transferSS(p)); + } + + protected AAFCon(AAFCon<CLIENT> copy) { + access = copy.access; + timeout = copy.timeout; + cleanInterval = copy.cleanInterval; + connTimeout = copy.connTimeout; + highCount = copy.highCount; + userExpires = copy.userExpires; + usageRefreshTriggerCount = copy.usageRefreshTriggerCount; + permsDF = copy.permsDF; + certsDF = copy.certsDF; + usersDF = copy.usersDF; + errDF = copy.errDF; + app = copy.app; + ss = copy.ss; + si = copy.si; + env = copy.env; + realm = copy.realm; + } + + protected AAFCon(Access access, String tag, SecurityInfoC<CLIENT> si) throws CadiException{ + if(tag==null) { + throw new CadiException("AAFCon cannot be constructed without a property tag or URL"); + } else { + String str = access.getProperty(tag,null); + if(str==null) { + if(tag.contains("://")) { // assume a URL + str = tag; + } else { + throw new CadiException("A URL or " + tag + " property is required."); + } + } + setInitURI(str); + } + try { + this.access = access; + this.si = si; + this.ss = si.defSS; + if(ss.getID().equals(SecurityInfoC.DEF_ID)) { // it's the Preliminary SS, try to get a better one + String mechid = access.getProperty(Config.AAF_APPID, null); + if(mechid==null) { + mechid=access.getProperty(Config.OAUTH_CLIENT_ID,null); + } + String encpass = access.getProperty(Config.AAF_APPPASS, null); + if(encpass==null) { + encpass = access.getProperty(Config.OAUTH_CLIENT_SECRET,null); + } + if(encpass==null) { + String alias = access.getProperty(Config.CADI_ALIAS, mechid); + if(alias==null) { + access.printf(Access.Level.WARN,"%s, %s or %s required before use.", Config.CADI_ALIAS, Config.AAF_APPID, Config.OAUTH_CLIENT_ID); + set(si.defSS); + } else { + set(si.defSS=x509Alias(alias)); + } + } else { + if(mechid!=null && encpass !=null) { + set(si.defSS=basicAuth(mechid, encpass)); + } else { + set(si.defSS=new SecuritySetter<CLIENT>() { + + @Override + public String getID() { + return ""; + } + + @Override + public void setSecurity(CLIENT client) throws CadiException { + throw new CadiException("AAFCon has not been initialized with Credentials (SecuritySetter)"); + } + + @Override + public int setLastResponse(int respCode) { + return 0; + } + }); + } + } + } + + timeout = Integer.parseInt(access.getProperty(Config.AAF_CALL_TIMEOUT, Config.AAF_CALL_TIMEOUT_DEF)); + cleanInterval = Integer.parseInt(access.getProperty(Config.AAF_CLEAN_INTERVAL, Config.AAF_CLEAN_INTERVAL_DEF)); + highCount = Integer.parseInt(access.getProperty(Config.AAF_HIGH_COUNT, Config.AAF_HIGH_COUNT_DEF).trim()); + connTimeout = Integer.parseInt(access.getProperty(Config.AAF_CONN_TIMEOUT, Config.AAF_CONN_TIMEOUT_DEF).trim()); + userExpires = Integer.parseInt(access.getProperty(Config.AAF_USER_EXPIRES, Config.AAF_USER_EXPIRES_DEF).trim()); + usageRefreshTriggerCount = Integer.parseInt(access.getProperty(Config.AAF_USER_EXPIRES, Config.AAF_USER_EXPIRES_DEF).trim())-1; // zero based + + app=FQI.reverseDomain(ss.getID()); + //TODO Get Realm from AAF + realm="csp.att.com"; + + env = new RosettaEnv(); + permsDF = env.newDataFactory(Perms.class); + usersDF = env.newDataFactory(Users.class); + certsDF = env.newDataFactory(Certs.class); + certsDF.rootMarshal(new CertsMarshal()); // Speedier Marshaling + errDF = env.newDataFactory(Error.class); + } catch (APIException e) { + throw new CadiException("AAFCon cannot be configured",e); + } + } + + public RosettaEnv env() { + return env; + } + + /** + * Return the backing AAFCon, if there is a Lur Setup that is AAF. + * + * If there is no AAFLur setup, it will return "null" + * @param servletRequest + * @return + */ + public static final AAFCon<?> obtain(Object servletRequest) { + if(servletRequest instanceof CadiWrap) { + Lur lur = ((CadiWrap)servletRequest).getLur(); + if(lur != null) { + if(lur instanceof EpiLur) { + AbsAAFLur<?> aal = (AbsAAFLur<?>) ((EpiLur)lur).subLur(AbsAAFLur.class); + if(aal!=null) { + return aal.aaf; + } + } else { + if(lur instanceof AbsAAFLur) { + return ((AbsAAFLur<?>)lur).aaf; + } + } + } + } + return null; + } + + public abstract AAFCon<CLIENT> clone(String url) throws CadiException, LocatorException; + + public AAFAuthn<CLIENT> newAuthn() throws APIException { + try { + return new AAFAuthn<CLIENT>(this); + } catch (APIException e) { + throw e; + } catch (Exception e) { + throw new APIException(e); + } + } + + public AAFAuthn<CLIENT> newAuthn(AbsUserCache<AAFPermission> c) { + return new AAFAuthn<CLIENT>(this,c); + } + + public AAFLurPerm newLur() throws CadiException { + try { + if(lur==null) { + return (lur = new AAFLurPerm(this)); + } else { + return new AAFLurPerm(this,lur); + } + } catch (CadiException e) { + throw e; + } catch (Exception e) { + throw new CadiException(e); + } + } + + public AAFLurPerm newLur(AbsUserCache<AAFPermission> c) throws APIException { + try { + return new AAFLurPerm(this,c); + } catch (APIException e) { + throw e; + } catch (Exception e) { + throw new APIException(e); + } + } + + protected abstract Rcli<CLIENT> rclient(URI uri, SecuritySetter<CLIENT> ss) throws CadiException; + + public abstract Rcli<CLIENT> rclient(Locator<URI> loc, SecuritySetter<CLIENT> ss) throws CadiException; + + public Rcli<CLIENT> client(Locator<URI> locator) throws CadiException { + return rclient(locator,ss); + } + + public abstract<RET> RET best(Retryable<RET> retryable) throws LocatorException, CadiException, APIException; + + public abstract<RET> RET bestForUser(GetSetter get, Retryable<RET> retryable) throws LocatorException, CadiException, APIException; + + public abstract SecuritySetter<CLIENT> basicAuth(String user, String password) throws CadiException; + + public abstract SecuritySetter<CLIENT> transferSS(TaggedPrincipal principal) throws CadiException; + + public abstract SecuritySetter<CLIENT> basicAuthSS(BasicPrincipal principal) throws CadiException; + + public abstract SecuritySetter<CLIENT> tokenSS(final String client_id, final String accessToken) throws CadiException; + + public abstract SecuritySetter<CLIENT> x509Alias(String alias) throws APIException, CadiException; + + + public String getRealm() { + return realm; + + } + + /** + * This interface allows the AAFCon, even though generic, to pass in correctly typed values based on the above SS commands. + * @author Jonathan + * + */ + public interface GetSetter { + public<CLIENT> SecuritySetter<CLIENT> get(AAFCon<CLIENT> con) throws CadiException; + } + + public SecuritySetter<CLIENT> set(final SecuritySetter<CLIENT> ss) { + this.ss = ss; + for(Rcli<CLIENT> client : clients.values()) { + client.setSecuritySetter(ss); + } + return ss; + } + + public SecurityInfoC<CLIENT> securityInfo() { + return si; + } + + public String defID() { + if(ss!=null) { + return ss.getID(); + } + return "unknown"; + } + + public void invalidate() throws CadiException { + for(Rcli<CLIENT> client : clients.values()) { + client.invalidate(); + } + clients.clear(); + } + + public String readableErrMsg(Future<?> f) { + String text = f.body(); + if(text==null || text.length()==0) { + text = f.code() + ": **No Message**"; + } else if(text.contains("%")) { + try { + Error err = errDF.newData().in(TYPE.JSON).load(f.body()).asObject(); + return Vars.convert(err.getText(),err.getVariables()); + } catch (APIException e){ + // just return the body below + } + } + return text; + } + + public static AAFCon<?> newInstance(PropAccess pa) throws APIException, CadiException, LocatorException { + // Potentially add plugin for other kinds of Access + return new AAFConHttp(pa); + } +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFConHttp.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFConHttp.java new file mode 100644 index 00000000..6d54e36f --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFConHttp.java @@ -0,0 +1,229 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.aaf.v2_0; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.Locator; +import org.onap.aaf.cadi.Locator.Item; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.SecuritySetter; +import org.onap.aaf.cadi.client.AbsTransferSS; +import org.onap.aaf.cadi.client.Rcli; +import org.onap.aaf.cadi.client.Retryable; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.config.SecurityInfoC; +import org.onap.aaf.cadi.http.HBasicAuthSS; +import org.onap.aaf.cadi.http.HMangr; +import org.onap.aaf.cadi.http.HRcli; +import org.onap.aaf.cadi.http.HTokenSS; +import org.onap.aaf.cadi.http.HTransferSS; +import org.onap.aaf.cadi.http.HX509SS; +import org.onap.aaf.cadi.principal.BasicPrincipal; +import org.onap.aaf.cadi.principal.TaggedPrincipal; +import org.onap.aaf.misc.env.APIException; + +public class AAFConHttp extends AAFCon<HttpURLConnection> { + private final HMangr hman; + + public AAFConHttp(Access access) throws APIException, CadiException, LocatorException { + super(access,Config.AAF_URL,SecurityInfoC.instance(access, HttpURLConnection.class)); + bestSS(si); + hman = new HMangr(access,Config.loadLocator(si, access.getProperty(Config.AAF_URL,null))); + } + + public static SecuritySetter<HttpURLConnection> bestSS(SecurityInfoC<HttpURLConnection> si) throws APIException, CadiException { + Access access = si.access; + String s; + if((s = access.getProperty(Config.CADI_ALIAS, null))!=null) { + return new HX509SS(s,si,true); + } else if((s = access.getProperty(Config.AAF_APPID, null))!=null){ + try { + return new HBasicAuthSS(si,true); + } catch (IOException /*| GeneralSecurityException*/ e) { + throw new CadiException(e); + } + } + return null; + } + + public AAFConHttp(Access access, String tag) throws APIException, CadiException, LocatorException { + super(access,tag,SecurityInfoC.instance(access, HttpURLConnection.class)); + bestSS(si); + hman = new HMangr(access,Config.loadLocator(si, access.getProperty(tag,tag/*try the content itself*/))); + } + + public AAFConHttp(Access access, String urlTag, SecurityInfoC<HttpURLConnection> si) throws CadiException, APIException, LocatorException { + super(access,urlTag,si); + bestSS(si); + hman = new HMangr(access,Config.loadLocator(si, access.getProperty(urlTag,null))); + } + + public AAFConHttp(Access access, Locator<URI> locator) throws CadiException, LocatorException, APIException { + super(access,Config.AAF_URL,SecurityInfoC.instance(access, HttpURLConnection.class)); + bestSS(si); + hman = new HMangr(access,locator); + } + + public AAFConHttp(Access access, Locator<URI> locator, SecurityInfoC<HttpURLConnection> si) throws CadiException, LocatorException { + super(access,Config.AAF_URL,si); + hman = new HMangr(access,locator); + } + + public AAFConHttp(Access access, Locator<URI> locator, SecurityInfoC<HttpURLConnection> si, String tag) throws CadiException, LocatorException { + super(access,tag,si); + hman = new HMangr(access, locator); + } + + private AAFConHttp(AAFCon<HttpURLConnection> aafcon, String url) throws LocatorException { + super(aafcon); + hman = new HMangr(aafcon.access,Config.loadLocator(si, url)); + } + + @Override + public AAFCon<HttpURLConnection> clone(String url) throws LocatorException { + return new AAFConHttp(this,url); + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.aaf.v2_0.AAFCon#basicAuth(java.lang.String, java.lang.String) + */ + @Override + public SecuritySetter<HttpURLConnection> basicAuth(String user, String password) throws CadiException { + if(password.startsWith("enc:")) { + try { + password = access.decrypt(password, true); + } catch (IOException e) { + throw new CadiException("Error decrypting password",e); + } + } + try { + return new HBasicAuthSS(si,user,password); + } catch (IOException e) { + throw new CadiException("Error creating HBasicAuthSS",e); + } + } + + public SecuritySetter<HttpURLConnection> x509Alias(String alias) throws APIException, CadiException { + try { + return set(new HX509SS(alias,si)); + } catch (Exception e) { + throw new CadiException("Error creating X509SS",e); + } + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.aaf.v2_0.AAFCon#rclient(java.net.URI, org.onap.aaf.cadi.SecuritySetter) + */ + @Override + protected Rcli<HttpURLConnection> rclient(URI ignoredURI, SecuritySetter<HttpURLConnection> ss) throws CadiException { + if(hman.loc==null) { + throw new CadiException("No Locator set in AAFConHttp"); + } + try { + return new HRcli(hman, hman.loc.best() ,ss); + } catch (Exception e) { + throw new CadiException(e); + } + } + + @Override + public Rcli<HttpURLConnection> rclient(Locator<URI> loc, SecuritySetter<HttpURLConnection> ss) throws CadiException { + try { + HMangr newHMan = new HMangr(access, loc); + return new HRcli(newHMan,newHMan.loc.best(),ss); + } catch (Exception e) { + throw new CadiException(e); + } + } + @Override + public AbsTransferSS<HttpURLConnection> transferSS(TaggedPrincipal principal) throws CadiException { + return new HTransferSS(principal, app,si); + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.aaf.v2_0.AAFCon#basicAuthSS(java.security.Principal) + */ + @Override + public SecuritySetter<HttpURLConnection> basicAuthSS(BasicPrincipal principal) throws CadiException { + try { + return new HBasicAuthSS(principal,si); + } catch (IOException e) { + throw new CadiException("Error creating HBasicAuthSS",e); + } + } + + @Override + public SecuritySetter<HttpURLConnection> tokenSS(final String client_id, final String accessToken) throws CadiException { + try { + return new HTokenSS(si, client_id, accessToken); + } catch (IOException e) { + throw new CadiException(e); + } + } + + public HMangr hman() { + return hman; + } + + @Override + public <RET> RET best(Retryable<RET> retryable) throws LocatorException, CadiException, APIException { + return hman.best(ss, (Retryable<RET>)retryable); + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.aaf.v2_0.AAFCon#bestForUser(org.onap.aaf.cadi.SecuritySetter, org.onap.aaf.cadi.client.Retryable) + */ + @Override + public <RET> RET bestForUser(GetSetter getSetter, Retryable<RET> retryable) throws LocatorException, CadiException, APIException { + return hman.best(getSetter.get(this), (Retryable<RET>)retryable); + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.aaf.v2_0.AAFCon#initURI() + */ + @Override + protected URI initURI() { + try { + Item item = hman.loc.best(); + if(item!=null) { + return hman.loc.get(item); + } + } catch (LocatorException e) { + access.log(e, "Error in AAFConHttp obtaining initial URI"); + } + return null; + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.aaf.v2_0.AAFCon#setInitURI(java.lang.String) + */ + @Override + protected void setInitURI(String uriString) throws CadiException { + // Using Locator, not URLString, which is mostly for DME2 + } + +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFLocator.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFLocator.java new file mode 100644 index 00000000..e7e3ef35 --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFLocator.java @@ -0,0 +1,137 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.aaf.v2_0; + +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.SecuritySetter; +import org.onap.aaf.cadi.client.Future; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.config.SecurityInfoC; +import org.onap.aaf.cadi.http.HClient; +import org.onap.aaf.cadi.util.Split; +import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.misc.env.Data.TYPE; +import org.onap.aaf.misc.env.impl.BasicTrans; +import org.onap.aaf.misc.rosetta.env.RosettaDF; +import org.onap.aaf.misc.rosetta.env.RosettaEnv; + +import locate.v1_0.Endpoint; +import locate.v1_0.Endpoints; + +public class AAFLocator extends AbsAAFLocator<BasicTrans> { + private static RosettaEnv env; + HClient client; + private RosettaDF<Endpoints> epsDF; + + public AAFLocator(SecurityInfoC<HttpURLConnection> si, URI locatorURI) throws LocatorException { + super(si.access, nameFromLocatorURI(locatorURI), 10000L /* Wait at least 10 seconds between refreshes */); + SecuritySetter<HttpURLConnection> ss; + try { + ss=AAFConHttp.bestSS(si); + } catch (APIException | CadiException e1) { + throw new LocatorException(e1); + } + synchronized(sr) { + if(env==null) { + env = new RosettaEnv(access.getProperties()); + } + } + + int connectTimeout = Integer.parseInt(si.access.getProperty(Config.AAF_CONN_TIMEOUT, Config.AAF_CONN_TIMEOUT_DEF)); + try { + String[] path = Split.split('/',locatorURI.getPath()); + if(path.length>2 && "locate".equals(path[1])) { + StringBuilder sb = new StringBuilder(); + for(int i=3;i<path.length;++i) { + sb.append('/'); + sb.append(path[i]); + } + setPathInfo(sb.toString()); + String host = locatorURI.getHost(); + if(aaf_locator_host!=null && (host==null || "AAF_LOCATOR_URL".equals(host))) { + int slash = aaf_locator_host.lastIndexOf("//"); + host = aaf_locator_host.substring(slash+2); + } + URI uri = new URI( + locatorURI.getScheme(), + locatorURI.getUserInfo(), + host, + locatorURI.getPort(), + "/locate/"+name + '/' + version, + null, + null + ); + client = createClient(ss, uri, connectTimeout); + } else { + client = new HClient(ss, locatorURI, connectTimeout); + } + epsDF = env.newDataFactory(Endpoints.class); + refresh(); + } catch (APIException | URISyntaxException e) { + throw new LocatorException(e); + } + } + + @Override + public boolean refresh() { + try { + client.setMethod("GET"); + client.send(); + Future<Endpoints> fr = client.futureRead(epsDF, TYPE.JSON); + if(fr.get(client.timeout())) { + List<EP> epl = new LinkedList<EP>(); + for(Endpoint endpoint : fr.value.getEndpoint()) { + epl.add(new EP(endpoint,latitude,longitude)); + } + + Collections.sort(epl); + replace(epl); + return true; + } else { + env.error().printf("Error reading location information from %s: %d %s\n",client.getURI().toString(),fr.code(),fr.body()); + } + } catch (CadiException | URISyntaxException | APIException e) { + env.error().log(e,"Error connecting " + client.getURI() + " for location."); + } + return false; + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.aaf.v2_0.AbsAAFLocator#getURI() + */ + @Override + protected URI getURI() { + return client.getURI(); + } + + protected HClient createClient(SecuritySetter<HttpURLConnection> ss, URI uri, int connectTimeout) throws LocatorException { + return new HClient(ss, uri, connectTimeout); + } +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFLurPerm.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFLurPerm.java new file mode 100644 index 00000000..84d23655 --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFLurPerm.java @@ -0,0 +1,245 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.aaf.v2_0; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.net.ConnectException; +import java.net.URISyntaxException; +import java.security.Principal; +import java.util.Map; + +import org.onap.aaf.cadi.AbsUserCache; +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.Lur; +import org.onap.aaf.cadi.Permission; +import org.onap.aaf.cadi.User; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.CachedPrincipal.Resp; +import org.onap.aaf.cadi.aaf.AAFPermission; +import org.onap.aaf.cadi.client.Future; +import org.onap.aaf.cadi.client.Rcli; +import org.onap.aaf.cadi.client.Retryable; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.lur.LocalPermission; +import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.misc.env.util.Split; + +import aaf.v2_0.Perm; +import aaf.v2_0.Perms; + +/** + * Use AAF Service as Permission Service. + * + * This Lur goes after AAF Permissions, which are elements of Roles, not the Roles themselves. + * + * If you want a simple Role Lur, use AAFRoleLur + * + * @author Jonathan + * + */ +public class AAFLurPerm extends AbsAAFLur<AAFPermission> { + private static final String ORG_OSAAF_CADI_OAUTH_O_AUTH2_LUR = "org.osaaf.cadi.oauth.OAuth2Lur"; + + /** + * Need to be able to transmutate a Principal into either ATTUID or MechID, which are the only ones accepted at this + * point by AAF. There is no "domain", aka, no "@att.com" in "ab1234@att.com". + * + * The only thing that matters here for AAF is that we don't waste calls with IDs that obviously aren't valid. + * Thus, we validate that the ID portion follows the rules before we waste time accessing AAF remotely + * @throws APIException + * @throws URISyntaxException + * @throws DME2Exception + */ + // Package on purpose + AAFLurPerm(AAFCon<?> con) throws CadiException, APIException { + super(con); + attachOAuth2(con); + } + + // Package on purpose + AAFLurPerm(AAFCon<?> con, AbsUserCache<AAFPermission> auc) throws APIException { + super(con,auc); + attachOAuth2(con); + } + + private void attachOAuth2(AAFCon<?> con) throws APIException { + String oauth2_url; + Class<?> tmcls = Config.loadClass(access,"org.osaaf.cadi.oauth.TokenMgr"); + if(tmcls!=null) { + if((oauth2_url = con.access.getProperty(Config.CADI_OAUTH2_URL,null))!=null) { + try { + Constructor<?> tmconst = tmcls.getConstructor(AAFCon.class,String.class); + Object tokMangr = tmconst.newInstance(con,oauth2_url); + @SuppressWarnings("unchecked") + Class<Lur> oa2cls = (Class<Lur>)Config.loadClass(access,ORG_OSAAF_CADI_OAUTH_O_AUTH2_LUR); + Constructor<Lur> oa2const = oa2cls.getConstructor(tmcls); + Lur oa2 = oa2const.newInstance(tokMangr); + setPreemptiveLur(oa2); + } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + throw new APIException(e); + } + } else { + access.log(Level.INIT, "Both cadi-oauth jar and Property",Config.CADI_OAUTH2_URL,"is required to initialize OAuth2"); + } + } + } + + protected User<AAFPermission> loadUser(final Principal principal) { + final String name = principal.getName(); +// // Note: The rules for AAF is that it only stores permissions for ATTUID and MechIDs, which don't +// // have domains. We are going to make the Transitive Class (see this.transmutative) to convert +// final Principal tp = principal; //transmutate.mutate(principal); +// if(tp==null) { +// return null; // if not a valid Transmutated credential, don't bother calling... +// } +// TODO Create a dynamic way to declare domains supported. + final long start = System.nanoTime(); + final boolean[] success = new boolean[]{false}; + +// new Exception("loadUser").printStackTrace(); + try { + return aaf.best(new Retryable<User<AAFPermission>>() { + @Override + public User<AAFPermission> code(Rcli<?> client) throws CadiException, ConnectException, APIException { + Future<Perms> fp = client.read("/authz/perms/user/"+name,aaf.permsDF); + + // In the meantime, lookup User, create if necessary + User<AAFPermission> user = getUser(principal); + Principal p; + if(user!=null && user.principal == null) { + p = new Principal() {// Create a holder for lookups + private String n = name; + public String getName() { + return n; + } + }; + } else { + p = principal; + } + + if(user==null) { + addUser(user = new User<AAFPermission>(p,aaf.userExpires)); // no password + } + + // OK, done all we can, now get content + if(fp.get(aaf.timeout)) { + success[0]=true; + Map<String, Permission> newMap = user.newMap(); + boolean willLog = aaf.access.willLog(Level.DEBUG); + for(Perm perm : fp.value.getPerm()) { + user.add(newMap,new AAFPermission(perm.getType(),perm.getInstance(),perm.getAction(),perm.getRoles())); + if(willLog) { + aaf.access.log(Level.DEBUG, name,"has '",perm.getType(),'|',perm.getInstance(),'|',perm.getAction(),'\''); + } + } + user.setMap(newMap); + } else { + int code; + switch(code=fp.code()) { + case 401: + aaf.access.log(Access.Level.ERROR, code, "Unauthorized to make AAF calls"); + break; + case 404: + user.setNoPerms(); + break; + default: + aaf.access.log(Access.Level.ERROR, code, fp.body()); + } + } + + return user; + } + }); + } catch (Exception e) { + aaf.access.log(e,"Calling","/authz/perms/user/"+name); + success[0]=false; + return null; + } finally { + float time = (System.nanoTime()-start)/1000000f; + aaf.access.log(Level.INFO, success[0]?"Loaded":"Load Failure",name,"from AAF in",time,"ms"); + } + } + + public Resp reload(User<AAFPermission> user) { + final String name = user.name; + long start = System.nanoTime(); + boolean success = false; + try { + Future<Perms> fp = aaf.client(Config.AAF_DEFAULT_VERSION).read( + "/authz/perms/user/"+name, + aaf.permsDF + ); + + // OK, done all we can, now get content + if(fp.get(aaf.timeout)) { + success = true; + Map<String,Permission> newMap = user.newMap(); + boolean willLog = aaf.access.willLog(Level.DEBUG); + for(Perm perm : fp.value.getPerm()) { + user.add(newMap, new AAFPermission(perm.getType(),perm.getInstance(),perm.getAction(),perm.getRoles())); + if(willLog) { + aaf.access.log(Level.DEBUG, name,"has",perm.getType(),perm.getInstance(),perm.getAction()); + } + } + user.renewPerm(); + return Resp.REVALIDATED; + } else { + int code; + switch(code=fp.code()) { + case 401: + aaf.access.log(Access.Level.ERROR, code, "Unauthorized to make AAF calls"); + break; + default: + aaf.access.log(Access.Level.ERROR, code, fp.body()); + } + return Resp.UNVALIDATED; + } + } catch (Exception e) { + aaf.access.log(e,"Calling","/authz/perms/user/"+name); + return Resp.INACCESSIBLE; + } finally { + float time = (System.nanoTime()-start)/1000000f; + aaf.access.log(Level.AUDIT, success?"Reloaded":"Reload Failure",name,"from AAF in",time,"ms"); + } + } + + @Override + protected boolean isCorrectPermType(Permission pond) { + return pond instanceof AAFPermission; + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.Lur#createPerm(java.lang.String) + */ + @Override + public Permission createPerm(String p) { + String[] params = Split.split('|', p); + if(params.length==3) { + return new AAFPermission(params[0],params[1],params[2]); + } else { + return new LocalPermission(p); + } + } + +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFTaf.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFTaf.java new file mode 100644 index 00000000..42f3ec4d --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFTaf.java @@ -0,0 +1,203 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.aaf.v2_0; + +import java.io.IOException; +import java.net.ConnectException; +import java.security.Principal; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.onap.aaf.cadi.AbsUserCache; +import org.onap.aaf.cadi.CachedPrincipal; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.Connector; +import org.onap.aaf.cadi.GetCred; +import org.onap.aaf.cadi.Hash; +import org.onap.aaf.cadi.SecuritySetter; +import org.onap.aaf.cadi.User; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.CachedPrincipal.Resp; +import org.onap.aaf.cadi.Taf.LifeForm; +import org.onap.aaf.cadi.aaf.AAFPermission; +import org.onap.aaf.cadi.aaf.v2_0.AAFCon.GetSetter; +import org.onap.aaf.cadi.client.Future; +import org.onap.aaf.cadi.client.Rcli; +import org.onap.aaf.cadi.client.Retryable; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.principal.BasicPrincipal; +import org.onap.aaf.cadi.principal.CachedBasicPrincipal; +import org.onap.aaf.cadi.taf.HttpTaf; +import org.onap.aaf.cadi.taf.TafResp; +import org.onap.aaf.cadi.taf.TafResp.RESP; +import org.onap.aaf.cadi.taf.basic.BasicHttpTafResp; +import org.onap.aaf.misc.env.APIException; + +public class AAFTaf<CLIENT> extends AbsUserCache<AAFPermission> implements HttpTaf { +// private static final String INVALID_AUTH_TOKEN = "Invalid Auth Token"; +// private static final String AUTHENTICATING_SERVICE_UNAVAILABLE = "Authenticating Service unavailable"; + private AAFCon<CLIENT> aaf; + private boolean warn; + + public AAFTaf(AAFCon<CLIENT> con, boolean turnOnWarning) { + super(con.access,con.cleanInterval,con.highCount, con.usageRefreshTriggerCount); + aaf = con; + warn = turnOnWarning; + } + + public AAFTaf(AAFCon<CLIENT> con, boolean turnOnWarning, AbsUserCache<AAFPermission> other) { + super(other); + aaf = (AAFCon<CLIENT>)con; + warn = turnOnWarning; + } + + // Note: Needed for Creation of this Object with Generics + @SuppressWarnings("unchecked") + public AAFTaf(Connector mustBeAAFCon, boolean turnOnWarning, AbsUserCache<AAFPermission> other) throws CadiException { + this((AAFCon<CLIENT>)mustBeAAFCon,turnOnWarning,other); + } + + // Note: Needed for Creation of this Object with Generics + @SuppressWarnings("unchecked") + public AAFTaf(Connector mustBeAAFCon, boolean turnOnWarning) throws CadiException { + this((AAFCon<CLIENT>)mustBeAAFCon,turnOnWarning); + } + + + public TafResp validate(final LifeForm reading, final HttpServletRequest req, final HttpServletResponse resp) { + //TODO Do we allow just anybody to validate? + + // Note: Either Carbon or Silicon based LifeForms ok + String authz = req.getHeader("Authorization"); + if(authz != null && authz.startsWith("Basic ")) { + if(warn&&!req.isSecure())aaf.access.log(Level.WARN,"WARNING! BasicAuth has been used over an insecure channel"); + try { + final CachedBasicPrincipal bp; + if(req.getUserPrincipal() instanceof CachedBasicPrincipal) { + bp = (CachedBasicPrincipal)req.getUserPrincipal(); + } else { + bp = new CachedBasicPrincipal(this,authz,aaf.getRealm(),aaf.userExpires); + } + // First try Cache + final User<AAFPermission> usr = getUser(bp); + if(usr != null && usr.principal != null) { + if(usr.principal instanceof GetCred) { + if(Hash.isEqual(bp.getCred(),((GetCred)usr.principal).getCred())) { + return new BasicHttpTafResp(aaf.access,bp,bp.getName()+" authenticated by cached AAF password",RESP.IS_AUTHENTICATED,resp,aaf.getRealm(),false); + } + } + } + + Miss miss = missed(bp.getName(), bp.getCred()); + if(miss!=null && !miss.mayContinue()) { + return new BasicHttpTafResp(aaf.access,null,buildMsg(bp,req, + "User/Pass Retry limit exceeded"), + RESP.TRY_AUTHENTICATING,resp,aaf.getRealm(),true); + } + + return aaf.bestForUser( + new GetSetter() { + @Override + public <CL> SecuritySetter<CL> get(AAFCon<CL> con) throws CadiException { + return con.basicAuthSS(bp); + } + },new Retryable<BasicHttpTafResp>() { + @Override + public BasicHttpTafResp code(Rcli<?> client) throws CadiException, ConnectException, APIException { + Future<String> fp = client.read("/authn/basicAuth", "text/plain"); + if(fp.get(aaf.timeout)) { + if(usr!=null) { + usr.principal = bp; + } else { + addUser(new User<AAFPermission>(bp,aaf.userExpires)); + } + return new BasicHttpTafResp(aaf.access,bp,bp.getName()+" authenticated by AAF password",RESP.IS_AUTHENTICATED,resp,aaf.getRealm(),false); + } else { + // Note: AddMiss checks for miss==null, and is part of logic + boolean rv= addMiss(bp.getName(),bp.getCred()); + if(rv) { + return new BasicHttpTafResp(aaf.access,null,buildMsg(bp,req, + "user/pass combo invalid via AAF from " + req.getRemoteAddr()), + RESP.TRY_AUTHENTICATING,resp,aaf.getRealm(),true); + } else { + return new BasicHttpTafResp(aaf.access,null,buildMsg(bp,req, + "user/pass combo invalid via AAF from " + req.getRemoteAddr() + " - Retry limit exceeded"), + RESP.FAIL,resp,aaf.getRealm(),true); + } + } + } + } + ); + } catch (IOException e) { + String msg = buildMsg(null,req,"Invalid Auth Token"); + aaf.access.log(Level.WARN,msg,'(', e.getMessage(), ')'); + return new BasicHttpTafResp(aaf.access,null,msg, RESP.TRY_AUTHENTICATING, resp, aaf.getRealm(),true); + } catch (Exception e) { + String msg = buildMsg(null,req,"Authenticating Service unavailable"); + try { + aaf.invalidate(); + } catch (CadiException e1) { + aaf.access.log(e1, "Error Invalidating Client"); + } + aaf.access.log(Level.WARN,msg,'(', e.getMessage(), ')'); + return new BasicHttpTafResp(aaf.access,null,msg, RESP.FAIL, resp, aaf.getRealm(),false); + } + } + return new BasicHttpTafResp(aaf.access,null,"Requesting HTTP Basic Authorization",RESP.TRY_AUTHENTICATING,resp,aaf.getRealm(),false); + } + + public String buildMsg(Principal pr, HttpServletRequest req, Object ... msg) { + StringBuilder sb = new StringBuilder(); + for(Object s : msg) { + sb.append(s.toString()); + } + if(pr!=null) { + sb.append(" for "); + sb.append(pr.getName()); + } + sb.append(" from "); + sb.append(req.getRemoteAddr()); + sb.append(':'); + sb.append(req.getRemotePort()); + return sb.toString(); + } + + + + public Resp revalidate(CachedPrincipal prin, Object state) { + // !!!! TEST THIS.. Things may not be revalidated, if not BasicPrincipal + if(prin instanceof BasicPrincipal) { + Future<String> fp; + try { + Rcli<CLIENT> userAAF = aaf.client(Config.AAF_DEFAULT_VERSION).forUser(aaf.transferSS((BasicPrincipal)prin)); + fp = userAAF.read("/authn/basicAuth", "text/plain"); + return fp.get(aaf.timeout)?Resp.REVALIDATED:Resp.UNVALIDATED; + } catch (Exception e) { + aaf.access.log(e, "Cannot Revalidate",prin.getName()); + return Resp.INACCESSIBLE; + } + } + return Resp.NOT_MINE; + } + +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFTrustChecker.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFTrustChecker.java new file mode 100644 index 00000000..2094948a --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AAFTrustChecker.java @@ -0,0 +1,116 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.aaf.v2_0; + +import javax.servlet.http.HttpServletRequest ; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.Lur; +import org.onap.aaf.cadi.TrustChecker; +import org.onap.aaf.cadi.aaf.AAFPermission; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.principal.TrustPrincipal; +import org.onap.aaf.cadi.taf.TafResp; +import org.onap.aaf.cadi.taf.TrustNotTafResp; +import org.onap.aaf.cadi.taf.TrustTafResp; +import org.onap.aaf.misc.env.Env; +import org.onap.aaf.misc.env.util.Split; + +public class AAFTrustChecker implements TrustChecker { + private final String tag, id; + private final AAFPermission perm; + private Lur lur; + + /** + * + * Instance will be replaced by Identity + * @param lur + * + * @param tag + * @param perm + */ + public AAFTrustChecker(final Env env) { + tag = env.getProperty(Config.CADI_USER_CHAIN_TAG, Config.CADI_USER_CHAIN); + id = env.getProperty(Config.CADI_ALIAS,env.getProperty(Config.AAF_APPID)); // share between components + String str = env.getProperty(Config.CADI_TRUST_PERM); + AAFPermission temp=null; + if(str!=null) { + String[] sp = Split.splitTrim('|', str); + if(sp.length==3) { + temp = new AAFPermission(sp[0],sp[1],sp[2]); + } + } + perm=temp; + } + + public AAFTrustChecker(final Access access) { + tag = access.getProperty(Config.CADI_USER_CHAIN_TAG, Config.CADI_USER_CHAIN); + id = access.getProperty(Config.CADI_ALIAS,access.getProperty(Config.AAF_APPID,null)); // share between components + String str = access.getProperty(Config.CADI_TRUST_PERM,null); + AAFPermission temp=null; + if(str!=null) { + String[] sp = Split.splitTrim('|', str); + if(sp.length==3) { + temp = new AAFPermission(sp[0],sp[1],sp[2]); + } + } + perm=temp; + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.TrustChecker#setLur(org.onap.aaf.cadi.Lur) + */ + @Override + public void setLur(Lur lur) { + this.lur = lur; + } + + @Override + public TafResp mayTrust(TafResp tresp, HttpServletRequest req) { + String user_info = req.getHeader(tag); + if (user_info == null) { + return tresp; + } + + String[] info = Split.split(',', user_info); + String[] flds = Split.splitTrim(':', info[0]); + if (flds.length < 4) { + return tresp; + } + if (!("AS".equals(flds[3]))) { // is it set for "AS" + return tresp; + } + + String principalName = tresp.getPrincipal().getName(); + if(principalName.equals(id) // We do trust our own App Components: if a trust entry is made with self, always accept + || lur.fish(tresp.getPrincipal(), perm)) { // Have Perm set by Config.CADI_TRUST_PERM + String desc = " " + flds[0] + " validated using " + flds[2] + " by " + flds[1] + ','; + return new TrustTafResp(tresp, new TrustPrincipal(tresp.getPrincipal(), flds[0]), desc); + } else if(principalName.equals(flds[0])) { // Ignore if same identity + return tresp; + } else { + String desc = tresp.getPrincipal().getName() + " requested trust as " + flds[0] + ", but does not have Authorization"; + return new TrustNotTafResp(tresp, desc); + } + } + +}
\ No newline at end of file diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AbsAAFLocator.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AbsAAFLocator.java new file mode 100644 index 00000000..fc297606 --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AbsAAFLocator.java @@ -0,0 +1,486 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.aaf.v2_0; + +import java.net.URI; +import java.net.URISyntaxException; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.NoSuchElementException; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.Locator; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.locator.PropertyLocator; +import org.onap.aaf.cadi.routing.GreatCircle; +import org.onap.aaf.misc.env.Trans; +import org.onap.aaf.misc.env.util.Split; + +import locate.v1_0.Endpoint; + +public abstract class AbsAAFLocator<TRANS extends Trans> implements Locator<URI> { + protected static final SecureRandom sr = new SecureRandom(); + private static LocatorCreator locatorCreator; + protected final Access access; + + protected final double latitude; + protected final double longitude; + protected List<EP> epList; + protected final String name, version; + private String pathInfo = null; + private String query = null; + private String fragment = null; + private boolean additional = false; + protected String myhostname; + protected int myport; + protected final String aaf_locator_host; + private long earliest; + private final long refreshWait; + + + public AbsAAFLocator(Access access, String name, final long refreshMin) throws LocatorException { + aaf_locator_host = access.getProperty(Config.AAF_LOCATE_URL, null); + + epList = new LinkedList<EP>(); + refreshWait = refreshMin; + + this.access = access; + String lat = access.getProperty(Config.CADI_LATITUDE,null); + String lng = access.getProperty(Config.CADI_LONGITUDE,null); + if(lat==null || lng==null) { + throw new LocatorException(Config.CADI_LATITUDE + " and " + Config.CADI_LONGITUDE + " properties are required."); + } else { + latitude = Double.parseDouble(lat); + longitude = Double.parseDouble(lng); + } + if(name.startsWith("http")) { // simple URL + this.name = name; + this.version = Config.AAF_DEFAULT_VERSION; + } else { + String[] split = Split.split(':', name); + this.name = split[0]; + this.version = (split.length > 1) ? split[1] : Config.AAF_DEFAULT_VERSION; + } + + } + + /** + * This is the way to setup specialized AAFLocators ahead of time. + * @param preload + */ + public static void setCreator(LocatorCreator lc) { + locatorCreator = lc; + } + + public static Locator<URI> create(String key) throws LocatorException { + String name = null; + String version = Config.AAF_DEFAULT_VERSION; + String pathInfo = null; + int prev = key.indexOf("/locate"); + if(prev>0) { + prev = key.indexOf('/',prev+6); + if(prev>0) { + int next = key.indexOf('/',++prev); + if(next>0) { + name = key.substring(prev, next); + pathInfo=key.substring(next); + } else { + name = key.substring(prev); + } + String[] split = Split.split(':', name); + switch(split.length) { + case 3: + case 2: + version = split[1]; + name = split[0]; + break; + } + } + } + + if(key.startsWith("http")) { + if(name!=null) { + if(locatorCreator != null) { + AbsAAFLocator<?> aal = locatorCreator.create(name, version); + if(pathInfo!=null) { + aal.setPathInfo(pathInfo); + } + return aal; + } + } else { + return new PropertyLocator(key); + } + } + return null; + } + + public static Locator<URI> create(final String name, final String version) throws LocatorException { + return locatorCreator.create(name, version); + } + + public interface LocatorCreator { + public AbsAAFLocator<?> create(String key, String version) throws LocatorException; + public void setSelf(String hostname, int port); + } + + protected static String nameFromLocatorURI(URI locatorURI) { + String[] path = Split.split('/', locatorURI.getPath()); + if(path.length>2 && "locate".equals(path[1])) { + return path[2]; + } else { + return locatorURI.toString(); + } + } + + /** + * Setting "self" excludes this service from the list. Critical for contacting peers. + */ + public void setSelf(final String hostname, final int port) { + myhostname=hostname; + myport=port; + } + + + public static void setCreatorSelf(final String hostname, final int port) { + if(locatorCreator!=null) { + locatorCreator.setSelf(hostname,port); + } + } + + protected final synchronized void replace(List<EP> list) { + epList = list; + } + + /** + * Call _refresh as needed during calls, but actual refresh will not occur if there + * are existing entities or if it has been called in the last 10 (settable) seconds. + * Timed Refreshes happen by Scheduled Thread + */ + private final boolean _refresh() { + boolean rv = false; + long now=System.currentTimeMillis(); + if(noEntries()) { + if(earliest<now) { + synchronized(epList) { + rv = refresh(); + earliest = now + refreshWait; // call only up to 10 seconds. + } + } else { + access.log(Level.ERROR, "Must wait at least " + refreshWait/1000 + " seconds for Locator Refresh"); + } + } + return rv; + } + + private boolean noEntries() { + return epList.size()<=0; + } + + @Override + public URI get(Item item) throws LocatorException { + if(item==null) { + return null; + } else if(item instanceof AAFLItem) { + return getURI(((AAFLItem)item).uri); + } else { + throw new LocatorException(item.getClass().getName() + " does not belong to AAFLocator"); + } + } + + @Override + public boolean hasItems() { + boolean isEmpty = epList.isEmpty(); + if(!isEmpty) { + for(Iterator<EP> iter = epList.iterator(); iter.hasNext(); ) { + EP ep = iter.next(); + if(ep.valid) { + return true; + } + } + isEmpty = true; + } + if(_refresh()) { // is refreshed... check again + isEmpty = epList.isEmpty(); + } + return !isEmpty; + } + + @Override + public void invalidate(Item item) throws LocatorException { + if(item!=null) { + if(item instanceof AAFLItem) { + AAFLItem ali =(AAFLItem)item; + EP ep = ali.ep; + synchronized(epList) { + epList.remove(ep); + } + ep.invalid(); + ali.iter = getIterator(); // for next guy... fresh iterator + } else { + throw new LocatorException(item.getClass().getName() + " does not belong to AAFLocator"); + } + } + } + + @Override + public Item best() throws LocatorException { + if(!hasItems()) { + throw new LocatorException("No Entries found" + (pathInfo==null?"":(" for " + pathInfo))); + } + List<EP> lep = new ArrayList<EP>(); + EP first = null; + // Note: Deque is sorted on the way by closest distance + Iterator<EP> iter = getIterator(); + EP ep; + while(iter.hasNext()) { + ep = iter.next(); + if(ep.valid) { + if(first==null) { + first = ep; + lep.add(first); + } else { + if(Math.abs(ep.distance-first.distance)<.1) { // allow for nearby/precision issues. + lep.add(ep); + } else { + break; + } + } + } + } + switch(lep.size()) { + case 0: + return null; + case 1: + return new AAFLItem(iter,first); + default: + int rand = sr.nextInt(); // Sonar chokes without. + int i = Math.abs(rand)%lep.size(); + if(i<0) { + return null; + } else { + return new AAFLItem(iter,lep.get(i)); + } + + } + } + + private Iterator<EP> getIterator() { + Object[] epa = epList.toArray(); + if(epa.length==0) { + _refresh(); + epa = epList.toArray(); + } + return new EPIterator(epa, epList); + } + + public class EPIterator implements Iterator<EP> { + private final Object[] epa; + private final List<EP> epList; + private int idx; + + public EPIterator(Object[] epa, List<EP> epList) { + this.epa = epa; + this.epList = epList; + idx = epa.length>0?0:-1; + } + + @Override + public boolean hasNext() { + if(idx<0) { + return false; + } else { + Object obj; + while(idx<epa.length) { + if((obj=epa[idx])==null || !((EP)obj).valid) { + ++idx; + continue; + } + break; + } + return idx<epa.length; + } + } + + @Override + public EP next() { + if(!hasNext() ) { + throw new NoSuchElementException(); + } + return (EP)epa[idx++]; + } + + @Override + public void remove() { + if(idx>=0 && idx<epa.length) { + synchronized(epList) { + epList.remove(epa[idx]); + } + } + } + } + + @Override + public Item first() { + Iterator<EP> iter = getIterator(); + EP ep = AAFLItem.next(iter); + if(ep==null) { + return null; + } + return new AAFLItem(iter,ep); + } + + @Override + public Item next(Item prev) throws LocatorException { + if(prev==null) { + StringBuilder sb = new StringBuilder("Locator Item passed in next(item) is null."); + int lines = 0; + for(StackTraceElement st : Thread.currentThread().getStackTrace()) { + sb.append("\n\t"); + sb.append(st.toString()); + if(++lines > 5) { + sb.append("\n\t..."); + break; + } + } + access.log(Level.ERROR, sb); + } else { + if(prev instanceof AAFLItem) { + AAFLItem ali = (AAFLItem)prev; + EP ep = AAFLItem.next(ali.iter); + if(ep!=null) { + return new AAFLItem(ali.iter,ep); + } + } else { + throw new LocatorException(prev.getClass().getName() + " does not belong to AAFLocator"); + } + } + return null; + } + + protected static class AAFLItem implements Item { + private Iterator<EP> iter; + private URI uri; + private EP ep; + + public AAFLItem(Iterator<EP> iter, EP ep) { + this.iter = iter; + this.ep = ep; + uri = ep.uri; + } + + private static EP next(Iterator<EP> iter) { + EP ep=null; + while(iter.hasNext() && (ep==null || !ep.valid)) { + ep = iter.next(); + } + return ep; + } + + public String toString() { + return ep==null?"Locator Item Invalid":ep.toString(); + } + } + + protected static class EP implements Comparable<EP> { + public URI uri; + public final double distance; + private boolean valid; + + public EP(final Endpoint ep, double latitude, double longitude) throws URISyntaxException { + uri = new URI(ep.getProtocol(),null,ep.getHostname(),ep.getPort(),null,null,null); + distance = GreatCircle.calc(latitude, longitude, ep.getLatitude(), ep.getLongitude()); + valid = true; + } + + public void invalid() { + valid = false; + } + + @Override + public int compareTo(EP o) { + if(distance<o.distance) { + return -1; + } else if(distance>o.distance) { + return 1; + } else { + return 0; + } + } + + @Override + public String toString() { + return distance + ": " + uri + (valid?" valid":" invalidate"); + } + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.Locator#destroy() + */ + @Override + public void destroy() { + // Nothing to do + } + + @Override + public String toString() { + return "AAFLocator for " + name + " on " + getURI(); + } + + public AbsAAFLocator<TRANS> setPathInfo(String pathInfo) { + this.pathInfo = pathInfo; + additional=true; + return this; + } + + public AbsAAFLocator<TRANS> setQuery(String query) { + this.query = query; + additional=true; + return this; + } + + public AbsAAFLocator<TRANS> setFragment(String fragment) { + this.fragment = fragment; + additional=true; + return this; + } + + // Core URI, for reporting purposes + protected abstract URI getURI(); + + protected URI getURI(URI rv) throws LocatorException { + if(additional) { + try { + return new URI(rv.getScheme(),rv.getUserInfo(),rv.getHost(),rv.getPort(),pathInfo,query,fragment); + } catch (URISyntaxException e) { + throw new LocatorException("Error copying URL"); + } + } + return rv; + } + + +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AbsAAFLur.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AbsAAFLur.java new file mode 100644 index 00000000..083537a8 --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/aaf/v2_0/AbsAAFLur.java @@ -0,0 +1,289 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.aaf.v2_0; + +import java.security.Principal; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.onap.aaf.cadi.AbsUserCache; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.CachingLur; +import org.onap.aaf.cadi.Lur; +import org.onap.aaf.cadi.Permission; +import org.onap.aaf.cadi.User; +import org.onap.aaf.cadi.aaf.AAFPermission; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.misc.env.util.Split; + +public abstract class AbsAAFLur<PERM extends Permission> extends AbsUserCache<PERM> implements CachingLur<PERM> { + protected static final byte[] BLANK_PASSWORD = new byte[0]; + private String[] debug = null; + public AAFCon<?> aaf; + public Lur preemptiveLur=null; // Initial Use is for OAuth2, preemptive Lur + private String[] supports; + + public AbsAAFLur(AAFCon<?> con) throws APIException { + super(con.access, con.cleanInterval, con.highCount, con.usageRefreshTriggerCount); + aaf = con; + setLur(this); + supports = con.access.getProperty(Config.AAF_DOMAIN_SUPPORT, Config.AAF_DOMAIN_SUPPORT_DEF).split("\\s*:\\s*"); + } + + public AbsAAFLur(AAFCon<?> con, AbsUserCache<PERM> auc) throws APIException { + super(auc); + aaf = con; + setLur(this); + supports = con.access.getProperty(Config.AAF_DOMAIN_SUPPORT, Config.AAF_DOMAIN_SUPPORT_DEF).split("\\s*:\\s*"); + } + + @Override + public void setDebug(String ids) { + this.debug = ids==null?null:Split.split(',', ids); + } + + public void setPreemptiveLur(Lur preemptive) { + this.preemptiveLur = preemptive; + } + + protected abstract User<PERM> loadUser(Principal bait); + + @Override + public final boolean handles(Principal principal) { + if(preemptiveLur!=null) { + if(preemptiveLur.handles(principal)) { + return true; + } + } + String userName=principal.getName(); + if(userName!=null) { + for(String s : supports) { + if(userName.endsWith(s)) + return true; + } + } + return false; + } + + + protected abstract boolean isCorrectPermType(Permission pond); + + // This is where you build AAF CLient Code. Answer the question "Is principal "bait" in the "pond" + public boolean fish(Principal bait, Permission pond) { + if(preemptiveLur!=null && preemptiveLur.handles(bait)) { + return preemptiveLur.fish(bait, pond); + } else { + if(pond==null) { + return false; + } + if(isDebug(bait)) { + boolean rv = false; + StringBuilder sb = new StringBuilder("Log for "); + sb.append(bait); + if(handles(bait)) { + User<PERM> user = getUser(bait); + if(user==null) { + sb.append("\n\tUser is not in Cache"); + } else { + if(user.noPerms()) { + sb.append("\n\tUser has no Perms"); + } + if(user.permExpired()) { + sb.append("\n\tUser's perm expired ["); + sb.append(new Date(user.permExpires())); + sb.append(']'); + } else { + sb.append("\n\tUser's perm expires ["); + sb.append(new Date(user.permExpires())); + sb.append(']'); + } + } + if(user==null || user.permsUnloaded() || user.permExpired()) { + user = loadUser(bait); + sb.append("\n\tloadUser called"); + } + if(user==null) { + sb.append("\n\tUser was not Loaded"); + } else if(user.contains(pond)) { + sb.append("\n\tUser contains "); + sb.append(pond.getKey()); + rv = true; + } else { + sb.append("\n\tUser does not contain "); + sb.append(pond.getKey()); + List<Permission> perms = new ArrayList<Permission>(); + user.copyPermsTo(perms); + for(Permission p : perms) { + sb.append("\n\t\t"); + sb.append(p.getKey()); + } + } + } else { + sb.append("AAF Lur does not support ["); + sb.append(bait); + sb.append("]"); + } + aaf.access.log(Level.INFO, sb); + return rv; + } else { + if(handles(bait)) { + User<PERM> user = getUser(bait); + if(user==null || user.permsUnloaded() || user.permExpired()) { + user = loadUser(bait); + } + return user==null?false:user.contains(pond); + } + return false; + } + } + } + + public void fishAll(Principal bait, List<Permission> perms) { + if(preemptiveLur!=null && preemptiveLur.handles(bait)) { + preemptiveLur.fishAll(bait, perms); + } else { + if(isDebug(bait)) { + StringBuilder sb = new StringBuilder("Log for "); + sb.append(bait); + if(handles(bait)) { + User<PERM> user = getUser(bait); + if(user==null) { + sb.append("\n\tUser is not in Cache"); + } else { + if(user.noPerms()) { + sb.append("\n\tUser has no Perms"); + } + if(user.permExpired()) { + sb.append("\n\tUser's perm expired ["); + sb.append(new Date(user.permExpires())); + sb.append(']'); + } else { + sb.append("\n\tUser's perm expires ["); + sb.append(new Date(user.permExpires())); + sb.append(']'); + } + } + if(user==null || user.permsUnloaded() || user.permExpired()) { + user = loadUser(bait); + sb.append("\n\tloadUser called"); + } + if(user==null) { + sb.append("\n\tUser was not Loaded"); + } else { + sb.append("\n\tCopying Perms "); + user.copyPermsTo(perms); + for(Permission p : perms) { + sb.append("\n\t\t"); + sb.append(p.getKey()); + } + } + } else { + sb.append("AAF Lur does not support ["); + sb.append(bait); + sb.append("]"); + } + aaf.access.log(Level.INFO, sb); + } else { + if(handles(bait)) { + User<PERM> user = getUser(bait); + if(user==null || user.permsUnloaded() || user.permExpired()) { + user = loadUser(bait); + } + if(user!=null) { + user.copyPermsTo(perms); + } + } + } + } + } + + @Override + public void remove(String user) { + super.remove(user); + } + + private boolean isDebug(Principal p) { + if(debug!=null) { + if(debug.length==1 && "all".equals(debug[0])) { + return true; + } + String name = p.getName(); + for(String s : debug) { + if(s.equals(name)) { + return true; + } + } + } + return false; + } + /** + * This special case minimizes loops, avoids multiple Set hits, and calls all the appropriate Actions found. + * + * @param bait + * @param obj + * @param type + * @param instance + * @param actions + */ + public<A> void fishOneOf(Principal princ, A obj, String type, String instance, List<Action<A>> actions) { + User<PERM> user = getUser(princ); + if(user==null || user.permsUnloaded() || user.permExpired()) { + user = loadUser(princ); + } + if(user!=null) { + ReuseAAFPermission perm = new ReuseAAFPermission(type,instance); + for(Action<A> action : actions) { + perm.setAction(action.getName()); + if(user.contains(perm)) { + if(action.exec(obj))return; + } + } + } + } + + public static interface Action<A> { + public String getName(); + /** + * Return false to continue, True to end now + * @return + */ + public boolean exec(A a); + } + + private class ReuseAAFPermission extends AAFPermission { + public ReuseAAFPermission(String type, String instance) { + super(type,instance,null,null); + } + + public void setAction(String s) { + action = s; + } + + /** + * This function understands that AAF Keys are hierarchical, :A:B:C, + * Cassandra follows a similar method, so we'll short circuit and do it more efficiently when there isn't a first hit + * @return + */ + } +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/ArtifactDir.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/ArtifactDir.java new file mode 100644 index 00000000..7259d68e --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/ArtifactDir.java @@ -0,0 +1,286 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.cm; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.security.KeyStore; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.Symm; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.util.Chmod; +import org.onap.aaf.misc.env.Trans; +import org.onap.aaf.misc.env.util.Chrono; + +import certman.v1_0.Artifacts.Artifact; +import certman.v1_0.CertInfo; + +public abstract class ArtifactDir implements PlaceArtifact { + + protected static final String C_R = "\n"; + protected File dir; + private List<String> encodeds = new ArrayList<String>(); + + private Symm symm; + // This checks for multiple passes of Dir on the same objects. Run clear after done. + protected static Map<String,Object> processed = new HashMap<String,Object>(); + + + /** + * Note: Derived Classes should ALWAYS call "super.place(cert,arti)" first, and + * then "placeProperties(arti)" just after they implement + */ + @Override + public final boolean place(Trans trans, CertInfo certInfo, Artifact arti, String machine) throws CadiException { + validate(arti); + + try { + // Obtain/setup directory as required + dir = new File(arti.getDir()); + if(processed.get("dir")==null) { + if(!dir.exists()) { + Chmod.to755.chmod(dir); + if(!dir.mkdirs()) { + throw new CadiException("Could not create " + dir); + } + } + + // Also place cm_url and Host Name + addProperty(Config.CM_URL,trans.getProperty(Config.CM_URL)); + addProperty(Config.HOSTNAME,machine); + addProperty(Config.AAF_ENV,certInfo.getEnv()); + // Obtain Issuers + boolean first = true; + StringBuilder issuers = new StringBuilder(); + for(String dn : certInfo.getCaIssuerDNs()) { + if(first) { + first=false; + } else { + issuers.append(':'); + } + issuers.append(dn); + } + addProperty(Config.CADI_X509_ISSUERS,issuers.toString()); + } + symm = (Symm)processed.get("symm"); + if(symm==null) { + // CADI Key Gen + File f = new File(dir,arti.getNs() + ".keyfile"); + if(!f.exists()) { + write(f,Chmod.to400,Symm.keygen()); + } + symm = Symm.obtain(f); + + addEncProperty("ChallengePassword", certInfo.getChallenge()); + + processed.put("symm",symm); + } + + _place(trans, certInfo,arti); + + placeProperties(arti); + + processed.put("dir",dir); + + } catch (Exception e) { + throw new CadiException(e); + } + return true; + } + + /** + * Derived Classes implement this instead, so Dir can process first, and write any Properties last + * @param cert + * @param arti + * @return + * @throws CadiException + */ + protected abstract boolean _place(Trans trans, CertInfo certInfo, Artifact arti) throws CadiException; + + protected void addProperty(String tag, String value) throws IOException { + StringBuilder sb = new StringBuilder(); + sb.append(tag); + sb.append('='); + sb.append(value); + encodeds.add(sb.toString()); + } + + protected void addEncProperty(String tag, String value) throws IOException { + StringBuilder sb = new StringBuilder(); + sb.append(tag); + sb.append('='); + sb.append("enc:"); + sb.append(symm.enpass(value)); + encodeds.add(sb.toString()); + } + + protected void write(File f, Chmod c, String ... data) throws IOException { + f.setWritable(true,true); + + FileOutputStream fos = new FileOutputStream(f); + PrintStream ps = new PrintStream(fos); + try { + for(String s : data) { + ps.print(s); + } + } finally { + ps.close(); + c.chmod(f); + } + } + + protected void write(File f, Chmod c, byte[] bytes) throws IOException { + f.setWritable(true,true); + + FileOutputStream fos = new FileOutputStream(f); + try { + fos.write(bytes); + } finally { + fos.close(); + c.chmod(f); + } + } + + protected void write(File f, Chmod c, KeyStore ks, char[] pass ) throws IOException, CadiException { + f.setWritable(true,true); + + FileOutputStream fos = new FileOutputStream(f); + try { + ks.store(fos, pass); + } catch (Exception e) { + throw new CadiException(e); + } finally { + fos.close(); + c.chmod(f); + } + } + + + private void validate(Artifact a) throws CadiException { + StringBuilder sb = new StringBuilder(); + if(a.getDir()==null) { + sb.append("File Artifacts require a path"); + } + + if(a.getNs()==null) { + if(sb.length()>0) { + sb.append('\n'); + } + sb.append("File Artifacts require an AAF Namespace"); + } + + if(sb.length()>0) { + throw new CadiException(sb.toString()); + } + } + + private boolean placeProperties(Artifact arti) throws CadiException { + if(encodeds.size()==0) { + return true; + } + boolean first=processed.get("dir")==null; + try { + File f = new File(dir,arti.getNs()+".props"); + if(f.exists()) { + if(first) { + f.delete(); + } else { + f.setWritable(true); + } + } + + // Append if not first + PrintWriter pw = new PrintWriter(new FileWriter(f,!first)); + try { + // Write a Header + if(first) { + for(int i=0;i<60;++i) { + pw.print('#'); + } + pw.println(); + pw.println("# Properties Generated by AT&T Certificate Manager"); + pw.print("# by "); + pw.println(System.getProperty("user.name")); + pw.print("# on "); + pw.println(Chrono.dateStamp()); + pw.println("# @copyright 2016, AT&T"); + for(int i=0;i<60;++i) { + pw.print('#'); + } + pw.println(); + for(String prop : encodeds) { + if( prop.startsWith("cm_") + || prop.startsWith(Config.HOSTNAME) + || prop.startsWith(Config.AAF_ENV)) { + pw.println(prop); + } + } + } + + for(String prop : encodeds) { + if(prop.startsWith("cadi")) { + pw.println(prop); + } + } + } finally { + pw.close(); + } + Chmod.to644.chmod(f); + + if(first) { + // Challenge + f = new File(dir,arti.getNs()+".chal"); + if(f.exists()) { + f.delete(); + } + pw = new PrintWriter(new FileWriter(f)); + try { + for(String prop : encodeds) { + if(prop.startsWith("Challenge")) { + pw.println(prop); + } + } + } finally { + pw.close(); + } + Chmod.to400.chmod(f); + } + } catch(Exception e) { + throw new CadiException(e); + } + return true; + } + + public static void clear() { + processed.clear(); + } + +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/CertException.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/CertException.java new file mode 100644 index 00000000..5c525ff2 --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/CertException.java @@ -0,0 +1,45 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.cm; + +public class CertException extends Exception { + + /** + * + */ + private static final long serialVersionUID = 1373028409048516401L; + + public CertException() { + } + + public CertException(String message) { + super(message); + } + + public CertException(Throwable cause) { + super(cause); + } + + public CertException(String message, Throwable cause) { + super(message, cause); + } +}
\ No newline at end of file diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/CmAgent.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/CmAgent.java new file mode 100644 index 00000000..f900a1f4 --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/CmAgent.java @@ -0,0 +1,722 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.cm; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.security.KeyStore; +import java.security.cert.X509Certificate; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.Symm; +import org.onap.aaf.cadi.aaf.client.ErrMessage; +import org.onap.aaf.cadi.aaf.v2_0.AAFCon; +import org.onap.aaf.cadi.aaf.v2_0.AAFConHttp; +import org.onap.aaf.cadi.client.Future; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.http.HBasicAuthSS; +import org.onap.aaf.cadi.sso.AAFSSO; +import org.onap.aaf.cadi.util.FQI; +import org.onap.aaf.misc.env.Env; +import org.onap.aaf.misc.env.TimeTaken; +import org.onap.aaf.misc.env.Trans; +import org.onap.aaf.misc.env.Data.TYPE; +import org.onap.aaf.misc.env.util.Chrono; +import org.onap.aaf.misc.env.util.Split; +import org.onap.aaf.misc.rosetta.env.RosettaDF; +import org.onap.aaf.misc.rosetta.env.RosettaEnv; + +import java.util.Properties; + +import certman.v1_0.Artifacts; +import certman.v1_0.Artifacts.Artifact; +import certman.v1_0.CertInfo; +import certman.v1_0.CertificateRequest; + +public class CmAgent { + private static final String PRINT = "print"; + private static final String FILE = "file"; + private static final String PKCS12 = "pkcs12"; + private static final String JKS = "jks"; + private static final String SCRIPT="script"; + + private static final String CM_VER = "1.0"; + public static final int PASS_SIZE = 24; + private static int TIMEOUT; + + private static RosettaDF<CertificateRequest> reqDF; + private static RosettaDF<CertInfo> certDF; + private static RosettaDF<Artifacts> artifactsDF; + private static ErrMessage errMsg; + private static Map<String,PlaceArtifact> placeArtifact; + private static RosettaEnv env; + + public static void main(String[] args) { + int exitCode = 0; + try { + AAFSSO aafsso = new AAFSSO(args); + if(aafsso.loginOnly()) { + aafsso.setLogDefault(); + aafsso.writeFiles(); + System.out.println("AAF SSO information created in ~/.aaf"); + } else { + PropAccess access = aafsso.access(); + env = new RosettaEnv(access.getProperties()); + Deque<String> cmds = new ArrayDeque<String>(); + for(String p : args) { + if(p.indexOf('=')<0) { + cmds.add(p); + } + } + + if(cmds.size()==0) { + aafsso.setLogDefault(); + System.out.println("Usage: java -jar <cadi-aaf-*-full.jar> cmd [<tag=value>]*"); + System.out.println(" create <mechID> [<machine>]"); + System.out.println(" read <mechID> [<machine>]"); + System.out.println(" update <mechID> [<machine>]"); + System.out.println(" delete <mechID> [<machine>]"); + System.out.println(" copy <mechID> <machine> <newmachine>[,<newmachine>]*"); + System.out.println(" place <mechID> [<machine>]"); + System.out.println(" showpass <mechID> [<machine>]"); + System.out.println(" check <mechID> [<machine>]"); + System.out.println(" genkeypair"); + System.exit(1); + } + + TIMEOUT = Integer.parseInt(env.getProperty(Config.AAF_CONN_TIMEOUT, "5000")); + + reqDF = env.newDataFactory(CertificateRequest.class); + artifactsDF = env.newDataFactory(Artifacts.class); + certDF = env.newDataFactory(CertInfo.class); + errMsg = new ErrMessage(env); + + placeArtifact = new HashMap<String,PlaceArtifact>(); + placeArtifact.put(JKS, new PlaceArtifactInKeystore(JKS)); + placeArtifact.put(PKCS12, new PlaceArtifactInKeystore(PKCS12)); + placeArtifact.put(FILE, new PlaceArtifactInFiles()); + placeArtifact.put(PRINT, new PlaceArtifactOnStream(System.out)); + placeArtifact.put(SCRIPT, new PlaceArtifactScripts()); + + Trans trans = env.newTrans(); + String token; + if((token=access.getProperty("oauth_token"))!=null) { + trans.setProperty("oauth_token", token); + } + try { + // show Std out again + aafsso.setLogDefault(); + aafsso.setStdErrDefault(); + + // if CM_URL can be obtained, add to sso.props, if written + String cm_url = getProperty(access,env,false, Config.CM_URL,Config.CM_URL+": "); + if(cm_url!=null) { + aafsso.addProp(Config.CM_URL, cm_url); + } + aafsso.writeFiles(); + + AAFCon<?> aafcon = new AAFConHttp(access,Config.CM_URL); + + String cmd = cmds.removeFirst(); + if("place".equals(cmd)) { + placeCerts(trans,aafcon,cmds); + } else if("create".equals(cmd)) { + createArtifact(trans, aafcon,cmds); + } else if("read".equals(cmd)) { + readArtifact(trans, aafcon, cmds); + } else if("copy".equals(cmd)) { + copyArtifact(trans, aafcon, cmds); + } else if("update".equals(cmd)) { + updateArtifact(trans, aafcon, cmds); + } else if("delete".equals(cmd)) { + deleteArtifact(trans, aafcon, cmds); + } else if("showpass".equals(cmd)) { + showPass(trans,aafcon,cmds); + } else if("check".equals(cmd)) { + try { + exitCode = check(trans,aafcon,cmds); + } catch (Exception e) { + exitCode = 1; + throw e; + } + } else { + AAFSSO.cons.printf("Unknown command \"%s\"\n", cmd); + } + } finally { + StringBuilder sb = new StringBuilder(); + trans.auditTrail(4, sb, Trans.REMOTE); + if(sb.length()>0) { + trans.info().log("Trans Info\n",sb); + } + } + aafsso.close(); + } + } catch (Exception e) { + e.printStackTrace(); + } + if(exitCode!=0) { + System.exit(exitCode); + } + } + + private static String getProperty(PropAccess pa, Env env, boolean secure, String tag, String prompt, Object ... def) { + String value; + if((value=pa.getProperty(tag))==null) { + if(secure) { + value = new String(AAFSSO.cons.readPassword(prompt, def)); + } else { + value = AAFSSO.cons.readLine(prompt,def).trim(); + } + if(value!=null) { + if(value.length()>0) { + pa.setProperty(tag,value); + env.setProperty(tag,value); + } else if(def.length==1) { + value=def[0].toString(); + pa.setProperty(tag,value); + env.setProperty(tag,value); + } + } + } + return value; + } + + private static String mechID(Deque<String> cmds) { + if(cmds.size()<1) { + String alias = env.getProperty(Config.CADI_ALIAS); + return alias!=null?alias:AAFSSO.cons.readLine("MechID: "); + } + return cmds.removeFirst(); + } + + private static String machine(Deque<String> cmds) throws UnknownHostException { + if(cmds.size()>0) { + return cmds.removeFirst(); + } else { + String mach = env.getProperty(Config.HOSTNAME); + return mach!=null?mach:InetAddress.getLocalHost().getHostName(); + } + } + + private static String[] machines(Deque<String> cmds) { + String machines; + if(cmds.size()>0) { + machines = cmds.removeFirst(); + } else { + machines = AAFSSO.cons.readLine("Machines (sep by ','): "); + } + return Split.split(',', machines); + } + + private static void createArtifact(Trans trans, AAFCon<?> aafcon, Deque<String> cmds) throws Exception { + String mechID = mechID(cmds); + String machine = machine(cmds); + + Artifacts artifacts = new Artifacts(); + Artifact arti = new Artifact(); + artifacts.getArtifact().add(arti); + arti.setMechid(mechID!=null?mechID:AAFSSO.cons.readLine("MechID: ")); + arti.setMachine(machine!=null?machine:AAFSSO.cons.readLine("Machine (%s): ",InetAddress.getLocalHost().getHostName())); + arti.setCa(AAFSSO.cons.readLine("CA: (%s): ","aaf")); + + String resp = AAFSSO.cons.readLine("Types [file,jks,script] (%s): ", "jks"); + for(String s : Split.splitTrim(',', resp)) { + arti.getType().add(s); + } + // Always do Script + if(!resp.contains(SCRIPT)) { + arti.getType().add(SCRIPT); + } + + // Note: Sponsor is set on Creation by CM + String configRootName = FQI.reverseDomain(arti.getMechid()); + arti.setNs(AAFSSO.cons.readLine("Namespace (%s): ",configRootName)); + arti.setDir(AAFSSO.cons.readLine("Directory (%s): ", System.getProperty("user.dir"))); + arti.setOsUser(AAFSSO.cons.readLine("OS User (%s): ", System.getProperty("user.name"))); + arti.setRenewDays(Integer.parseInt(AAFSSO.cons.readLine("Renewal Days (%s):", "30"))); + arti.setNotification(toNotification(AAFSSO.cons.readLine("Notification (mailto owner):", ""))); + + TimeTaken tt = trans.start("Create Artifact", Env.REMOTE); + try { + Future<Artifacts> future = aafcon.client(CM_VER).create("/cert/artifacts", artifactsDF, artifacts); + if(future.get(TIMEOUT)) { + trans.info().printf("Call to AAF Certman successful %s, %s",arti.getMechid(), arti.getMachine()); + } else { + trans.error().printf("Call to AAF Certman failed, %s", + errMsg.toMsg(future)); + } + } finally { + tt.done(); + } + } + + private static String toNotification(String notification) { + if(notification==null) { + notification=""; + } else if(notification.length()>0) { + if(notification.indexOf(':')<0) { + notification = "mailto:" + notification; + } + } + return notification; + } + + + private static void readArtifact(Trans trans, AAFCon<?> aafcon, Deque<String> cmds) throws Exception { + String mechID = mechID(cmds); + String machine = machine(cmds); + + TimeTaken tt = trans.start("Read Artifact", Env.SUB); + try { + Future<Artifacts> future = aafcon.client(CM_VER) + .read("/cert/artifacts/"+mechID+'/'+machine, artifactsDF,"Authorization","Bearer " + trans.getProperty("oauth_token")); + + if(future.get(TIMEOUT)) { + boolean printed = false; + for(Artifact a : future.value.getArtifact()) { + AAFSSO.cons.printf("MechID: %s\n",a.getMechid()); + AAFSSO.cons.printf(" Sponsor: %s\n",a.getSponsor()); + AAFSSO.cons.printf("Machine: %s\n",a.getMachine()); + AAFSSO.cons.printf("CA: %s\n",a.getCa()); + StringBuilder sb = new StringBuilder(); + boolean first = true; + for(String t : a.getType()) { + if(first) {first=false;} + else{sb.append(',');} + sb.append(t); + } + AAFSSO.cons.printf("Types: %s\n",sb); + AAFSSO.cons.printf("Namespace: %s\n",a.getNs()); + AAFSSO.cons.printf("Directory: %s\n",a.getDir()); + AAFSSO.cons.printf("O/S User: %s\n",a.getOsUser()); + AAFSSO.cons.printf("Renew Days: %d\n",a.getRenewDays()); + AAFSSO.cons.printf("Notification %s\n",a.getNotification()); + printed = true; + } + if(!printed) { + AAFSSO.cons.printf("Artifact for %s %s does not exist\n", mechID, machine); + } + } else { + trans.error().log(errMsg.toMsg(future)); + } + } finally { + tt.done(); + } + } + + private static void copyArtifact(Trans trans, AAFCon<?> aafcon, Deque<String> cmds) throws Exception { + String mechID = mechID(cmds); + String machine = machine(cmds); + String[] newmachs = machines(cmds); + if(machine==null || newmachs == null) { + trans.error().log("No machines listed to copy to"); + } else { + TimeTaken tt = trans.start("Copy Artifact", Env.REMOTE); + try { + Future<Artifacts> future = aafcon.client(CM_VER) + .read("/cert/artifacts/"+mechID+'/'+machine, artifactsDF); + + if(future.get(TIMEOUT)) { + boolean printed = false; + for(Artifact a : future.value.getArtifact()) { + for(String m : newmachs) { + a.setMachine(m); + Future<Artifacts> fup = aafcon.client(CM_VER).update("/cert/artifacts", artifactsDF, future.value); + if(fup.get(TIMEOUT)) { + trans.info().printf("Copy of %s %s successful to %s",mechID,machine,m); + } else { + trans.error().printf("Call to AAF Certman failed, %s", + errMsg.toMsg(fup)); + } + + printed = true; + } + } + if(!printed) { + AAFSSO.cons.printf("Artifact for %s %s does not exist", mechID, machine); + } + } else { + trans.error().log(errMsg.toMsg(future)); + } + } finally { + tt.done(); + } + } + } + + private static void updateArtifact(Trans trans, AAFCon<?> aafcon, Deque<String> cmds) throws Exception { + String mechID = mechID(cmds); + String machine = machine(cmds); + + TimeTaken tt = trans.start("Update Artifact", Env.REMOTE); + try { + Future<Artifacts> fread = aafcon.client(CM_VER) + .read("/cert/artifacts/"+mechID+'/'+machine, artifactsDF); + + if(fread.get(TIMEOUT)) { + Artifacts artifacts = new Artifacts(); + for(Artifact a : fread.value.getArtifact()) { + Artifact arti = new Artifact(); + artifacts.getArtifact().add(arti); + + AAFSSO.cons.printf("For %s on %s\n", a.getMechid(),a.getMachine()); + arti.setMechid(a.getMechid()); + arti.setMachine(a.getMachine()); + arti.setCa(AAFSSO.cons.readLine("CA: (%s): ",a.getCa())); + StringBuilder sb = new StringBuilder(); + boolean first = true; + for(String t : a.getType()) { + if(first) {first=false;} + else{sb.append(',');} + sb.append(t); + } + + String resp = AAFSSO.cons.readLine("Types [file,jks,pkcs12] (%s): ", sb); + for(String s : Split.splitTrim(',', resp)) { + arti.getType().add(s); + } + // Always do Script + if(!resp.contains(SCRIPT)) { + arti.getType().add(SCRIPT); + } + + // Note: Sponsor is set on Creation by CM + arti.setNs(AAFSSO.cons.readLine("Namespace (%s): ",a.getNs())); + arti.setDir(AAFSSO.cons.readLine("Directory (%s): ", a.getDir())); + arti.setOsUser(AAFSSO.cons.readLine("OS User (%s): ", a.getOsUser())); + arti.setRenewDays(Integer.parseInt(AAFSSO.cons.readLine("Renew Days (%s):", a.getRenewDays()))); + arti.setNotification(toNotification(AAFSSO.cons.readLine("Notification (%s):", a.getNotification()))); + + } + if(artifacts.getArtifact().size()==0) { + AAFSSO.cons.printf("Artifact for %s %s does not exist", mechID, machine); + } else { + Future<Artifacts> fup = aafcon.client(CM_VER).update("/cert/artifacts", artifactsDF, artifacts); + if(fup.get(TIMEOUT)) { + trans.info().printf("Call to AAF Certman successful %s, %s",mechID,machine); + } else { + trans.error().printf("Call to AAF Certman failed, %s", + errMsg.toMsg(fup)); + } + } + } else { + trans.error().printf("Call to AAF Certman failed, %s %s, %s", + errMsg.toMsg(fread),mechID,machine); + } + } finally { + tt.done(); + } + } + + private static void deleteArtifact(Trans trans, AAFCon<?> aafcon, Deque<String> cmds) throws Exception { + String mechid = mechID(cmds); + String machine = machine(cmds); + + TimeTaken tt = trans.start("Delete Artifact", Env.REMOTE); + try { + Future<Void> future = aafcon.client(CM_VER) + .delete("/cert/artifacts/"+mechid+"/"+machine,"application/json" ); + + if(future.get(TIMEOUT)) { + trans.info().printf("Call to AAF Certman successful %s, %s",mechid,machine); + } else { + trans.error().printf("Call to AAF Certman failed, %s %s, %s", + errMsg.toMsg(future),mechid,machine); + } + } finally { + tt.done(); + } + } + + + + private static boolean placeCerts(Trans trans, AAFCon<?> aafcon, Deque<String> cmds) throws Exception { + boolean rv = false; + String mechID = mechID(cmds); + String machine = machine(cmds); + String[] fqdns = Split.split(':', machine); + String key; + if(fqdns.length>1) { + key = fqdns[0]; + machine = fqdns[1]; + } else { + key = machine; + } + + TimeTaken tt = trans.start("Place Artifact", Env.REMOTE); + try { + Future<Artifacts> acf = aafcon.client(CM_VER) + .read("/cert/artifacts/"+mechID+'/'+key, artifactsDF); + if(acf.get(TIMEOUT)) { + if(acf.value.getArtifact()==null || acf.value.getArtifact().isEmpty()) { + AAFSSO.cons.printf("===> There are no artifacts for %s on machine '%s'\n", mechID, key); + } else { + for(Artifact a : acf.value.getArtifact()) { + String osID = System.getProperty("user.name"); + if(a.getOsUser().equals(osID)) { + CertificateRequest cr = new CertificateRequest(); + cr.setMechid(a.getMechid()); + cr.setSponsor(a.getSponsor()); + for(int i=0;i<fqdns.length;++i) { + cr.getFqdns().add(fqdns[i]); + } + Future<String> f = aafcon.client(CM_VER) + .setQueryParams("withTrust") + .updateRespondString("/cert/" + a.getCa(),reqDF, cr); + if(f.get(TIMEOUT)) { + CertInfo capi = certDF.newData().in(TYPE.JSON).load(f.body()).asObject(); + for(String type : a.getType()) { + PlaceArtifact pa = placeArtifact.get(type); + if(pa!=null) { + if(rv = pa.place(trans, capi, a,machine)) { + notifyPlaced(a,rv); + } + } + } + // Cover for the above multiple pass possibilities with some static Data, then clear per Artifact + } else { + trans.error().log(errMsg.toMsg(f)); + } + } else { + trans.error().log("You must be OS User \"" + a.getOsUser() +"\" to place Certificates on this box"); + } + } + } + } else { + trans.error().log(errMsg.toMsg(acf)); + } + } finally { + tt.done(); + } + return rv; + } + + private static void notifyPlaced(Artifact a, boolean rv) { + } + + private static void showPass(Trans trans, AAFCon<?> aafcon, Deque<String> cmds) throws Exception { + String mechID = mechID(cmds); + String machine = machine(cmds); + + TimeTaken tt = trans.start("Show Password", Env.REMOTE); + try { + Future<Artifacts> acf = aafcon.client(CM_VER) + .read("/cert/artifacts/"+mechID+'/'+machine, artifactsDF); + if(acf.get(TIMEOUT)) { + // Have to wait for JDK 1.7 source... + //switch(artifact.getType()) { + if(acf.value.getArtifact()==null || acf.value.getArtifact().isEmpty()) { + AAFSSO.cons.printf("No Artifacts found for %s on %s", mechID, machine); + } else { + String id = aafcon.defID(); + boolean allowed; + for(Artifact a : acf.value.getArtifact()) { + allowed = id!=null && (id.equals(a.getSponsor()) || + (id.equals(a.getMechid()) + && aafcon.securityInfo().defSS.getClass().isAssignableFrom(HBasicAuthSS.class))); + if(!allowed) { + Future<String> pf = aafcon.client(CM_VER).read("/cert/may/" + + a.getNs() + ".certman|"+a.getCa()+"|showpass","*/*"); + if(pf.get(TIMEOUT)) { + allowed = true; + } else { + trans.error().log(errMsg.toMsg(pf)); + } + } + if(allowed) { + File dir = new File(a.getDir()); + Properties props = new Properties(); + FileInputStream fis = new FileInputStream(new File(dir,a.getNs()+".props")); + try { + props.load(fis); + fis.close(); + fis = new FileInputStream(new File(dir,a.getNs()+".chal")); + props.load(fis); + } finally { + fis.close(); + } + + File f = new File(dir,a.getNs()+".keyfile"); + if(f.exists()) { + Symm symm = Symm.obtain(f); + + for(Iterator<Entry<Object,Object>> iter = props.entrySet().iterator(); iter.hasNext();) { + Entry<Object,Object> en = iter.next(); + if(en.getValue().toString().startsWith("enc:")) { + System.out.printf("%s=%s\n", en.getKey(), symm.depass(en.getValue().toString())); + } + } + } else { + trans.error().printf("%s.keyfile must exist to read passwords for %s on %s", + f.getAbsolutePath(),a.getMechid(), a.getMachine()); + } + } + } + } + } else { + trans.error().log(errMsg.toMsg(acf)); + } + } finally { + tt.done(); + } + + } + + + /** + * Check returns Error Codes, so that Scripts can know what to do + * + * 0 - Check Complete, nothing to do + * 1 - General Error + * 2 - Error for specific Artifact - read check.msg + * 10 - Certificate Updated - check.msg is email content + * + * @param trans + * @param aafcon + * @param cmds + * @return + * @throws Exception + */ + private static int check(Trans trans, AAFCon<?> aafcon, Deque<String> cmds) throws Exception { + int exitCode=1; + String mechID = mechID(cmds); + String machine = machine(cmds); + + TimeTaken tt = trans.start("Check Certificate", Env.REMOTE); + try { + + Future<Artifacts> acf = aafcon.client(CM_VER) + .read("/cert/artifacts/"+mechID+'/'+machine, artifactsDF); + if(acf.get(TIMEOUT)) { + // Have to wait for JDK 1.7 source... + //switch(artifact.getType()) { + if(acf.value.getArtifact()==null || acf.value.getArtifact().isEmpty()) { + AAFSSO.cons.printf("No Artifacts found for %s on %s", mechID, machine); + } else { + String id = aafcon.defID(); + GregorianCalendar now = new GregorianCalendar(); + for(Artifact a : acf.value.getArtifact()) { + if(id.equals(a.getMechid())) { + File dir = new File(a.getDir()); + Properties props = new Properties(); + FileInputStream fis = new FileInputStream(new File(dir,a.getNs()+".props")); + try { + props.load(fis); + } finally { + fis.close(); + } + + String prop; + File f; + + if((prop=props.getProperty(Config.CADI_KEYFILE))==null || + !(f=new File(prop)).exists()) { + trans.error().printf("Keyfile must exist to check Certificates for %s on %s", + a.getMechid(), a.getMachine()); + } else { + String ksf = props.getProperty(Config.CADI_KEYSTORE); + String ksps = props.getProperty(Config.CADI_KEYSTORE_PASSWORD); + if(ksf==null || ksps == null) { + trans.error().printf("Properties %s and %s must exist to check Certificates for %s on %s", + Config.CADI_KEYSTORE, Config.CADI_KEYSTORE_PASSWORD,a.getMechid(), a.getMachine()); + } else { + KeyStore ks = KeyStore.getInstance("JKS"); + Symm symm = Symm.obtain(f); + + fis = new FileInputStream(ksf); + try { + ks.load(fis,symm.depass(ksps).toCharArray()); + } finally { + fis.close(); + } + X509Certificate cert = (X509Certificate)ks.getCertificate(mechID); + String msg = null; + + if(cert==null) { + msg = String.format("X509Certificate does not exist for %s on %s in %s", + a.getMechid(), a.getMachine(), ksf); + trans.error().log(msg); + exitCode = 2; + } else { + GregorianCalendar renew = new GregorianCalendar(); + renew.setTime(cert.getNotAfter()); + renew.add(GregorianCalendar.DAY_OF_MONTH,-1*a.getRenewDays()); + if(renew.after(now)) { + msg = String.format("X509Certificate for %s on %s has been checked on %s. It expires on %s; it will not be renewed until %s.\n", + a.getMechid(), a.getMachine(),Chrono.dateOnlyStamp(now),cert.getNotAfter(),Chrono.dateOnlyStamp(renew)); + trans.info().log(msg); + exitCode = 0; // OK + } else { + trans.info().printf("X509Certificate for %s on %s expiration, %s, needs Renewal.\n", + a.getMechid(), a.getMachine(),cert.getNotAfter()); + cmds.offerLast(mechID); + cmds.offerLast(machine); + if(placeCerts(trans,aafcon,cmds)) { + msg = String.format("X509Certificate for %s on %s has been renewed. Ensure services using are refreshed.\n", + a.getMechid(), a.getMachine()); + exitCode = 10; // Refreshed + } else { + msg = String.format("X509Certificate for %s on %s attempted renewal, but failed. Immediate Investigation is required!\n", + a.getMechid(), a.getMachine()); + exitCode = 1; // Error Renewing + } + } + } + if(msg!=null) { + FileOutputStream fos = new FileOutputStream(a.getDir()+'/'+a.getNs()+".msg"); + try { + fos.write(msg.getBytes()); + } finally { + fos.close(); + } + } + } + + } + } + } + } + } else { + trans.error().log(errMsg.toMsg(acf)); + exitCode=1; + } + } finally { + tt.done(); + } + return exitCode; + } + +} + + + + diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/Factory.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/Factory.java new file mode 100644 index 00000000..e969fab3 --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/Factory.java @@ -0,0 +1,486 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.cm; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringReader; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.Security; +import java.security.Signature; +import java.security.SignatureException; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Collection; +import java.util.List; + +import javax.crypto.Cipher; +import javax.crypto.NoSuchPaddingException; + +import org.onap.aaf.cadi.Symm; +import org.onap.aaf.misc.env.Env; +import org.onap.aaf.misc.env.TimeTaken; +import org.onap.aaf.misc.env.Trans; + +public class Factory { + private static final String PRIVATE_KEY_HEADER = "PRIVATE KEY"; + public static final String KEY_ALGO = "RSA"; + public static final String SIG_ALGO = "SHA256withRSA"; + + public static final int KEY_LENGTH = 2048; + private static final KeyPairGenerator keygen; + private static final KeyFactory keyFactory; + private static final CertificateFactory certificateFactory; + private static final SecureRandom random; + + + private static final Symm base64 = Symm.base64.copy(64); + + static { + random = new SecureRandom(); + KeyPairGenerator tempKeygen; + try { + tempKeygen = KeyPairGenerator.getInstance(KEY_ALGO);//,"BC"); + tempKeygen.initialize(KEY_LENGTH, random); + } catch (NoSuchAlgorithmException e) { + tempKeygen = null; + e.printStackTrace(System.err); + } + keygen = tempKeygen; + + KeyFactory tempKeyFactory; + try { + tempKeyFactory=KeyFactory.getInstance(KEY_ALGO);//,"BC" + } catch (NoSuchAlgorithmException e) { + tempKeyFactory = null; + e.printStackTrace(System.err); + }; + keyFactory = tempKeyFactory; + + CertificateFactory tempCertificateFactory; + try { + tempCertificateFactory = CertificateFactory.getInstance("X.509"); + } catch (CertificateException e) { + tempCertificateFactory = null; + e.printStackTrace(System.err); + } + certificateFactory = tempCertificateFactory; + + + } + + + public static KeyPair generateKeyPair(Trans trans) { + TimeTaken tt; + if(trans!=null) { + tt = trans.start("Generate KeyPair", Env.SUB); + } else { + tt = null; + } + try { + return keygen.generateKeyPair(); + } finally { + if(tt!=null) { + tt.done(); + } + } + } + + private static final String LINE_END = "-----\n"; + + protected static String textBuilder(String kind, byte[] bytes) throws IOException { + StringBuilder sb = new StringBuilder(); + sb.append("-----BEGIN "); + sb.append(kind); + sb.append(LINE_END); + + ByteArrayInputStream bais = new ByteArrayInputStream(bytes); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + base64.encode(bais, baos); + sb.append(new String(baos.toByteArray())); + + if(sb.charAt(sb.length()-1)!='\n') { + sb.append('\n'); + } + sb.append("-----END "); + sb.append(kind); + sb.append(LINE_END); + return sb.toString(); + } + + public static PrivateKey toPrivateKey(Trans trans, String pk) throws IOException, CertException { + byte[] bytes = decode(new StringReader(pk)); + return toPrivateKey(trans, bytes); + } + + public static PrivateKey toPrivateKey(Trans trans, byte[] bytes) throws IOException, CertException { + TimeTaken tt=trans.start("Reconstitute Private Key", Env.SUB); + try { + return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(bytes)); + } catch (InvalidKeySpecException e) { + throw new CertException("Translating Private Key from PKCS8 KeySpec",e); + } finally { + tt.done(); + } + } + + public static PrivateKey toPrivateKey(Trans trans, File file) throws IOException, CertException { + TimeTaken tt = trans.start("Decode Private Key File", Env.SUB); + try { + return toPrivateKey(trans,decode(file)); + }finally { + tt.done(); + } + } + + public static String toString(Trans trans, PrivateKey pk) throws IOException { +// PKCS8EncodedKeySpec pemContents = new PKCS8EncodedKeySpec(pk.getEncoded()); + trans.debug().log("Private Key to String"); + return textBuilder(PRIVATE_KEY_HEADER,pk.getEncoded()); + } + + public static PublicKey toPublicKey(Trans trans, String pk) throws IOException { + TimeTaken tt = trans.start("Reconstitute Public Key", Env.SUB); + try { + ByteArrayInputStream bais = new ByteArrayInputStream(pk.getBytes()); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + Symm.base64noSplit.decode(bais, baos); + + return keyFactory.generatePublic(new X509EncodedKeySpec(baos.toByteArray())); + } catch (InvalidKeySpecException e) { + trans.error().log(e,"Translating Public Key from X509 KeySpec"); + return null; + } finally { + tt.done(); + } + } + + public static String toString(Trans trans, PublicKey pk) throws IOException { + trans.debug().log("Public Key to String"); + return textBuilder("PUBLIC KEY",pk.getEncoded()); + } + + public static Collection<? extends Certificate> toX509Certificate(String x509) throws CertificateException { + return toX509Certificate(x509.getBytes()); + } + + public static Collection<? extends Certificate> toX509Certificate(List<String> x509s) throws CertificateException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + for(String x509 : x509s) { + baos.write(x509.getBytes()); + } + } catch (IOException e) { + throw new CertificateException(e); + } + return toX509Certificate(new ByteArrayInputStream(baos.toByteArray())); + } + + public static Collection<? extends Certificate> toX509Certificate(byte[] x509) throws CertificateException { + return certificateFactory.generateCertificates(new ByteArrayInputStream(x509)); + } + + public static Collection<? extends Certificate> toX509Certificate(Trans trans, File file) throws CertificateException, FileNotFoundException { + FileInputStream fis = new FileInputStream(file); + try { + try { + return toX509Certificate(fis); + } finally { + fis.close(); + } + } catch (IOException e) { + throw new CertificateException(e); + } + } + + public static Collection<? extends Certificate> toX509Certificate(InputStream is) throws CertificateException { + return certificateFactory.generateCertificates(is); + } + + public static String toString(Trans trans, Certificate cert) throws IOException, CertException { + if(trans.debug().isLoggable()) { + StringBuilder sb = new StringBuilder("Certificate to String"); + if(cert instanceof X509Certificate) { + sb.append(" - "); + sb.append(((X509Certificate)cert).getSubjectDN()); + } + trans.debug().log(sb); + } + try { + if(cert==null) { + throw new CertException("Certificate not built"); + } + return textBuilder("CERTIFICATE",cert.getEncoded()); + } catch (CertificateEncodingException e) { + throw new CertException(e); + } + } + + public static Cipher pkCipher() throws NoSuchAlgorithmException, NoSuchPaddingException { + return Cipher.getInstance(KEY_ALGO); + } + + public static Cipher pkCipher(Key key, boolean encrypt) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException { + Cipher cipher = Cipher.getInstance(KEY_ALGO); + cipher.init(encrypt?Cipher.ENCRYPT_MODE:Cipher.DECRYPT_MODE,key); + return cipher; + } + + public static byte[] strip(Reader rdr) throws IOException { + BufferedReader br = new BufferedReader(rdr); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + String line; + while((line=br.readLine())!=null) { + if(line.length()>0 && + !line.startsWith("-----") && + line.indexOf(':')<0) { // Header elements + baos.write(line.getBytes()); + } + } + return baos.toByteArray(); + } + + public static class StripperInputStream extends InputStream { + private Reader created; + private BufferedReader br; + private int idx; + private String line; + + public StripperInputStream(Reader rdr) { + if(rdr instanceof BufferedReader) { + br = (BufferedReader)rdr; + } else { + br = new BufferedReader(rdr); + } + created = null; + } + + public StripperInputStream(File file) throws FileNotFoundException { + this(new FileReader(file)); + created = br; + } + + public StripperInputStream(InputStream is) throws FileNotFoundException { + this(new InputStreamReader(is)); + created = br; + } + + @Override + public int read() throws IOException { + if(line==null || idx>=line.length()) { + while((line=br.readLine())!=null) { + if(line.length()>0 && + !line.startsWith("-----") && + line.indexOf(':')<0) { // Header elements + break; + } + } + + if(line==null) { + return -1; + } + idx = 0; + } + return line.charAt(idx++); + } + + /* (non-Javadoc) + * @see java.io.InputStream#close() + */ + @Override + public void close() throws IOException { + if(created!=null) { + created.close(); + } + } + } + + public static class Base64InputStream extends InputStream { + private InputStream created; + private InputStream is; + private byte trio[]; + private byte duo[]; + private int idx; + + + public Base64InputStream(File file) throws FileNotFoundException { + this(new FileInputStream(file)); + created = is; + } + + public Base64InputStream(InputStream is) throws FileNotFoundException { + this.is = is; + trio = new byte[3]; + idx = 4; + } + + @Override + public int read() throws IOException { + if(duo==null || idx>=duo.length) { + int read = is.read(trio); + if(read==-1) { + return -1; + } + duo = Symm.base64.decode(trio); + if(duo==null || duo.length==0) { + return -1; + } + idx=0; + } + + return duo[idx++]; + } + + /* (non-Javadoc) + * @see java.io.InputStream#close() + */ + @Override + public void close() throws IOException { + if(created!=null) { + created.close(); + } + } + } + + public static byte[] decode(byte[] bytes) throws IOException { + ByteArrayInputStream bais = new ByteArrayInputStream(bytes); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + Symm.base64.decode(bais, baos); + return baos.toByteArray(); + } + + public static byte[] decode(File f) throws IOException { + FileReader fr = new FileReader(f); + try { + return Factory.decode(fr); + } finally { + fr.close(); + } + + } + public static byte[] decode(Reader rdr) throws IOException { + return decode(strip(rdr)); + } + + + public static byte[] binary(File file) throws IOException { + DataInputStream dis = new DataInputStream(new FileInputStream(file)); + try { + byte[] bytes = new byte[(int)file.length()]; + dis.readFully(bytes); + return bytes; + } finally { + dis.close(); + } + } + + + public static byte[] sign(Trans trans, byte[] bytes, PrivateKey pk) throws IOException, InvalidKeyException, SignatureException, NoSuchAlgorithmException { + TimeTaken tt = trans.start("Sign Data", Env.SUB); + try { + Signature sig = Signature.getInstance(SIG_ALGO); + sig.initSign(pk, random); + sig.update(bytes); + return sig.sign(); + } finally { + tt.done(); + } + } + + public static String toSignatureString(byte[] signed) throws IOException { + return textBuilder("SIGNATURE", signed); + } + + public static boolean verify(Trans trans, byte[] bytes, byte[] signature, PublicKey pk) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { + TimeTaken tt = trans.start("Verify Data", Env.SUB); + try { + Signature sig = Signature.getInstance(SIG_ALGO); + sig.initVerify(pk); + sig.update(bytes); + return sig.verify(signature); + } finally { + tt.done(); + } + } + + /** + * Get the Security Provider, or, if not exists yet, attempt to load + * + * @param providerType + * @param params + * @return + * @throws CertException + */ + public static synchronized Provider getSecurityProvider(String providerType, String[][] params) throws CertException { + Provider p = Security.getProvider(providerType); + if(p!=null) { + switch(providerType) { + case "PKCS12": + + break; + case "PKCS11": // PKCS11 only known to be supported by Sun + try { + Class<?> clsSunPKCS11 = Class.forName("sun.security.pkcs11.SunPKCS11"); + Constructor<?> cnst = clsSunPKCS11.getConstructor(String.class); + Object sunPKCS11 = cnst.newInstance(params[0][0]); + if (sunPKCS11==null) { + throw new CertException("SunPKCS11 Provider cannot be constructed for " + params[0][0]); + } + Security.addProvider((Provider)sunPKCS11); + } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + throw new CertException(e); + } + break; + default: + throw new CertException(providerType + " is not a known Security Provider for your JDK."); + } + } + return p; + } +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/PlaceArtifact.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/PlaceArtifact.java new file mode 100644 index 00000000..369f48d0 --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/PlaceArtifact.java @@ -0,0 +1,32 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.cm; + +import certman.v1_0.Artifacts.Artifact; +import certman.v1_0.CertInfo; + +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.misc.env.Trans; + +public interface PlaceArtifact { + public boolean place(Trans trans, CertInfo cert, Artifact arti, String machine) throws CadiException; +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/PlaceArtifactInFiles.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/PlaceArtifactInFiles.java new file mode 100644 index 00000000..f419577b --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/PlaceArtifactInFiles.java @@ -0,0 +1,52 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.cm; + +import java.io.File; + +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.util.Chmod; +import org.onap.aaf.misc.env.Trans; + +import certman.v1_0.Artifacts.Artifact; +import certman.v1_0.CertInfo; + +public class PlaceArtifactInFiles extends ArtifactDir { + @Override + public boolean _place(Trans trans, CertInfo certInfo, Artifact arti) throws CadiException { + try { + // Setup Public Cert + File f = new File(dir,arti.getNs()+".crt"); + write(f,Chmod.to644,certInfo.getCerts().get(0),C_R); + + // Setup Private Key + f = new File(dir,arti.getNs()+".key"); + write(f,Chmod.to400,certInfo.getPrivatekey(),C_R); + + } catch (Exception e) { + throw new CadiException(e); + } + return true; + } +} + + diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/PlaceArtifactInKeystore.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/PlaceArtifactInKeystore.java new file mode 100644 index 00000000..2b498d4f --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/PlaceArtifactInKeystore.java @@ -0,0 +1,140 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.cm; + +import java.io.File; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.Symm; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.util.Chmod; +import org.onap.aaf.misc.env.Trans; + +import certman.v1_0.Artifacts.Artifact; +import certman.v1_0.CertInfo; + +public class PlaceArtifactInKeystore extends ArtifactDir { + private String kst; + + public PlaceArtifactInKeystore(String kst) { + this.kst = kst; + } + + @Override + public boolean _place(Trans trans, CertInfo certInfo, Artifact arti) throws CadiException { + File fks = new File(dir,arti.getNs()+'.'+kst); + try { + KeyStore jks = KeyStore.getInstance(kst); + if(fks.exists()) { + fks.delete(); + } + + // Get the Cert(s)... Might include Trust store + Collection<? extends Certificate> certColl = Factory.toX509Certificate(certInfo.getCerts()); + // find where the trusts end in 1.0 API + + X509Certificate x509; + List<X509Certificate> certList = new ArrayList<X509Certificate>(); + Certificate[] trustChain = null; + Certificate[] trustCAs; + for(Certificate c : certColl) { + x509 = (X509Certificate)c; + if(trustChain==null && x509.getSubjectDN().equals(x509.getIssuerDN())) { + trustChain = new Certificate[certList.size()]; + certList.toArray(trustChain); + certList.clear(); // reuse + } + certList.add(x509); + } + + // remainder should be Trust CAs + trustCAs = new Certificate[certList.size()]; + certList.toArray(trustCAs); + + // Properties, etc + // Add CADI Keyfile Entry to Properties + addProperty(Config.CADI_KEYFILE,arti.getDir()+'/'+arti.getNs() + ".keyfile"); + // Set Keystore Password + addProperty(Config.CADI_KEYSTORE,fks.getAbsolutePath()); + String keystorePass = Symm.randomGen(CmAgent.PASS_SIZE); + addEncProperty(Config.CADI_KEYSTORE_PASSWORD,keystorePass); + char[] keystorePassArray = keystorePass.toCharArray(); + jks.load(null,keystorePassArray); // load in + + // Add Private Key/Cert Entry for App + // Note: Java SSL security classes, while having a separate key from keystore, + // is documented to not actually work. + // java.security.UnrecoverableKeyException: Cannot recover key + // You can create a custom Key Manager to make it work, but Practicality + // dictates that you live with the default, meaning, they are the same + String keyPass = keystorePass; //Symm.randomGen(CmAgent.PASS_SIZE); + PrivateKey pk = Factory.toPrivateKey(trans, certInfo.getPrivatekey()); + addEncProperty(Config.CADI_KEY_PASSWORD, keyPass); + addProperty(Config.CADI_ALIAS, arti.getMechid()); +// Set<Attribute> attribs = new HashSet<Attribute>(); +// if(kst.equals("pkcs12")) { +// // Friendly Name +// attribs.add(new PKCS12Attribute("1.2.840.113549.1.9.20", arti.getNs())); +// } +// + KeyStore.ProtectionParameter protParam = + new KeyStore.PasswordProtection(keyPass.toCharArray()); + + KeyStore.PrivateKeyEntry pkEntry = + new KeyStore.PrivateKeyEntry(pk, trustChain); + jks.setEntry(arti.getMechid(), + pkEntry, protParam); + + // Write out + write(fks,Chmod.to400,jks,keystorePassArray); + + // Change out to TrustStore + fks = new File(dir,arti.getNs()+".trust."+kst); + jks = KeyStore.getInstance(kst); + + // Set Truststore Password + addProperty(Config.CADI_TRUSTSTORE,fks.getAbsolutePath()); + String trustStorePass = Symm.randomGen(CmAgent.PASS_SIZE); + addEncProperty(Config.CADI_TRUSTSTORE_PASSWORD,trustStorePass); + char[] truststorePassArray = trustStorePass.toCharArray(); + jks.load(null,truststorePassArray); // load in + + // Add Trusted Certificates, but PKCS12 doesn't support + for(int i=0; i<trustCAs.length;++i) { + jks.setCertificateEntry("ca_" + arti.getCa() + '_' + i, trustCAs[i]); + } + // Write out + write(fks,Chmod.to644,jks,truststorePassArray); + return true; + } catch (Exception e) { + throw new CadiException(e); + } + } + +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/PlaceArtifactOnStream.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/PlaceArtifactOnStream.java new file mode 100644 index 00000000..1ae5be94 --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/PlaceArtifactOnStream.java @@ -0,0 +1,51 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.cm; + +import java.io.PrintStream; + +import org.onap.aaf.misc.env.Trans; + +import certman.v1_0.Artifacts.Artifact; +import certman.v1_0.CertInfo; + +public class PlaceArtifactOnStream implements PlaceArtifact { + private PrintStream out; + + public PlaceArtifactOnStream(PrintStream printStream) { + out = printStream; + } + + @Override + public boolean place(Trans trans, CertInfo capi, Artifact a, String machine) { + if(capi.getNotes()!=null && capi.getNotes().length()>0) { + trans.info().printf("Warning: %s\n",capi.getNotes()); + } + out.printf("Challenge: %s\n",capi.getChallenge()); + out.printf("PrivateKey:\n%s\n",capi.getPrivatekey()); + out.println("Certificate Chain:"); + for(String c : capi.getCerts()) { + out.println(c); + } + return true; + } +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/PlaceArtifactScripts.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/PlaceArtifactScripts.java new file mode 100644 index 00000000..9347f70e --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/cm/PlaceArtifactScripts.java @@ -0,0 +1,157 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.cm; + +import java.io.File; + +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.util.Chmod; +import org.onap.aaf.misc.env.Trans; +import org.onap.aaf.misc.env.util.Chrono; +import org.onap.aaf.misc.env.util.Split; + +import certman.v1_0.Artifacts.Artifact; +import certman.v1_0.CertInfo; + +public class PlaceArtifactScripts extends ArtifactDir { + @Override + public boolean _place(Trans trans, CertInfo certInfo, Artifact arti) throws CadiException { + try { + // Setup check.sh script + String filename = arti.getNs()+".check.sh"; + File f1 = new File(dir,filename); + String email = arti.getNotification() + '\n'; + if(email.startsWith("mailto:")) { + email=email.substring(7); + } else { + email=arti.getOsUser() + '\n'; + } + + StringBuilder classpath = new StringBuilder(); + boolean first = true; + for(String pth : Split.split(File.pathSeparatorChar, System.getProperty("java.class.path"))) { + if(first) { + first=false; + } else { + classpath.append(File.pathSeparatorChar); + } + File f = new File(pth); + classpath.append(f.getCanonicalPath().replaceAll("[0-9]+\\.[0-9]+\\.[0-9]+","*")); + } + + write(f1,Chmod.to644, + "#!/bin/bash " + f1.getCanonicalPath()+'\n', + "# Certificate Manager Check Script\n", + "# Check on Certificate, and renew if needed.\n", + "# Generated by Certificate Manager " + Chrono.timeStamp()+'\n', + "DIR="+arti.getDir()+'\n', + "APP="+arti.getNs()+'\n', + "EMAIL="+email, + "CP=\""+classpath.toString()+"\"\n", + checkScript + ); + + // Setup check.sh script + File f2 = new File(dir,arti.getNs()+".crontab.sh"); + write(f2,Chmod.to644, + "#!/bin/bash " + f2.getCanonicalPath()+'\n', + "# Certificate Manager Crontab Loading Script\n", + "# Add/Update a Crontab entry, that adds a check on Certificate Manager generated Certificate nightly.\n", + "# Generated by Certificate Manager " + Chrono.timeStamp()+'\n', + "TFILE=\"/tmp/cmcron$$.temp\"\n", + "DIR=\""+arti.getDir()+"\"\n", + "CF=\""+arti.getNs()+" Certificate Check Script\"\n", + "SCRIPT=\""+f1.getCanonicalPath()+"\"\n", + cronScript + ); + + } catch (Exception e) { + throw new CadiException(e); + } + return true; + } + + /** + * Note: java.home gets Absolute Path of Java, where we probably want soft links from + * JAVA_HOME + * @return + */ + private final static String javaHome() { + String rc = System.getenv("JAVA_HOME"); + return rc==null?System.getProperty("java.home"):rc; + } + private final static String checkScript = + "> $DIR/$APP.msg\n\n" + + "function mailit {\n" + + " if [ -e /bin/mail ]; then\n" + + " MAILER=/bin/mail\n" + + " elif [ -e /usr/bin/mail ]; then \n" + + " MAILER=/usr/bin/mail\n" + + " else \n" + + " MAILER=\"\"\n" + + " fi\n" + + " if [ \"$MAILER\" = \"\" ]; then\n" + + " printf \"$*\"\n" + + " else \n" + + " printf \"$*\" | $MAILER -s \"AAF Certman Notification for `uname -n`\" $EMAIL\n"+ + " fi\n" + + "}\n\n" + + javaHome() + "/bin/" +"java -cp $CP " + + CmAgent.class.getName() + + " cadi_prop_files=$DIR/$APP.props check 2> $DIR/$APP.STDERR > $DIR/$APP.STDOUT\n" + + "case \"$?\" in\n" + + " 0)\n" + + " # Note: Validation will be mailed only the first day after any modification\n" + + " if [ \"`find $DIR -mtime 0 -name $APP.check.sh`\" != \"\" ] ; then\n" + + " mailit `echo \"Certficate Validated:\\n\\n\" | cat - $DIR/$APP.msg`\n" + + " else\n" + + " cat $DIR/$APP.msg\n" + + " fi\n" + + " ;;\n" + + " 1) mailit \"Error with Certificate Check:\\\\n\\\\nCheck logs $DIR/$APP.STDOUT and $DIR/$APP.STDERR on `uname -n`\"\n" + + " ;;\n" + + " 2) mailit `echo \"Certificate Check Error\\\\n\\\\n\" | cat - $DIR/$APP.msg`\n" + + " ;;\n" + + " 10) mailit `echo \"Certificate Replaced\\\\n\\\\n\" | cat - $DIR/$APP.msg`\n" + + " if [ -e $DIR/$APP.restart.sh ]; then\n" + + " # Note: it is THIS SCRIPT'S RESPONSIBILITY to notify upon success or failure as necessary!!\n" + + " /bin/sh $DIR/$APP.restart.sh\n" + + " fi\n" + + " ;;\n" + + " *) mailit `echo \"Unknown Error code for CM Agent\\\\n\\\\n\" | cat - $DIR/$APP.msg`\n" + + " ;;\n" + + " esac\n\n" + + " # Note: make sure to cover this sripts' exit Code\n"; + + private final static String cronScript = + "crontab -l | sed -n \"/#### BEGIN $CF/,/END $CF ####/!p\" > $TFILE\n" + + "# Note: Randomize Minutes (0-60) and hours (1-4)\n" + + "echo \"#### BEGIN $CF ####\" >> $TFILE\n" + + "echo \"$(( $RANDOM % 60)) $(( $(( $RANDOM % 3 )) + 1 )) * * * /bin/bash $SCRIPT " + + ">> $DIR/cronlog 2>&1 \" >> $TFILE\n" + + "echo \"#### END $CF ####\" >> $TFILE\n" + + "crontab $TFILE\n" + + "rm $TFILE\n"; +} + + + diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/AAFToken.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/AAFToken.java new file mode 100644 index 00000000..16bd8669 --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/AAFToken.java @@ -0,0 +1,86 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.oauth; + +import java.nio.ByteBuffer; +import java.security.SecureRandom; +import java.util.UUID; + +import org.onap.aaf.cadi.Hash; + +public class AAFToken { + private static final int CAPACITY = (Long.SIZE*2+Byte.SIZE*3)/8; + private static final SecureRandom sr = new SecureRandom(); + + public static final String toToken(UUID uuid) { + long lsb = uuid.getLeastSignificantBits(); + long msb = uuid.getMostSignificantBits(); + int sum=35; // AAF + for(int i=0;i<Long.SIZE;i+=8) { + sum+=((lsb>>i) & 0xFF); + } + for(int i=0;i<Long.SIZE;i+=8) { + sum+=((((msb>>i) & 0xFF))<<0xB); + } + sum+=(sr.nextInt()&0xEFC00000); // this is just to not leave zeros laying around + + ByteBuffer bb = ByteBuffer.allocate(CAPACITY); + bb.put((byte)sum); + bb.putLong(msb); + bb.put((byte)(sum>>8)); + bb.putLong(lsb); + bb.put((byte)(sum>>16)); + return Hash.toHexNo0x(bb.array()); + } + + public static final UUID fromToken(String token) { + byte[] bytes = Hash.fromHexNo0x(token); + if(bytes==null) { + return null; + } + ByteBuffer bb = ByteBuffer.wrap(bytes); + if(bb.capacity()!=CAPACITY ) { + return null; // not a CADI Token + } + byte b1 = bb.get(); + long msb = bb.getLong(); + byte b2 = bb.get(); + long lsb = bb.getLong(); + byte b3 = (byte)(0x3F&bb.get()); + int sum=35; + + for(int i=0;i<Long.SIZE;i+=8) { + sum+=((lsb>>i) & 0xFF); + } + for(int i=0;i<Long.SIZE;i+=8) { + sum+=((((msb>>i) & 0xFF))<<0xB); + } + + if(b1!=((byte)sum) || + b2!=((byte)(sum>>8)) || + b3!=((byte)((sum>>16)))) { + return null; // not a CADI Token + } + return new UUID(msb, lsb); + } + +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/AbsOTafLur.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/AbsOTafLur.java new file mode 100644 index 00000000..616e2dc9 --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/AbsOTafLur.java @@ -0,0 +1,130 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.oauth; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.Principal; + +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.misc.env.util.Pool; +import org.onap.aaf.misc.env.util.Pool.Creator; + +public abstract class AbsOTafLur { + protected static final String ERROR_GETTING_TOKEN_CLIENT = "Error getting TokenClient"; + protected static final String REQUIRED_FOR_OAUTH2 = " is required for OAuth Access"; + + protected final TokenMgr tkMgr; + protected final PropAccess access; + protected final String client_id; + protected static Pool<TokenClient> tokenClientPool; + + protected AbsOTafLur(final PropAccess access, final String token_url, final String introspect_url) throws CadiException { + this.access = access; + if((client_id = access.getProperty(Config.AAF_APPID,null))==null) { + throw new CadiException(Config.AAF_APPID + REQUIRED_FOR_OAUTH2); + } + + synchronized(access) { + if(tokenClientPool==null) { + tokenClientPool = new Pool<TokenClient>(new TCCreator(access)); + } + try { + tkMgr = TokenMgr.getInstance(access, token_url, introspect_url); + } catch (APIException e) { + throw new CadiException("Unable to create TokenManager",e); + } + } + } + + private class TCCreator implements Creator<TokenClient> { + private TokenClientFactory tcf; + private final int timeout; + private final String url,enc_secret; + + public TCCreator(PropAccess access) throws CadiException { + try { + tcf = TokenClientFactory.instance(access); + } catch (APIException | GeneralSecurityException | IOException e1) { + throw new CadiException(e1); + } + + if((url = access.getProperty(Config.AAF_OAUTH2_TOKEN_URL,null))==null) { + throw new CadiException(Config.AAF_OAUTH2_TOKEN_URL + REQUIRED_FOR_OAUTH2); + } + + try { + timeout = Integer.parseInt(access.getProperty(Config.AAF_CONN_TIMEOUT, Config.AAF_CONN_TIMEOUT_DEF)); + } catch (NumberFormatException e) { + throw new CadiException("Bad format for " + Config.AAF_CONN_TIMEOUT, e); + } + if((enc_secret= access.getProperty(Config.AAF_APPPASS,null))==null) { + throw new CadiException(Config.AAF_APPPASS + REQUIRED_FOR_OAUTH2); + } + } + + @Override + public TokenClient create() throws APIException { + try { + TokenClient tc = tcf.newClient(url, timeout); + tc.client_creds(client_id, access.decrypt(enc_secret, true)); + return tc; + } catch (CadiException | LocatorException | IOException e) { + throw new APIException(e); + } + } + + @Override + public void destroy(TokenClient t) { + } + + @Override + public boolean isValid(TokenClient t) { + return t!=null && t.client_id()!=null; + } + + @Override + public void reuse(TokenClient t) { + } + }; + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.Lur#destroy() + */ + public void destroy() { + tkMgr.close(); + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.Lur#clear(java.security.Principal, java.lang.StringBuilder) + */ + public void clear(Principal p, StringBuilder report) { + tkMgr.clear(p, report); + } + + + +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/HRenewingTokenSS.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/HRenewingTokenSS.java new file mode 100644 index 00000000..dc6fe390 --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/HRenewingTokenSS.java @@ -0,0 +1,104 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.oauth; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.security.GeneralSecurityException; + +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.client.Result; +import org.onap.aaf.cadi.config.SecurityInfoC; +import org.onap.aaf.cadi.http.HAuthorizationHeader; +import org.onap.aaf.cadi.principal.Kind; +import org.onap.aaf.cadi.util.FQI; +import org.onap.aaf.misc.env.APIException; + +public class HRenewingTokenSS extends HAuthorizationHeader { + private TokenClientFactory tcf; + private final TokenClient tc; + private final String[] scopes; + private final String tokenURL; + + public HRenewingTokenSS(final PropAccess access, final String tokenURL, final String ... nss) throws CadiException, IOException, GeneralSecurityException { + this(access,SecurityInfoC.instance(access, HttpURLConnection.class),tokenURL,nss); + } + + public HRenewingTokenSS(final PropAccess access, final SecurityInfoC<HttpURLConnection> si, final String tokenURL, final String ... nss) throws CadiException, IOException, GeneralSecurityException { + super(si,null,null/*Note: HeadValue overloaded */); + this.tokenURL = tokenURL; + try { + tcf = TokenClientFactory.instance(access); + tc = tcf.newClient(tokenURL); + tc.client_creds(access); + setUser(tc.client_id()); + String defaultNS = FQI.reverseDomain(tc.client_id()); + if(nss.length>0) { + boolean hasDefault = false; + for(String ns : nss) { + if(ns.equals(defaultNS)) { + hasDefault = true; + } + } + if(hasDefault) { + scopes=nss; + } else { + String[] nssPlus = new String[nss.length+1]; + nssPlus[0]=defaultNS; + System.arraycopy(nss, 0, nssPlus, 1, nss.length); + scopes = nssPlus; + } + } else { + scopes = new String[] {defaultNS}; + } + + } catch (GeneralSecurityException | IOException | LocatorException | APIException e) { + throw new CadiException(e); + } + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.client.AbsAuthentication#headValue() + */ + @Override + protected String headValue() throws IOException { + Result<TimedToken> token; + try { + token = tc.getToken(Kind.OAUTH,scopes); + if(token.isOK()) { + return "Bearer " + token.value.getAccessToken(); + } else { + throw new IOException("Token cannot be obtained: " + token.code + '-' + token.error); + } + } catch (IOException e) { + throw e; + } catch (LocatorException | CadiException | APIException e) { + throw new IOException(e); + } + } + + public String tokenURL() { + return tokenURL; + } +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/OAuth2HttpTaf.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/OAuth2HttpTaf.java new file mode 100644 index 00000000..3d5f7d9a --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/OAuth2HttpTaf.java @@ -0,0 +1,82 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.oauth; + +import java.security.NoSuchAlgorithmException; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.CachedPrincipal; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.Hash; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.CachedPrincipal.Resp; +import org.onap.aaf.cadi.Taf.LifeForm; +import org.onap.aaf.cadi.client.Result; +import org.onap.aaf.cadi.taf.HttpTaf; +import org.onap.aaf.cadi.taf.TafResp; +import org.onap.aaf.cadi.taf.TafResp.RESP; +import org.onap.aaf.misc.env.APIException; + +public class OAuth2HttpTaf implements HttpTaf { + final private Access access; + final private TokenMgr tmgr; + + public OAuth2HttpTaf(final Access access, final TokenMgr tmgr) { + this.tmgr = tmgr; + this.access = access; + } + + @Override + public TafResp validate(LifeForm reading, HttpServletRequest req, HttpServletResponse resp) { + String authz = req.getHeader("Authorization"); + if(authz != null && authz.length()>7 && authz.startsWith("Bearer ")) { + if(!req.isSecure()) { + access.log(Level.WARN,"WARNING! OAuth has been used over an insecure channel"); + } + try { + String tkn = authz.substring(7); + Result<OAuth2Principal> rp = tmgr.toPrincipal(tkn,Hash.hashSHA256(tkn.getBytes())); + if(rp.isOK()) { + return new OAuth2HttpTafResp(access,rp.value,rp.value.getName()+" authenticated by Bearer Token",RESP.IS_AUTHENTICATED,resp,false); + } else { + return new OAuth2HttpTafResp(access,null,rp.error,RESP.FAIL,resp,true); + } + } catch (APIException | CadiException | LocatorException e) { + return new OAuth2HttpTafResp(access,null,"Bearer Token invalid",RESP.FAIL,resp,true); + } catch (NoSuchAlgorithmException e) { + return new OAuth2HttpTafResp(access,null,"Security Algorithm not available",RESP.FAIL,resp,true); + } + } + return new OAuth2HttpTafResp(access,null,"No OAuth2 ",RESP.TRY_ANOTHER_TAF,resp,true); + } + + @Override + public Resp revalidate(CachedPrincipal prin,Object state) { + //TODO!!!! + return null; + } + +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/OAuth2HttpTafResp.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/OAuth2HttpTafResp.java new file mode 100644 index 00000000..7e1028a5 --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/OAuth2HttpTafResp.java @@ -0,0 +1,66 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.oauth; + +import java.io.IOException; + +import javax.servlet.http.HttpServletResponse; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.principal.TrustPrincipal; +import org.onap.aaf.cadi.taf.AbsTafResp; +import org.onap.aaf.cadi.taf.TafResp; + +public class OAuth2HttpTafResp extends AbsTafResp implements TafResp { + private HttpServletResponse httpResp; + private RESP status; + private final boolean wasFailed; + + public OAuth2HttpTafResp(Access access, OAuth2Principal principal, String desc, RESP status, HttpServletResponse resp, boolean wasFailed) { + super(access,principal, desc); + httpResp = resp; + this.status = status; + this.wasFailed = wasFailed; + } + + public OAuth2HttpTafResp(Access access, TrustPrincipal principal, String desc, RESP status,HttpServletResponse resp) { + super(access,principal, desc); + httpResp = resp; + this.status = status; + wasFailed = true; // if Trust Principal added, must be good + } + + public RESP authenticate() throws IOException { + httpResp.setStatus(401); // Unauthorized + return RESP.HTTP_REDIRECT_INVOKED; + } + + public RESP isAuthenticated() { + return status; + } + + public boolean isFailedAttempt() { + return wasFailed; + } + + +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/OAuth2Lur.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/OAuth2Lur.java new file mode 100644 index 00000000..89816a2c --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/OAuth2Lur.java @@ -0,0 +1,107 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.oauth; + +import java.security.Principal; +import java.util.List; + +import org.onap.aaf.cadi.Lur; +import org.onap.aaf.cadi.Permission; +import org.onap.aaf.cadi.aaf.AAFPermission; +import org.onap.aaf.cadi.lur.LocalPermission; +import org.onap.aaf.cadi.principal.BearerPrincipal; +import org.onap.aaf.misc.env.util.Split; + +public class OAuth2Lur implements Lur { + private TokenMgr tm; + + public OAuth2Lur(TokenMgr tm) { + this.tm = tm; + } + + @Override + public Permission createPerm(String p) { + String[] params = Split.split('|', p); + if(params.length==3) { + return new AAFPermission(params[0],params[1],params[2]); + } else { + return new LocalPermission(p); + } + } + + @Override + public boolean fish(Principal bait, Permission pond) { + AAFPermission apond = (AAFPermission)pond; + OAuth2Principal oap; + if(bait instanceof OAuth2Principal) { + oap = (OAuth2Principal)bait; + } else { + // Here is the spot to put in Principal Conversions + return false; + } + + TokenPerm tp = oap.tokenPerm(); + if(tp==null) { + } else { + for(Permission p : tp.perms()) { + if(p.match(apond)) { + return true; + } + } + } + return false; + } + + @Override + public void fishAll(Principal bait, List<Permission> permissions) { + OAuth2Principal oap = (OAuth2Principal)bait; + TokenPerm tp = oap.tokenPerm(); + if(tp!=null) { + for(AAFPermission p : tp.perms()) { + permissions.add(p); + } + } + } + + @Override + public void destroy() { + } + + @Override + public boolean handlesExclusively(Permission pond) { + return false; + } + + @Override + public boolean handles(Principal p) { + if(p!=null && p instanceof BearerPrincipal) { + return ((BearerPrincipal)p).getBearer()!=null; + } + return false; + } + + @Override + public void clear(Principal p, StringBuilder report) { + tm.clear(p,report); + } + +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/OAuth2Principal.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/OAuth2Principal.java new file mode 100644 index 00000000..90d59635 --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/OAuth2Principal.java @@ -0,0 +1,54 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.oauth; + +import org.onap.aaf.cadi.principal.TaggedPrincipal; + +public class OAuth2Principal extends TaggedPrincipal { + private TokenPerm tp; +// private byte[] hash; // hashed cred for disk validation + + public OAuth2Principal(TokenPerm tp, byte[] hash) { + this.tp = tp; +// this.hash = hash; + } + + @Override + public String getName() { + return tp.getUsername(); + } + + public TokenPerm tokenPerm() { + return tp; + } + + @Override + public String tag() { + return "OAuth"; + } + + @Override + public String personalName() { + return tp.getUsername(); + } + +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TimedToken.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TimedToken.java new file mode 100644 index 00000000..d4f343f9 --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TimedToken.java @@ -0,0 +1,132 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.oauth; + +import java.nio.file.Path; + +import org.onap.aaf.cadi.persist.Persist; +import org.onap.aaf.cadi.persist.Persistable; +import org.onap.aaf.cadi.persist.Persisting; + +import aafoauth.v2_0.Token; + +/** + * TimedToken + * Tokens come from the Token Server with an "Expired In" setting. This class will take that, and + * create a date from time of Creation, which works with local code. + * + * We create a Derived class, so that it can be used as is the originating Token type. + * + * "expired" is local computer time + * @author Jonathan + * + */ +// Package on purpose +public class TimedToken extends Token implements Persistable<Token> { + private Persisting<Token> cacheable; // no double inheritance... + +// public TimedToken(Token t, byte[] hash) { +// this(t,(System.currentTimeMillis()/1000)+t.getExpiresIn(),hash,null); +// } +// + public TimedToken(Persist<Token,?> p, Token t, byte[] hash, Path path){ + this(p,t,t.getExpiresIn()+(System.currentTimeMillis()/1000),hash, path); + } + + public TimedToken(Persist<Token,?> p, Token t, long expires_secsFrom1970, byte[] hash, Path path) { + cacheable = new Persisting<Token>(p, t,expires_secsFrom1970, hash, path); + accessToken=t.getAccessToken(); + expiresIn=t.getExpiresIn(); + refreshToken=t.getRefreshToken(); + scope = t.getScope(); + state = t.getState(); + tokenType = t.getTokenType(); + } + + + @Override + public Token get() { + return cacheable.get(); + } + + @Override + public boolean checkSyncTime() { + return cacheable.checkSyncTime(); + } + + @Override + public boolean checkReloadable() { + return cacheable.checkReloadable(); + } + + @Override + public boolean hasBeenTouched() { + return cacheable.hasBeenTouched(); + } + + @Override + public long expires() { + return cacheable.expires(); + } + + @Override + public boolean expired() { + return cacheable.expired(); + } + + @Override + public boolean match(byte[] hashIn) { + return cacheable.match(hashIn); + } + + @Override + public byte[] getHash() { + return cacheable.getHash(); + } + + @Override + public void inc() { + cacheable.inc(); + } + + @Override + public int count() { + return cacheable.count(); + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.oauth.Persistable#clearCount() + */ + @Override + public void clearCount() { + cacheable.clearCount(); + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.persist.Persistable#path() + */ + @Override + public Path path() { + return cacheable.path(); + } + +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TokenClient.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TokenClient.java new file mode 100644 index 00000000..4b0c944c --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TokenClient.java @@ -0,0 +1,474 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.oauth; + +import java.io.IOException; +import java.net.ConnectException; +import java.net.URLEncoder; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.List; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.Hash; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.SecuritySetter; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.aaf.v2_0.AAFCon; +import org.onap.aaf.cadi.aaf.v2_0.AAFCon.GetSetter; +import org.onap.aaf.cadi.client.Future; +import org.onap.aaf.cadi.client.Rcli; +import org.onap.aaf.cadi.client.Result; +import org.onap.aaf.cadi.client.Retryable; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.persist.Persist.Loader; +import org.onap.aaf.cadi.principal.Kind; +import org.onap.aaf.cadi.util.FQI; +import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.misc.rosetta.env.RosettaDF; + +import aafoauth.v2_0.Introspect; +import aafoauth.v2_0.Token; + +public class TokenClient { + private static final String UTF_8 = "UTF-8"; + + public enum AUTHN_METHOD {client_credentials,password,payload,basic_auth,certificate,refresh_token, none} + + private final TokenClientFactory factory; + private final AAFCon<?> tkCon; + private static RosettaDF<Token> tokenDF; + protected static RosettaDF<Introspect> introspectDF; + + + private int timeout; + private String client_id, username; + private byte[] enc_client_secret, enc_password; + + private GetSetter ss; + private AUTHN_METHOD authn_method; + private byte[] hash; + private final char okind; + private String default_scope; + + // Package on Purpose + TokenClient(char okind, final TokenClientFactory tcf, final AAFCon<?> tkCon, final int timeout, AUTHN_METHOD am) throws CadiException, APIException { + this.okind = okind; + factory = tcf; + this.tkCon = tkCon; + this.timeout = timeout; + ss = null; + authn_method = am; + synchronized(tcf) { + if(introspectDF==null) { + tokenDF = tkCon.env().newDataFactory(Token.class); + introspectDF = tkCon.env().newDataFactory(Introspect.class); + } + } + + } + + public void client_id(String client_id) { + this.client_id = client_id; + default_scope = FQI.reverseDomain(client_id); + } + + public String client_id() { + return client_id; + } + + /** + * This scope based on client_id... the App configured for call + * @return + */ + public String defaultScope() { + return default_scope; + } + + public void client_creds(Access access) throws CadiException { + if(okind=='A') { + client_creds(access.getProperty(Config.AAF_APPID, null),access.getProperty(Config.AAF_APPPASS, null)); + } else { + client_creds(access.getProperty(Config.AAF_ALT_CLIENT_ID, null),access.getProperty(Config.AAF_ALT_CLIENT_SECRET, null)); + } + } + + /** + * Note: OAuth2 provides for normal Authentication parameters when getting tokens. Basic Auth is one such valid + * way to get Credentials. However, support is up to the OAuth2 Implementation + * + * This method is for setting an App's creds (client) to another App. + * + * @param client_id + * @param client_secret + * @throws IOException + */ + public void client_creds(final String client_id, final String client_secret) throws CadiException { + if(client_id==null) { + throw new CadiException(Config.AAF_ALT_CLIENT_ID + " is null"); + } + this.client_id = client_id; + default_scope = FQI.reverseDomain(client_id); + + if(client_secret!=null) { + try { + if(client_secret.startsWith("enc:")) { + final String temp = factory.access.decrypt(client_secret, false); // this is a more powerful, but non-thread-safe encryption + hash = Hash.hashSHA256(temp.getBytes()); + this.enc_client_secret = factory.symm.encode(temp.getBytes()); + ss = new GetSetter() { + @Override + public <CLIENT> SecuritySetter<CLIENT> get(AAFCon<CLIENT> con) throws CadiException { + return con.basicAuth(client_id, temp);// Base class encrypts password + } + }; + } else { + byte[] temp = client_secret.getBytes(); + hash = Hash.hashSHA256(temp); + this.enc_client_secret = factory.symm.encode(temp); + ss = new GetSetter() { + @Override + public <CLIENT> SecuritySetter<CLIENT> get(AAFCon<CLIENT> con) throws CadiException { + return con.basicAuth(client_id, client_secret);// Base class encrypts password + } + }; + } + authn_method = AUTHN_METHOD.client_credentials; + } catch(IOException | NoSuchAlgorithmException e) { + throw new CadiException(e); + } + } + } + + public void username(String username) { + this.username = username; + } + + /** + * Note: OAuth2 provides for normal Authentication parameters when getting tokens. Basic Auth is one such valid + * way to get Credentials. However, support is up to the OAuth2 Implementation + * + * This method is for setting the End-User's Creds + * + * @param client_id + * @param client_secret + * @throws IOException + */ + public void password(final String user, final String password) throws CadiException { + this.username = user; + if(password!=null) { + try { + if(password.startsWith("enc:")) { + final String temp = factory.access.decrypt(password, false); // this is a more powerful, but non-thread-safe encryption + hash = Hash.hashSHA256(temp.getBytes()); + this.enc_password = factory.symm.encode(temp.getBytes()); + ss = new GetSetter() { + @Override + public <CLIENT> SecuritySetter<CLIENT> get(AAFCon<CLIENT> con) throws CadiException { + return con.basicAuth(user, temp);// Base class encrypts password + } + }; + } else { + byte[] temp = password.getBytes(); + hash = Hash.hashSHA256(temp); + this.enc_password = factory.symm.encode(temp); + ss = new GetSetter() { + @Override + public <CLIENT> SecuritySetter<CLIENT> get(AAFCon<CLIENT> con) throws CadiException { + return con.basicAuth(user, password);// Base class encrypts password + } + }; + } + authn_method = AUTHN_METHOD.password; + } catch (IOException | NoSuchAlgorithmException e) { + throw new CadiException(e); + } + } + } + + public void clearEndUser() { + username = null; + enc_password = null; + if(client_id!=null && enc_client_secret!=null) { + authn_method = AUTHN_METHOD.client_credentials; + } else { + authn_method = AUTHN_METHOD.password; + } + } + + public Result<TimedToken> getToken(final String ... scopes) throws LocatorException, CadiException, APIException { + return getToken(Kind.OAUTH,scopes); + } + + public void clearToken(final String ... scopes) throws CadiException { + clearToken(Kind.OAUTH,scopes); + } + + public void clearToken(final char kind, final String ... scopes) throws CadiException { + final String scope = addScope(scopes); + char c; + if(kind==Kind.OAUTH) { + c = okind; + } else { + c = kind; + } + final String key = TokenClientFactory.getKey(c,client_id,username,hash,scope); + factory.delete(key); + } + /** + * Get AuthToken + * @throws APIException + * @throws CadiException + * @throws LocatorException + */ + public Result<TimedToken> getToken(final char kind, final String ... scopes) throws LocatorException, CadiException, APIException { + final String scope = addScope(scopes); + char c; + if(kind==Kind.OAUTH) { + c = okind; + } else { + c = kind; + } + final String key = TokenClientFactory.getKey(c,client_id,username,hash,scope); + if(ss==null) { + throw new APIException("client_creds(...) must be set before obtaining Access Tokens"); + } + + Result<TimedToken> rtt = factory.get(key,hash,new Loader<TimedToken>() { + @Override + public Result<TimedToken> load(final String key) throws APIException, CadiException, LocatorException { + final List<String> params = new ArrayList<String>(); + params.add(scope); + addSecurity(params,authn_method); + + final String paramsa[] = new String[params.size()]; + params.toArray(paramsa); + Result<Token> rt = tkCon.best(new Retryable<Result<Token>>() { + @Override + public Result<Token> code(Rcli<?> client) throws CadiException, ConnectException, APIException { + // /token?grant_type=client_credential&scope=com.att.aaf+com.att.test + Future<Token> f = client.postForm(null,tokenDF,paramsa); + if(f.get(timeout)) { + return Result.ok(f.code(),f.value); + } else { + return Result.err(f.code(), f.body()); + } + } + }); + + if(rt.isOK()) { + try { + return Result.ok(rt.code,factory.putTimedToken(key,rt.value, hash)); + } catch (IOException e) { + // TODO What to do here? + e.printStackTrace(); + return Result.err(999,e.getMessage()); + } + } else { + return Result.err(rt); + } + } + }); + if(rtt.isOK()) { // not validated for Expired + TimedToken tt = rtt.value; + if(tt.expired()) { + rtt = refreshToken(tt); + if(rtt.isOK()) { + tkCon.access.printf(Level.INFO, "Refreshed token %s to %s",tt.getAccessToken(),rtt.value.getAccessToken()); + return Result.ok(200,rtt.value); + } else { + tkCon.access.printf(Level.INFO, "Expired token %s cannot be renewed %d %s",tt.getAccessToken(),rtt.code,rtt.error); + factory.delete(key); + tt=null; + } + } else { + return Result.ok(200,tt); + } + } else { + Result.err(rtt); + } + return Result.err(404,"Not Found"); + } + + public Result<TimedToken> refreshToken(Token token) throws APIException, LocatorException, CadiException { + if(ss==null) { + throw new APIException("client_creds(...) must be set before obtaining Access Tokens"); + } + final List<String> params = new ArrayList<String>(); + params.add("refresh_token="+token.getRefreshToken()); + addSecurity(params,AUTHN_METHOD.refresh_token); + final String scope="scope="+token.getScope().replace(' ', '+'); + params.add(scope); + + final String paramsa[] = new String[params.size()]; + params.toArray(paramsa); + Result<Token> rt = tkCon.best(new Retryable<Result<Token>>() { + @Override + public Result<Token> code(Rcli<?> client) throws CadiException, ConnectException, APIException { + // /token?grant_type=client_credential&scope=com.att.aaf+com.att.test + Future<Token> f = client.postForm(null,tokenDF,paramsa); + if(f.get(timeout)) { + return Result.ok(f.code(),f.value); + } else { + return Result.err(f.code(), f.body()); + } + } + }); + String key = TokenClientFactory.getKey(okind,client_id, username, hash, scope); + if(rt.isOK()) { + try { + return Result.ok(200,factory.putTimedToken(key, rt.value, hash)); + } catch (IOException e) { + //TODO what to do here? + return Result.err(999, e.getMessage()); + } + } else if(rt.code==404) { + factory.deleteFromDisk(key); + } + return Result.err(rt); + } + + public Result<Introspect> introspect(final String token) throws APIException, LocatorException, CadiException { + if(ss==null) { + throw new APIException("client_creds(...) must be set before introspecting Access Tokens"); + } + + return tkCon.best(new Retryable<Result<Introspect>>() { + @Override + public Result<Introspect> code(Rcli<?> client) throws CadiException, ConnectException, APIException { + final List<String> params = new ArrayList<String>(); + params.add("token="+token); + addSecurity(params,AUTHN_METHOD.client_credentials); + final String paramsa[] = new String[params.size()]; + params.toArray(paramsa); + // /token?grant_type=client_credential&scope=com.att.aaf+com.att.test + Future<Introspect> f = client.postForm(null,introspectDF,paramsa); + if(f.get(timeout)) { + return Result.ok(f.code(),f.value); + } else { + return Result.err(f.code(), f.body()); + } + } + } + ); + } + + private String addScope(String[] scopes) { + String rv = null; + StringBuilder scope=null; + boolean first = true; + for(String s : scopes) { + if(first) { + scope = new StringBuilder(); + scope.append("scope="); + first=false; + } else { + scope.append('+'); + } + scope.append(s); + } + if(scope!=null) { + rv=scope.toString(); + } + return rv; + } + + private void addSecurity(List<String> params, AUTHN_METHOD authn) throws APIException { + // Set GrantType... different than Credentials + switch(authn) { + case client_credentials: + params.add("grant_type=client_credentials"); + break; + case password: + params.add("grant_type=password"); + break; + case refresh_token: + params.add("grant_type=refresh_token"); + break; + case none: + break; + default: + // Nothing to do + break; + } + + // Set Credentials appropriate + switch(authn_method) { + case client_credentials: + if(client_id!=null) { + params.add("client_id="+client_id); + } + + if(enc_client_secret!=null) { + try { + params.add("client_secret="+URLEncoder.encode(new String(factory.symm.decode(enc_client_secret)),UTF_8)); + } catch (IOException e) { + throw new APIException("Error Decrypting Password",e); + } + } + break; + case refresh_token: + if(client_id!=null) { + params.add("client_id="+client_id); + } + + if(enc_client_secret!=null) { + try { + params.add("client_secret="+URLEncoder.encode(new String(factory.symm.decode(enc_client_secret)),UTF_8)); + } catch (IOException e) { + throw new APIException("Error Decrypting Password",e); + } + } + break; + + case password: + if(client_id!=null) { + params.add("client_id="+client_id); + } + + if(enc_client_secret!=null) { + try { + params.add("client_secret="+ URLEncoder.encode(new String(factory.symm.decode(enc_client_secret)),UTF_8)); + } catch (IOException e) { + throw new APIException("Error Decrypting Password",e); + } + } + if(username!=null) { + params.add("username="+username); + } + + if(enc_password!=null) { + try { + params.add("password="+ URLEncoder.encode(new String(factory.symm.decode(enc_password)),UTF_8)); + } catch (IOException e) { + throw new APIException("Error Decrypting Password",e); + } + } + + break; + default: + // Nothing to do + break; + } + } +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TokenClientFactory.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TokenClientFactory.java new file mode 100644 index 00000000..3f6fa599 --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TokenClientFactory.java @@ -0,0 +1,170 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.oauth; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.Path; +import java.security.GeneralSecurityException; +import java.security.NoSuchAlgorithmException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Pattern; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.Hash; +import org.onap.aaf.cadi.Locator; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.Symm; +import org.onap.aaf.cadi.aaf.v2_0.AAFConHttp; +import org.onap.aaf.cadi.aaf.v2_0.AAFLocator; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.config.SecurityInfoC; +import org.onap.aaf.cadi.locator.PropertyLocator; +import org.onap.aaf.cadi.oauth.TokenClient.AUTHN_METHOD; +import org.onap.aaf.cadi.persist.Persist; +import org.onap.aaf.cadi.principal.Kind; +import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.misc.rosetta.env.RosettaEnv; + +import aafoauth.v2_0.Token; + +public class TokenClientFactory extends Persist<Token,TimedToken> { + private static TokenClientFactory instance; + private Map<String,AAFConHttp> aafcons = new ConcurrentHashMap<String, AAFConHttp>(); + private SecurityInfoC<HttpURLConnection> hsi; + // Package on purpose + final Symm symm; + + private TokenClientFactory(Access pa) throws APIException, GeneralSecurityException, IOException, CadiException { + super(pa, new RosettaEnv(pa.getProperties()),Token.class,"outgoing"); + symm = Symm.encrypt.obtain(); + hsi = SecurityInfoC.instance(access, HttpURLConnection.class); + } + + public synchronized static final TokenClientFactory instance(Access access) throws APIException, GeneralSecurityException, IOException, CadiException { + if(instance==null) { + instance = new TokenClientFactory(access); + } + return instance; + } + + /** + * Pickup Timeout from Properties + * + * @param tagOrURL + * @return + * @throws CadiException + * @throws LocatorException + * @throws APIException + */ + public<INTR> TokenClient newClient(final String tagOrURL) throws CadiException, LocatorException, APIException { + return newClient(tagOrURL,Integer.parseInt(access.getProperty(Config.AAF_CONN_TIMEOUT, Config.AAF_CONN_TIMEOUT_DEF))); + } + + public<INTR> TokenClient newClient(final String tagOrURL, final int timeout) throws CadiException, LocatorException, APIException { + AAFConHttp ach; + if(tagOrURL==null) { + throw new CadiException("parameter tagOrURL cannot be null."); + } else { + ach = aafcons.get(tagOrURL); + if(ach==null) { + aafcons.put(tagOrURL, ach=new AAFConHttp(access,tagOrURL)); + } + } + char okind; + if(Config.AAF_OAUTH2_TOKEN_URL.equals(tagOrURL) || + tagOrURL.equals(access.getProperty(Config.AAF_OAUTH2_TOKEN_URL, null))) { + okind = Kind.AAF_OAUTH; + } else { + okind = Kind.OAUTH; + } + return new TokenClient( + okind, + this, + ach, + timeout, + AUTHN_METHOD.none); + } + + public TzClient newTzClient(final String locatorURL) throws CadiException, LocatorException { + try { + return new TzHClient(access,hsi,bestLocator(locatorURL)); + } catch (URISyntaxException e) { + throw new LocatorException(e); + } + } + + static String getKey(char tokenSource,String client_id, String username, byte[] hash, String scope) throws CadiException { + try { + StringBuilder sb = new StringBuilder(client_id); + sb.append('_'); + if(username!=null) { + sb.append(username); + } + sb.append('_'); + sb.append(tokenSource); + byte[] tohash=scope.getBytes(); + if(hash!=null && hash.length>0) { + byte temp[] = new byte[hash.length+tohash.length]; + System.arraycopy(tohash, 0, temp, 0, tohash.length); + System.arraycopy(hash, 0, temp, tohash.length, hash.length); + tohash = temp; + } + if(scope!=null && scope.length()>0) { + sb.append(Hash.toHexNo0x(Hash.hashSHA256(tohash))); + } + return sb.toString(); + } catch (NoSuchAlgorithmException e) { + throw new CadiException(e); + } + } + + @Override + protected TimedToken newCacheable(Token t, long expires, byte[] hash, Path path) throws IOException { + return new TimedToken(this,t,expires,hash,path); + } + + public TimedToken putTimedToken(String key, Token token, byte[] hash) throws IOException, CadiException { + TimedToken tt = new TimedToken(this,token,token.getExpiresIn()+(System.currentTimeMillis()/1000),hash,getPath(key)); + put(key,tt); + return tt; + } + + private static final Pattern locatePattern = Pattern.compile("https://.*/locate/.*"); + public Locator<URI> bestLocator(final String locatorURL ) throws LocatorException, URISyntaxException { + if(locatorURL==null) { + throw new LocatorException("Cannot have a null locatorURL in bestLocator"); + } + if(locatePattern.matcher(locatorURL).matches()) { + return new AAFLocator(hsi,new URI(locatorURL)); + } else if(locatorURL.contains("//DME2RESOLVE/")) { + throw new LocatorException("DME2Locator doesn't exist. Use DME2 specific Clients"); + } else { + return new PropertyLocator(locatorURL); + } + // Note: Removed DME2Locator... If DME2 client is needed, use DME2Clients + } +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TokenMgr.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TokenMgr.java new file mode 100644 index 00000000..d8fd88f6 --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TokenMgr.java @@ -0,0 +1,193 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.oauth; + +import java.io.IOException; +import java.nio.file.Path; +import java.security.GeneralSecurityException; +import java.security.Principal; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.client.Result; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.persist.Persist; +import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.misc.rosetta.env.RosettaDF; +import org.onap.aaf.misc.rosetta.env.RosettaEnv; + +import aaf.v2_0.Perms; +import aafoauth.v2_0.Introspect; + +public class TokenMgr extends Persist<Introspect, TokenPerm> { + protected static Map<String,TokenPerm> tpmap = new ConcurrentHashMap<String, TokenPerm>(); + protected static Map<String,TokenMgr> tmmap = new HashMap<String, TokenMgr>(); // synchronized in getInstance + protected static Map<String,String> currentToken = new HashMap<String,String>(); // synchronized in getTP + public static RosettaDF<Perms> permsDF; + public static RosettaDF<Introspect> introspectDF; + + private final TokenPermLoader tpLoader; + + private TokenMgr(PropAccess access, String tokenURL, String introspectURL) throws APIException, CadiException { + super(access,new RosettaEnv(access.getProperties()),Introspect.class,"introspect"); + synchronized(access) { + if(permsDF==null) { + permsDF = env.newDataFactory(Perms.class); + introspectDF = env.newDataFactory(Introspect.class); + } + } + if("dbToken".equals(tokenURL) && "dbIntrospect".equals(introspectURL)) { + tpLoader = new TokenPermLoader() { // null Loader + @Override + public Result<TokenPerm> load(String accessToken, byte[] cred) + throws APIException, CadiException, LocatorException { + return Result.err(404, "DBLoader"); + } + }; + } else { + RemoteTokenPermLoader rtpl = new RemoteTokenPermLoader(tokenURL, introspectURL); // default is remote + String i = access.getProperty(Config.AAF_APPID,null); + String p = access.getProperty(Config.AAF_APPPASS, null); + if(i==null || p==null) { + throw new CadiException(Config.AAF_APPID + " and " + Config.AAF_APPPASS + " must be set to initialize TokenMgr"); + } + rtpl.introCL.client_creds(i,p); + tpLoader = rtpl; + } + } + + private TokenMgr(PropAccess access, TokenPermLoader tpl) throws APIException, CadiException { + super(access,new RosettaEnv(access.getProperties()),Introspect.class,"incoming"); + synchronized(access) { + if(permsDF==null) { + permsDF = env.newDataFactory(Perms.class); + introspectDF = env.newDataFactory(Introspect.class); + } + } + tpLoader = tpl; + } + + public static synchronized TokenMgr getInstance(final PropAccess access, final String tokenURL, final String introspectURL) throws APIException, CadiException { + String key; + TokenMgr tm = tmmap.get(key=tokenURL+'/'+introspectURL); + if(tm==null) { + tmmap.put(key, tm=new TokenMgr(access,tokenURL,introspectURL)); + } + return tm; + } + + public Result<OAuth2Principal> toPrincipal(final String accessToken, final byte[] hash) throws APIException, CadiException, LocatorException { + Result<TokenPerm> tp = get(accessToken, hash, new Loader<TokenPerm>() { + @Override + public Result<TokenPerm> load(String key) throws APIException, CadiException, LocatorException { + try { + return tpLoader.load(accessToken,hash); + } catch (APIException | LocatorException e) { + throw new CadiException(e); + } + } + }); + if(tp.isOK()) { + return Result.ok(200, new OAuth2Principal(tp.value,hash)); + } else { + return Result.err(tp); + } + } + + public Result<TokenPerm> get(final String accessToken, final byte[] hash) throws APIException, CadiException, LocatorException { + return get(accessToken,hash,new Loader<TokenPerm>() { + @Override + public Result<TokenPerm> load(String key) throws APIException, CadiException, LocatorException { + return tpLoader.load(key,hash); + } + + }); +// return tpLoader.load(accessToken,hash); + } + + public interface TokenPermLoader{ + public Result<TokenPerm> load(final String accessToken, final byte[] cred) throws APIException, CadiException, LocatorException; + } + + private class RemoteTokenPermLoader implements TokenPermLoader { + private TokenClientFactory tcf; + private TokenClient tokenCL, introCL; + + public RemoteTokenPermLoader(final String tokenURL, final String introspectURL) throws APIException, CadiException { + try { + tcf = TokenClientFactory.instance(access); + int timeout = Integer.parseInt(access.getProperty(Config.AAF_CONN_TIMEOUT, Config.AAF_CONN_TIMEOUT_DEF)); + tokenCL = tcf.newClient(tokenURL, + timeout); + if(introspectURL.equals(tokenURL)) { + introCL = tokenCL; + } else { + introCL = tcf.newClient(introspectURL, + timeout); + } + + } catch (GeneralSecurityException | IOException | NumberFormatException | LocatorException e) { + throw new CadiException(e); + } + } + + public Result<TokenPerm> load(final String accessToken, final byte[] cred) throws APIException, CadiException, LocatorException { + long start = System.currentTimeMillis(); + try { + Result<Introspect> ri = introCL.introspect(accessToken); + if(ri.isOK()) { + return Result.ok(ri.code, new TokenPerm(TokenMgr.this,permsDF,ri.value,cred,getPath(accessToken))); + } else { + return Result.err(ri); + } + } finally { + access.printf(Level.INFO, "Token loaded in %d ms",System.currentTimeMillis()-start); + } + } + } + + public void clear(Principal p, StringBuilder report) { + TokenPerm tp = tpmap.remove(p.getName()); + if(tp==null) { + report.append("Nothing to clear"); + } else { + report.append("Cleared "); + report.append(p.getName()); + } + } + + @Override + protected TokenPerm newCacheable(Introspect i, long expires, byte[] hash, Path path) throws APIException { + // Note: Introspect drives the Expiration... ignoring expires. + return new TokenPerm(this,permsDF,i,hash,path); + } + + public TokenPerm putIntrospect(Introspect intro, byte[] cred) throws APIException { + return newCacheable(intro, intro.getExp(), cred, getPath(intro.getAccessToken())); + } + +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TokenPerm.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TokenPerm.java new file mode 100644 index 00000000..4a0259a4 --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TokenPerm.java @@ -0,0 +1,171 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.oauth; + +import java.io.Reader; +import java.io.StringReader; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +import org.onap.aaf.cadi.aaf.AAFPermission; +import org.onap.aaf.cadi.persist.Persist; +import org.onap.aaf.cadi.persist.Persisting; +import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.misc.rosetta.InJson; +import org.onap.aaf.misc.rosetta.Parse; +import org.onap.aaf.misc.rosetta.ParseException; +import org.onap.aaf.misc.rosetta.Parsed; +import org.onap.aaf.misc.rosetta.InJson.State; +import org.onap.aaf.misc.rosetta.env.RosettaDF; + +import aaf.v2_0.Perms; +import aafoauth.v2_0.Introspect; + +public class TokenPerm extends Persisting<Introspect>{ + private static final List<AAFPermission> NULL_PERMS = new ArrayList<AAFPermission>(); + private Introspect introspect; + private List<AAFPermission> perms; + private String scopes; + public TokenPerm(Persist<Introspect,?> p, RosettaDF<Perms> permsDF, Introspect ti, byte[] hash, Path path) throws APIException { + super(p,ti,ti.getExp(),hash,path); // ti.getExp() is seconds after Jan 1, 1970 ) + this.introspect = ti; + if(ti.getContent()==null || ti.getContent().length()==0) { + perms = NULL_PERMS; + } else { + LoadPermissions lp; + try { + lp = new LoadPermissions(new StringReader(ti.getContent())); + perms = lp.perms; + } catch (ParseException e) { + throw new APIException("Error parsing Content",e); + } + } + scopes = ti.getScope(); + } + + public List<AAFPermission> perms() { + return perms; + } + + public String getClientId() { + return introspect.getClientId(); + } + + public String getUsername() { + return introspect.getUsername(); + } + + public String getToken() { + return introspect.getAccessToken(); + } + + public synchronized String getScopes() { + return scopes; + } + + public Introspect getIntrospect() { + return introspect; + } + + // Direct Parse Perms into List + public static class LoadPermissions { + public List<AAFPermission> perms; + + public LoadPermissions(Reader r) throws ParseException { + PermInfo pi = new PermInfo(); + InJson ij = new InJson(); + Parsed<State> pd = ij.newParsed(); + boolean inPerms = false, inPerm = false; + while((pd = ij.parse(r,pd.reuse())).valid()) { + switch(pd.event) { + case Parse.START_DOC: + perms = new ArrayList<AAFPermission>(); + break; + case Parse.START_ARRAY: + inPerms = "perm".equals(pd.name); + break; + case '{': + if(inPerms) { + inPerm=true; + pi.clear(); + } + break; + case ',': + if(inPerm) { + pi.eval(pd); + } + break; + case '}': + if(inPerms) { + if(inPerm) { + pi.eval(pd); + AAFPermission perm = pi.create(); + if(perm!=null) { + perms.add(perm); + } + } + inPerm=false; + } + break; + case Parse.END_ARRAY: + if(inPerms) { + inPerms=false; + } + break; + case Parse.END_DOC: + break; + } + } + } + } + + // Gathering object for parsing objects, then creating AAF Permission + private static class PermInfo { + public String type,instance,action; + public void clear() { + type=instance=action=null; + } + public void eval(Parsed<State> pd) { + if(pd.hasName()) { + switch(pd.name) { + case "type": + type=pd.sb.toString(); + break; + case "instance": + instance=pd.sb.toString(); + break; + case "action": + action=pd.sb.toString(); + break; + } + } + } + public AAFPermission create() { + if(type!=null && instance!=null && action !=null) { + return new AAFPermission(type, instance, action); + } else { + return null; + } + } + } +}
\ No newline at end of file diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TzClient.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TzClient.java new file mode 100644 index 00000000..a14c0f8e --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TzClient.java @@ -0,0 +1,40 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.oauth; + +import java.io.IOException; + +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.client.Retryable; +import org.onap.aaf.misc.env.APIException; + +/** + * TimedToken Client + * + * @author Jonathan + * + */ +public abstract class TzClient { + public abstract void setToken(final String client_id, final TimedToken token) throws IOException; + public abstract <RET> RET best(Retryable<RET> rcode) throws CadiException, LocatorException, APIException; +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TzHClient.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TzHClient.java new file mode 100644 index 00000000..c565fa84 --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/oauth/TzHClient.java @@ -0,0 +1,82 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.oauth; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URISyntaxException; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.Locator; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.SecuritySetter; +import org.onap.aaf.cadi.aaf.v2_0.AAFLocator; +import org.onap.aaf.cadi.client.Retryable; +import org.onap.aaf.cadi.config.SecurityInfoC; +import org.onap.aaf.cadi.http.HMangr; +import org.onap.aaf.cadi.http.HTokenSS; +import org.onap.aaf.misc.env.APIException; + +/** + * Tokenized HClient + * + * @author Jonathan + * + */ +public class TzHClient extends TzClient { + private HMangr hman; + public SecurityInfoC<HttpURLConnection> si; + private TimedToken token; + private SecuritySetter<HttpURLConnection> tokenSS; + + public TzHClient(Access access, String tagOrURL) throws CadiException, LocatorException { + try { + si = SecurityInfoC.instance(access, HttpURLConnection.class); + hman = new HMangr(access, new AAFLocator(si,new URI(access.getProperty(tagOrURL, tagOrURL)))); + } catch (URISyntaxException e) { + throw new CadiException(e); + } + } + public TzHClient(Access access, SecurityInfoC<HttpURLConnection> hsi, Locator<URI> loc) throws LocatorException { + si = hsi; + hman = new HMangr(access, loc); + } + + public void setToken(final String client_id, TimedToken token) throws IOException { + this.token = token; + tokenSS = new HTokenSS(si, client_id, token.getAccessToken()); + } + + public <RET> RET best (Retryable<RET> retryable) throws CadiException, LocatorException, APIException { + if(token == null || tokenSS==null) { + throw new CadiException("OAuth2 Token has not been set"); + } + if(token.expired()) { + //TODO Refresh? + throw new CadiException("Expired Token"); + } else { + return hman.best(tokenSS, retryable); + } + } +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/obasic/OBasicHttpTaf.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/obasic/OBasicHttpTaf.java new file mode 100644 index 00000000..ff0c246b --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/obasic/OBasicHttpTaf.java @@ -0,0 +1,196 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.obasic; + +import java.io.IOException; +import java.security.NoSuchAlgorithmException; +import java.security.Principal; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.onap.aaf.cadi.BasicCred; +import org.onap.aaf.cadi.CachedPrincipal; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.CredVal; +import org.onap.aaf.cadi.Hash; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.Symm; +import org.onap.aaf.cadi.Taf; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.CachedPrincipal.Resp; +import org.onap.aaf.cadi.CredVal.Type; +import org.onap.aaf.cadi.client.Result; +import org.onap.aaf.cadi.oauth.AbsOTafLur; +import org.onap.aaf.cadi.oauth.OAuth2Principal; +import org.onap.aaf.cadi.oauth.TimedToken; +import org.onap.aaf.cadi.oauth.TokenClient; +import org.onap.aaf.cadi.taf.HttpTaf; +import org.onap.aaf.cadi.taf.TafResp; +import org.onap.aaf.cadi.taf.TafResp.RESP; +import org.onap.aaf.cadi.taf.basic.BasicHttpTafResp; +import org.onap.aaf.cadi.util.FQI; +import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.misc.env.util.Pool.Pooled; + +/** + * BasicHttpTaf + * + * This TAF implements the "Basic Auth" protocol. + * + * WARNING! It is true for any implementation of "Basic Auth" that the password is passed unencrypted. + * This is because the expectation, when designed years ago, was that it would only be used in + * conjunction with SSL (https). It is common, however, for users to ignore this on the assumption that + * their internal network is secure, or just ignorance. Therefore, a WARNING will be printed + * when the HTTP Channel is not encrypted (unless explicitly turned off). + * + * @author Jonathan + * + */ +public class OBasicHttpTaf extends AbsOTafLur implements HttpTaf { + private final String realm; + private final CredVal rbac; + + + public OBasicHttpTaf(final PropAccess access, final CredVal rbac, final String realm, final String token_url, final String introspect_url) throws CadiException { + super(access, token_url,introspect_url); + this.rbac = rbac; + this.realm = realm; + } + + /** + * Note: BasicHttp works for either Carbon Based (Humans) or Silicon Based (machine) Lifeforms. + * @see Taf + */ + public TafResp validate(Taf.LifeForm reading, HttpServletRequest req, HttpServletResponse resp) { + // See if Request implements BasicCred (aka CadiWrap or other), and if User/Pass has already been set separately + final String user; + String password=null; + byte[] cred=null; + if(req instanceof BasicCred) { + BasicCred bc = (BasicCred)req; + user = bc.getUser(); + cred = bc.getCred(); + } else { + String authz = req.getHeader("Authorization"); + if(authz != null && authz.startsWith("Basic ")) { + if(!req.isSecure()) { + access.log(Level.WARN,"WARNING! BasicAuth has been used over an insecure channel"); + } + try { + String temp = Symm.base64noSplit.decode(authz.substring(6)); + int colon = temp.lastIndexOf(':'); + if(colon>0) { + user = temp.substring(0,colon); + password = temp.substring(colon+1); + } else { + access.printf(Level.AUDIT,"Malformed BasicAuth entry ip=%s, entry=%s",req.getRemoteAddr(), + access.encrypt(temp)); + return new BasicHttpTafResp(access,null,"Malformed BasicAuth entry",RESP.FAIL,resp,realm,false); + } + if(!rbac.validate(user,Type.PASSWORD,password.getBytes(),req)) { + return new BasicHttpTafResp(access,null,buildMsg(null,req,"user/pass combo invalid for ",user,"from",req.getRemoteAddr()), + RESP.TRY_AUTHENTICATING,resp,realm,true); + } + } catch (IOException e) { + access.log(e, ERROR_GETTING_TOKEN_CLIENT); + return new BasicHttpTafResp(access,null,ERROR_GETTING_TOKEN_CLIENT,RESP.FAIL,resp,realm,false); + } + } else { + return new BasicHttpTafResp(access,null,"Not a Basic Auth",RESP.TRY_ANOTHER_TAF,resp,realm,false); + } + } + + try { + if(password==null && cred!=null) { + password = new String(cred); + cred = Hash.hashSHA256(cred); + } else if(password!=null && cred==null) { + cred = Hash.hashSHA256(password.getBytes()); + } + Pooled<TokenClient> pclient = tokenClientPool.get(); + try { + pclient.content.password(user, password); + String scope=FQI.reverseDomain(client_id); + Result<TimedToken> rtt = pclient.content.getToken('B',scope); + if(rtt.isOK()) { + if(rtt.value.expired()) { + return new BasicHttpTafResp(access,null,"BasicAuth/OAuth Token: Token Expired",RESP.FAIL,resp,realm,true); + } else { + TimedToken tt = rtt.value; + Result<OAuth2Principal> prin = tkMgr.toPrincipal(tt.getAccessToken(), cred); + if(prin.isOK()) { + return new BasicHttpTafResp(access,prin.value,"BasicAuth/OAuth Token Authentication",RESP.IS_AUTHENTICATED,resp,realm,true); + } else { + return new BasicHttpTafResp(access,null,"BasicAuth/OAuth Token: " + prin.code + ' ' + prin.error,RESP.FAIL,resp,realm,true); + } + } + } else { + return new BasicHttpTafResp(access,null,"BasicAuth/OAuth Token: " + rtt.code + ' ' + rtt.error,RESP.FAIL,resp,realm,true); + } + } finally { + pclient.done(); + } + } catch (APIException | CadiException | LocatorException | NoSuchAlgorithmException e) { + access.log(e, ERROR_GETTING_TOKEN_CLIENT); + return new BasicHttpTafResp(access,null,ERROR_GETTING_TOKEN_CLIENT,RESP.TRY_ANOTHER_TAF,resp,realm,false); + } + } + + protected String buildMsg(Principal pr, HttpServletRequest req, Object ... msg) { + StringBuilder sb = new StringBuilder(); + if(pr!=null) { + sb.append("user="); + sb.append(pr.getName()); + sb.append(','); + } + sb.append("ip="); + sb.append(req.getRemoteAddr()); + sb.append(",port="); + sb.append(req.getRemotePort()); + if(msg.length>0) { + sb.append(",msg=\""); + for(Object s : msg) { + sb.append(s.toString()); + } + sb.append('"'); + } + return sb.toString(); + } + + @Override + public Resp revalidate(CachedPrincipal prin, Object state) { +// if(prin instanceof BasicPrincipal) { +// BasicPrincipal ba = (BasicPrincipal)prin; +// if(DenialOfServiceTaf.isDeniedID(ba.getName())!=null) { +// return Resp.UNVALIDATED; +// } +// return rbac.validate(ba.getName(), Type.PASSWORD, ba.getCred(), state)?Resp.REVALIDATED:Resp.UNVALIDATED; +// } + return Resp.NOT_MINE; + } + + public String toString() { + return "Basic Auth enabled on realm: " + realm; + } +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/olur/OLur.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/olur/OLur.java new file mode 100644 index 00000000..74d88fc2 --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/olur/OLur.java @@ -0,0 +1,150 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.olur; + +import java.security.Principal; +import java.util.List; + +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.Lur; +import org.onap.aaf.cadi.Permission; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.aaf.AAFPermission; +import org.onap.aaf.cadi.client.Result; +import org.onap.aaf.cadi.oauth.AbsOTafLur; +import org.onap.aaf.cadi.oauth.OAuth2Principal; +import org.onap.aaf.cadi.oauth.TimedToken; +import org.onap.aaf.cadi.oauth.TokenClient; +import org.onap.aaf.cadi.oauth.TokenPerm; +import org.onap.aaf.cadi.principal.Kind; +import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.misc.env.util.Split; +import org.onap.aaf.misc.env.util.Pool.Pooled; + +public class OLur extends AbsOTafLur implements Lur { + public OLur(PropAccess access, final String token_url, final String introspect_url) throws APIException, CadiException { + super(access, token_url, introspect_url); + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.Lur#fish(java.security.Principal, org.onap.aaf.cadi.Permission) + */ + @Override + public boolean fish(Principal bait, Permission pond) { + TokenPerm tp; + if(bait instanceof OAuth2Principal) { + OAuth2Principal oa2p = (OAuth2Principal)bait; + tp = oa2p.tokenPerm(); + } else { + tp=null; + } + if(tp==null) { + // if no Token Perm preset, get + try { + Pooled<TokenClient> tcp = tokenClientPool.get(); + try { + TokenClient tc = tcp.content; + tc.username(bait.getName()); + Result<TimedToken> rtt = tc.getToken(Kind.getKind(bait),tc.defaultScope()); + if(rtt.isOK()) { + Result<TokenPerm> rtp = tkMgr.get(rtt.value.getAccessToken(), bait.getName().getBytes()); + if(rtp.isOK()) { + tp = rtp.value; + } + } + } finally { + tcp.done(); + } + } catch (APIException | LocatorException | CadiException e) { + access.log(Level.ERROR, "Unable to Get a Token: " + e.getMessage()); + } + } + if(tp!=null) { + if(tkMgr.access.willLog(Level.DEBUG)) { + StringBuilder sb = new StringBuilder("AAF Permissions for user "); + sb.append(bait.getName()); + sb.append(", from token "); + sb.append(tp.get().getAccessToken()); + for (AAFPermission p : tp.perms()) { + sb.append("\n\t"); + sb.append(p.getName()); + sb.append('|'); + sb.append(p.getInstance()); + sb.append('|'); + sb.append(p.getAction()); + } + sb.append('\n'); + access.log(Level.DEBUG, sb); + } + for (AAFPermission p : tp.perms()) { + if (p.match(pond)) { + return true; + } + } + } + return false; + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.Lur#fishAll(java.security.Principal, java.util.List) + */ + @Override + public void fishAll(Principal bait, List<Permission> permissions) { + if(bait instanceof OAuth2Principal) { + for (AAFPermission p : ((OAuth2Principal)bait).tokenPerm().perms()) { + permissions.add(p); + } + } + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.Lur#handlesExclusively(org.onap.aaf.cadi.Permission) + */ + @Override + public boolean handlesExclusively(Permission pond) { + return false; + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.Lur#handles(java.security.Principal) + */ + @Override + public boolean handles(Principal principal) { + return principal instanceof OAuth2Principal; + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.Lur#createPerm(java.lang.String) + */ + @Override + public Permission createPerm(final String p) { + String[] s = Split.split('|',p); + if(s!=null && s.length==3) { + return new AAFPermission(s[0],s[1],s[2]); + } else { + return null; + } + } + +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/persist/Persist.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/persist/Persist.java new file mode 100644 index 00000000..9754b1e6 --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/persist/Persist.java @@ -0,0 +1,301 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.persist; + +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.FileVisitor; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.Date; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Queue; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.client.Holder; +import org.onap.aaf.cadi.client.Result; +import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.misc.env.util.Chrono; +import org.onap.aaf.misc.rosetta.env.RosettaDF; +import org.onap.aaf.misc.rosetta.env.RosettaEnv; + +public abstract class Persist<T,CT extends Persistable<T>> extends PersistFile { + private static final long ONE_DAY = 86400000L; + private static final long CLEAN_CHECK = 2*60*1000L; // check every 2 mins + private static Timer clean; + + // store all the directories to review + // No Concurrent HashSet, or at least, it is all implemented with HashMap in older versions + private static Queue<Persist<?,?>> allPersists = new ConcurrentLinkedQueue<Persist<?,?>>(); + + private Map<String,CT> tmap; + protected RosettaEnv env; + private RosettaDF<T> df; + + + public Persist(Access access, RosettaEnv env, Class<T> cls, String sub_dir) throws CadiException, APIException { + super(access, sub_dir); + this.env = env; + df = env.newDataFactory(cls); + tmap = new ConcurrentHashMap<String, CT>(); + synchronized(Persist.class) { + if(clean==null) { + clean = new Timer(true); + clean.schedule(new Clean(access), 20000, CLEAN_CHECK); + } + } + allPersists.add(this); + } + + public void close() { + allPersists.remove(this); + } + + protected abstract CT newCacheable(T t, long expires_secsFrom1970, byte[] hash, Path path) throws APIException, IOException; + + public RosettaDF<T> getDF() { + return df; + } + public Result<CT> get(final String key, final byte[] hash, Loader<CT> rl) throws CadiException, APIException, LocatorException { + if(key==null) { + return null; + } + Holder<Path> hp = new Holder<Path>(null); + CT ct = tmap.get(key); + // Make sure cached Item is synced with Disk, but only even Minute to save Disk hits + if(ct!=null && ct.checkSyncTime()) { // check File Time only every SYNC Period (2 min) + if(ct.hasBeenTouched()) { + tmap.remove(key); + ct = null; + access.log(Level.DEBUG,"File for",key,"has been touched, removing memory entry"); + } + } + + // If not currently in memory, check with Disk (which might have been updated by other processes) + if(ct==null) { + Holder<Long> hl = new Holder<Long>(0L); + T t; + if((t = readDisk(df, hash, key, hp, hl))!=null) { + try { + if((ct = newCacheable(t,hl.get(),hash,hp.get()))!=null) { + tmap.put(key, ct); + } + access.log(Level.DEBUG,"Read Token from",key); + } catch (IOException e) { + access.log(e,"Reading Token from",key); + } + } // if not read, then ct still==null + + // If not in memory, or on disk, get from Remote... IF reloadable (meaning, isn't hitting too often, etc). + if(ct==null || ct.checkReloadable()) { + // Load from external (if makes sense) + Result<CT> rtp = rl.load(key); + if(rtp.isOK()) { + ct = rtp.value; + try { + Path p = getPath(key); + writeDisk(df, ct.get(),ct.getHash(),p,ct.expires()); + access.log(Level.DEBUG, "Writing token",key); + } catch(CadiException e) { + throw e; + } catch (Exception e) { + throw new CadiException(e); + } + } else { + return Result.err(rtp); + } + } + + if(ct!=null) { + tmap.put(key, ct); + } + } else { + access.log(Level.DEBUG,"Found token in memory",key); + } + // ct can only be not-null here + ct.inc(); + return Result.ok(200,ct); + } + + public void put(String key, CT ct) throws CadiException { + writeDisk(df, ct.get(), ct.getHash(), key, ct.expires()); + tmap.put(key,ct); + } + + public void delete(String key) { + tmap.remove(key); + deleteFromDisk(key); + } + + public interface Loader<CT> { + Result<CT> load(String key) throws APIException, CadiException, LocatorException; + } + + /** + * Clean will examine resources, and remove those that have expired. + * + * If "highs" have been exceeded, then we'll expire 10% more the next time. This will adjust after each run + * without checking contents more than once, making a good average "high" in the minimum speed. + * + * @author Jonathan + * + */ + private static final class Clean extends TimerTask { + private final Access access; + private long hourly; + + public Clean(Access access) { + this.access = access; + hourly=0; + } + + private static class Metrics { + public int mexists = 0, dexists=0; + public int mremoved = 0, dremoved=0; + } + + public void run() { + final long now = System.currentTimeMillis(); + final long dayFromNow = now + ONE_DAY; + final Metrics metrics = new Metrics(); + for(final Persist<?,?> persist : allPersists) { + // Clear memory + if(access.willLog(Level.DEBUG)) { + access.log(Level.DEBUG, "Persist: Cleaning memory cache for",persist.tokenPath.toAbsolutePath()); + } + for(Entry<String, ?> es : persist.tmap.entrySet()) { + ++metrics.mexists; + Persistable<?> p = (Persistable<?>)es.getValue(); + if(p.checkSyncTime()) { + if(p.count()==0) { + ++metrics.mremoved; + persist.tmap.remove(es.getKey()); + access.printf(Level.DEBUG, "Persist: removed cached item %s from memory\n", es.getKey()); + } else { + p.clearCount(); + } + } else if(Files.exists(p.path())) { + + } + } + // Clear disk + try { + final StringBuilder sb = new StringBuilder(); + Files.walkFileTree(persist.tokenPath, new FileVisitor<Path>() { + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + sb.setLength(0); + sb.append("Persist: Cleaning files from "); + sb.append(dir.toAbsolutePath()); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + if(attrs.isRegularFile()) { + ++metrics.dexists; + try { + + long exp = persist.readExpiration(file)*1000; // readExpiration is seconds from 1970 + if(now > exp) { // cover for bad token + sb.append("\n\tFile "); + sb.append(file.getFileName()); + sb.append(" expired "); + sb.append(Chrono.dateTime(new Date(exp))); + persist.deleteFromDisk(file); + ++metrics.dremoved; + } else if(exp > dayFromNow) { + sb.append("\n\tFile "); + sb.append(file.toString()); + sb.append(" data corrupted."); + persist.deleteFromDisk(file); + ++metrics.dremoved; + } + } catch (CadiException e) { + sb.append("\n\tError reading File "); + sb.append(file.toString()); + sb.append(". "); + sb.append(e.getMessage()); + ++metrics.dremoved; + } + + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { + access.log(Level.ERROR,"Error visiting file %s (%s)\n",file.toString(),exc.getMessage()); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + access.log(Level.DEBUG, sb); + return FileVisitResult.CONTINUE; + } + + }); + } catch (IOException e) { + access.log(e, "Exception while cleaning Persistance"); + } + + } + + // We want to print some activity of Persistence Check at least hourly, even if no activity has occurred, but not litter the log if nothing is happening + boolean go=false; + Level level=Level.WARN; + if(access.willLog(Level.INFO)) { + go = true; + level=Level.INFO; + } else if(access.willLog(Level.WARN)) { + go = metrics.mremoved>0 || metrics.dremoved>0 || --hourly <= 0; + } + + if(go) { + access.printf(level, "Persist Cache: removed %d of %d items from memory and %d of %d from disk", + metrics.mremoved, metrics.mexists, metrics.dremoved, metrics.dexists); + hourly = 3600000/CLEAN_CHECK; + } + } + } + + /* (non-Javadoc) + * @see java.lang.Object#finalize() + */ + @Override + protected void finalize() throws Throwable { + close(); // can call twice. + } + + + +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/persist/PersistFile.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/persist/PersistFile.java new file mode 100644 index 00000000..8fd2c986 --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/persist/PersistFile.java @@ -0,0 +1,255 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.persist; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.nio.file.StandardOpenOption; +import java.nio.file.attribute.FileTime; +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.PosixFilePermissions; +import java.util.Set; + +import javax.crypto.CipherInputStream; +import javax.crypto.CipherOutputStream; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.Symm; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.Symm.Encryption; +import org.onap.aaf.cadi.client.Holder; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.misc.rosetta.env.RosettaDF; + +public class PersistFile { + + private static final String HASH_NO_MATCH = "Hash does not match in Persistence"; + private static final Object LOCK = new Object(); + + protected static Symm symm; + public Access access; + protected final Path tokenPath; + protected final String tokenDir; + private static final boolean isWindows = System.getProperty("os.name").startsWith("Windows"); + + public PersistFile(Access access, String sub_dir) throws CadiException, APIException { + this.access = access; + tokenPath = Paths.get(access.getProperty(Config.CADI_TOKEN_DIR,"tokens"), sub_dir); + try { + if(!Files.exists(tokenPath)) { + if(isWindows) { + // Sorry Windows users, you need to secure your own paths + Files.createDirectories(tokenPath); + } else { + Set<PosixFilePermission> spfp = PosixFilePermissions.fromString("rwxr-x---"); + Files.createDirectories(tokenPath,PosixFilePermissions.asFileAttribute(spfp)); + } + } + tokenDir=tokenPath.toRealPath().toString(); + } catch (IOException e) { + throw new CadiException(e); + } + synchronized(LOCK) { + if(symm==null) { + symm = Symm.obtain(access); + } + } + } + + public<T> Path writeDisk(final RosettaDF<T> df, final T t, final byte[] cred, final String filename, final long expires) throws CadiException { + return writeDisk(df,t,cred,Paths.get(tokenDir,filename),expires); + } + + public<T> Path writeDisk(final RosettaDF<T> df, final T t, final byte[] cred, final Path target, final long expires) throws CadiException { + // Make sure File is completely written before making accessible on disk... avoid corruption. + try { + Path tpath = Files.createTempFile(tokenPath,target.getFileName().toString(), ".tmp"); + final OutputStream dos = Files.newOutputStream(tpath, StandardOpenOption.CREATE,StandardOpenOption.WRITE); + try { + // Write Expires so that we can read unencrypted. + for(int i=0;i<Long.SIZE;i+=8) { + dos.write((byte)((expires>>i)&0xFF)); + } + + symm.exec(new Symm.SyncExec<Void>() { + @Override + public Void exec(Encryption enc) throws Exception { + CipherOutputStream os = enc.outputStream(dos, true); + try { + int size = cred==null?0:cred.length; + for(int i=0;i<Integer.SIZE;i+=8) { + os.write((byte)((size>>i)&0xFF)); + } + if(cred!=null) { + os.write(cred); + } + df.newData().load(t).to(os); + } finally { + // Note: Someone on the Web noticed that using a DataOutputStream would not full close out without a flush first, + // leaving files open. + try { + os.flush(); + } catch (IOException e) { + access.log(Level.INFO, "Note: Caught Exeption while flushing CipherStream. Handled."); + } + try { + os.close(); + } catch (IOException e) { + access.log(Level.INFO, "Note: Caught Exeption while closing CipherStream. Handled."); + } + } + return null; + } + }); + } catch(Exception e) { + throw new CadiException(e); + } finally { + dos.close(); + } + return Files.move(tpath, target, StandardCopyOption.ATOMIC_MOVE,StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { + throw new CadiException(e); + } + + } + + public <T> T readDisk(final RosettaDF<T> df, final byte[] cred, final String filename,final Holder<Path> hp, final Holder<Long> hl) throws CadiException { + if(hp.get()==null) { + hp.set(Paths.get(tokenDir,filename)); + } + return readDisk(df,cred,hp.get(),hl); + } + + public <T> T readDisk(final RosettaDF<T> df, final byte[] cred, final Path target, final Holder<Long> hexpired) throws CadiException { + // Try from Disk + T t = null; + if(Files.exists(target)) { + try { + final InputStream is = Files.newInputStream(target,StandardOpenOption.READ); + try { + // Read Expired unencrypted + long exp=0; + for(int i=0;i<Long.SIZE;i+=8) { + exp |= ((long)is.read()<<i); + } + hexpired.set(exp); + + t = symm.exec(new Symm.SyncExec<T>() { + @Override + public T exec(Encryption enc) throws Exception { + CipherInputStream dis = enc.inputStream(is,false); + try { + int size=0; + for(int i=0;i<Integer.SIZE;i+=8) { + size |= ((int)dis.read()<<i); + } + if(size>256) { + throw new CadiException("Invalid size in Token Persistence"); + } else if(cred!=null && size!=cred.length) { + throw new CadiException(HASH_NO_MATCH); + } + if(cred!=null) { + byte[] array = new byte[size]; + if(dis.read(array)>0) { + for(int i=0;i<size;++i) { + if(cred[i]!=array[i]) { + throw new CadiException(HASH_NO_MATCH); + } + } + } + } + return df.newData().load(dis).asObject(); + } finally { + dis.close(); + } + } + }); + } finally { + is.close(); + } + } catch (NoSuchFileException e) { + return t; + } catch (Exception e) { + throw new CadiException(e); + } + } + return t; + } + + public long readExpiration(final Path target) throws CadiException { + long exp=0L; + if(Files.exists(target)) { + try { + final InputStream is = Files.newInputStream(target,StandardOpenOption.READ); + try { + for(int i=0;i<Long.SIZE;i+=8) { + exp |= ((long)is.read()<<i); + } + } finally { + is.close(); + } + return exp; + } catch (Exception e) { + throw new CadiException(e); + } + } + return exp; + } + + public void deleteFromDisk(Path path) { + try { + Files.deleteIfExists(path); + } catch (IOException e) { + access.log(Level.ERROR, e); + } + } + + public void deleteFromDisk(String token) { + Path tpath = Paths.get(tokenDir,token); + try { + Files.deleteIfExists(tpath); + } catch (IOException e) { + access.log(Level.ERROR, e); + } + } + + public Path getPath(String filename) { + return Paths.get(tokenDir,filename); + } + + public FileTime getFileTime(String filename, Holder<Path> hp) throws IOException { + Path p = hp.get(); + if(p==null) { + hp.set(p=Paths.get(tokenDir,filename)); + } + return Files.getLastModifiedTime(p); + } + +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/persist/Persistable.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/persist/Persistable.java new file mode 100644 index 00000000..65437795 --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/persist/Persistable.java @@ -0,0 +1,39 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.persist; + +import java.nio.file.Path; + +public interface Persistable<T> { + public boolean checkSyncTime(); + public boolean checkReloadable(); + public void inc(); + public int count(); + public void clearCount(); + public boolean hasBeenTouched(); + public long expires(); // seconds from 1970 + public boolean expired(); + public byte[] getHash(); + public boolean match(byte[] hashIn); + public T get(); + public Path path(); +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/persist/Persisting.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/persist/Persisting.java new file mode 100644 index 00000000..8b98f5bf --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/persist/Persisting.java @@ -0,0 +1,163 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.persist; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.nio.file.attribute.FileTime; + +import org.onap.aaf.cadi.Access.Level; + +public class Persisting<T> implements Persistable<T> { + private static final byte[] EMPTY = new byte[0]; + private final byte[] hash; // need to be able to validate disk entry + + private static final long SYNC_TIME = 1000*60*1L; // Checking File change max 1 min + private FileTime lastTouched; + private int count; + private long expires; + private long nextCheck; + private T t; + private Path path; + private Persist<T, ?> persist; + + public Persisting(Persist<T,?> p, T t, long expiresSecsFrom1970, byte[] hash, Path path) { + persist = p; + this.t=t; + expires = expiresSecsFrom1970; + this.path = path; + try { + lastTouched = Files.getLastModifiedTime(path); + } catch (IOException e) { + lastTouched = null; + } + count=0; + nextCheck=0; + if(hash==null) { + this.hash = EMPTY; + } else { + this.hash = hash; + } + } + + @Override + public T get() { + return t; + } + + @Override + public long expires() { + return expires; + } + + @Override + public boolean expired() { + return System.currentTimeMillis()/1000>expires; + } + + @Override + public boolean hasBeenTouched() { + try { + FileTime modT = Files.getLastModifiedTime(path); + if(lastTouched==null) { + lastTouched = modT; + return true; + } else { + return !modT.equals(lastTouched); + } + } catch (NoSuchFileException e) { + persist.access.log(Level.DEBUG, "File not found " + e.getMessage() + ", this is ok, marking as touched."); + return true; + } catch (IOException e) { + persist.access.log(e, "Accessing File Time"); + return true; + } + } + + @Override + public synchronized boolean checkSyncTime() { + long temp=System.currentTimeMillis(); + if(nextCheck==0 || nextCheck<temp) { + nextCheck = temp+SYNC_TIME; + return true; + } + return false; + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.oauth.Persistable#checkReloadTime() + */ + @Override + public boolean checkReloadable() { + //TODO other elements to add here... + // Ideas: Is it valid? + // if not, How many times has it been checked in the last minute + return expired(); + } + + @Override + public byte[] getHash() { + return hash; + } + + @Override + public boolean match(byte[] hashIn) { + if(hash==null || hashIn==null || hash.length!=hashIn.length) { + return false; + } + for(int i=0;i<hashIn.length;++i) { + if(hash[i]!=hashIn[i]) { + return false; + } + } + return true; + } + + @Override + public synchronized void inc() { + ++count; + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.oauth.Cacheable#count() + */ + @Override + public int count() { + return count; + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.oauth.Persistable#clearCount() + */ + @Override + public synchronized void clearCount() { + count=0; + } + + @Override + public Path path() { + return path; + } + +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/register/Registrant.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/register/Registrant.java new file mode 100644 index 00000000..17e850ff --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/register/Registrant.java @@ -0,0 +1,30 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.register; + +import org.onap.aaf.cadi.client.Result; +import org.onap.aaf.misc.env.impl.BasicEnv; + +public interface Registrant<ENV extends BasicEnv> { + public Result<Void> update(ENV env); + public Result<Void> cancel(ENV env); +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/register/Registrar.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/register/Registrar.java new file mode 100644 index 00000000..954c8555 --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/register/Registrar.java @@ -0,0 +1,102 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.register; + +import java.util.Deque; +import java.util.Iterator; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.ConcurrentLinkedDeque; + +import org.onap.aaf.cadi.client.Result; +import org.onap.aaf.misc.env.impl.BasicEnv; + +public class Registrar<ENV extends BasicEnv> { + private static final String REGISTRAR = "Registrar"; + private static final long INTERVAL = 15*60*1000L; // 15 mins + private static final long START = 3000; // Start in 3 seconds + private static final Object LOCK = new Object(); + private Deque<Registrant<ENV>> registrants; + private Timer timer, erroringTimer; + + public Registrar(final ENV env, boolean shutdownHook) { + registrants = new ConcurrentLinkedDeque<Registrant<ENV>>(); + + erroringTimer = null; + timer = new Timer(REGISTRAR,true); + timer.schedule(new RegistrationTimerTask(env), START, INTERVAL); + + if(shutdownHook) { + Runtime.getRuntime().addShutdownHook(new Thread() { + public void run() { + close(env); + } + }); + } + } + + private class RegistrationTimerTask extends TimerTask { + private final ENV env; + public RegistrationTimerTask(ENV env) { + this.env = env; + } + @Override + public void run() { + for(Iterator<Registrant<ENV>> iter = registrants.iterator(); iter.hasNext();) { + Registrant<ENV> reg = iter.next(); + Result<Void> rv = reg.update(env); + synchronized(LOCK) { + if(rv.isOK()) { + if(erroringTimer!=null) { + erroringTimer.cancel(); + erroringTimer = null; + } + } else { + // Account for different Registrations not being to same place + if(erroringTimer==null) { + erroringTimer = new Timer(REGISTRAR + " error re-check ",true); + erroringTimer.schedule(new RegistrationTimerTask(env),20000,20000); + } + } + } + } + } + } + + public void register(Registrant<ENV> r) { + registrants.addLast(r); + } + + public void deregister(Registrant<ENV> r) { + registrants.remove(r); + } + + public void close(ENV env) { + timer.cancel(); + + Registrant<ENV> r; + while(registrants.peek()!=null) { + r = registrants.pop(); + r.cancel(env); + } + } +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/register/RemoteRegistrant.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/register/RemoteRegistrant.java new file mode 100644 index 00000000..e9a80dda --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/register/RemoteRegistrant.java @@ -0,0 +1,173 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.register; + +import java.net.HttpURLConnection; +import java.net.Inet4Address; +import java.net.URI; +import java.net.UnknownHostException; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.Locator; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.aaf.v2_0.AAFCon; +import org.onap.aaf.cadi.client.Future; +import org.onap.aaf.cadi.client.Rcli; +import org.onap.aaf.cadi.client.Result; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.locator.PropertyLocator; +import org.onap.aaf.cadi.util.Split; +import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.misc.env.impl.BasicEnv; +import org.onap.aaf.misc.rosetta.env.RosettaDF; + +import locate.v1_0.MgmtEndpoint; +import locate.v1_0.MgmtEndpoints; + +public class RemoteRegistrant<ENV extends BasicEnv> implements Registrant<ENV> { + private final MgmtEndpoint mep; + private final MgmtEndpoints meps; + private final AAFCon<HttpURLConnection> aafcon; + private final RosettaDF<MgmtEndpoints> mgmtEndpointsDF; + private final Locator<URI> locator; + private final Access access; + private final int timeout; + + @SafeVarargs + public RemoteRegistrant(AAFCon<HttpURLConnection> aafcon, String name, String version, int port, RemoteRegistrant<ENV> ... others) throws CadiException, LocatorException { + this.aafcon = aafcon; + access = aafcon.access; + try { + mgmtEndpointsDF = aafcon.env.newDataFactory(MgmtEndpoints.class); + } catch (APIException e1) { + throw new CadiException(e1); + } + timeout = Integer.parseInt(access.getProperty(Config.AAF_CONN_TIMEOUT, Config.AAF_CONN_TIMEOUT_DEF)); + String aaf_locate = access.getProperty(Config.AAF_LOCATE_URL,null); + if(aaf_locate==null) { + throw new CadiException(Config.AAF_LOCATE_URL + " is required."); + } else { + // Note: want Property Locator, not AAFLocator, because we want the core service, not what it can find + locator = new PropertyLocator(aaf_locate); + } + + mep = new MgmtEndpoint(); + mep.setName(name); + mep.setPort(port); + + try { + String hostnameToRegister = access.getProperty(Config.CADI_REGISTRATION_HOSTNAME, null); + if(hostnameToRegister==null) { + hostnameToRegister = access.getProperty(Config.HOSTNAME, null); + } + if(hostnameToRegister==null) { + hostnameToRegister = Inet4Address.getLocalHost().getHostName(); + } + mep.setHostname(hostnameToRegister); + + String latitude = access.getProperty(Config.CADI_LATITUDE, null); + if(latitude==null) { + latitude = access.getProperty("AFT_LATITUDE", null); + } + String longitude = access.getProperty(Config.CADI_LONGITUDE, null); + if(longitude==null) { + longitude = access.getProperty("AFT_LONGITUDE", null); + } + if(latitude==null || longitude==null) { + throw new CadiException(Config.CADI_LATITUDE + " and " + Config.CADI_LONGITUDE + " is required"); + } else { + mep.setLatitude(Float.parseFloat(latitude)); + mep.setLongitude(Float.parseFloat(longitude)); + } + String split[] = Split.split('.', version); + mep.setPkg(split.length>3?Integer.parseInt(split[3]):0); + mep.setPatch(split.length>2?Integer.parseInt(split[2]):0); + mep.setMinor(split.length>1?Integer.parseInt(split[1]):0); + mep.setMajor(split.length>0?Integer.parseInt(split[0]):0); + + String subprotocols = access.getProperty(Config.CADI_PROTOCOLS, null); + if(subprotocols==null) { + mep.setProtocol("http"); + } else { + mep.setProtocol("https"); + for(String s : Split.split(',', subprotocols)) { + mep.getSubprotocol().add(s); + } + } + } catch (NumberFormatException | UnknownHostException e) { + throw new CadiException("Error extracting Data from Properties for Registrar",e); + } + meps = new MgmtEndpoints(); + meps.getMgmtEndpoint().add(mep); + for(RemoteRegistrant<ENV> rr : others) { + meps.getMgmtEndpoint().add(rr.mep); + } + } + + @Override + public Result<Void> update(ENV env) { + try { + Rcli<?> client = aafcon.client(locator); + try { + Future<MgmtEndpoints> fup = client.update("/registration",mgmtEndpointsDF,meps); + if(fup.get(timeout)) { + access.log(Level.INFO, "Registration complete to",client.getURI()); + return Result.ok(fup.code(),null); + } else { + access.log(Level.ERROR,"Error registering to AAF Locator on ", client.getURI()); + return Result.err(fup.code(),fup.body()); + } + } catch (APIException e) { + access.log(e, "Error registering service to AAF Locator"); + return Result.err(503,e.getMessage()); + } + + } catch (CadiException e) { + return Result.err(503,e.getMessage()); + } + } + + @Override + public Result<Void> cancel(ENV env) { + try { + Rcli<?> client = aafcon.client(locator); + try { + Future<MgmtEndpoints> fup = client.delete("/registration",mgmtEndpointsDF,meps); + if(fup.get(timeout)) { + access.log(Level.INFO, "Deregistration complete on",client.getURI()); + return Result.ok(fup.code(),null); + } else { + return Result.err(fup.code(),fup.body()); + } + } catch (APIException e) { + access.log(e, "Error deregistering service on AAF Locator"); + return Result.err(503,e.getMessage()); + } + + } catch (CadiException e) { + return Result.err(503,e.getMessage()); + } + } + +} diff --git a/cadi/aaf/src/main/java/org/onap/aaf/cadi/sso/AAFSSO.java b/cadi/aaf/src/main/java/org/onap/aaf/cadi/sso/AAFSSO.java new file mode 100644 index 00000000..8948bc3c --- /dev/null +++ b/cadi/aaf/src/main/java/org/onap/aaf/cadi/sso/AAFSSO.java @@ -0,0 +1,285 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.sso; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.Symm; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.util.MyConsole; +import org.onap.aaf.cadi.util.SubStandardConsole; +import org.onap.aaf.cadi.util.TheConsole; + +public class AAFSSO { + public static final MyConsole cons = TheConsole.implemented() ? new TheConsole() : new SubStandardConsole(); + private static final int EIGHT_HOURS = 8 * 60 * 60 * 1000; + + private Properties diskprops = null; // use for temp storing User/Password on disk + private File dot_aaf = null; + private File sso = null; // instantiated, if ever, with diskprops + + boolean removeSSO = false; + boolean loginOnly = false; + boolean doExit = true; + private PropAccess access; + private StringBuilder err; + private String user; + private String encrypted_pass; + private boolean use_X509; + + private PrintStream os; + + private Method close; + + public AAFSSO(String[] args) throws IOException, CadiException { + String[] nargs = parseArgs(args); + + dot_aaf = new File(System.getProperty("user.home") + "/.aaf"); + if (!dot_aaf.exists()) { + dot_aaf.mkdirs(); + } + File f = new File(dot_aaf, "sso.out"); + os = new PrintStream(new FileOutputStream(f, true)); + System.setOut(os); + System.setErr(os); + + access = new PropAccess(os, nargs); + Config.setDefaultRealm(access); + + user = access.getProperty(Config.AAF_APPID); + encrypted_pass = access.getProperty(Config.AAF_APPPASS); + + File dot_aaf_kf = new File(dot_aaf, "keyfile"); + + sso = new File(dot_aaf, "sso.props"); + if (removeSSO) { + if (dot_aaf_kf.exists()) { + dot_aaf_kf.setWritable(true, true); + dot_aaf_kf.delete(); + } + if (sso.exists()) { + sso.delete(); + } + System.out.println("AAF SSO information removed"); + if (doExit) { + System.exit(0); + } + } + + if (!dot_aaf_kf.exists()) { + FileOutputStream fos = new FileOutputStream(dot_aaf_kf); + try { + fos.write(Symm.keygen()); + setReadonly(dot_aaf_kf); + } finally { + fos.close(); + } + } + + String keyfile = access.getProperty(Config.CADI_KEYFILE); // in case it's CertificateMan props + if (keyfile == null) { + access.setProperty(Config.CADI_KEYFILE, dot_aaf_kf.getAbsolutePath()); + } + + String alias = access.getProperty(Config.CADI_ALIAS); + if ((user == null) && (alias != null) && (access.getProperty(Config.CADI_KEYSTORE_PASSWORD) != null)) { + user = alias; + access.setProperty(Config.AAF_APPID, user); + use_X509 = true; + } else { + use_X509 = false; + Symm decryptor = Symm.obtain(dot_aaf_kf); + if (user == null) { + if (sso.exists() && (sso.lastModified() > (System.currentTimeMillis() - EIGHT_HOURS))) { + String cm_url = access.getProperty(Config.CM_URL); // SSO might overwrite... + FileInputStream fos = new FileInputStream(sso); + try { + access.load(fos); + user = access.getProperty(Config.AAF_APPID); + encrypted_pass = access.getProperty(Config.AAF_APPPASS); + // decrypt with .aaf, and re-encrypt with regular Keyfile + access.setProperty(Config.AAF_APPPASS, + access.encrypt(decryptor.depass(encrypted_pass))); + if (cm_url != null) { //Command line CM_URL Overwrites ssofile. + access.setProperty(Config.CM_URL, cm_url); + } + } finally { + fos.close(); + } + } else { + diskprops = new Properties(); + String realm = Config.getDefaultRealm(); + // Turn on Console Sysout + System.setOut(System.out); + user = cons.readLine("aaf_id(%s@%s): ", System.getProperty("user.name"), realm); + if (user == null) { + user = System.getProperty("user.name") + '@' + realm; + } else if (user.length() == 0) { // + user = System.getProperty("user.name") + '@' + realm; + } else if ((user.indexOf('@') < 0) && (realm != null)) { + user = user + '@' + realm; + } + access.setProperty(Config.AAF_APPID, user); + diskprops.setProperty(Config.AAF_APPID, user); + encrypted_pass = new String(cons.readPassword("aaf_password: ")); + System.setOut(os); + encrypted_pass = Symm.ENC + decryptor.enpass(encrypted_pass); + access.setProperty(Config.AAF_APPPASS, encrypted_pass); + diskprops.setProperty(Config.AAF_APPPASS, encrypted_pass); + diskprops.setProperty(Config.CADI_KEYFILE, access.getProperty(Config.CADI_KEYFILE)); + } + } + } + if (user == null) { + err = new StringBuilder("Add -D" + Config.AAF_APPID + "=<id> "); + } + + if (encrypted_pass == null && alias == null) { + if (err == null) { + err = new StringBuilder(); + } else { + err.append("and "); + } + err.append("-D" + Config.AAF_APPPASS + "=<passwd> "); + } + } + + public void setLogDefault() { + this.setLogDefault(PropAccess.DEFAULT); + } + + public void setStdErrDefault() { + access.setLogLevel(PropAccess.DEFAULT); + System.setErr(System.err); + } + + public void setLogDefault(Level level) { + access.setLogLevel(level); + System.setOut(System.out); + } + + public boolean loginOnly() { + return loginOnly; + } + + public void addProp(String key, String value) { + if (diskprops != null) { + diskprops.setProperty(key, value); + } + } + + public void writeFiles() throws IOException { + // Store Creds, if they work + if (diskprops != null) { + if (!dot_aaf.exists()) { + dot_aaf.mkdirs(); + } + FileOutputStream fos = new FileOutputStream(sso); + try { + diskprops.store(fos, "AAF Single Signon"); + } finally { + fos.close(); + setReadonly(sso); + } + } + if (sso != null) { + setReadonly(sso); + sso.setWritable(true, true); + } + } + + public PropAccess access() { + return access; + } + + public StringBuilder err() { + return err; + } + + public String user() { + return user; + } + + public String enc_pass() { + return encrypted_pass; + } + + public boolean useX509() { + return use_X509; + } + + public void close() { + if (close != null) { + try { + close.invoke(null); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + // nothing to do here. + } + close = null; + } + } + + private String[] parseArgs(String[] args) + { + List<String> larg = new ArrayList<String>(args.length); + + // Cover for bash's need to escape *.. (\\*) + // also, remove SSO if required + for (int i = 0; i < args.length; ++i) { + if ("\\*".equals(args[i])) { + args[i] = "*"; + } + + if ("-logout".equalsIgnoreCase(args[i])) { + removeSSO = true; + } else if ("-login".equalsIgnoreCase(args[i])) { + loginOnly = true; + } else if ("-noexit".equalsIgnoreCase(args[i])) { + doExit = false; + } else { + larg.add(args[i]); + } + } + String[] nargs = new String[larg.size()]; + larg.toArray(nargs); + return nargs; + } + + private void setReadonly(File file) { + file.setExecutable(false, false); + file.setWritable(false, false); + file.setReadable(false, false); + file.setReadable(true, true); + } +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/cert/test/JU_AAFListedCertIdentity.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/cert/test/JU_AAFListedCertIdentity.java new file mode 100644 index 00000000..f2d91b02 --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/cert/test/JU_AAFListedCertIdentity.java @@ -0,0 +1,177 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.aaf.cert.test; + +import static org.mockito.Mockito.*; +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import org.junit.*; +import org.mockito.*; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Set; + +import javax.servlet.http.HttpServletRequest; + +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.aaf.cert.AAFListedCertIdentity; +import org.onap.aaf.cadi.aaf.v2_0.AAFCon; +import org.onap.aaf.cadi.client.Future; +import org.onap.aaf.cadi.client.Rcli; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.misc.env.util.Chrono; +import org.onap.aaf.misc.rosetta.env.RosettaDF; + +import aaf.v2_0.Certs; +import aaf.v2_0.Certs.Cert; +import aaf.v2_0.Users; +import aaf.v2_0.Users.User; + +public class JU_AAFListedCertIdentity { + + @Mock private AAFCon<?> conMock; + @Mock private Rcli<Object> rcliMock; + @Mock private RosettaDF<Users> userDFMock; + @Mock private RosettaDF<Certs> certDFMock; + @Mock private Future<Users> futureUsersMock; + @Mock private Future<Certs> futureCertsMock; + + @Mock private Users usersMock; + @Mock private User userMock1; + @Mock private User userMock2; + @Mock private User userMock3; + + @Mock private Certs certsMock; + @Mock private Cert certMock1; + @Mock private Cert certMock2; + @Mock private Cert certMock3; + + @Mock private HttpServletRequest reqMock; + @Mock private X509Certificate x509Mock; + + private List<User> usersList; + private List<Cert> certsList; + + private PropAccess access; + + private ByteArrayOutputStream outStream; + + private static final String USERS = "user1,user2,user3"; + private static final String ID = "id"; + private static final String FINGERPRINT = "fingerprint"; + + private static final byte[] certBytes = "certificate".getBytes(); + + @Before + public void setup() throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException { + MockitoAnnotations.initMocks(this); + + certsList = new ArrayList<>(); + certsList.add(certMock1); + certsList.add(certMock2); + certsList.add(certMock3); + + usersList = new ArrayList<>(); + usersList.add(userMock1); + usersList.add(userMock2); + usersList.add(userMock3); + + outStream = new ByteArrayOutputStream(); + access = new PropAccess(new PrintStream(outStream), new String[0]); + outStream.reset(); + access.setProperty(Config.AAF_CERT_IDS, USERS); + setFinal(conMock, conMock.getClass().getField("usersDF"), userDFMock); + setFinal(conMock, conMock.getClass().getField("certsDF"), certDFMock); + setFinal(conMock, conMock.getClass().getField("access"), access); + } + + @Test + public void test() throws APIException, CadiException, CertificateException { + doReturn(rcliMock).when(conMock).client(Config.AAF_DEFAULT_VERSION); + when(rcliMock.read("/authz/users/perm/com.att.aaf.trust/tguard/authenticate", Users.class, userDFMock)).thenReturn(futureUsersMock); + when(rcliMock.read("/authz/users/perm/com.att.aaf.trust/basicAuth/authenticate", Users.class, userDFMock)).thenReturn(futureUsersMock); + when(rcliMock.read("/authz/users/perm/com.att.aaf.trust/csp/authenticate", Users.class, userDFMock)).thenReturn(futureUsersMock); + + when(futureUsersMock.get(5000)).thenReturn(true); + futureUsersMock.value = usersMock; + when(usersMock.getUser()).thenReturn(usersList); + + when(rcliMock.read("/authn/cert/id/user1", Certs.class, conMock.certsDF)).thenReturn(futureCertsMock); + when(rcliMock.read("/authn/cert/id/user2", Certs.class, conMock.certsDF)).thenReturn(futureCertsMock); + when(rcliMock.read("/authn/cert/id/user3", Certs.class, conMock.certsDF)).thenReturn(futureCertsMock); + + when(futureCertsMock.get(5000)).thenReturn(true); + futureCertsMock.value = certsMock; + when(certsMock.getCert()).thenReturn(certsList); + + when(userMock1.getId()).thenReturn("user1"); + when(userMock2.getId()).thenReturn("user2"); + when(userMock3.getId()).thenReturn("user3"); + + prepareCert(certMock1); + prepareCert(certMock2); + prepareCert(certMock3); + + AAFListedCertIdentity certID = new AAFListedCertIdentity(access, conMock); + + when(x509Mock.getEncoded()).thenReturn(certBytes); + certID.identity(reqMock, null, null); + certID.identity(reqMock, null, certBytes); + certID.identity(reqMock, x509Mock, null); + certID.identity(reqMock, x509Mock, certBytes); + + Set<String> hashSetOfUsers = AAFListedCertIdentity.trusted("basicAuth"); + assertThat(hashSetOfUsers.contains("user1"), is(true)); + assertThat(hashSetOfUsers.contains("user2"), is(true)); + assertThat(hashSetOfUsers.contains("user3"), is(true)); + + } + + private void setFinal(Object object, Field field, Object newValue) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException { + field.setAccessible(true); + + Field modifiersField = Field.class.getDeclaredField("modifiers"); + modifiersField.setAccessible(true); + modifiersField.setInt(field, field.getModifiers() & Modifier.FINAL); + + field.set(object, newValue); + } + + private void prepareCert(Cert cert) { + Date date = new Date(); + when(cert.getExpires()).thenReturn(Chrono.timeStamp(new Date(date.getTime() + (60 * 60 * 24)))); + when(cert.getId()).thenReturn(ID); + when(cert.getFingerprint()).thenReturn(FINGERPRINT.getBytes()); + } + +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/client/test/JU_ErrMessageTest.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/client/test/JU_ErrMessageTest.java new file mode 100644 index 00000000..273affd3 --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/client/test/JU_ErrMessageTest.java @@ -0,0 +1,134 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.aaf.client.test; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.when; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Answers; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.aaf.client.ErrMessage; +import org.onap.aaf.cadi.client.Future; +import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.misc.env.Data.TYPE; +import org.onap.aaf.misc.rosetta.env.RosettaDF; +import org.onap.aaf.misc.rosetta.env.RosettaEnv; + +import aaf.v2_0.Error; + +public class JU_ErrMessageTest { + + @Mock + private RosettaEnv env; + + @Mock(answer=Answers.RETURNS_DEEP_STUBS) + private RosettaDF<Object> errDF; + + private ErrMessage errMessage; + + private String attErrJson = "key:value"; + + private Error error; + + private Future<?> future; + + private ByteArrayOutputStream errStream; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + when(env.newDataFactory(Error.class)).thenReturn(errDF); + + future = new Future<Error>() { + + @Override + public boolean get(int timeout) throws CadiException { + return false; + } + + @Override + public int code() { + return 0; + } + + @Override + public String body() { + return "Body"; + } + + @Override + public String header(String tag) { + return "header"; + } + }; + + error = new Error(); + error.setMessageId("Error Message Id"); + error.setText("Error Text"); + errMessage = new ErrMessage(env); + + errStream = new ByteArrayOutputStream(); + } + + @Test + public void testPrintErrMessage() throws APIException { + when(errDF.newData().in(TYPE.JSON).load(attErrJson).asObject()).thenReturn(error); + + errMessage.printErr(new PrintStream(errStream), attErrJson); + assertEquals("Error Message Id Error Text\n", errStream.toString()); + } + + @Test + public void testToMsgJsonErrAttribute() throws APIException { + when(errDF.newData().in(TYPE.JSON).load(attErrJson).asObject()).thenReturn(error); + + StringBuilder sb = new StringBuilder(); + errMessage.toMsg(sb,attErrJson); + + assertEquals(sb.toString(),"Error Message Id Error Text"); + } + + @Test + public void testToMsgFuture() { + StringBuilder sb = errMessage.toMsg(future); + + assertEquals(sb.toString(), "0: Body"); + } + + + @Test + public void testToMsgFutureWithoutException() throws APIException { + when(errDF.newData().in(TYPE.JSON).load(future.body()).asObject()).thenReturn(error); + + StringBuilder sb = errMessage.toMsg(future); + + assertEquals(sb.toString(), "Error Message Id Error Text"); + } +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/marshal/test/JU_CertMarshal.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/marshal/test/JU_CertMarshal.java new file mode 100644 index 00000000..560014d1 --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/marshal/test/JU_CertMarshal.java @@ -0,0 +1,99 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.aaf.marshal.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; + +import javax.xml.datatype.XMLGregorianCalendar; + +import org.junit.*; + +import org.onap.aaf.cadi.aaf.marshal.CertMarshal; +import org.onap.aaf.misc.env.util.Chrono; +import org.onap.aaf.misc.rosetta.OutRaw; +import org.onap.aaf.misc.rosetta.ParseException; +import org.onap.aaf.misc.rosetta.marshal.DataWriter; + +import aaf.v2_0.Certs.Cert; + +public class JU_CertMarshal { + + private static final String fingerprint = "fingerprint"; + private static final String id = "id"; + private static final String x500 = "x500"; + + private String fingerprintAsString; + + private XMLGregorianCalendar expires; + + private ByteArrayOutputStream outStream; + + @Before + public void setup() { + expires = Chrono.timeStamp(); + outStream = new ByteArrayOutputStream(); + StringBuilder sb = new StringBuilder(); + DataWriter.HEX_BINARY.write(fingerprint.getBytes(), sb); + fingerprintAsString = sb.toString(); + } + + @Test + public void test() throws ParseException, IOException { + Cert cert = setupCert(); + CertMarshal cm = new CertMarshal(); + OutRaw raw = new OutRaw(); + + raw.extract(cert, new PrintStream(outStream), cm); + + String[] output = outStream.toString().split("\n"); + + String[] expected = new String[] { + "{ - ", + ", - fingerprint : \"" + fingerprintAsString + "\"", + ", - id : \"" + id + "\"", + ", - x500 : \"" + x500 + "\"", + ", - expires : \"" + Chrono.dateTime(expires) + "\"", + "} - ", + }; + + assertThat(output.length, is(expected.length)); + + for (int i = 0; i < output.length; i++) { + assertThat(output[i], is(expected[i])); + } + } + + private Cert setupCert() { + Cert cert = new Cert(); + cert.setId(id); + cert.setX500(x500); + cert.setExpires(expires); + cert.setFingerprint(fingerprint.getBytes()); + return cert; + } + +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/marshal/test/JU_CertsMarshal.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/marshal/test/JU_CertsMarshal.java new file mode 100644 index 00000000..6598fbe4 --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/marshal/test/JU_CertsMarshal.java @@ -0,0 +1,118 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.aaf.marshal.test; + +import org.junit.*; +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.util.ArrayList; + +import javax.xml.datatype.XMLGregorianCalendar; +import org.onap.aaf.cadi.aaf.marshal.CertsMarshal; +import org.onap.aaf.misc.env.util.Chrono; +import org.onap.aaf.misc.rosetta.OutRaw; +import org.onap.aaf.misc.rosetta.ParseException; +import org.onap.aaf.misc.rosetta.marshal.DataWriter; + +import aaf.v2_0.Certs; +import aaf.v2_0.Certs.Cert; + +public class JU_CertsMarshal { + + private static final String fingerprint = "fingerprint"; + private static final String id = "id"; + private static final String x500 = "x500"; + + private String fingerprintAsString; + + private XMLGregorianCalendar expires; + + private ByteArrayOutputStream outStream; + + @Before + public void setup() { + expires = Chrono.timeStamp(); + outStream = new ByteArrayOutputStream(); + StringBuilder sb = new StringBuilder(); + DataWriter.HEX_BINARY.write(fingerprint.getBytes(), sb); + fingerprintAsString = sb.toString(); + } + + @Test + public void test() throws ParseException, IOException { + CertsStub certs = new CertsStub(); + CertsMarshal cm = new CertsMarshal(); + OutRaw raw = new OutRaw(); + + raw.extract(certs, new PrintStream(outStream), cm); + String[] output = outStream.toString().split("\n"); + + String[] expected = new String[] { + "{ - ", + "[ - cert", + "{ - ", + ", - fingerprint : \"" + fingerprintAsString + "\"", + ", - id : \"" + id + "\"", + ", - x500 : \"" + x500 + "\"", + ", - expires : \"" + Chrono.dateTime(expires) + "\"", + "} - ", + ", - ", + "{ - ", + ", - fingerprint : \"" + fingerprintAsString + "\"", + ", - id : \"" + id + "\"", + ", - x500 : \"" + x500 + "\"", + ", - expires : \"" + Chrono.dateTime(expires) + "\"", + "} - ", + "] - ", + "} - ", + }; + + assertThat(output.length, is(expected.length)); + + for (int i = 0; i < output.length; i++) { + assertThat(output[i], is(expected[i])); + } + } + + private Cert setupCert() { + Cert cert = new Cert(); + cert.setId(id); + cert.setX500(x500); + cert.setExpires(expires); + cert.setFingerprint(fingerprint.getBytes()); + return cert; + } + + private class CertsStub extends Certs { + public CertsStub() { + cert = new ArrayList<>(); + for (int i = 0; i < 2; i++) { + cert.add(setupCert()); + } + } + } + +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/test/JU_AAFPermission.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/test/JU_AAFPermission.java new file mode 100644 index 00000000..10958a23 --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/test/JU_AAFPermission.java @@ -0,0 +1,140 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ + +package org.onap.aaf.cadi.aaf.test; + +import static org.junit.Assert.*; + +import java.util.ArrayList; +import java.util.List; + +import static org.hamcrest.CoreMatchers.*; +import org.junit.*; +import org.onap.aaf.cadi.Permission; +import org.onap.aaf.cadi.aaf.AAFPermission; + +public class JU_AAFPermission { + + private final static String type = "type"; + private final static String instance = "instance"; + private final static String action = "action"; + private final static String key = type + '|' + instance + '|' + action; + private final static String role = "role"; + + private static List<String> roles; + + @Before + public void setup() { + roles = new ArrayList<String>(); + roles.add(role); + } + + @Test + public void constructor1Test() { + AAFPermission perm = new AAFPermission(type, instance, action); + assertThat(perm.getName(), is(type)); + assertThat(perm.getInstance(), is(instance)); + assertThat(perm.getAction(), is(action)); + assertThat(perm.getKey(), is(key)); + assertThat(perm.permType(), is("AAF")); + assertThat(perm.roles().size(), is(0)); + assertThat(perm.toString(), is("AAFPermission:\n\tType: " + type + + "\n\tInstance: " + instance + + "\n\tAction: " + action + + "\n\tKey: " + key)); + } + + @Test + public void constructor2Test() { + AAFPermission perm; + + perm = new AAFPermission(type, instance, action, null); + assertThat(perm.getName(), is(type)); + assertThat(perm.getInstance(), is(instance)); + assertThat(perm.getAction(), is(action)); + assertThat(perm.getKey(), is(key)); + assertThat(perm.permType(), is("AAF")); + assertThat(perm.roles().size(), is(0)); + assertThat(perm.toString(), is("AAFPermission:\n\tType: " + type + + "\n\tInstance: " + instance + + "\n\tAction: " + action + + "\n\tKey: " + key)); + + perm = new AAFPermission(type, instance, action, roles); + assertThat(perm.getName(), is(type)); + assertThat(perm.getInstance(), is(instance)); + assertThat(perm.getAction(), is(action)); + assertThat(perm.getKey(), is(key)); + assertThat(perm.permType(), is("AAF")); + assertThat(perm.roles().size(), is(1)); + assertThat(perm.roles().get(0), is(role)); + assertThat(perm.toString(), is("AAFPermission:\n\tType: " + type + + "\n\tInstance: " + instance + + "\n\tAction: " + action + + "\n\tKey: " + key)); + } + + @Test + public void matchTest() { + final AAFPermission controlPermission = new AAFPermission(type, instance, action); + PermissionStub perm; + AAFPermission aafperm; + + aafperm = new AAFPermission(type, instance, action); + assertThat(controlPermission.match(aafperm), is(true)); + + perm = new PermissionStub(key); + assertThat(controlPermission.match(perm), is(true)); + + // Coverage tests + perm = new PermissionStub("not a valid key"); + assertThat(controlPermission.match(perm), is(false)); + perm = new PermissionStub("type"); + assertThat(controlPermission.match(perm), is(false)); + perm = new PermissionStub("type|instance|badAction"); + assertThat(controlPermission.match(perm), is(false)); + } + + @Test + public void coverageTest() { + AAFPermissionStub aafps = new AAFPermissionStub(); + assertThat(aafps.getName(), is(nullValue())); + assertThat(aafps.getInstance(), is(nullValue())); + assertThat(aafps.getAction(), is(nullValue())); + assertThat(aafps.getKey(), is(nullValue())); + assertThat(aafps.permType(), is("AAF")); + assertThat(aafps.roles().size(), is(0)); + } + + private class PermissionStub implements Permission { + private String key; + + public PermissionStub(String key) { this.key = key; } + @Override public String permType() { return null; } + @Override public String getKey() { return key; } + @Override public boolean match(Permission p) { return false; } + } + + private class AAFPermissionStub extends AAFPermission { + + } +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/test/JU_PermEval.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/test/JU_PermEval.java new file mode 100644 index 00000000..9433cef1 --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/test/JU_PermEval.java @@ -0,0 +1,213 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.aaf.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import org.junit.*; + +import org.onap.aaf.cadi.aaf.PermEval; + +public class JU_PermEval { + + @Test + public void instanceNullTest() { + assertThat(PermEval.evalInstance(null, null), is(false)); + assertThat(PermEval.evalInstance(null, "test"), is(false)); + assertThat(PermEval.evalInstance("test", null), is(false)); + } + + @Test + public void instanceEmptyTest() { + assertThat(PermEval.evalInstance("", ""), is(false)); + assertThat(PermEval.evalInstance("", "test"), is(false)); + assertThat(PermEval.evalInstance("test", ""), is(false)); + } + + @Test + public void instanceAsterixTest() { + assertThat(PermEval.evalInstance("*", "*"), is(true)); + assertTrue(PermEval.evalInstance("*","fred")); + } + + @Test + public void instanceRegexTest() { + assertThat(PermEval.evalInstance("test", "!test"), is(true)); + assertThat(PermEval.evalInstance(",", "!"), is(true)); + assertThat(PermEval.evalInstance("test,test", "!test"), is(true)); + + assertThat(PermEval.evalInstance("test", "!"), is(false)); + assertThat(PermEval.evalInstance("test", "!mismatch"), is(false)); + assertThat(PermEval.evalInstance("test,mismatch", "!mismatch"), is(false)); + } + + @Test + public void instanceKeyTest() { + // Reject non-keys + assertThat(PermEval.evalInstance("fred", ":fred"), is(false)); + + // Reject differing number of keys + assertThat(PermEval.evalInstance(":fred:barney", ":fred"), is(false)); + assertThat(PermEval.evalInstance(":fred", ":fred:barney"), is(false)); + + // Accept all wildcard keys + assertThat(PermEval.evalInstance(":*", ":fred"), is(true)); + + // Accept matching empty keys + assertThat(PermEval.evalInstance(":", ":"), is(true)); + + // Reject non-matching empty keys + assertThat(PermEval.evalInstance(":fred", ":"), is(false)); + + // Accept matches starting with a wildcard + assertThat(PermEval.evalInstance(":!.*ed", ":fred"), is(true)); + + // Reject non-matches starting with a wildcard + assertThat(PermEval.evalInstance(":!.*arney", ":fred"), is(false)); + + // Accept matches ending with a wildcard + assertThat(PermEval.evalInstance(":fr*", ":fred"), is(true)); + + // Reject non-matches ending with a wildcard + assertThat(PermEval.evalInstance(":bar*", ":fred"), is(false)); + + // Accept exact keys + assertThat(PermEval.evalInstance(":fred", ":fred"), is(true)); + + // Reject mismatched keys + assertThat(PermEval.evalInstance(":fred", ":barney"), is(false)); + + // Check using alt-start character + assertThat(PermEval.evalInstance("/fred", "/fred"), is(true)); + assertThat(PermEval.evalInstance("/barney", "/fred"), is(false)); + } + + @Test + public void instanceDirectTest() { + assertThat(PermEval.evalInstance("fred","fred"), is(true)); + assertThat(PermEval.evalInstance("fred,wilma","fred"), is(true)); + assertThat(PermEval.evalInstance("barney,betty,fred,wilma","fred"), is(true)); + assertThat(PermEval.evalInstance("barney,betty,wilma","fred"), is(false)); + + assertThat(PermEval.evalInstance("fr*","fred"), is(true)); + assertThat(PermEval.evalInstance("freddy*","fred"), is(false)); + assertThat(PermEval.evalInstance("ba*","fred"), is(false)); + } + + @Test + public void actionTest() { + // Accept server * + assertThat(PermEval.evalAction("*", ""), is(true)); + assertThat(PermEval.evalAction("*", "literally anything"), is(true)); + + // Reject empty actions + assertThat(PermEval.evalAction("literally anything", ""), is(false)); + + // Accept match as regex + assertThat(PermEval.evalAction("action", "!action"), is(true)); + + // Reject non-match as regex + assertThat(PermEval.evalAction("action", "!nonaction"), is(false)); + + // Accept exact match + assertThat(PermEval.evalAction("action", "action"), is(true)); + + // Reject non-match + assertThat(PermEval.evalAction("action", "nonaction"), is(false)); + } + + @Test + public void redundancyTest() { + // TRUE + assertTrue(PermEval.evalInstance(":fred:fred",":fred:fred")); + assertTrue(PermEval.evalInstance(":fred:fred,wilma",":fred:fred")); + assertTrue(PermEval.evalInstance(":fred:barney,betty,fred,wilma",":fred:fred")); + assertTrue(PermEval.evalInstance(":*:fred",":fred:fred")); + assertTrue(PermEval.evalInstance(":fred:*",":fred:fred")); + assertTrue(PermEval.evalInstance(":!f.*:fred",":fred:fred")); + assertTrue(PermEval.evalInstance(":fred:!f.*",":fred:fred")); + + // FALSE + assertFalse(PermEval.evalInstance("fred","wilma")); + assertFalse(PermEval.evalInstance("fred,barney,betty","wilma")); + assertFalse(PermEval.evalInstance(":fred:fred",":fred:wilma")); + assertFalse(PermEval.evalInstance(":fred:fred",":wilma:fred")); + assertFalse(PermEval.evalInstance(":wilma:!f.*",":fred:fred")); + assertFalse(PermEval.evalInstance(":!f.*:wilma",":fred:fred")); + assertFalse(PermEval.evalInstance(":!w.*:!f.*",":fred:fred")); + assertFalse(PermEval.evalInstance(":!f.*:!w.*",":fred:fred")); + + assertFalse(PermEval.evalInstance(":fred:!x.*",":fred:fred")); + + // MSO Tests 12/3/2015 + assertFalse(PermEval.evalInstance("/v1/services/features/*","/v1/services/features")); + assertFalse(PermEval.evalInstance(":v1:services:features:*",":v1:services:features")); + assertTrue(PermEval.evalInstance("/v1/services/features/*","/v1/services/features/api1")); + assertTrue(PermEval.evalInstance(":v1:services:features:*",":v1:services:features:api2")); + // MSO - Xue Gao + assertTrue(PermEval.evalInstance(":v1:requests:*",":v1:requests:test0-service")); + + + + // Same tests, with Slashes + assertTrue(PermEval.evalInstance("/fred/fred","/fred/fred")); + assertTrue(PermEval.evalInstance("/fred/fred,wilma","/fred/fred")); + assertTrue(PermEval.evalInstance("/fred/barney,betty,fred,wilma","/fred/fred")); + assertTrue(PermEval.evalInstance("*","fred")); + assertTrue(PermEval.evalInstance("/*/fred","/fred/fred")); + assertTrue(PermEval.evalInstance("/fred/*","/fred/fred")); + assertTrue(PermEval.evalInstance("/!f.*/fred","/fred/fred")); + assertTrue(PermEval.evalInstance("/fred/!f.*","/fred/fred")); + + // FALSE + assertFalse(PermEval.evalInstance("fred","wilma")); + assertFalse(PermEval.evalInstance("fred,barney,betty","wilma")); + assertFalse(PermEval.evalInstance("/fred/fred","/fred/wilma")); + assertFalse(PermEval.evalInstance("/fred/fred","/wilma/fred")); + assertFalse(PermEval.evalInstance("/wilma/!f.*","/fred/fred")); + assertFalse(PermEval.evalInstance("/!f.*/wilma","/fred/fred")); + assertFalse(PermEval.evalInstance("/!w.*/!f.*","/fred/fred")); + assertFalse(PermEval.evalInstance("/!f.*/!w.*","/fred/fred")); + + assertFalse(PermEval.evalInstance("/fred/!x.*","/fred/fred")); + + assertTrue(PermEval.evalInstance(":!com.att.*:role:write",":com.att.temp:role:write")); + + // CPFSF-431 Group needed help with Wild Card + // They tried + assertTrue(PermEval.evalInstance( + ":topic.com.att.ecomp_test.crm.pre*", + ":topic.com.att.ecomp_test.crm.predemo100" + )); + + // Also can be + assertTrue(PermEval.evalInstance( + ":!topic.com.att.ecomp_test.crm.pre.*", + ":topic.com.att.ecomp_test.crm.predemo100" + )); + + // coverage + @SuppressWarnings("unused") + PermEval pe = new PermEval(); + } + +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/test/TestHClient.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/test/TestHClient.java new file mode 100644 index 00000000..9536cd90 --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/test/TestHClient.java @@ -0,0 +1,87 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.aaf.test; + +import java.net.HttpURLConnection; +import java.net.URI; + +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.SecuritySetter; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.Locator.Item; +import org.onap.aaf.cadi.aaf.v2_0.AAFLocator; +import org.onap.aaf.cadi.aaf.v2_0.AbsAAFLocator; +import org.onap.aaf.cadi.client.Future; +import org.onap.aaf.cadi.client.Rcli; +import org.onap.aaf.cadi.client.Retryable; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.config.SecurityInfoC; +import org.onap.aaf.cadi.http.HBasicAuthSS; +import org.onap.aaf.cadi.http.HMangr; +import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.misc.env.impl.BasicTrans; + +public class TestHClient { + public static void main(String[] args) { + try { + PropAccess access = new PropAccess(args); + String aaf_url = access.getProperty(Config.AAF_URL); + if(aaf_url == null) { + access.log(Level.ERROR, Config.AAF_URL," is required"); + } else { + HMangr hman = null; + try { + SecurityInfoC<HttpURLConnection> si = SecurityInfoC.instance(access, HttpURLConnection.class); + AbsAAFLocator<BasicTrans> loc = new AAFLocator(si,new URI(aaf_url)); + for(Item item = loc.first(); item!=null; item=loc.next(item)) { + System.out.println(loc.get(item)); + } + SecuritySetter<HttpURLConnection> ss = new HBasicAuthSS(si); + // SecuritySetter<HttpURLConnection> ss = new X509SS(si, "aaf"); + + hman = new HMangr(access,loc); + final String path = String.format("/authz/perms/user/%s", + access.getProperty(Config.AAF_APPID,"xx9999@csp.att.com")); + hman.best(ss, new Retryable<Void>() { + @Override + public Void code(Rcli<?> cli) throws APIException, CadiException { + Future<String> ft = cli.read(path,"application/json"); + if(ft.get(10000)) { + System.out.println("Hurray,\n"+ft.body()); + } else { + System.out.println("not quite: " + ft.code()); + } + return null; + }}); + } finally { + if(hman!=null) { + hman.close(); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/v2_0/test/JU_AAFLocator.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/v2_0/test/JU_AAFLocator.java new file mode 100644 index 00000000..5388f75b --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/v2_0/test/JU_AAFLocator.java @@ -0,0 +1,123 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.aaf.v2_0.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import static org.mockito.Mockito.*; + +import org.junit.*; +import org.mockito.*; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.lang.reflect.Field; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.SecuritySetter; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.aaf.v2_0.AAFLocator; +import org.onap.aaf.cadi.aaf.v2_0.AbsAAFLocator; +import org.onap.aaf.cadi.client.Future; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.config.SecurityInfoC; +import org.onap.aaf.cadi.http.HClient; +import org.onap.aaf.misc.env.Data.TYPE; +import org.onap.aaf.misc.env.impl.BasicTrans; +import org.onap.aaf.misc.rosetta.env.RosettaDF; + +import locate.v1_0.Endpoint; +import locate.v1_0.Endpoints; + +public class JU_AAFLocator { + + @Mock private HClient clientMock; + @Mock private Future<Endpoints> futureMock; + @Mock private Endpoints endpointsMock; + + private PropAccess access; + + private ByteArrayOutputStream errStream; + + private static final String uriString = "https://example.com"; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + doReturn(futureMock).when(clientMock).futureRead((RosettaDF<?>)any(), eq(TYPE.JSON)); + when(clientMock.timeout()).thenReturn(1); + when(clientMock.getURI()).thenReturn(new URI(uriString)); + when(futureMock.get(1)).thenReturn(true); + + futureMock.value = endpointsMock; + List<Endpoint> endpoints = new ArrayList<>(); + endpoints.add(new Endpoint()); + when(endpointsMock.getEndpoint()).thenReturn(endpoints); + + access = new PropAccess(new PrintStream(new ByteArrayOutputStream()), new String[0]); + + errStream = new ByteArrayOutputStream(); + + System.setErr(new PrintStream(errStream)); + } + + @After + public void tearDown() { + System.setErr(System.err); + } + + @AfterClass + public static void tearDownAfterClass() throws Exception { + Field field = SecurityInfoC.class.getDeclaredField("sicMap"); + field.setAccessible(true); + field.set(null, new HashMap<Class<?>,SecurityInfoC<?>>()); + } + + @Test + public void test() throws CadiException, URISyntaxException, LocatorException { + access.setProperty(Config.CADI_LATITUDE, "38.62"); // St Louis approx lat + access.setProperty(Config.CADI_LONGITUDE, "90.19"); // St Louis approx lon + SecurityInfoC<HttpURLConnection> si = SecurityInfoC.instance(access, HttpURLConnection.class); + String alu = access.getProperty(Config.AAF_LOCATE_URL,"https://mithrilcsp.sbc.com:8095/locate"); + URI locatorURI = new URI(alu+"/com.att.aaf.service/2.0"); + AbsAAFLocator<BasicTrans> al = new AAFLocator(si, locatorURI) { + @Override + protected HClient createClient(SecuritySetter<HttpURLConnection> ss, URI uri, int connectTimeout) throws LocatorException { + return clientMock; + } + }; + assertThat(al.refresh(), is(true)); + when(futureMock.get(1)).thenReturn(false); + assertThat(al.refresh(), is(false)); + String errorMessage = errStream.toString().split(": ", 2)[1]; + assertThat(errorMessage, is("Error reading location information from " + uriString + ": 0 null\n \n")); + } + +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/v2_0/test/JU_AAFTrustChecker.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/v2_0/test/JU_AAFTrustChecker.java new file mode 100644 index 00000000..1e469eca --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/v2_0/test/JU_AAFTrustChecker.java @@ -0,0 +1,130 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.aaf.v2_0.test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.when; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import javax.servlet.http.HttpServletRequest; + +import org.onap.aaf.cadi.Lur; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.aaf.v2_0.AAFTrustChecker; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.principal.TaggedPrincipal; +import org.onap.aaf.cadi.taf.TafResp; +import org.onap.aaf.cadi.taf.TrustNotTafResp; +import org.onap.aaf.cadi.taf.TrustTafResp; +import org.onap.aaf.misc.env.Env; + +public class JU_AAFTrustChecker { + + private final static String type = "type"; + private final static String instance = "instance"; + private final static String action = "action"; + private final static String key = type + '|' + instance + '|' + action; + private final static String name = "name"; + private final static String otherName = "otherName"; + + private PropAccess access; + + @Mock private Env envMock; + @Mock private TafResp trespMock; + @Mock private HttpServletRequest reqMock; + @Mock private TaggedPrincipal tpMock; + @Mock private Lur lurMock; + @Mock private TaggedPrincipal princMock; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + access = new PropAccess(new PrintStream(new ByteArrayOutputStream()), new String[0]); + } + + @Test + public void test() { + AAFTrustChecker trustChecker; + + // coverage calls + trustChecker = new AAFTrustChecker(access); + trustChecker = new AAFTrustChecker(envMock); + + access.setProperty(Config.CADI_TRUST_PERM, "example"); + when(envMock.getProperty(Config.CADI_TRUST_PERM)).thenReturn("example"); + trustChecker = new AAFTrustChecker(access); + trustChecker = new AAFTrustChecker(envMock); + + access.setProperty(Config.CADI_TRUST_PERM, key); + when(envMock.getProperty(Config.CADI_TRUST_PERM)).thenReturn(key); + trustChecker = new AAFTrustChecker(access); + trustChecker = new AAFTrustChecker(envMock); + + trustChecker.setLur(lurMock); + + assertThat(trustChecker.mayTrust(trespMock, reqMock), is(trespMock)); + + when(reqMock.getHeader(null)).thenReturn("comma,comma,comma"); + assertThat(trustChecker.mayTrust(trespMock, reqMock), is(trespMock)); + + when(reqMock.getHeader(null)).thenReturn("colon:colon:colon:colon,comma,comma"); + assertThat(trustChecker.mayTrust(trespMock, reqMock), is(trespMock)); + + when(reqMock.getHeader(null)).thenReturn("colon:colon:colon:AS,comma,comma"); + when(trespMock.getPrincipal()).thenReturn(tpMock); + when(tpMock.getName()).thenReturn(name); + when(lurMock.fish(princMock, null)).thenReturn(true); + TafResp tntResp = trustChecker.mayTrust(trespMock, reqMock); + + assertThat(tntResp instanceof TrustNotTafResp, is(true)); + assertThat(tntResp.toString(), is("name requested trust as colon, but does not have Authorization")); + + when(reqMock.getHeader(null)).thenReturn(name + ":colon:colon:AS,comma,comma"); + assertThat(trustChecker.mayTrust(trespMock, reqMock), is(trespMock)); + + when(envMock.getProperty(Config.CADI_ALIAS, null)).thenReturn(name); + when(envMock.getProperty(Config.CADI_TRUST_PERM)).thenReturn(null); + trustChecker = new AAFTrustChecker(envMock); + trustChecker.setLur(lurMock); + + when(trespMock.getPrincipal()).thenReturn(princMock); + when(princMock.getName()).thenReturn(otherName); + when(lurMock.fish(princMock, null)).thenReturn(true); + TafResp ttResp = trustChecker.mayTrust(trespMock, reqMock); + assertThat(ttResp instanceof TrustTafResp, is(true)); + assertThat(ttResp.toString(), is(name + " by trust of " + name + " validated using colon by colon, null")); + + when(princMock.getName()).thenReturn(name); + ttResp = trustChecker.mayTrust(trespMock, reqMock); + assertThat(ttResp instanceof TrustTafResp, is(true)); + assertThat(ttResp.toString(), is(name + " by trust of " + name + " validated using colon by colon, null")); + } + +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/v2_0/test/JU_AbsAAFLocator.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/v2_0/test/JU_AbsAAFLocator.java new file mode 100644 index 00000000..e9c74cbf --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/cadi/aaf/v2_0/test/JU_AbsAAFLocator.java @@ -0,0 +1,193 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.aaf.v2_0.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; +import org.junit.*; +import org.mockito.*; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.net.URI; +import java.net.URISyntaxException; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.Locator.Item; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.aaf.v2_0.AbsAAFLocator; +import org.onap.aaf.cadi.aaf.v2_0.AbsAAFLocator.LocatorCreator; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.misc.env.impl.BasicTrans; + +public class JU_AbsAAFLocator { + + @Mock private LocatorCreator locatorCreatorMock; + + private PropAccess access; + private URI uri; + + private static final String uriString = "example.com"; + + @Before + public void setup() throws URISyntaxException { + MockitoAnnotations.initMocks(this); + + access = new PropAccess(new PrintStream(new ByteArrayOutputStream()), new String[0]); + access.setProperty(Config.CADI_LATITUDE, "38.62"); // St Louis approx lat + access.setProperty(Config.CADI_LONGITUDE, "90.19"); // St Louis approx lon + + uri = new URI(uriString); + } + + @AfterClass + public static void tearDownAfterClass() throws Exception { + AbsAAFLocator.setCreator(null); + } + + @Test + public void test() throws LocatorException { + AAFLocatorStub loc; + + // Test with http + loc = new AAFLocatorStub(access, "httpname"); + assertThat(loc.getName(), is("httpname")); + assertThat(loc.getVersion(), is(Config.AAF_DEFAULT_VERSION)); + assertThat(loc.toString(), is("AAFLocator for " + "httpname" + " on " + loc.getURI())); + + loc = new AAFLocatorStub(access, "name"); + assertThat(loc.getName(), is("name")); + assertThat(loc.getVersion(), is(Config.AAF_DEFAULT_VERSION)); + loc = new AAFLocatorStub(access, "name:v2.0"); + assertThat(loc.getName(), is("name")); + assertThat(loc.getVersion(), is("v2.0")); + } + + @Test + public void createTest() throws LocatorException { + AbsAAFLocator.setCreator(locatorCreatorMock); + + assertThat(AbsAAFLocator.create("nonsense"), is(nullValue())); + assertThat(AbsAAFLocator.create("nonsense/locate"), is(nullValue())); + assertThat(AbsAAFLocator.create("nonsense/locate/"), is(nullValue())); + assertThat(AbsAAFLocator.create("nonsense/locate//"), is(nullValue())); + assertThat(AbsAAFLocator.create("nonsense/locate/name:v2.0"), is(nullValue())); + + assertThat(AbsAAFLocator.create("http/locate/name:v2.0"), is(nullValue())); + + doReturn(mock(AbsAAFLocator.class)).when(locatorCreatorMock).create(anyString(), anyString()); + assertThat(AbsAAFLocator.create("http/locate/name:v2.0/path"), is(not(nullValue()))); + + AbsAAFLocator.setCreator(null); + assertThat(AbsAAFLocator.create("http/locate/name:v2.0"), is(nullValue())); + + assertThat(AbsAAFLocator.create("http"), is(not(nullValue()))); + + AbsAAFLocator.setCreator(locatorCreatorMock); + assertThat(AbsAAFLocator.create("first", "second"), is(not(nullValue()))); + } + + @Test + public void nameFromLocatorURITest() throws LocatorException, URISyntaxException { + AAFLocatorStub loc = new AAFLocatorStub(access, "name:v2.0"); + assertThat(loc.getNameFromURI(new URI("example.com")), is("example.com")); + assertThat(loc.getNameFromURI(new URI("example.com/extra/stuff")), is("example.com/extra/stuff")); + assertThat(loc.getNameFromURI(new URI("example.com/locate/stuff")), is("stuff")); // n' stuff + } + + @Test + public void setSelfTest() throws LocatorException { + AbsAAFLocator.setCreatorSelf("host", 8000); + AbsAAFLocator.setCreator(null); + AbsAAFLocator.setCreatorSelf("host", 8000); + (new AAFLocatorStub(access, "name:v2.0")).setSelf("host", 8000); // oof + } + + @Test + public void coverage() throws LocatorException { + AAFLocatorStub loc = new AAFLocatorStub(access, "name:v2.0"); + assertThat(loc.get(null), is(nullValue())); + + try { + loc.get(mock(Item.class)); + fail("Should've thrown an exception"); + } catch (Exception e) { + } + + try { + loc.invalidate(mock(Item.class)); + fail("Should've thrown an exception"); + } catch (Exception e) { + } + + try { + loc.best(); + fail("Should've thrown an exception"); + } catch (Exception e) { + } + + assertThat(loc.first(), is(nullValue())); + + assertThat(loc.hasItems(), is(false)); + assertThat(loc.next(null), is(nullValue())); + + try { + loc.next(mock(Item.class)); + fail("Should've thrown an exception"); + } catch (Exception e) { + } + + loc.destroy(); + + + assertThat(loc.exposeGetURI(uri), is(uri)); + + assertThat(loc.setPathInfo("pathInfo"), is(not(nullValue()))); + assertThat(loc.setQuery("query"), is(not(nullValue()))); + assertThat(loc.setFragment("fragment"), is(not(nullValue()))); + + assertThat(loc.exposeGetURI(uri), is(not(uri))); + } + + + @Test(expected = LocatorException.class) + public void throwsTest() throws LocatorException { + @SuppressWarnings("unused") + AAFLocatorStub loc = new AAFLocatorStub(new PropAccess(), "name"); + } + + private class AAFLocatorStub extends AbsAAFLocator<BasicTrans> { + public AAFLocatorStub(Access access, String name) throws LocatorException { + super(access, name, 10000L); + } + @Override public boolean refresh() { return false; } + @Override protected URI getURI() { return uri; } + public String getName() { return name; } + public String getVersion() { return version; } + public String getNameFromURI(URI uri) { return nameFromLocatorURI(uri); } + public URI exposeGetURI(URI uri) throws LocatorException { return super.getURI(uri); } + } + +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/cm/test/JU_ArtifactDir.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/cm/test/JU_ArtifactDir.java new file mode 100644 index 00000000..d0d67e23 --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/cadi/cm/test/JU_ArtifactDir.java @@ -0,0 +1,171 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.cm.test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.io.IOException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.util.ArrayList; +import java.util.List; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.cm.ArtifactDir; +import org.onap.aaf.cadi.util.Chmod; +import org.onap.aaf.misc.env.Trans; + +import certman.v1_0.Artifacts.Artifact; +import certman.v1_0.CertInfo; + +public class JU_ArtifactDir { + + @Mock private Trans transMock; + @Mock private CertInfo certInfoMock; + @Mock private Artifact artiMock; + + private static final String dirName = "src/test/resources/artifacts"; + private static final String nsName = "org.onap.test"; + private static final String luggagePassword = "12345"; // That's the stupidest combination I've ever heard in my life + + private List<String> issuers; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + + issuers = new ArrayList<>(); + issuers.add("issuer1"); + issuers.add("issuer2"); + } + + @After + public void tearDown() { + ArtifactDir.clear(); + } + + @AfterClass + public static void tearDownOnce() { + cleanup(); + } + + @Test + public void test() throws CadiException, IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException { + ArtifactDirStud artiDir = new ArtifactDirStud(); + + try { + artiDir.place(transMock, certInfoMock, artiMock, "machine"); + fail("Should've thrown an exception"); + } catch (CadiException e) { + assertThat(e.getMessage(), is("File Artifacts require a path\nFile Artifacts require an AAF Namespace")); + } + + when(artiMock.getDir()).thenReturn(dirName); + try { + artiDir.place(transMock, certInfoMock, artiMock, "machine"); + fail("Should've thrown an exception"); + } catch (CadiException e) { + assertThat(e.getMessage(), is("File Artifacts require an AAF Namespace")); + } + + when(artiMock.getNs()).thenReturn(nsName); + when(certInfoMock.getCaIssuerDNs()).thenReturn(issuers); + when(certInfoMock.getChallenge()).thenReturn(luggagePassword); + artiDir.place(transMock, certInfoMock, artiMock, "machine"); + + File writableFile = new File(dirName + '/' + nsName + "writable.txt"); + artiDir.write(writableFile, Chmod.to755, "first data point", "second data point"); + try { + artiDir.write(writableFile, Chmod.to755, (String[])null); + fail("Should've thrown an exception"); + } catch(NullPointerException e) { + } + + KeyStore ks = KeyStore.getInstance("pkcs12"); + try { + artiDir.write(writableFile, Chmod.to755, ks, luggagePassword.toCharArray()); + fail("Should've thrown an exception"); + } catch(CadiException e) { + } + + ks.load(null, null); + artiDir.write(writableFile, Chmod.to755, ks, luggagePassword.toCharArray()); + + ArtifactDirStud artiDir2 = new ArtifactDirStud(); + artiDir2.place(transMock, certInfoMock, artiMock, "machine"); + + // coverage + artiDir.place(transMock, certInfoMock, artiMock, "machine"); + + ArtifactDir.clear(); + artiDir.place(transMock, certInfoMock, artiMock, "machine"); + + } + + @Test(expected = CadiException.class) + public void throwsTest() throws CadiException { + ArtifactDirStud artiDir = new ArtifactDirStud(); + when(artiMock.getDir()).thenReturn(dirName); + when(artiMock.getNs()).thenReturn(nsName); + artiDir.place(transMock, certInfoMock, artiMock, "machine"); + } + + private class ArtifactDirStud extends ArtifactDir { + @Override + protected boolean _place(Trans trans, CertInfo certInfo, Artifact arti) throws CadiException { + // This is only here so that we have a concrete class to test + return false; + } + + // Expose the protected methods + + public void write(File f, Chmod c, String ... data) throws IOException { + super.write(f, c, data); + } + public void write(File f, Chmod c, KeyStore ks, char[] pass ) throws IOException, CadiException { + super.write(f, c, ks, pass); + } + } + + private static void cleanup() { + File dir = new File(dirName); + if (dir.exists()) { + for (File f : dir.listFiles()) { + f.delete(); + } + dir.delete(); + } + } + +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/cm/test/JU_CertException.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/cm/test/JU_CertException.java new file mode 100644 index 00000000..aa12d7c6 --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/cadi/cm/test/JU_CertException.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.cm.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import org.junit.*; + +import org.onap.aaf.cadi.cm.CertException; + +public class JU_CertException { + + private static final String message = "The message associated with the exception"; + + @Test(expected = CertException.class) + public void test() throws CertException { + CertException except; + + except = new CertException(message); + assertThat(except.getMessage(), is(message)); + + except = new CertException(new Exception(message)); + assertThat(except.getMessage(), is("java.lang.Exception: " + message)); + + except = new CertException(message, new Exception(message)); + assertThat(except.getMessage(), is(message)); + + throw new CertException(); + } + +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/cm/test/JU_CmAgent.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/cm/test/JU_CmAgent.java new file mode 100644 index 00000000..34ccf57b --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/cadi/cm/test/JU_CmAgent.java @@ -0,0 +1,123 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ + +package org.onap.aaf.cadi.cm.test; + +import java.io.ByteArrayInputStream; +import java.io.File; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onap.aaf.cadi.cm.CmAgent; + +public class JU_CmAgent { + + private static final String resourceDirString = "src/test/resources"; + private static final String aafDir = resourceDirString + "/aaf"; + + private ByteArrayInputStream inStream; + + @Before + public void setup() { + System.setProperty("user.home", aafDir); + + // Simulate user input + inStream = new ByteArrayInputStream("test\nhttp://example.com\nhttp://example.com".getBytes()); + System.setIn(inStream); + } + + @After + public void tearDown() { + recursiveDelete(new File(aafDir)); + } + + @Test + public void test() { + String[] args; + args = new String[] { + "-login", + "-noexit", + }; + CmAgent.main(args); + + inStream.reset(); + args = new String[] { + "noexit=true", + }; + CmAgent.main(args); + + inStream.reset(); + args = new String[] { + "place", + }; + CmAgent.main(args); + + inStream.reset(); + args = new String[] { + "create" + }; + CmAgent.main(args); + + inStream.reset(); + args = new String[] { + "read" + }; + CmAgent.main(args); + + inStream.reset(); + args = new String[] { + "copy" + }; + CmAgent.main(args); + + inStream.reset(); + args = new String[] { + "update" + }; + CmAgent.main(args); + + inStream.reset(); + args = new String[] { + "delete" + }; + CmAgent.main(args); + + inStream.reset(); + args = new String[] { + "showpass" + }; + CmAgent.main(args); + + } + + private void recursiveDelete(File file) { + for (File f : file.listFiles()) { + if (f.isDirectory()) { + recursiveDelete(f); + } + f.delete(); + } + file.delete(); + } + +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/cm/test/JU_Factory.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/cm/test/JU_Factory.java new file mode 100644 index 00000000..fb186b89 --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/cadi/cm/test/JU_Factory.java @@ -0,0 +1,367 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ + +package org.onap.aaf.cadi.cm.test; + +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.anyString; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.security.KeyPair; +import java.security.Principal; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import javax.crypto.Cipher; + +import org.onap.aaf.cadi.cm.CertException; +import org.onap.aaf.cadi.cm.Factory; +import org.onap.aaf.cadi.cm.Factory.Base64InputStream; +import org.onap.aaf.cadi.cm.Factory.StripperInputStream; + +import org.onap.aaf.misc.env.Env; +import org.onap.aaf.misc.env.LogTarget; +import org.onap.aaf.misc.env.TimeTaken; +import org.onap.aaf.misc.env.Trans; + +public class JU_Factory { + + private static final String message = "The quick brown fox jumps over the lazy dog."; + private static final String subjectDNText = "subjectDN"; + private static final String certText = "Some text that might be included in a certificate"; + private static final String resourceDirName = "src/test/resources"; + + private File resourceDir; + private File publicKeyFile; + private File privateKeyFile; + private File certFile; + + @Mock private Trans transMock; + @Mock private TimeTaken timeTakenMock; + @Mock private LogTarget logTargetMock; + @Mock private X509Certificate x509CertMock; + @Mock private Certificate certMock; + @Mock private Principal subjectDN; + + + @Before + public void setup() throws CertificateEncodingException { + MockitoAnnotations.initMocks(this); + + resourceDir = new File(resourceDirName); + resourceDir.mkdirs(); + publicKeyFile = new File(resourceDirName, "/publicKey"); + privateKeyFile = new File(resourceDirName, "/privateKey"); + publicKeyFile.delete(); + privateKeyFile.delete(); + + certFile = new File(resourceDirName + "/exampleCertificate.cer"); + + when(transMock.start(anyString(), anyInt())).thenReturn(timeTakenMock); + when(transMock.debug()).thenReturn(logTargetMock); + + when(subjectDN.toString()).thenReturn(subjectDNText); + + when(x509CertMock.getSubjectDN()).thenReturn(subjectDN); + when(x509CertMock.getEncoded()).thenReturn(certText.getBytes()); + + when(certMock.getEncoded()).thenReturn(certText.getBytes()); + } + + @After + public void tearDown() { + publicKeyFile = new File(resourceDirName, "/publicKey"); + privateKeyFile = new File(resourceDirName, "/privateKey"); + publicKeyFile.delete(); + privateKeyFile.delete(); + } + + @Test + public void generateKeyPairTest() throws Exception { + // This instatiation isn't actually necessary, but it gets coverage + Cipher encryptor = Factory.pkCipher(); + Cipher decryptor = Factory.pkCipher(); + + KeyPair kp1 = Factory.generateKeyPair(transMock); + encryptor = Factory.pkCipher(kp1.getPublic(), true); + decryptor = Factory.pkCipher(kp1.getPrivate(), false); + byte[] encrypedMessage1 = encryptor.doFinal(message.getBytes(StandardCharsets.UTF_8)); + String output1 = new String(decryptor.doFinal(encrypedMessage1)); + assertThat(output1, is(message)); + + // coverage + when(transMock.start("Generate KeyPair", Env.SUB)).thenReturn(null); + KeyPair kp2 = Factory.generateKeyPair(transMock); + encryptor = Factory.pkCipher(kp2.getPublic(), true); + decryptor = Factory.pkCipher(kp2.getPrivate(), false); + byte[] encrypedMessage2 = encryptor.doFinal(message.getBytes(StandardCharsets.UTF_8)); + String output2 = new String(decryptor.doFinal(encrypedMessage2)); + assertThat(output2, is(message)); + + KeyPair kp3 = Factory.generateKeyPair(null); + encryptor = Factory.pkCipher(kp3.getPublic(), true); + decryptor = Factory.pkCipher(kp3.getPrivate(), false); + byte[] encrypedMessage3 = encryptor.doFinal(message.getBytes(StandardCharsets.UTF_8)); + String output3 = new String(decryptor.doFinal(encrypedMessage3)); + assertThat(output3, is(message)); + } + + @Test + public void keyStringManipTest() throws Exception { + KeyPair kp = Factory.generateKeyPair(transMock); + + String publicKeyString = Factory.toString(transMock, kp.getPublic()); + String privateKeyString = Factory.toString(transMock, kp.getPrivate()); + + assertThat(publicKeyString.startsWith("-----BEGIN PUBLIC KEY-----"), is(true)); + assertThat(publicKeyString.endsWith("-----END PUBLIC KEY-----\n"), is(true)); + + assertThat(privateKeyString.startsWith("-----BEGIN PRIVATE KEY-----"), is(true)); + assertThat(privateKeyString.endsWith("-----END PRIVATE KEY-----\n"), is(true)); + + PublicKey publicKey = Factory.toPublicKey(transMock, cleanupString(publicKeyString)); + PrivateKey privateKey = Factory.toPrivateKey(transMock, cleanupString(privateKeyString)); + + Cipher encryptor = Factory.pkCipher(publicKey, true); + Cipher decryptor = Factory.pkCipher(privateKey, false); + byte[] encrypedMessage = encryptor.doFinal(message.getBytes(StandardCharsets.UTF_8)); + String output = new String(decryptor.doFinal(encrypedMessage)); + assertThat(output, is(message)); + } + + @Test + public void keyFileManipTest() throws Exception { + KeyPair kp = Factory.generateKeyPair(transMock); + + String privateKeyString = Factory.toString(transMock, kp.getPrivate()); + writeToFile(privateKeyFile, privateKeyString, "Header:this line has a header"); + + PublicKey publicKey = kp.getPublic(); + PrivateKey privateKey = Factory.toPrivateKey(transMock, privateKeyFile); + + Cipher encryptor = Factory.pkCipher(publicKey, true); + Cipher decryptor = Factory.pkCipher(privateKey, false); + byte[] encrypedMessage = encryptor.doFinal(message.getBytes(StandardCharsets.UTF_8)); + String output = new String(decryptor.doFinal(encrypedMessage)); + assertThat(output, is(message)); + } + + @Test + public void certToStringTest() throws IOException, CertException, CertificateEncodingException { + String certString; + when(logTargetMock.isLoggable()).thenReturn(true); + + certString = Factory.toString(transMock, x509CertMock); + assertThat(certString.startsWith("-----BEGIN CERTIFICATE-----"), is(true)); + assertThat(certString.endsWith("-----END CERTIFICATE-----\n"), is(true)); + + certString = Factory.toString(transMock, certMock); + assertThat(certString.startsWith("-----BEGIN CERTIFICATE-----"), is(true)); + assertThat(certString.endsWith("-----END CERTIFICATE-----\n"), is(true)); + + try { + certString = Factory.toString(transMock, (Certificate)null); + fail("Should have thrown an exception"); + } catch (CertException e) { + assertThat(e.getMessage(), is("Certificate not built")); + } + + when(certMock.getEncoded()).thenThrow(new CertificateEncodingException()); + try { + certString = Factory.toString(transMock, certMock); + fail("Should have thrown an exception"); + } catch (CertException e) { + } + + // coverage + when(logTargetMock.isLoggable()).thenReturn(false); + certString = Factory.toString(transMock, x509CertMock); + } + + @Test + public void toX509Test() throws CertificateException, IOException, CertException { + String output; + Collection<? extends Certificate> certs; + when(logTargetMock.isLoggable()).thenReturn(true); + + String certString = readFromFile(certFile, false); + + certs = Factory.toX509Certificate(certString); + // Contrived way of getting a Certificate out of a Collection + output = Factory.toString(transMock, certs.toArray(new Certificate[0])[0]); + assertThat(output, is(certString)); + + certs = Factory.toX509Certificate(transMock, certFile); + // Contrived way of getting a Certificate out of a Collection + output = Factory.toString(transMock, certs.toArray(new Certificate[0])[0]); + assertThat(output, is(certString)); + + List<String> certStrings = new ArrayList<String>(); + certStrings.add(certString); + certStrings.add(certString); + certs = Factory.toX509Certificate(certStrings); + // Contrived way of getting a Certificate out of a Collection + // it doesn't matter which one we get - they're the same + output = Factory.toString(transMock, certs.toArray(new Certificate[0])[0]); + assertThat(output, is(certString)); + } + + @Test + public void stripperTest() throws Exception { + KeyPair kp = Factory.generateKeyPair(transMock); + String privateKeyString = Factory.toString(transMock, kp.getPrivate()); + writeToFile(privateKeyFile, privateKeyString, "Header:this line has a header"); + + StripperInputStream stripper = new StripperInputStream(privateKeyFile); + + String expected = cleanupString(privateKeyString); + byte[] buffer = new byte[10000]; + stripper.read(buffer); + String output = new String(buffer, 0, expected.length()); + assertThat(output, is(expected)); + stripper.close(); + + // coverage + stripper = new StripperInputStream(new FileInputStream(privateKeyFile)); + stripper.close(); + stripper = new StripperInputStream(new BufferedReader(new FileReader(privateKeyFile))); + stripper.close(); + stripper.close(); // also coverage... + } + + @Test + public void binaryTest() throws IOException { + String output = new String(Factory.binary(certFile)); + String expected = readFromFile(certFile, true); + assertThat(output, is(expected)); + } + + @Test + public void signatureTest() throws Exception { + KeyPair kp = Factory.generateKeyPair(transMock); + String signedString = "Something that needs signing"; + byte[] signedBytes = Factory.sign(transMock, signedString.getBytes(), kp.getPrivate()); + String output = Factory.toSignatureString(signedBytes); + assertThat(output.startsWith("-----BEGIN SIGNATURE-----"), is(true)); + assertThat(output.endsWith("-----END SIGNATURE-----\n"), is(true)); + assertThat(Factory.verify(transMock, signedString.getBytes(), signedBytes, kp.getPublic()), is(true)); + } + + @Test + public void base64ISTest() throws Exception { + KeyPair kp = Factory.generateKeyPair(transMock); + + String privateKeyString = Factory.toString(transMock, kp.getPrivate()); + String cleaned = cleanupString(privateKeyString); + writeToFile(privateKeyFile, cleaned, null); + Base64InputStream b64is = new Base64InputStream(privateKeyFile); + byte[] buffer = new byte[10000]; + b64is.read(buffer); + b64is.close(); + + FileInputStream fis = new FileInputStream(privateKeyFile); + b64is = new Base64InputStream(fis); + b64is.close(); + fis.close(); + } + + @Test + public void getSecurityProviderTest() throws CertException { + String[][] params = { + {"test", "test"}, + {"test", "test"}, + }; + assertThat(Factory.getSecurityProvider("PKCS12", params), is(nullValue())); + } + + private String cleanupString(String str) { + String[] lines = str.split("\n", 0); + List<String> rawLines = new ArrayList<String>(); + for (int i = 0; i < lines.length - 2; i++) { + rawLines.add(lines[i + 1]); + } + return join("", rawLines); + } + + /** + * Note: String.join is not part of JDK 7, which is what we compile to for CADI + */ + private String join(String delim, List<String> rawLines) { + StringBuilder sb = new StringBuilder(); + boolean first = true; + for(String s : rawLines) { + if(first) { + first = false; + } else { + sb.append(delim); + } + sb.append(s); + } + return sb.toString(); + } + + private void writeToFile(File file, String contents, String header) throws Exception { + PrintWriter writer = new PrintWriter(file, "UTF-8"); + if (header != null) { + writer.println(header); + } + writer.println(contents); + writer.close(); + } + + private String readFromFile(File file, boolean addCR) throws IOException { + BufferedReader br = new BufferedReader(new FileReader(file)); + StringBuilder sb = new StringBuilder(); + String line; + while ((line = br.readLine()) != null) { + String lineEnd = (addCR) ? "\r\n" : "\n"; + sb.append(line + lineEnd); + } + br.close(); + return sb.toString(); + } + +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/cm/test/JU_PlaceArtifactInFiles.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/cm/test/JU_PlaceArtifactInFiles.java new file mode 100644 index 00000000..3c83112c --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/cadi/cm/test/JU_PlaceArtifactInFiles.java @@ -0,0 +1,100 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.cm.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import static org.mockito.Mockito.*; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import org.junit.*; +import org.mockito.*; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.cm.PlaceArtifactInFiles; +import org.onap.aaf.misc.env.Trans; + +import certman.v1_0.Artifacts.Artifact; +import certman.v1_0.CertInfo; + +public class JU_PlaceArtifactInFiles { + + @Mock private Trans transMock; + @Mock private CertInfo certInfoMock; + @Mock private Artifact artiMock; + + private static final String dirName = "src/test/resources/artifacts"; + private static final String nsName = "org.onap.test"; + private static final String luggagePassword = "12345"; // That's the stupidest combination I've ever heard in my life + + private List<String> certs; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + + certs = new ArrayList<>(); + certs.add("cert1"); + certs.add("cert2"); + + when(certInfoMock.getChallenge()).thenReturn(luggagePassword); + when(certInfoMock.getCerts()).thenReturn(certs); + + when(artiMock.getDir()).thenReturn(dirName); + when(artiMock.getNs()).thenReturn(nsName); + } + + @AfterClass + public static void tearDownOnce() { + cleanup(); + PlaceArtifactInFiles.clear(); + } + + @Test + public void test() throws CadiException { + PlaceArtifactInFiles placer = new PlaceArtifactInFiles(); + placer.place(transMock, certInfoMock, artiMock, "machine"); + assertThat(placer._place(transMock, certInfoMock, artiMock), is(true)); + assertThat(new File(dirName + '/' + nsName + ".crt").exists(), is(true)); + assertThat(new File(dirName + '/' + nsName + ".key").exists(), is(true)); + + when(certInfoMock.getCerts()).thenReturn(null); + try { + placer._place(transMock, certInfoMock, artiMock); + fail("Should've thrown an exception"); + } catch (Exception e) { + } + } + + private static void cleanup() { + File dir = new File(dirName); + if (dir.exists()) { + for (File f : dir.listFiles()) { + f.delete(); + } + dir.delete(); + } + } + +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/cm/test/JU_PlaceArtifactInKeystore.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/cm/test/JU_PlaceArtifactInKeystore.java new file mode 100644 index 00000000..d146f631 --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/cadi/cm/test/JU_PlaceArtifactInKeystore.java @@ -0,0 +1,147 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.cm.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import static org.mockito.Mockito.*; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import java.security.cert.CertificateException; + +import org.junit.*; +import org.mockito.*; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.cm.PlaceArtifactInKeystore; +import org.onap.aaf.misc.env.Env; +import org.onap.aaf.misc.env.TimeTaken; +import org.onap.aaf.misc.env.Trans; + +import certman.v1_0.Artifacts.Artifact; +import certman.v1_0.CertInfo; + +public class JU_PlaceArtifactInKeystore { + + @Mock private Trans transMock; + @Mock private CertInfo certInfoMock; + @Mock private Artifact artiMock; + + private static final String caName = "onap"; + private static final String dirName = "src/test/resources/artifacts"; + private static final String nsName = "org.onap.test"; + private static final String mechID = "m12345"; + private static final String luggagePassword = "12345"; // That's the stupidest combination I've ever heard in my life + + private static String privateKeyString; + private static String x509Chain; + private static String x509String; + + private List<String> certs; + + @Before + public void setup() throws FileNotFoundException, IOException, CertificateException { + MockitoAnnotations.initMocks(this); + + x509Chain = fromFile(new File("src/test/resources/cert.pem")); + x509String = fromFile(new File("src/test/resources/exampleCertificate.cer")); + privateKeyString = fromFile(new File("src/test/resources/key.pem")); + + certs = new ArrayList<>(); + + when(certInfoMock.getChallenge()).thenReturn(luggagePassword); + when(certInfoMock.getCerts()).thenReturn(certs); + + when(artiMock.getCa()).thenReturn(caName); + when(artiMock.getDir()).thenReturn(dirName); + when(artiMock.getNs()).thenReturn(nsName); + when(artiMock.getMechid()).thenReturn(mechID); + + when(certInfoMock.getPrivatekey()).thenReturn(privateKeyString); + + when(transMock.start("Reconstitute Private Key", Env.SUB)).thenReturn(mock(TimeTaken.class)); + } + + @AfterClass + public static void tearDownOnce() { + cleanup(); + PlaceArtifactInKeystore.clear(); + } + + @Test + public void test() throws CadiException { + // Note: PKCS12 can't be tested in JDK 7 and earlier. Can't handle Trusting Certificates. + PlaceArtifactInKeystore placer = new PlaceArtifactInKeystore("jks"); + + certs.add(x509String); + certs.add(x509Chain); + assertThat(placer.place(transMock, certInfoMock, artiMock, "machine"), is(true)); + for (String ext : new String[] {"chal", "keyfile", "jks", "props", "trust.jks"}) { + assertThat(new File(dirName + '/' + nsName + '.' + ext).exists(), is(true)); + } + + // coverage + assertThat(placer.place(transMock, certInfoMock, artiMock, "machine"), is(true)); + + when(certInfoMock.getCerts()).thenReturn(null); + try { + placer._place(transMock, certInfoMock, artiMock); + fail("Should've thrown an exception"); + } catch (Exception e) { + } + + } + + private static void cleanup() { + File dir = new File(dirName); + if (dir.exists()) { + for (File f : dir.listFiles()) { + f.delete(); + } + dir.delete(); + } + } + + public String fromFile(File file) throws IOException { + BufferedReader br = new BufferedReader(new FileReader(file)); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + String line; + baos.write(br.readLine().getBytes()); + // Here comes the hacky part + baos.write("\n".getBytes()); + while((line=br.readLine())!=null) { + if(line.length()>0) { + baos.write(line.getBytes()); + baos.write("\n".getBytes()); + } + } + br.close(); + return baos.toString(); + } +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/cm/test/JU_PlaceArtifactOnStream.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/cm/test/JU_PlaceArtifactOnStream.java new file mode 100644 index 00000000..6e390bed --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/cadi/cm/test/JU_PlaceArtifactOnStream.java @@ -0,0 +1,101 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.cm.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import static org.mockito.Mockito.*; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.List; + +import org.junit.*; +import org.mockito.*; + +import org.onap.aaf.cadi.cm.PlaceArtifactOnStream; +import org.onap.aaf.misc.env.LogTarget; +import org.onap.aaf.misc.env.Trans; + +import certman.v1_0.Artifacts.Artifact; +import certman.v1_0.CertInfo; + +public class JU_PlaceArtifactOnStream { + + @Mock private Trans transMock; + @Mock private CertInfo certInfoMock; + @Mock private Artifact artiMock; + + private static final String luggagePassword = "12345"; // That's the stupidest combination I've ever heard in my life + private static final String privateKeyString = "I'm a private key!"; + + private ByteArrayOutputStream outStream; + + private List<String> certs; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + + certs = new ArrayList<>(); + certs.add("cert1"); + certs.add("cert2"); + + when(certInfoMock.getChallenge()).thenReturn(luggagePassword); + when(certInfoMock.getCerts()).thenReturn(certs); + when(certInfoMock.getPrivatekey()).thenReturn(privateKeyString); + + outStream = new ByteArrayOutputStream(); + } + + @Test + public void test() { + PlaceArtifactOnStream placer = new PlaceArtifactOnStream(new PrintStream(outStream)); + placer.place(transMock, certInfoMock, artiMock, "machine"); + + String[] output = outStream.toString().split("\n", 0); + + String[] expected = { + "Challenge: " + luggagePassword, + "PrivateKey:", + privateKeyString, + "Certificate Chain:", + "cert1", + "cert2" + }; + + assertThat(output.length, is(expected.length)); + for (int i = 0; i < output.length; i++) { + assertThat(output[i], is(expected[i])); + } + + // coverage + when(certInfoMock.getNotes()).thenReturn(""); + placer.place(transMock, certInfoMock, artiMock, "machine"); + + when(certInfoMock.getNotes()).thenReturn("Some Notes"); + when(transMock.info()).thenReturn(mock(LogTarget.class)); + placer.place(transMock, certInfoMock, artiMock, "machine"); + } + +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/cm/test/JU_PlaceArtifactScripts.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/cm/test/JU_PlaceArtifactScripts.java new file mode 100644 index 00000000..0ed29e10 --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/cadi/cm/test/JU_PlaceArtifactScripts.java @@ -0,0 +1,92 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.cm.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import static org.mockito.Mockito.*; + +import java.io.File; + +import org.junit.*; +import org.mockito.*; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.cm.PlaceArtifactScripts; +import org.onap.aaf.misc.env.Trans; + +import certman.v1_0.Artifacts.Artifact; +import certman.v1_0.CertInfo; + +public class JU_PlaceArtifactScripts { + + @Mock private Trans transMock; + @Mock private CertInfo certInfoMock; + @Mock private Artifact artiMock; + + private static final String dirName = "src/test/resources/artifacts"; + private static final String nsName = "org.onap.test"; + private static final String luggagePassword = "12345"; // That's the stupidest combination I've ever heard in my life + private static final String notification = "A notification"; + private static final String osUser = "user"; // That's the stupidest combination I've ever heard in my life + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + + when(artiMock.getDir()).thenReturn(dirName); + when(artiMock.getNs()).thenReturn(nsName); + when(artiMock.getNotification()).thenReturn(notification); + when(artiMock.getOsUser()).thenReturn(osUser); + + when(certInfoMock.getChallenge()).thenReturn(luggagePassword); + } + + @AfterClass + public static void tearDownOnce() { + cleanup(); + PlaceArtifactScripts.clear(); + } + + @Test + public void test() throws CadiException { + PlaceArtifactScripts placer = new PlaceArtifactScripts(); + placer.place(transMock, certInfoMock, artiMock, "machine"); + + assertThat(new File(dirName + '/' + nsName + ".crontab.sh").exists(), is(true)); + assertThat(new File(dirName + '/' + nsName + ".check.sh").exists(), is(true)); + + //coverage + when(artiMock.getNotification()).thenReturn("mailto: " + notification); + placer.place(transMock, certInfoMock, artiMock, "machine"); + } + + private static void cleanup() { + File dir = new File(dirName); + if (dir.exists()) { + for (File f : dir.listFiles()) { + f.delete(); + } + dir.delete(); + } + } + +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/lur/aaf/test/JU_JMeter.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/lur/aaf/test/JU_JMeter.java new file mode 100644 index 00000000..a4fb20f9 --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/cadi/lur/aaf/test/JU_JMeter.java @@ -0,0 +1,179 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.lur.aaf.test; + +import org.junit.*; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileReader; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.reflect.Field; +import java.net.HttpURLConnection; +import java.security.Principal; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Properties; + +import org.onap.aaf.cadi.Permission; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.aaf.v2_0.AAFAuthn; +import org.onap.aaf.cadi.aaf.v2_0.AAFConHttp; +import org.onap.aaf.cadi.aaf.v2_0.AAFLurPerm; +import org.onap.aaf.cadi.aaf.v2_0.AAFTaf; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.config.SecurityInfoC; +import org.onap.aaf.cadi.locator.DNSLocator; +import org.onap.aaf.cadi.principal.CachedBasicPrincipal; + +import junit.framework.Assert; + +public class JU_JMeter { + private static AAFConHttp aaf; + private static AAFAuthn<HttpURLConnection> aafAuthn; + private static AAFLurPerm aafLur; + private static ArrayList<Principal> perfIDs; + + private static AAFTaf<HttpURLConnection> aafTaf; + private static PropAccess access; + + private static ByteArrayOutputStream outStream; + private static ByteArrayOutputStream errStream; + + @BeforeClass + public static void before() throws Exception { + outStream = new ByteArrayOutputStream(); + errStream = new ByteArrayOutputStream(); + + System.setOut(new PrintStream(outStream)); + System.setErr(new PrintStream(errStream)); + + if(aafLur==null) { + Properties props = System.getProperties(); + props.setProperty("AFT_LATITUDE", "32.780140"); + props.setProperty("AFT_LONGITUDE", "-96.800451"); + props.setProperty("DME2_EP_REGISTRY_CLASS","DME2FS"); + props.setProperty("AFT_DME2_EP_REGISTRY_FS_DIR","/Volumes/Data/src/authz/dme2reg"); + props.setProperty("AFT_ENVIRONMENT", "AFTUAT"); + props.setProperty("SCLD_PLATFORM", "NON-PROD"); + props.setProperty(Config.AAF_URL,"https://DME2RESOLVE/service=com.att.authz.AuthorizationService/version=2.0/envContext=DEV/routeOffer=BAU_SE"); + props.setProperty(Config.AAF_CALL_TIMEOUT, "2000"); + int timeToLive = 3000; + props.setProperty(Config.AAF_CLEAN_INTERVAL, Integer.toString(timeToLive)); + props.setProperty(Config.AAF_HIGH_COUNT, "4"); + + String aafPerfIDs = props.getProperty("AAF_PERF_IDS"); + perfIDs = new ArrayList<Principal>(); + File perfFile = null; + if(aafPerfIDs!=null) { + perfFile = new File(aafPerfIDs); + } + + access = new PropAccess(); + aaf = new AAFConHttp(access, new DNSLocator(access,"https","localhost","8100")); + aafTaf = new AAFTaf<HttpURLConnection>(aaf,false); + aafLur = aaf.newLur(aafTaf); + aafAuthn = aaf.newAuthn(aafTaf); + aaf.basicAuth("testid@aaf.att.com", "whatever"); + + if(perfFile==null||!perfFile.exists()) { + perfIDs.add(new CachedBasicPrincipal(aafTaf, + "Basic dGVzdGlkOndoYXRldmVy", + "aaf.att.com",timeToLive)); + perfIDs.add(new Princ("ab1234@aaf.att.com")); // Example of Local ID, which isn't looked up + } else { + BufferedReader ir = new BufferedReader(new FileReader(perfFile)); + try { + String line; + while((line = ir.readLine())!=null) { + if((line=line.trim()).length()>0) + perfIDs.add(new Princ(line)); + } + } finally { + ir.close(); + } + } + Assert.assertNotNull(aafLur); + } + } + + @Before + public void setup() { + outStream = new ByteArrayOutputStream(); + errStream = new ByteArrayOutputStream(); + + System.setOut(new PrintStream(outStream)); + System.setErr(new PrintStream(errStream)); + } + + @After + public void tearDown() { + System.setOut(System.out); + System.setErr(System.err); + } + + private static class Princ implements Principal { + private String name; + public Princ(String name) { + this.name = name; + } + public String getName() { + return name; + } + + }; + + @AfterClass + public static void tearDownAfterClass() throws Exception { + Field field = SecurityInfoC.class.getDeclaredField("sicMap"); + field.setAccessible(true); + field.set(null, new HashMap<Class<?>,SecurityInfoC<?>>()); + } + + private static int index = -1; + + private synchronized Principal getIndex() { + if(perfIDs.size()<=++index)index=0; + return perfIDs.get(index); + } + @Test + public void test() { + try { + aafAuthn.validate("testid@aaf.att.com", "whatever"); + List<Permission> perms = new ArrayList<Permission>(); + aafLur.fishAll(getIndex(), perms); +// Assert.assertFalse(perms.isEmpty()); +// for(Permission p : perms) { +// //access.log(Access.Level.AUDIT, p.permType()); +// } + } catch (Exception e) { + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw)); + Assert.fail(sw.toString()); + } + } + +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/lur/aaf/test/JU_MultiThreadPermHit.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/lur/aaf/test/JU_MultiThreadPermHit.java new file mode 100644 index 00000000..46c1064b --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/cadi/lur/aaf/test/JU_MultiThreadPermHit.java @@ -0,0 +1,148 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.lur.aaf.test; + +import java.security.Principal; +import java.util.ArrayList; +import java.util.List; + +import org.onap.aaf.cadi.Permission; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.aaf.AAFPermission; +import org.onap.aaf.cadi.aaf.v2_0.AAFAuthn; +import org.onap.aaf.cadi.aaf.v2_0.AAFConHttp; +import org.onap.aaf.cadi.aaf.v2_0.AAFLurPerm; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.locator.PropertyLocator; +import org.onap.aaf.stillNeed.TestPrincipal; + +public class JU_MultiThreadPermHit { + public static void main(String args[]) { + // Link or reuse to your Logging mechanism + PropAccess myAccess = new PropAccess(); // + + // + try { + AAFConHttp con = new AAFConHttp(myAccess,new PropertyLocator("https://mithrilcsp.sbc.com:8100")); + + // AAFLur has pool of DME clients as needed, and Caches Client lookups + final AAFLurPerm aafLur = con.newLur(); + aafLur.setDebug("m12345@aaf.att.com"); + + // Note: If you need both Authn and Authz construct the following: + AAFAuthn<?> aafAuthn = con.newAuthn(aafLur); + + // Do not set Mech ID until after you construct AAFAuthn, + // because we initiate "401" info to determine the Realm of + // of the service we're after. + final String id = myAccess.getProperty(Config.AAF_APPID,null); + final String pass = myAccess.decrypt(myAccess.getProperty(Config.AAF_APPPASS,null),false); + if(id!=null && pass!=null) { + try { + + // Normally, you obtain Principal from Authentication System. + // // For J2EE, you can ask the HttpServletRequest for getUserPrincipal() + // // If you use CADI as Authenticator, it will get you these Principals from + // // CSP or BasicAuth mechanisms. + // String id = "cluster_admin@gridcore.att.com"; + // + // // If Validate succeeds, you will get a Null, otherwise, you will a String for the reason. + String ok; + ok = aafAuthn.validate(id, pass); + if(ok!=null) { + System.out.println(ok); + } + + List<Permission> pond = new ArrayList<Permission>(); + for(int i=0;i<20;++i) { + pond.clear(); + Principal p = new TestPrincipal(i+id); + aafLur.fishAll(p, pond); + if(ok!=null && i%1000==0) { + System.out.println(i + " " + ok); + } + } + + for(int i=0;i<1000000;++i) { + ok = aafAuthn.validate( i+ id, "wrongPass"); + if(ok!=null && i%1000==0) { + System.out.println(i + " " + ok); + } + } + + final AAFPermission perm = new AAFPermission("org.osaaf.aaf.access","*","*"); + + // Now you can ask the LUR (Local Representative of the User Repository about Authorization + // With CADI, in J2EE, you can call isUserInRole("org.osaaf.mygroup|mytype|write") on the Request Object + // instead of creating your own LUR + for(int i=0;i<4;++i) { + Principal p = new TestPrincipal(i+id); + + if(aafLur.fish(p, perm)) { + System.out.println("Yes, " + id + " has permission for " + perm.getKey()); + } else { + System.out.println("No, " + id + " does not have permission for " + perm.getKey()); + } + } + + + // Or you can all for all the Permissions available + List<Permission> perms = new ArrayList<Permission>(); + + Principal p = new TestPrincipal(id); + aafLur.fishAll(p,perms); + System.out.println("Perms for " + id); + for(Permission prm : perms) { + System.out.println(prm.getKey()); + } + + System.out.println("Press any key to continue"); + System.in.read(); + + for(int j=0;j<5;++j) { + new Thread(new Runnable() { + @Override + public void run() { + for(int i=0;i<20;++i) { + Principal p = new TestPrincipal(id); + if(aafLur.fish(p, perm)) { + System.out.println("Yes, " + id + " has permission for " + perm.getKey()); + } else { + System.out.println("No, " + id + " does not have permission for " + perm.getKey()); + } + } + } + }).start(); + } + + + } finally { + aafLur.destroy(); + } + } else { // checked on IDs + System.err.println(Config.AAF_APPID + " and/or " + Config.AAF_APPPASS + " are not set."); + } + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/lur/aaf/test1/MultiThreadPermHit.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/lur/aaf/test1/MultiThreadPermHit.java new file mode 100644 index 00000000..3a023d71 --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/cadi/lur/aaf/test1/MultiThreadPermHit.java @@ -0,0 +1,149 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.lur.aaf.test1; + +import java.security.Principal; +import java.util.ArrayList; +import java.util.List; + +import org.onap.aaf.cadi.Permission; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.aaf.AAFPermission; +import org.onap.aaf.cadi.aaf.v2_0.AAFAuthn; +import org.onap.aaf.cadi.aaf.v2_0.AAFConHttp; +import org.onap.aaf.cadi.aaf.v2_0.AAFLurPerm; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.locator.PropertyLocator; +import org.onap.aaf.cadi.principal.UnAuthPrincipal; +import org.onap.aaf.stillNeed.TestPrincipal; + +public class MultiThreadPermHit { + public static void main(String args[]) { + // Link or reuse to your Logging mechanism + PropAccess myAccess = new PropAccess(args); // + + // + try { + AAFConHttp con = new AAFConHttp(myAccess,new PropertyLocator("https://mithrilcsp.sbc.com:8100")); + + // AAFLur has pool of DME clients as needed, and Caches Client lookups + final AAFLurPerm aafLur = con.newLur(); + aafLur.setDebug("m12345@aaf.att.com"); + + // Note: If you need both Authn and Authz construct the following: + AAFAuthn<?> aafAuthn = con.newAuthn(aafLur); + + // Do not set Mech ID until after you construct AAFAuthn, + // because we initiate "401" info to determine the Realm of + // of the service we're after. + final String id = myAccess.getProperty(Config.AAF_APPID,null); + final String pass = myAccess.decrypt(myAccess.getProperty(Config.AAF_APPPASS,null),false); + if(id!=null && pass!=null) { + try { + + // Normally, you obtain Principal from Authentication System. + // // For J2EE, you can ask the HttpServletRequest for getUserPrincipal() + // // If you use CADI as Authenticator, it will get you these Principals from + // // CSP or BasicAuth mechanisms. + // String id = "cluster_admin@gridcore.att.com"; + // + // // If Validate succeeds, you will get a Null, otherwise, you will a String for the reason. + String ok; + ok = aafAuthn.validate(id, pass,null /* use AuthzTrans or HttpServlet, if you have it */); + if(ok!=null) { + System.out.println(ok); + } + + List<Permission> pond = new ArrayList<Permission>(); + for(int i=0;i<20;++i) { + pond.clear(); + aafLur.fishAll(new TestPrincipal(i+id), pond); + if(ok!=null && i%1000==0) { + System.out.println(i + " " + ok); + } + } + + for(int i=0;i<1000000;++i) { + ok = aafAuthn.validate( i+ id, "wrongPass",null /* use AuthzTrans or HttpServlet, if you have it */); + if(ok!=null && i%1000==0) { + System.out.println(i + " " + ok); + } + } + + final AAFPermission perm = new AAFPermission("org.osaaf.aaf.access","*","*"); + + // Now you can ask the LUR (Local Representative of the User Repository about Authorization + // With CADI, in J2EE, you can call isUserInRole("org.osaaf.mygroup|mytype|write") on the Request Object + // instead of creating your own LUR + // + // If possible, use the Principal provided by the Authentication Call. If that is not possible + // because of separation Classes by tooling, or other such reason, you can use "UnAuthPrincipal" + final Principal p = new UnAuthPrincipal(id); + for(int i=0;i<4;++i) { + if(aafLur.fish(p, perm)) { + System.out.println("Yes, " + id + " has permission for " + perm.getKey()); + } else { + System.out.println("No, " + id + " does not have permission for " + perm.getKey()); + } + } + + + // Or you can all for all the Permissions available + List<Permission> perms = new ArrayList<Permission>(); + + + aafLur.fishAll(p,perms); + System.out.println("Perms for " + id); + for(Permission prm : perms) { + System.out.println(prm.getKey()); + } + + System.out.println("Press any key to continue"); + System.in.read(); + + for(int j=0;j<5;++j) { + new Thread(new Runnable() { + @Override + public void run() { + for(int i=0;i<20;++i) { + if(aafLur.fish(p, perm)) { + System.out.println("Yes, " + id + " has permission for " + perm.getKey()); + } else { + System.out.println("No, " + id + " does not have permission for " + perm.getKey()); + } + } + } + }).start(); + } + + + } finally { + aafLur.destroy(); + } + } else { // checked on IDs + System.err.println(Config.AAF_APPID + " and/or " + Config.AAF_APPPASS + " are not set."); + } + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_AAFToken.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_AAFToken.java new file mode 100644 index 00000000..11d58ea0 --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_AAFToken.java @@ -0,0 +1,70 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.oauth.test; + +import static org.junit.Assert.*; +import org.junit.*; + +import java.util.UUID; + +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.oauth.AAFToken; + +public class JU_AAFToken { + + @Test + public void testMax() throws CadiException { + UUID uuid = new UUID(Long.MAX_VALUE,Long.MAX_VALUE); + String token = AAFToken.toToken(uuid); + UUID uuid2 = AAFToken.fromToken(token); + assertEquals(uuid, uuid2); + } + + @Test + public void testMin() throws CadiException { + UUID uuid = new UUID(Long.MIN_VALUE,Long.MIN_VALUE); + String token = AAFToken.toToken(uuid); + UUID uuid2 = AAFToken.fromToken(token); + assertEquals(uuid, uuid2); + } + + @Test + public void testRandom() throws CadiException { + for(int i=0;i<100;++i) { + UUID uuid = UUID.randomUUID(); + String token = AAFToken.toToken(uuid); + UUID uuid2 = AAFToken.fromToken(token); + assertEquals(uuid, uuid2); + } + } + + @Test + public void nullTest() { + // Invalid characters + assertNull(AAFToken.fromToken("~~invalid characters~~")); + + // Invalid CADI tokens + assertNull(AAFToken.fromToken("ABCDEF")); + assertNull(AAFToken.fromToken("12345678901234567890123456789012345678")); + } + +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_OAuth2HttpTaf.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_OAuth2HttpTaf.java new file mode 100644 index 00000000..52b2beb4 --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_OAuth2HttpTaf.java @@ -0,0 +1,85 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.oauth.test; + +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.any; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.oauth.OAuth2HttpTaf; +import org.onap.aaf.cadi.oauth.OAuth2Principal; +import org.onap.aaf.cadi.oauth.TokenMgr; +import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.cadi.Taf.LifeForm; +import org.onap.aaf.cadi.client.Result; + +public class JU_OAuth2HttpTaf { + + private static final String authz = "Bearer John Doe"; + + @Mock private TokenMgr tmgrMock; + @Mock private HttpServletResponse respMock; + @Mock private HttpServletRequest reqMock; + @Mock private OAuth2Principal princMock; + + private PropAccess access; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + + access = new PropAccess(new PrintStream(new ByteArrayOutputStream()), new String[0]); + } + + @Test + public void test() throws APIException, CadiException, LocatorException { + OAuth2HttpTaf taf = new OAuth2HttpTaf(access, tmgrMock); + + taf.validate(LifeForm.CBLF, reqMock, respMock); + when(reqMock.getHeader("Authorization")).thenReturn(authz); + + doReturn(Result.ok(200, princMock)).when(tmgrMock).toPrincipal(anyString(), (byte[])any()); + taf.validate(LifeForm.CBLF, reqMock, respMock); + + when(reqMock.isSecure()).thenReturn(true); + + doReturn(Result.err(404, "not found")).when(tmgrMock).toPrincipal(anyString(), (byte[])any()); + taf.validate(LifeForm.CBLF, reqMock, respMock); + + taf.revalidate(null, null); + } + +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_OAuth2HttpTafResp.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_OAuth2HttpTafResp.java new file mode 100644 index 00000000..94737b0c --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_OAuth2HttpTafResp.java @@ -0,0 +1,68 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.oauth.test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import java.io.IOException; + +import javax.servlet.http.HttpServletResponse; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.oauth.OAuth2HttpTafResp; +import org.onap.aaf.cadi.oauth.OAuth2Principal; +import org.onap.aaf.cadi.principal.TrustPrincipal; +import org.onap.aaf.cadi.taf.TafResp.RESP; + +public class JU_OAuth2HttpTafResp { + + private static final String description = "description"; + + @Mock private TrustPrincipal princMock; + @Mock private OAuth2Principal oauthMock; + @Mock private HttpServletResponse respMock; + + private PropAccess access; + + private RESP status; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + status = RESP.NO_FURTHER_PROCESSING; + } + + @Test + public void test() throws IOException { + OAuth2HttpTafResp resp = new OAuth2HttpTafResp(access, princMock, description, status, respMock); + resp = new OAuth2HttpTafResp(access, oauthMock, description, status, respMock, true); + assertThat(resp.isFailedAttempt(), is(true)); + assertThat(resp.isAuthenticated(), is(status)); + assertThat(resp.authenticate(), is(RESP.HTTP_REDIRECT_INVOKED)); + } + +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_OAuth2Lur.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_OAuth2Lur.java new file mode 100644 index 00000000..853c4ae3 --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_OAuth2Lur.java @@ -0,0 +1,100 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.oauth.test; + +import static org.mockito.Mockito.when; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import java.security.Principal; +import java.util.ArrayList; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.onap.aaf.cadi.Permission; +import org.onap.aaf.cadi.aaf.AAFPermission; +import org.onap.aaf.cadi.oauth.OAuth2Lur; +import org.onap.aaf.cadi.oauth.OAuth2Principal; +import org.onap.aaf.cadi.oauth.TokenMgr; +import org.onap.aaf.cadi.oauth.TokenPerm; +import org.onap.aaf.cadi.principal.BearerPrincipal; + +public class JU_OAuth2Lur { + + private List<AAFPermission> aafPerms; + private List<Permission> perms; + + @Mock private TokenMgr tmMock; + @Mock private AAFPermission pondMock; + @Mock private Principal princMock; + @Mock private OAuth2Principal oauthPrincMock; + @Mock private BearerPrincipal bearPrincMock; + @Mock private TokenPerm tpMock; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void test() { + OAuth2Lur lur = new OAuth2Lur(tmMock); + lur.createPerm("testPerm"); + lur.createPerm("testPerm1|testPerm2|testPerm3"); + + assertThat(lur.fish(princMock, pondMock), is(false)); + assertThat(lur.fish(oauthPrincMock, pondMock), is(false)); + + when(oauthPrincMock.tokenPerm()).thenReturn(tpMock); + assertThat(lur.fish(oauthPrincMock, pondMock), is(false)); + + aafPerms = new ArrayList<>(); + aafPerms.add(pondMock); + aafPerms.add(pondMock); + when(tpMock.perms()).thenReturn(aafPerms); + when(pondMock.match(pondMock)).thenReturn(false).thenReturn(true); + assertThat(lur.fish(oauthPrincMock, pondMock), is(true)); + + perms = new ArrayList<>(); + perms.add(pondMock); + perms.add(pondMock); + lur.fishAll(oauthPrincMock, perms); + + when(oauthPrincMock.tokenPerm()).thenReturn(null); + lur.fishAll(oauthPrincMock, perms); + + assertThat(lur.handlesExclusively(pondMock), is(false)); + + assertThat(lur.handles(null), is(false)); + assertThat(lur.handles(princMock), is(false)); + assertThat(lur.handles(bearPrincMock), is(false)); + when(bearPrincMock.getBearer()).thenReturn("not null :)"); + assertThat(lur.handles(bearPrincMock), is(true)); + + lur.destroy(); + lur.clear(null, null); + } + +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_OAuth2Principal.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_OAuth2Principal.java new file mode 100644 index 00000000..45736949 --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_OAuth2Principal.java @@ -0,0 +1,60 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.oauth.test; + +import static org.junit.Assert.assertThat; +import static org.hamcrest.CoreMatchers.is; +import static org.mockito.Mockito.when; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.onap.aaf.cadi.oauth.OAuth2Principal; +import org.onap.aaf.cadi.oauth.TokenPerm; + +public class JU_OAuth2Principal { + + @Mock TokenPerm tpMock; + + + private static final String username = "username"; + + private static final byte[] hash = "hashstring".getBytes(); + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + + when(tpMock.getUsername()).thenReturn(username); + } + + @Test + public void test() { + OAuth2Principal princ = new OAuth2Principal(tpMock, hash); + assertThat(princ.getName(), is(username)); + assertThat(princ.tokenPerm(), is(tpMock)); + assertThat(princ.tag(), is("OAuth")); + assertThat(princ.personalName(), is(username)); + } + +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_OAuthTest.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_OAuthTest.java new file mode 100644 index 00000000..a30f274f --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_OAuthTest.java @@ -0,0 +1,292 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.oauth.test; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.lang.reflect.Field; +import java.net.ConnectException; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.HashMap; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.client.Future; +import org.onap.aaf.cadi.client.Rcli; +import org.onap.aaf.cadi.client.Result; +import org.onap.aaf.cadi.client.Retryable; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.config.SecurityInfoC; +import org.onap.aaf.cadi.oauth.TimedToken; +import org.onap.aaf.cadi.oauth.TokenClient; +import org.onap.aaf.cadi.oauth.TokenClientFactory; +import org.onap.aaf.cadi.oauth.TzClient; +import org.onap.aaf.cadi.principal.Kind; +import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.misc.env.util.Chrono; + +import aafoauth.v2_0.Introspect; +import aafoauth.v2_0.Token; +import junit.framework.Assert; + +public class JU_OAuthTest { + + private ByteArrayOutputStream outStream; + + private static PropAccess access; + private static TokenClientFactory tcf; + + @BeforeClass + public static void setUpBeforeClass() { + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outStream)); + + access = new PropAccess(); + try { + tcf = TokenClientFactory.instance(access); + } catch (Exception e) { + e.printStackTrace(); + Assert.fail(); + } + } + + @AfterClass + public static void tearDownAfterClass() throws Exception { + Field field = SecurityInfoC.class.getDeclaredField("sicMap"); + field.setAccessible(true); + field.set(null, new HashMap<Class<?>,SecurityInfoC<?>>()); + } + + @Before + public void setUp() throws Exception { + outStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outStream)); + } + + @After + public void tearDown() throws Exception { + System.setOut(System.out); + } + + @Test + public void testROPCFlowHappy() { + try { + // AAF OAuth + String client_id = access.getProperty(Config.AAF_APPID); + String client_secret = access.getProperty(Config.AAF_APPPASS); + String tokenServiceURL = access.getProperty(Config.AAF_OAUTH2_TOKEN_URL); +// Assert.assertNotNull(tokenServiceURL); + String tokenIntrospectURL = access.getProperty(Config.AAF_OAUTH2_INTROSPECT_URL); + String tokenAltIntrospectURL = access.getProperty(Config.AAF_ALT_OAUTH2_INTROSPECT_URL); +// Assert.assertNotNull(tokenIntrospectURL); + final String endServicesURL = access.getProperty(Config.AAF_OAUTH2_HELLO_URL); + String username = access.getProperty("cadi_username"); + + TokenClient tc; + Result<TimedToken> rtt; + if(true) { + tc = tcf.newClient(tokenServiceURL, 3000); + tc.client_creds(client_id,client_secret); + tc.password(access.getProperty("cadi_username"),access.getProperty("cadi_password")); + rtt = tc.getToken(Kind.BASIC_AUTH,"org.osaaf.aaf","org.osaaf.test"); + if(rtt.isOK()) { + print(rtt.value); + rtt = tc.refreshToken(rtt.value); + if(rtt.isOK()) { + print(rtt.value); + TokenClient ic = tcf.newClient(tokenIntrospectURL,3000); + ic.client_creds(client_id,client_secret); + + Result<Introspect> ri = ic.introspect(rtt.value.getAccessToken()); + if(ri.isOK()) { + print(ri.value); + } else { + System.out.println(ri.code + ' ' + ri.error); + Assert.fail(ri.code + ' ' + ri.error); + } + TzClient helloClient = tcf.newTzClient(endServicesURL); + helloClient.setToken(client_id, rtt.value); +// String rv = serviceCall(helloClient); +// System.out.println(rv); + // Assert.assertEquals("Hello AAF OAuth2\n",rv); + } else { + System.out.println(rtt.code + ' ' + rtt.error); + Assert.fail(rtt.code + ' ' + rtt.error); + } + } else { + System.out.println(rtt.code + ' ' + rtt.error); + Assert.fail(rtt.code + ' ' + rtt.error); + } + } + + // ISAM Test + if(true) { + System.out.println("**** ISAM TEST ****"); + tokenServiceURL=access.getProperty(Config.AAF_ALT_OAUTH2_TOKEN_URL); + client_id=access.getProperty(Config.AAF_ALT_CLIENT_ID); + client_secret=access.getProperty(Config.AAF_ALT_CLIENT_SECRET); + if(tokenServiceURL!=null) { + tc = tcf.newClient(tokenServiceURL, 3000); + tc.client_creds(client_id, client_secret); + int at = username.indexOf('@'); + + tc.password(at>=0?username.substring(0, at):username,access.getProperty("cadi_password")); + rtt = tc.getToken("org.osaaf.aaf","org.osaaf.test"); + if(rtt.isOK()) { + print(rtt.value); + rtt = tc.refreshToken(rtt.value); + if(rtt.isOK()) { + print(rtt.value); + + tc = tcf.newClient(tokenAltIntrospectURL, 3000); + tc.client_creds(client_id, client_secret); + Result<Introspect> rti = tc.introspect(rtt.value.getAccessToken()); + if(rti.isOK()) { + System.out.print("Normal ISAM "); + print(rti.value); + } else { + System.out.println(rti.code + ' ' + rti.error); + Assert.fail(rtt.code + ' ' + rtt.error); + } + + tc = tcf.newClient(tokenIntrospectURL, 3000); + tc.client_creds(client_id, client_secret); + rti = tc.introspect(rtt.value.getAccessToken()); + if(rti.isOK()) { + System.out.print("AAF with ISAM Token "); + print(rti.value); + } else { + System.out.println(rti.code + ' ' + rti.error); + if(rti.code!=404) { + Assert.fail(rti.code + ' ' + rti.error); + } + } + + TzClient tzClient = tcf.newTzClient(endServicesURL); + tzClient.setToken(client_id, rtt.value); + // Note: this is AAF's "Hello" server + String rv = serviceCall(tzClient); + System.out.println(rv); + // Assert.assertEquals("Hello AAF OAuth2\n",rv); + } else { + System.out.println(rtt.code + ' ' + rtt.error); + Assert.fail(rtt.code + ' ' + rtt.error); + } + } else { + System.out.println(rtt.code + ' ' + rtt.error); + Assert.fail(rtt.code + ' ' + rtt.error); + } + } else { + Assert.fail(Config.AAF_ALT_OAUTH2_TOKEN_URL + " is required"); + } + } + } catch (Exception e) { +// Assert.fail(); + } + } + + +// private TokenClient testROPCFlow(final String url, final String client_id, final String client_secret, String user, String password, final String ... scope) throws Exception { +// TokenClient tclient = tcf.newClient(url,3000); +// tclient.client_creds(client_id, client_secret); +// if(user!=null && password!=null) { +// tclient.password(user,password); +// } +// Result<TimedToken> rt = tclient.getToken(scope); +// if(rt.isOK()) { +// print(rt.value); +// Result<Introspect> rti = tclient.introspect(rt.value.getAccessToken()); +// if(rti.isOK()) { +// print(rti.value); +// } else { +// printAndFail(rti); +// } +// } else { +// printAndFail(rt); +// } +// return tclient; +// } + + private String serviceCall(TzClient tzClient) throws Exception { + return tzClient.best(new Retryable<String>() { + @Override + public String code(Rcli<?> client) throws CadiException, ConnectException, APIException { + Future<String> future = client.read(null,"text/plain"); + if(future.get(3000)) { + return future.value; + } else { + throw new APIException(future.code() + future.body()); + } + } + }); + } +// private void printAndFail(Result<?> rt) { +// System.out.printf("HTTP Code %d: %s\n", rt.code, rt.error); +// Assert.fail(rt.toString()); +// } + + private void print(Token t) { + GregorianCalendar exp_date = new GregorianCalendar(); + exp_date.add(GregorianCalendar.SECOND, t.getExpiresIn()); + System.out.printf("Access Token\n\tToken:\t\t%s\n\tToken Type:\t%s\n\tExpires In:\t%d (%s)\n\tScope:\t\t%s\n\tRefresh Token:\t%s\n", + t.getAccessToken(), + t.getTokenType(), + t.getExpiresIn(), + Chrono.timeStamp(new Date(System.currentTimeMillis()+(t.getExpiresIn()*1000))), + t.getScope(), + t.getRefreshToken()); + } + + private void print(Introspect ti) { + if(ti==null || ti.getClientId()==null) { + System.out.println("Empty Introspect"); + return; + } + Date exp = new Date(ti.getExp()*1000); // seconds + System.out.printf("Introspect\n" + + "\tAccessToken:\t%s\n" + + "\tClient-id:\t%s\n" + + "\tClient Type:\t%s\n" + + "\tActive: \t%s\n" + + "\tUserName:\t%s\n" + + "\tExpires: \t%d (%s)\n" + + "\tScope:\t\t%s\n" + + "\tContent:\t\t%s\n", + ti.getAccessToken(), + ti.getClientId(), + ti.getClientType(), + ti.isActive()?Boolean.TRUE.toString():Boolean.FALSE.toString(), + ti.getUsername(), + ti.getExp(), + Chrono.timeStamp(exp), + ti.getScope(), + ti.getContent()==null?"":ti.getContent()); + + System.out.println(); + } +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_TimedToken.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_TimedToken.java new file mode 100644 index 00000000..775a0398 --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_TimedToken.java @@ -0,0 +1,84 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.oauth.test; + +import static org.hamcrest.CoreMatchers.is; +import static org.mockito.Mockito.when; +import static org.junit.Assert.assertThat; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.onap.aaf.cadi.oauth.TimedToken; +import org.onap.aaf.cadi.persist.Persist; + +import aafoauth.v2_0.Token; + +public class JU_TimedToken { + + private static final byte[] hash = "hashstring".getBytes(); + + private static final int expires = 10000; + + private Path path; + + @Mock private Persist<Token, ?> persistMock; + @Mock private Token tokenMock; + + @Before + public void setup() throws IOException { + MockitoAnnotations.initMocks(this); + + when(tokenMock.getExpiresIn()).thenReturn(expires); + path = Files.createTempFile("fake", ".txt"); + } + + @Test + public void test() { + int actuallyExpires = ((int)(System.currentTimeMillis() / 1000)) + expires; + TimedToken ttoken = new TimedToken(persistMock, tokenMock, hash, path); + + assertThat(ttoken.get(), is(tokenMock)); + assertThat(ttoken.checkSyncTime(), is(true)); + assertThat(ttoken.checkReloadable(), is(false)); + assertThat(ttoken.hasBeenTouched(), is(false)); + assertThat(Math.abs(ttoken.expires() - actuallyExpires) < 10, is(true)); + assertThat(ttoken.expired(), is(false)); + + assertThat(ttoken.match(hash), is(true)); + assertThat(ttoken.getHash(), is(hash)); + + assertThat(ttoken.path(), is(path)); + + assertThat(ttoken.count(), is(0)); + ttoken.inc(); + assertThat(ttoken.count(), is(1)); + ttoken.clearCount(); + assertThat(ttoken.count(), is(0)); + } + +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_TokenPerm.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_TokenPerm.java new file mode 100644 index 00000000..6bbed0ed --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_TokenPerm.java @@ -0,0 +1,196 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.oauth.test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.when; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.io.IOException; +import java.io.StringReader; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.onap.aaf.cadi.Permission; +import org.onap.aaf.cadi.oauth.TokenPerm; +import org.onap.aaf.cadi.oauth.TokenPerm.LoadPermissions; +import org.onap.aaf.cadi.persist.Persist; +import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.misc.rosetta.ParseException; +import org.onap.aaf.misc.rosetta.env.RosettaDF; + +import aaf.v2_0.Perms; +import aafoauth.v2_0.Introspect; + +public class JU_TokenPerm { + + private static final byte[] hash = "hashstring".getBytes(); + + private static final String clientId = "clientId"; + private static final String username = "username"; + private static final String token = "token"; + private static final String scopes = "scopes"; + private static final String content = "content"; + + private static final long expires = 10000L; + + private static Path path; + + @Mock private Persist<Introspect, ?> persistMock; + @Mock private RosettaDF<Perms> dfMock; + @Mock private Introspect introspectMock; + + @Before + public void setup() throws IOException { + MockitoAnnotations.initMocks(this); + + when(introspectMock.getExp()).thenReturn(expires); + when(introspectMock.getClientId()).thenReturn(clientId); + when(introspectMock.getUsername()).thenReturn(username); + when(introspectMock.getAccessToken()).thenReturn(token); + when(introspectMock.getScope()).thenReturn(scopes); + when(introspectMock.getExp()).thenReturn(expires); + + path = Files.createTempFile("fake", ".txt"); + } + + @Test + public void tokenTest() throws APIException { + TokenPerm tokenPerm = new TokenPerm(persistMock, dfMock, introspectMock, hash, path); + assertThat(tokenPerm.perms().size(), is(0)); + assertThat(tokenPerm.getClientId(), is(clientId)); + assertThat(tokenPerm.getUsername(), is(username)); + assertThat(tokenPerm.getToken(), is(token)); + assertThat(tokenPerm.getScopes(), is(scopes)); + assertThat(tokenPerm.getIntrospect(), is(introspectMock)); + + when(introspectMock.getContent()).thenReturn(content); + tokenPerm = new TokenPerm(persistMock, dfMock, introspectMock, hash, path); + } + + @Test + public void test() throws ParseException { + String json; + LoadPermissions lp; + Permission p; + + json = "{\"perm\":[" + + " {\"type\":\"com.access\",\"instance\":\"*\",\"action\":\"read,approve\"}," + + "]}"; + + lp = new LoadPermissions(new StringReader(json)); + assertThat(lp.perms.size(), is(1)); + + p = lp.perms.get(0); + assertThat(p.getKey(), is("com.access|*|read,approve")); + assertThat(p.permType(), is("AAF")); + + // Extra closing braces for coverage + json = "{\"perm\":[" + + " {\"type\":\"com.access\",\"instance\":\"*\",\"action\":\"read,approve\"}}," + + "]]}"; + + lp = new LoadPermissions(new StringReader(json)); + assertThat(lp.perms.size(), is(1)); + + p = lp.perms.get(0); + assertThat(p.getKey(), is("com.access|*|read,approve")); + assertThat(p.permType(), is("AAF")); + + // Test without a type + json = "{\"perm\":[" + + " {\"instance\":\"*\",\"action\":\"read,approve\"}," + + "]}"; + + lp = new LoadPermissions(new StringReader(json)); + assertThat(lp.perms.size(), is(0)); + + // Test without an instance + json = "{\"perm\":[" + + " {\"type\":\"com.access\",\"action\":\"read,approve\"}," + + "]}"; + + lp = new LoadPermissions(new StringReader(json)); + assertThat(lp.perms.size(), is(0)); + + // Test without an action + json = "{\"perm\":[" + + " {\"type\":\"com.access\",\"instance\":\"*\"}," + + "]}"; + + lp = new LoadPermissions(new StringReader(json)); + assertThat(lp.perms.size(), is(0)); + } + + @Test + public void redundancyTest() { + String json = "{\"perm\":[" + + " {\"type\":\"com.access\",\"instance\":\"*\",\"action\":\"read,approve\"}," + + " {\"type\":\"org.osaaf.aaf.access\",\"instance\":\"*\",\"action\":\"*\"}," + + " {\"type\":\"org.osaaf.aaf.access\",\"instance\":\"*\",\"action\":\"read\"}," + + " {\"type\":\"org.osaaf.aaf.attrib\",\"instance\":\":com.att.*:swm\",\"action\":\"*\"}," + + " {\"type\":\"org.osaaf.aaf.bogus\",\"instance\":\"sample\",\"action\":\"read\"}," + + " {\"type\":\"org.osaaf.aaf.ca\",\"instance\":\"aaf\",\"action\":\"ip\"}," + + " {\"type\":\"org.osaaf.aaf.ca\",\"instance\":\"local\",\"action\":\"domain\"}," + + " {\"type\":\"org.osaaf.aaf.cache\",\"instance\":\"*\",\"action\":\"clear\"}," + + " {\"type\":\"org.osaaf.aaf.cass\",\"instance\":\":mithril\",\"action\":\"*\"}," + + " {\"type\":\"org.osaaf.aaf.certman\",\"instance\":\"local\",\"action\":\"read,request,showpass\"}," + + " {\"type\":\"org.osaaf.aaf.db\",\"instance\":\"pool\",\"action\":\"clear\"}," + + " {\"type\":\"org.osaaf.aaf.deny\",\"instance\":\"com.att\",\"action\":\"*\"}," + + " {\"type\":\"org.osaaf.aaf.jenkins\",\"instance\":\"mithrilcsp.sbc.com\",\"action\":\"admin\"}," + + " {\"type\":\"org.osaaf.aaf.log\",\"instance\":\"com.att\",\"action\":\"id\"}," + + " {\"type\":\"org.osaaf.aaf.myPerm\",\"instance\":\"myInstance\",\"action\":\"myAction\"}," + + " {\"type\":\"org.osaaf.aaf.ns\",\"instance\":\":com.att.*:ns\",\"action\":\"write\"}," + + " {\"type\":\"org.osaaf.aaf.ns\",\"instance\":\":com.att:ns\",\"action\":\"write\"}," + + " {\"type\":\"org.osaaf.aaf.password\",\"instance\":\"com.att\",\"action\":\"extend\"}," + + " {\"type\":\"org.osaaf.access\",\"instance\":\"*\",\"action\":\"read\"}," + + " {\"type\":\"org.osaaf.authz.access\",\"instance\":\"*\",\"action\":\"read\"}," + + " {\"type\":\"org.osaaf.authz.dev.access\",\"instance\":\"*\",\"action\":\"*\"}," + + " {\"type\":\"org.osaaf.authz.swm.star\",\"instance\":\"*\",\"action\":\"*\"}," + + " {\"type\":\"org.osaaf.cadi.access\",\"instance\":\"*\",\"action\":\"*\"}," + + " {\"type\":\"org.osaaf.chris.access\",\"instance\":\"*\",\"action\":\"*\"}," + + " {\"type\":\"org.osaaf.csid.lab.swm.node\",\"instance\":\"*\",\"action\":\"*\"}," + + " {\"type\":\"org.osaaf.myapp.access\",\"instance\":\"*\",\"action\":\"*\"}," + + " {\"type\":\"org.osaaf.myapp.access\",\"instance\":\"*\",\"action\":\"read\"}," + + " {\"type\":\"org.osaaf.sample.access\",\"instance\":\"*\",\"action\":\"read\"}," + + " {\"type\":\"org.osaaf.sample.swm.myPerm\",\"instance\":\"*\",\"action\":\"read\"}," + + " {\"type\":\"org.osaaf.temp.access\",\"instance\":\"*\",\"action\":\"read\"}," + + " {\"type\":\"org.osaaf.test.access\",\"instance\":\"*\",\"action\":\"*\"}," + + " {\"type\":\"org.osaaf.test.access\",\"instance\":\"*\",\"action\":\"read\"}," + + " {\"type\":\"com.test.access\",\"instance\":\"*\",\"action\":\"read\"}," + + " {\"type\":\"com.test.access\",\"instance\":\"*\",\"action\":\"read\"}" + + "]}"; + try { + LoadPermissions lp = new LoadPermissions(new StringReader(json)); + assertThat(lp.perms.size(), is(34)); + } catch (ParseException e) { + fail(e.getMessage()); + } + } + +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_TzHClient.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_TzHClient.java new file mode 100644 index 00000000..7febf51f --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/cadi/oauth/test/JU_TzHClient.java @@ -0,0 +1,113 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.oauth.test; + +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.doReturn; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.net.HttpURLConnection; +import java.net.URI; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.Locator; +import org.onap.aaf.cadi.Locator.Item; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.client.Rcli; +import org.onap.aaf.cadi.client.Retryable; +import org.onap.aaf.cadi.oauth.TimedToken; +import org.onap.aaf.cadi.oauth.TzHClient; +import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.config.SecurityInfoC; + +public class JU_TzHClient { + + @Mock private Retryable<Integer> retryableMock; + @Mock private TimedToken tokenMock; + @Mock private SecurityInfoC<HttpURLConnection> siMock; + @Mock private Locator<URI> locMock; + @Mock private Item itemMock; + @Mock private Rcli<HttpURLConnection> clientMock; + + private PropAccess access; + + private ByteArrayOutputStream errStream; + + private final static String client_id = "id"; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + access = new PropAccess(new PrintStream(new ByteArrayOutputStream()), new String[0]); + access.setProperty(Config.CADI_LATITUDE, "38.62"); // St Louis approx lat + access.setProperty(Config.CADI_LONGITUDE, "90.19"); // St Louis approx lon } + + errStream = new ByteArrayOutputStream(); + System.setErr(new PrintStream(errStream)); + } + + @After + public void tearDown() { + System.setErr(System.err); + } + + @Test + public void test() throws CadiException, LocatorException, APIException, IOException { + TzHClient client = new TzHClient(access, "tag"); + try { + client.best(retryableMock); + fail("Should've thrown an exception"); + } catch (CadiException e) { + assertThat(e.getMessage(), is("OAuth2 Token has not been set")); + } + client.setToken(client_id, tokenMock); + when(tokenMock.expired()).thenReturn(true); + try { + client.best(retryableMock); + fail("Should've thrown an exception"); + } catch (CadiException e) { + assertThat(e.getMessage(), is("Expired Token")); + } + + client = new TzHClient(access, siMock, locMock); + when(tokenMock.expired()).thenReturn(false); + doReturn(clientMock).when(retryableMock).lastClient(); + + when(retryableMock.item()).thenReturn(itemMock); + client.setToken(client_id, tokenMock); + assertThat(client.best(retryableMock), is(nullValue())); + } + +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/persist/test/JU_Persist.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/persist/test/JU_Persist.java new file mode 100644 index 00000000..f8d76a95 --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/cadi/persist/test/JU_Persist.java @@ -0,0 +1,151 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.persist.test; + +import static org.junit.Assert.assertThat; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Matchers.any; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.PrintStream; +import java.nio.file.Path; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.client.Holder; +import org.onap.aaf.cadi.client.Result; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.persist.Persist; +import org.onap.aaf.cadi.persist.Persist.Loader; +import org.onap.aaf.cadi.persist.Persistable; +import org.onap.aaf.cadi.persist.Persisting; +import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.misc.rosetta.env.RosettaDF; +import org.onap.aaf.misc.rosetta.env.RosettaData; +import org.onap.aaf.misc.rosetta.env.RosettaEnv; + +public class JU_Persist { + + private static final String resourceDirString = "src/test/resources"; + private static final String tokenDirString = "tokenDir"; + private static final String key = "key"; + + private static final int data = 5; + + private static final byte[] cred = "password".getBytes(); + + private PropAccess access; + private Result<Persistable<Integer>> result; + + @Mock private RosettaEnv envMock; + @Mock private Persist<Integer, ?> persistMock; + @Mock private RosettaDF<Integer> dfMock; + @Mock private RosettaData<Integer> dataMock; + @Mock private Persistable<Integer> ctMock1; + @Mock private Persisting<Integer> ctMock2; + @Mock private Loader<Persistable<Integer>> loaderMock; + + @Before + public void setup() throws APIException, CadiException, LocatorException { + MockitoAnnotations.initMocks(this); + + doReturn(dfMock).when(envMock).newDataFactory((Class<?>[]) any()); + when(dfMock.newData()).thenReturn(dataMock); + when(dataMock.load(data)).thenReturn(dataMock); + + + result = Result.ok(200, ctMock1); + when(loaderMock.load(key)).thenReturn(result); + + access = new PropAccess(new PrintStream(new ByteArrayOutputStream()), new String[0]); + access.setProperty(Config.CADI_TOKEN_DIR, resourceDirString); + } + + @After + public void tearDown() { + File dir = new File(resourceDirString + '/' + tokenDirString); + for (File f : dir.listFiles()) { + f.delete(); + } + dir.delete(); + } + + @Test + public void test() throws CadiException, APIException, LocatorException, InterruptedException { + Persist<Integer, Persistable<Integer>> persist = new PersistStub(access, envMock, null, tokenDirString); + // Second call for coverage + persist = new PersistStub(access, envMock, null, tokenDirString); + assertThat(persist.getDF(), is(dfMock)); + persist.put(key, ctMock2); + Result<Persistable<Integer>> output = persist.get(key, cred, loaderMock); + assertThat(output.code, is(200)); + assertThat(output.isOK(), is(true)); + + when(ctMock2.checkSyncTime()).thenReturn(true); + when(ctMock2.hasBeenTouched()).thenReturn(true); + output = persist.get(key, cred, loaderMock); + assertThat(output.code, is(200)); + assertThat(output.isOK(), is(true)); + + persist.delete(key); + + assertThat(persist.get(null, null, null), is(nullValue())); + + // Uncommenting this lets us begin to test the nested Clean class, but + // will dramatically slow down every build that runs tests - We need to + // either refactor or find a more creative way to test Clean +// Thread.sleep(25000); + + persist.close(); + } + + private class PersistStub extends Persist<Integer, Persistable<Integer>> { + public PersistStub(Access access, RosettaEnv env, Class<Integer> cls, String sub_dir) + throws CadiException, APIException { super(access, env, cls, sub_dir); } + @Override + protected Persistable<Integer> newCacheable(Integer t, long expires_secsFrom1970, byte[] hash, Path path) + throws APIException, IOException { return null; } + @Override + public<T> Path writeDisk(final RosettaDF<T> df, final T t, final byte[] cred, final Path target, final long expires) throws CadiException { + return null; + } + @SuppressWarnings("unchecked") + @Override + public <T> T readDisk(final RosettaDF<T> df, final byte[] cred, final String filename,final Holder<Path> hp, final Holder<Long> hl) throws CadiException { + return (T)new Integer(data); + } + + } + +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/persist/test/JU_PersistFile.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/persist/test/JU_PersistFile.java new file mode 100644 index 00000000..cbe865eb --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/cadi/persist/test/JU_PersistFile.java @@ -0,0 +1,121 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.persist.test; + +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; +import static org.hamcrest.CoreMatchers.is; +import static org.mockito.Mockito.when; +import static org.mockito.Matchers.any; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.PrintStream; +import java.nio.file.Path; +import java.nio.file.attribute.FileTime; + +import javax.crypto.CipherInputStream; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.client.Holder; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.persist.PersistFile; +import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.misc.rosetta.env.RosettaDF; +import org.onap.aaf.misc.rosetta.env.RosettaData; + +public class JU_PersistFile { + + private static final String resourceDirString = "src/test/resources"; + private static final String tokenDirString = "tokenDir"; + private static final String tokenFileName = "token"; + + private static final int data = 5; + private static final long expires = 10000; + + private static final byte[] cred = "password".getBytes(); + + private PropAccess access; + private Holder<Path> hp = new Holder<Path>(null); + private Holder<Long> hl = new Holder<Long>(null); + + @Mock private RosettaDF<Integer> dfMock; + @Mock private RosettaData<Integer> dataMock; + @Mock private Holder<Path> hpMock; + + @Before + public void setup() throws APIException { + MockitoAnnotations.initMocks(this); + + when(dfMock.newData()).thenReturn(dataMock); + when(dataMock.load(data)).thenReturn(dataMock); + when(dataMock.load((CipherInputStream)any())).thenReturn(dataMock); + + access = new PropAccess(new PrintStream(new ByteArrayOutputStream()), new String[0]); + access.setProperty(Config.CADI_TOKEN_DIR, resourceDirString); + } + + @After + public void tearDown() { + File dir = new File(resourceDirString + '/' + tokenDirString); + for (File f : dir.listFiles()) { + f.delete(); + } + dir.delete(); + } + + @Test + public void test() throws CadiException, APIException, IOException { + PersistFile persistFile = new PersistFile(access, tokenDirString); + // Second call is for coverage + persistFile = new PersistFile(access, tokenDirString); + Path filepath = persistFile.writeDisk(dfMock, data, cred, tokenFileName, expires); + persistFile.readDisk(dfMock, cred, tokenFileName, hp, hl); + assertThat(persistFile.readExpiration(filepath), is(expires)); + + FileTime ft1 = persistFile.getFileTime(tokenFileName, hp); + FileTime ft2 = persistFile.getFileTime(tokenFileName, hpMock); + assertThat(ft1.toMillis(), is(ft2.toMillis())); + + persistFile.deleteFromDisk(filepath); + persistFile.deleteFromDisk(resourceDirString + '/' + tokenDirString + '/' + tokenFileName); + assertThat(persistFile.readExpiration(filepath), is(0L)); + + persistFile.getPath(resourceDirString + '/' + tokenDirString + '/' + tokenFileName); + + persistFile.writeDisk(dfMock, data, null, tokenFileName, expires); + try { + persistFile.readDisk(dfMock, cred, tokenFileName, hp, hl); + fail("Should've thrown an exception"); + } catch (CadiException e) { + assertThat(e.getMessage(), is(CadiException.class.getName() + ": Hash does not match in Persistence")); + } + } + +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/persist/test/JU_Persisting.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/persist/test/JU_Persisting.java new file mode 100644 index 00000000..bb2b918a --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/cadi/persist/test/JU_Persisting.java @@ -0,0 +1,130 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.persist.test; + +import static org.junit.Assert.assertThat; +import static org.hamcrest.CoreMatchers.is; +import static org.mockito.Mockito.when; +import static org.mockito.Matchers.any; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.PrintStream; +import java.nio.file.Path; +import java.nio.file.Paths; + +import javax.crypto.CipherInputStream; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.persist.Persist; +import org.onap.aaf.cadi.persist.PersistFile; +import org.onap.aaf.cadi.persist.Persisting; +import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.misc.rosetta.env.RosettaDF; +import org.onap.aaf.misc.rosetta.env.RosettaData; + +public class JU_Persisting { + + private static final String resourceDirString = "src/test/resources"; + private static final String tokenDirString = "tokenDir"; + private static final String tokenFileName = "token"; + + private static final int data = 5; + private static final long expires = 10000; + + private static final byte[] cred = "password".getBytes(); + + private PropAccess access; + + @Mock private Persist<Integer, ?> persistMock; + @Mock private RosettaDF<Integer> dfMock; + @Mock private RosettaData<Integer> dataMock; + + @Before + public void setup() throws APIException { + MockitoAnnotations.initMocks(this); + + when(dfMock.newData()).thenReturn(dataMock); + when(dataMock.load(data)).thenReturn(dataMock); + when(dataMock.load((CipherInputStream)any())).thenReturn(dataMock); + + access = new PropAccess(new PrintStream(new ByteArrayOutputStream()), new String[0]); + access.setProperty(Config.CADI_TOKEN_DIR, resourceDirString); + + persistMock.access = access; + } + + @After + public void tearDown() { + File dir = new File(resourceDirString + '/' + tokenDirString); + for (File f : dir.listFiles()) { + f.delete(); + } + dir.delete(); + } + + @Test + public void test() throws CadiException, APIException { + Path tokenPath = Paths.get(resourceDirString, tokenDirString); + + Persisting<Integer> persisting = new Persisting<>(persistMock, data, expires, cred, tokenPath); + assertThat(persisting.get(), is(data)); + assertThat(persisting.expires(), is(expires)); + assertThat(persisting.expired(), is(true)); + assertThat(persisting.hasBeenTouched(), is(true)); + + PersistFile persistFile = new PersistFile(access, tokenDirString); + tokenPath = persistFile.writeDisk(dfMock, data, cred, tokenFileName, expires); + persisting = new Persisting<>(persistMock, data, expires, cred, tokenPath); + assertThat(persisting.hasBeenTouched(), is(false)); + + persisting = new Persisting<>(persistMock, data, expires * (int)10e9, cred, tokenPath); + assertThat(persisting.expired(), is(false)); + + assertThat(persisting.checkSyncTime(), is(true)); + assertThat(persisting.checkSyncTime(), is(false)); + + assertThat(persisting.checkReloadable(), is(false)); + + assertThat(persisting.getHash(), is(cred)); + + assertThat(persisting.match(null), is(false)); + assertThat(persisting.match("random!".getBytes()), is(false)); + assertThat(persisting.match("passwrod".getBytes()), is(false)); + assertThat(persisting.match(cred), is(true)); + + persisting.clearCount(); + assertThat(persisting.count(), is(0)); + persisting.inc(); + assertThat(persisting.count(), is(1)); + + assertThat(persisting.path(), is(tokenPath)); + } + +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/cadi/sso/test/JU_AAFSSO.java b/cadi/aaf/src/test/java/org/onap/aaf/cadi/sso/test/JU_AAFSSO.java new file mode 100644 index 00000000..34997fe6 --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/cadi/sso/test/JU_AAFSSO.java @@ -0,0 +1,122 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.sso.test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.sso.AAFSSO; + +public class JU_AAFSSO { + + private static final String resourceDirString = "src/test/resources"; + private static final String aafDir = resourceDirString + "/aaf"; + + private ByteArrayInputStream inStream; + + @Before + public void setup() { + System.setProperty("user.home", aafDir); + + // Simulate user input + inStream = new ByteArrayInputStream("test\npassword".getBytes()); + System.setIn(inStream); + } + + @After + public void tearDown() { + recursiveDelete(new File(aafDir)); + } + + @Test + public void test() throws IOException, CadiException { + AAFSSO sso; + String[] args; + + args = new String[] { + "-login", + "-noexit", + }; + sso = new AAFSSO(args); + + assertThat(new File(aafDir).exists(), is(true)); + assertThat(new File(aafDir + "/.aaf").exists(), is(true)); + assertThat(new File(aafDir + "/.aaf/keyfile").exists(), is(true)); + assertThat(new File(aafDir + "/.aaf/sso.out").exists(), is(true)); + assertThat(sso.loginOnly(), is(true)); + + assertThat(new File(aafDir + "/.aaf/sso.props").exists(), is(false)); + sso.writeFiles(); + assertThat(new File(aafDir + "/.aaf/sso.props").exists(), is(true)); + + sso.setLogDefault(); + sso.setStdErrDefault(); + + inStream.reset(); + args = new String[] { + "-logout", + "\\*", + "-noexit", + }; + sso = new AAFSSO(args); + + assertThat(new File(aafDir).exists(), is(true)); + assertThat(new File(aafDir + "/.aaf").exists(), is(true)); + assertThat(new File(aafDir + "/.aaf/keyfile").exists(), is(true)); + assertThat(new File(aafDir + "/.aaf/sso.out").exists(), is(true)); + assertThat(sso.loginOnly(), is(false)); + + PropAccess access = sso.access(); + assertThat(sso.enc_pass(), is(access.getProperty(Config.AAF_APPPASS))); + assertThat(sso.user(), is(access.getProperty(Config.AAF_APPID))); + + sso.addProp("key", "value"); + assertThat(sso.err(), is(nullValue())); + + assertThat(sso.useX509(), is(false)); + + sso.close(); + sso.close(); + } + + private void recursiveDelete(File file) { + for (File f : file.listFiles()) { + if (f.isDirectory()) { + recursiveDelete(f); + } + f.delete(); + } + file.delete(); + } + +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/client/sample/Sample.java b/cadi/aaf/src/test/java/org/onap/aaf/client/sample/Sample.java new file mode 100644 index 00000000..ff170772 --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/client/sample/Sample.java @@ -0,0 +1,176 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.client.sample; + +import java.io.IOException; +import java.security.Principal; +import java.util.ArrayList; +import java.util.List; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.Permission; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.aaf.AAFPermission; +import org.onap.aaf.cadi.aaf.v2_0.AAFAuthn; +import org.onap.aaf.cadi.aaf.v2_0.AAFConHttp; +import org.onap.aaf.cadi.aaf.v2_0.AAFLurPerm; +import org.onap.aaf.cadi.principal.UnAuthPrincipal; +import org.onap.aaf.cadi.util.Split; +import org.onap.aaf.misc.env.APIException; + +public class Sample { + private static Sample singleton; + final private AAFConHttp aafcon; + final private AAFLurPerm aafLur; + final private AAFAuthn<?> aafAuthn; + + /** + * This method is to emphasize the importance of not creating the AAFObjects over and over again. + * @return + */ + public static Sample singleton() { + return singleton; + } + + public Sample(Access myAccess) throws APIException, CadiException, LocatorException { + aafcon = new AAFConHttp(myAccess); + aafLur = aafcon.newLur(); + aafAuthn = aafcon.newAuthn(aafLur); + } + + /** + * Checking credentials outside of HTTP/S presents fewer options initially. There is not, for instance, + * the option of using 2-way TLS HTTP/S. + * + * However, Password Checks are still useful, and, if the Client Certificate could be obtained in other ways, the + * Interface can be expanded in the future to include Certificates. + * @throws CadiException + * @throws IOException + */ + public Principal checkUserPass(String fqi, String pass) throws IOException, CadiException { + String ok = aafAuthn.validate(fqi, pass); + if(ok==null) { + System.out.println("Success!"); + /* + UnAuthPrincipal means that it is not coming from the official Authorization chain. + This is useful for Security Plugins which don't use Principal as the tie between + Authentication and Authorization + + You can also use this if you want to check Authorization without actually Authenticating, as may + be the case with certain Onboarding Tooling. + */ + return new UnAuthPrincipal(fqi); + } else { + System.out.printf("Failure: %s\n",ok); + return null; + } + + + } + + /** + * An example of looking for One Permission within all the permissions user has. CADI does cache these, + * so the call is not expensive. + * + * Note: If you are using "J2EE" (Servlets), CADI ties this function to the method: + * HttpServletRequest.isUserInRole(String user) + * + * The J2EE user can expect that his servlet will NOT be called without a Validated Principal, and that + * "isUserInRole()" will validate if the user has the Permission designated. + * + */ + public boolean oneAuthorization(Principal fqi, Permission p) { + return aafLur.fish(fqi, p); + } + + public List<Permission> allAuthorization(Principal fqi) { + List<Permission> pond = new ArrayList<Permission>(); + aafLur.fishAll(fqi, pond); + return pond; + } + + + public static void main(String[] args) { + // Note: you can pick up Properties from Command line as well as VM Properties + // Code "user_fqi=... user_pass=..." (where user_pass can be encrypted) in the command line for this sample. + // Also code "perm=<perm type>|<instance>|<action>" to test a specific Permission + PropAccess myAccess = new PropAccess(args); + try { + /* + * NOTE: Do NOT CREATE new aafcon, aafLur and aafAuthn each transaction. They are built to be + * reused! + * + * This is why this code demonstrates "Sample" as a singleton. + */ + singleton = new Sample(myAccess); + String user = myAccess.getProperty("user_fqi"); + String pass= myAccess.getProperty("user_pass"); + + if(user==null || pass==null) { + System.err.println("This Sample class requires properties user_fqi and user_pass"); + } else { + pass = myAccess.decrypt(pass, false); // Note, with "false", decryption will only happen if starts with "enc:" + // See the CODE for Java Methods used + Principal fqi = Sample.singleton().checkUserPass(user,pass); + + if(fqi==null) { + System.out.println("OK, normally, you would cease processing for an " + + "unauthenticated user, but for the purpose of Sample, we'll keep going.\n"); + fqi=new UnAuthPrincipal(user); + } + + // AGAIN, NOTE: If your client fails Authentication, the right behavior 99.9% + // of the time is to drop the transaction. We continue for sample only. + + // note, default String for perm + String permS = myAccess.getProperty("perm","org.osaaf.aaf.access|*|read"); + String[] permA = Split.splitTrim('|', permS); + if(permA.length>2) { + final Permission perm = new AAFPermission(permA[0],permA[1],permA[2]); + // See the CODE for Java Methods used + if(singleton().oneAuthorization(fqi, perm)) { + System.out.printf("Success: %s has %s\n",fqi.getName(),permS); + } else { + System.out.printf("%s does NOT have %s\n",fqi.getName(),permS); + } + } + + + // Another form, you can get ALL permissions in a list + // See the CODE for Java Methods used + List<Permission> permL = singleton().allAuthorization(fqi); + if(permL.size()==0) { + System.out.printf("User %s has no Permissions THAT THE CALLER CAN SEE\n",fqi.getName()); + } else { + System.out.print("Success:\n"); + for(Permission p : permL) { + System.out.printf("\t%s has %s\n",fqi.getName(),p.getKey()); + } + } + } + } catch (APIException | CadiException | LocatorException | IOException e) { + e.printStackTrace(); + } + } +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/content/JU_Content.java b/cadi/aaf/src/test/java/org/onap/aaf/content/JU_Content.java new file mode 100644 index 00000000..e997378d --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/content/JU_Content.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.content; + +import java.io.StringReader; + +import org.junit.*; +import org.onap.aaf.misc.rosetta.env.RosettaDF; +import org.onap.aaf.misc.rosetta.env.RosettaData; +import org.onap.aaf.misc.rosetta.env.RosettaEnv; + +import aaf.v2_0.Error; + +// TODO: This test doesn't really do anything. It should be deleted as soon as coverage is above 50% + +public class JU_Content { + + @Test + public void parseErrorJSON() throws Exception { + final String msg = "{\"messageId\":\"SVC2000\",\"text\":\"Select which cred to delete (or 0 to delete all):" + + "1) %1" + + "2) %2" + + "3) %3" + + "4) %4" + + "Run same command again with chosen entry as last parameter\"," + + "\"variables\":[" + + "\"m55555@jr583u.cred.test.com 1 Wed Oct 08 11:48:08 CDT 2014\"," + + "\"m55555@jr583u.cred.test.com 1 Thu Oct 09 12:54:46 CDT 2014\"," + + "\"m55555@jr583u.cred.test.com 1 Tue Jan 06 05:00:00 CST 2015\"," + + "\"m55555@jr583u.cred.test.com 1 Wed Jan 07 05:00:00 CST 2015\"]}"; + + Error err = new Error(); + err.setText("Hello"); + err.getVariables().add("I'm a teapot"); + err.setMessageId("12"); + + +// System.out.println(msg); + RosettaEnv env = new RosettaEnv(); + RosettaDF<aaf.v2_0.Error> errDF = env.newDataFactory(aaf.v2_0.Error.class); + errDF.in(RosettaData.TYPE.JSON); + errDF.out(RosettaData.TYPE.JSON); + RosettaData<Error> data = errDF.newData(); + data.load(err); + + @SuppressWarnings("unused") + String output = data.asString(); +// System.out.println(output); + + data.load(new StringReader(msg)); + err = data.asObject(); + output = err.getText(); +// System.out.println(output); + } + + +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/example/JU_ExampleAuthCheck.java b/cadi/aaf/src/test/java/org/onap/aaf/example/JU_ExampleAuthCheck.java new file mode 100644 index 00000000..387c4d1a --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/example/JU_ExampleAuthCheck.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.example; + +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.aaf.v2_0.AAFAuthn; +import org.onap.aaf.cadi.aaf.v2_0.AAFConHttp; +import org.onap.aaf.cadi.locator.DNSLocator; + +public class JU_ExampleAuthCheck { + public static void main(String args[]) { + // Link or reuse to your Logging mechanism + PropAccess myAccess = new PropAccess(); // + + try { + AAFConHttp acon = new AAFConHttp(myAccess, new DNSLocator( + myAccess,"https","localhost","8100")); + AAFAuthn<?> authn = acon.newAuthn(); + long start; + for (int i=0;i<10;++i) { + start = System.nanoTime(); + String err = authn.validate("", "gritty"); + if(err!=null) System.err.println(err); + else System.out.println("I'm ok"); + + err = authn.validate("bogus", "gritty"); + if(err!=null) System.err.println(err + " (correct error)"); + else System.out.println("I'm ok"); + + System.out.println((System.nanoTime()-start)/1000000f + " ms"); + } + } catch (Exception e) { + e.printStackTrace(); + } + + } +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/example/JU_X509Test.java b/cadi/aaf/src/test/java/org/onap/aaf/example/JU_X509Test.java new file mode 100644 index 00000000..732ea811 --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/example/JU_X509Test.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.example; + +import java.security.Principal; + +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.aaf.v2_0.AAFConHttp; +import org.onap.aaf.cadi.aaf.v2_0.AAFLurPerm; +import org.onap.aaf.cadi.client.Future; +import org.onap.aaf.cadi.locator.DNSLocator; +import org.onap.aaf.cadi.lur.LocalPermission; + +public class JU_X509Test { + public static void main(String args[]) { + // Link or reuse to your Logging mechanism + + PropAccess myAccess = new PropAccess(); + + // + try { + AAFConHttp con = new AAFConHttp(myAccess, + new DNSLocator(myAccess,"https","mithrilcsp.sbc.com","8100")); + + // AAFLur has pool of DME clients as needed, and Caches Client lookups + AAFLurPerm aafLur = con.newLur(); + + // Note: If you need both Authn and Authz construct the following: +// AAFAuthn<?> aafAuthn = con.newAuthn(aafLur); + + // con.x509Alias("aaf.att"); // alias in keystore + + try { + + // Normally, you obtain Principal from Authentication System. +// // For J2EE, you can ask the HttpServletRequest for getUserPrincipal() +// // If you use CADI as Authenticator, it will get you these Principals from +// // CSP or BasicAuth mechanisms. +// String id = "cluster_admin@gridcore.att.com"; +// +// // If Validate succeeds, you will get a Null, otherwise, you will a String for the reason. + Future<String> fs = + con.client("2.0").read("/authz/perms/com.att.aaf.ca","application/Perms+json"); + if(fs.get(3000)) { + System.out.println(fs.value); + } else { + System.out.println("Error: " + fs.code() + ':' + fs.body()); + } + + // Check on Perms with LUR + if(aafLur.fish(new Principal() { + @Override + public String getName() { + return "m12345@aaf.att.com"; + } + }, new LocalPermission("org.osaaf.aaf.ca|aaf|request"))) { + System.out.println("Has Perm"); + } else { + System.out.println("Does NOT Have Perm"); + } + } finally { + aafLur.destroy(); + } + } catch (Exception e) { + e.printStackTrace(); + } + + } +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/stillNeed/CadiTest.java b/cadi/aaf/src/test/java/org/onap/aaf/stillNeed/CadiTest.java new file mode 100644 index 00000000..960ea069 --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/stillNeed/CadiTest.java @@ -0,0 +1,63 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ +package org.onap.aaf.stillNeed; + +import java.net.HttpURLConnection; +import java.net.URI; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.SecuritySetter; +import org.onap.aaf.cadi.client.Future; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.config.SecurityInfoC; +import org.onap.aaf.cadi.http.HBasicAuthSS; +import org.onap.aaf.cadi.http.HClient; +import org.onap.aaf.cadi.http.HX509SS; + +public class CadiTest { + public static void main(String args[]) { + Access access = new PropAccess(); + try { + SecurityInfoC<HttpURLConnection> si = SecurityInfoC.instance(access, HttpURLConnection.class); + SecuritySetter<HttpURLConnection> ss; + if(access.getProperty(Config.CADI_ALIAS,null)!=null) { + ss = new HX509SS(si); + } else { + ss = new HBasicAuthSS(si); + } + HClient hclient = new HClient(ss,new URI("https://zlp08851.vci.att.com:8095"),3000); + hclient.setMethod("OPTIONS"); + hclient.setPathInfo("/cadi/log/set/WARN"); + hclient.send(); + Future<String> future = hclient.futureReadString(); + if(future.get(5000)) { + System.out.printf("Success %s",future.value); + } else { + System.out.printf("Error: %d-%s", future.code(),future.body()); + } + + } catch (Exception e) { + e.printStackTrace(); + } + + } +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/stillNeed/ExampleAuthCheck.java b/cadi/aaf/src/test/java/org/onap/aaf/stillNeed/ExampleAuthCheck.java new file mode 100644 index 00000000..a4b1cf1b --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/stillNeed/ExampleAuthCheck.java @@ -0,0 +1,55 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ +package org.onap.aaf.stillNeed; + +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.aaf.v2_0.AAFAuthn; +import org.onap.aaf.cadi.aaf.v2_0.AAFConHttp; +import org.onap.aaf.cadi.locator.DNSLocator; + +public class ExampleAuthCheck { + public static void main(String args[]) { + // Link or reuse to your Logging mechanism + PropAccess myAccess = new PropAccess(); // + + try { + AAFConHttp acon = new AAFConHttp(myAccess, new DNSLocator( + myAccess,"https","localhost","8100")); + AAFAuthn<?> authn = acon.newAuthn(); + long start; + for (int i=0;i<10;++i) { + start = System.nanoTime(); + String err = authn.validate("", "gritty",null); + if(err!=null) System.err.println(err); + else System.out.println("I'm ok"); + + err = authn.validate("bogus", "gritty",null); + if(err!=null) System.err.println(err + " (correct error)"); + else System.out.println("I'm ok"); + + System.out.println((System.nanoTime()-start)/1000000f + " ms"); + } + } catch (Exception e) { + e.printStackTrace(); + } + + } +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/stillNeed/TestPrincipal.java b/cadi/aaf/src/test/java/org/onap/aaf/stillNeed/TestPrincipal.java new file mode 100644 index 00000000..12569023 --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/stillNeed/TestPrincipal.java @@ -0,0 +1,35 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ +package org.onap.aaf.stillNeed; + +import java.security.Principal; + +public class TestPrincipal implements Principal { + private String name; + public TestPrincipal(String name) { + this.name = name; + } + @Override + public String getName() { + return name; + } + +} diff --git a/cadi/aaf/src/test/java/org/onap/aaf/stillNeed/X509Test.java b/cadi/aaf/src/test/java/org/onap/aaf/stillNeed/X509Test.java new file mode 100644 index 00000000..290f573e --- /dev/null +++ b/cadi/aaf/src/test/java/org/onap/aaf/stillNeed/X509Test.java @@ -0,0 +1,89 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ +package org.onap.aaf.stillNeed; + +import java.security.Principal; + +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.aaf.v2_0.AAFConHttp; +import org.onap.aaf.cadi.aaf.v2_0.AAFLurPerm; +import org.onap.aaf.cadi.client.Future; +import org.onap.aaf.cadi.locator.DNSLocator; +import org.onap.aaf.cadi.lur.LocalPermission; + +//TODO Needs running service to TEST + +public class X509Test { + public static void main(String args[]) { + // Link or reuse to your Logging mechanism + + PropAccess myAccess = new PropAccess(); + + // + try { + AAFConHttp con = new AAFConHttp(myAccess, + new DNSLocator(myAccess,"https","mithrilcsp.sbc.com","8100")); + + // AAFLur has pool of DME clients as needed, and Caches Client lookups + AAFLurPerm aafLur = con.newLur(); + + // Note: If you need both Authn and Authz construct the following: +// AAFAuthn<?> aafAuthn = con.newAuthn(aafLur); + + // con.x509Alias("aaf.att"); // alias in keystore + + try { + + // Normally, you obtain Principal from Authentication System. +// // For J2EE, you can ask the HttpServletRequest for getUserPrincipal() +// // If you use CADI as Authenticator, it will get you these Principals from +// // CSP or BasicAuth mechanisms. +// String id = "cluster_admin@gridcore.att.com"; +// +// // If Validate succeeds, you will get a Null, otherwise, you will a String for the reason. + Future<String> fs = + con.client("2.0").read("/authz/perms/com.att.aaf.ca","application/Perms+json"); + if(fs.get(3000)) { + System.out.println(fs.value); + } else { + System.out.println("Error: " + fs.code() + ':' + fs.body()); + } + + // Check on Perms with LUR + if(aafLur.fish(new Principal() { + @Override + public String getName() { + return "m12345@aaf.att.com"; + } + }, new LocalPermission("org.osaaf.aaf.ca|aaf|request"))) { + System.out.println("Has Perm"); + } else { + System.out.println("Does NOT Have Perm"); + } + } finally { + aafLur.destroy(); + } + } catch (Exception e) { + e.printStackTrace(); + } + + } +} diff --git a/cadi/aaf/src/test/resources/cadi.properties b/cadi/aaf/src/test/resources/cadi.properties new file mode 100644 index 00000000..810df571 --- /dev/null +++ b/cadi/aaf/src/test/resources/cadi.properties @@ -0,0 +1,37 @@ +## +## AUTHZ API (authz-service) Properties +## +AFT_LATITUDE=32.780140 +AFT_LONGITUDE=-96.800451 +AFT_ENVIRONMENT=AFTUAT +DEPLOYED_VERSION=2.0.MITHRIL +cadi_prop_files=/opt/app/aaf/common/com.att.aaf.props + +#cadi_keystore=/Volumes/Data/src/authz/common/cadiaaf.jks +#cadi_truststore=/Volumes/Data/src/authz/common/caditrust.jks +#cadi_keystore_password=enc:4s9TVkWDpUhjgimeXEDL7fE7gaTvppkGwiU7arrtu504ol9uB51swkZkqW7qTr_T +#cadi_key_password=enc:4s9TVkWDpUhjgimeXEDL7fE7gaTvppkGwiU7arrtu504ol9uB51swkZkqW7qTr_T +#cadi_truststore_password=enc:HHFqU-eYs2653Ifsm4m-m4TkehxB13x4kZxQqsf-ydz +# cadi_trust_all_x509=true +#cadi_alias=aaf.att +https.protocols=TLSv1.1,TLSv1.2 + +# cm_url=https://mithrilcsp.sbc.com:8150 + +#basic_realm=localized +#basic_warn=false +#localhost_deny=false + +#cass_group_name=com.att.aaf +#cass_cluster_name=mithrilcsp.sbc.com +#aaf_default_realm=com.att.csp + +#aaf_url=https://135.110.241.35:8100 +#aaf_url=https://mithrilcsp.sbc.com:8095/proxy +aaf_url=https://DME2RESOLVE/service=com.att.authz.AuthorizationService/version=2.0/envContext=DEV/routeOffer=BAU_SE +#aaf_id=m12345@aaf.att.com +#aaf_password=enc:mH3t-3tBYXrf8RUhGP6unXH9z75Ba-kBDQTiAHblMju + +aaf_user_expires=3000 +aaf_clean_interval=4000 + diff --git a/cadi/aaf/src/test/resources/cert.pem b/cadi/aaf/src/test/resources/cert.pem new file mode 100644 index 00000000..175c949d --- /dev/null +++ b/cadi/aaf/src/test/resources/cert.pem @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE----- +MIIFqzCCA5OgAwIBAgIJAKR74mLLmqGoMA0GCSqGSIb3DQEBCwUAMGwxCzAJBgNV +BAYTAlVTMREwDwYDVQQIDAhNaXNzb3VyaTERMA8GA1UEBwwIU3QgTG91aXMxETAP +BgNVBAoMCEZha2UgT3JnMREwDwYDVQQLDAhmYWtlLm9yZzERMA8GA1UEAwwISm9o +biBEb2UwHhcNMTgwNTAzMjEwMzEzWhcNMTgwNjAyMjEwMzEzWjBsMQswCQYDVQQG +EwJVUzERMA8GA1UECAwITWlzc291cmkxETAPBgNVBAcMCFN0IExvdWlzMREwDwYD +VQQKDAhGYWtlIE9yZzERMA8GA1UECwwIZmFrZS5vcmcxETAPBgNVBAMMCEpvaG4g +RG9lMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvfadEz8rdI3Q6LsA +3e4cPYGkYkty7gyVmD52DYxQYsrykJewI4iqJ+jZb2kfEYjz5Tw3hAi1cw2Db5Vr +2yB3GLR9lk6Eryj1/tDEVXrWDJpXPSEKcyLDzvsLEXi6ZabVZbSzX41/YSct1Hn/ +ucHo2oFtKz6GLVQ0Jb5dp5sQiV8KDdrj2+KDRkQR6WeEY5a89wAwcoYEOlIXx6+4 +jurhUzdvyRiXFxEl2B20IGDQ0byEUnbXEgcCDBJVNyU+dxXMSLHcxFNKEjhaYcn1 +29nEzStfEV8NuxqiE7TCZNUCy2BAMxd9k4kmZ4Tb6tOyza+fEUBu4BLBywusyeVb +D/mupHyG6K/xyMAVmSqGYVjweEFX+UkITHsvkZS2+Iizjt1x658RuLcI8YvEHPbm +lU+wirNoc/1wOxR3V53ZSjqnghLql8TUDVH7ysp+khthiJnr26fRSZNSkNBbNhax +FkC9UYuVuoHscUjsRzX0RkELo4OJG6n11SUyB0K9WLI6b33yfBXFOpOXByavvjkS +BZM7pNOG77GSz/uCaQ/glE7PSnDx1AzGWGdv9YqKAFU6lEMdw2HCozzc2aX/GXPW +hvh2Hjvt2ZKJc87DVvLsdySQwsJ05YF71kxMmxqnwqnD5/h0pMjxThyDm7DfaGek +9gAw7nqCOQJbvafl8ZnKFKnAI/0CAwEAAaNQME4wHQYDVR0OBBYEFFn1zEUXwHY2 +odqzPA0BTkoBqTzWMB8GA1UdIwQYMBaAFFn1zEUXwHY2odqzPA0BTkoBqTzWMAwG +A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBADWOO+YOdwIL0Wdws89s2h1I +TAN2glpQNDcwiMlT5VISqrb4R5oGYQuc7eR3X6fUArZwo38QW2C5+A4gXCUmy+UE +Hyneac+RXTxD29Glxn14dt174VsJ7mlFxkOd7ft8beaRhga5DAot6HyjJwS2K8GP +cNoM9zJFbJcRjs4oO93fOdp5M3mOOcwMbfQLZIFUx93Y7cn3Uoyz/Yfws/KKuY9r +faUGNB9bSSZc+aM7ZLorMwDb45Beu443czUfzOhWLxiDK9pqwY9k7DV4x4ahvPhx +OiRl31ksL/esCc4G2oOe9wATh1gwnIDJWE1bgNepKwjqinlWRQqq7JcRbpXyQ2t0 +0v0P60cVcIMO6iCuCvKO4wZh5nUrHQlTfHfWDyH5UN2nUa12BpOidvgp5AzuVG6e +pIYbRViwdOzEOAKOlHCuZN/rFkQAmi6baz4/7JV9GeW92xZyDc9GGM/JQY3lMRfw +ablgXEuJFJGVQkO6/LkqcEvFpLVcdTeJeWxJvR9lwJJX1NXTQN91aFqLznc50idK +UiKjE+3eBG/S64htp48+a6xi2r6uujRl/VAOoTjunGuSvDdmThlwnnlnp4iqcm7k +m4nB2/4SvSzQ8r4cUl0sFCZ7OLW8WM4dpZcfklk7ApZ4TFTMzUi4zUtCk4Vfdxbm +MX+3SmP+Pjf0p+1DtdhM +-----END CERTIFICATE----- diff --git a/cadi/aaf/src/test/resources/exampleCertificate.cer b/cadi/aaf/src/test/resources/exampleCertificate.cer new file mode 100755 index 00000000..76ed12dc --- /dev/null +++ b/cadi/aaf/src/test/resources/exampleCertificate.cer @@ -0,0 +1,59 @@ +-----BEGIN CERTIFICATE-----
+MIIKkTCCCXmgAwIBAgIQBO9R08Vvthj6ifmVeLw94DANBgkqhkiG9w0BAQsFADBe
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMR0wGwYDVQQDExRHZW9UcnVzdCBSU0EgQ0EgMjAxODAe
+Fw0xODAzMjUwMDAwMDBaFw0xOTA0MjMxMjAwMDBaMIGaMQswCQYDVQQGEwJVUzET
+MBEGA1UECBMKQ2FsaWZvcm5pYTEXMBUGA1UEBxMOUmVkd29vZCBTaG9yZXMxGzAZ
+BgNVBAoTEk9yYWNsZSBDb3Jwb3JhdGlvbjEnMCUGA1UECxMeQ29udGVudCBNYW5h
+Z2VtZW50IFNlcnZpY2VzIElUMRcwFQYDVQQDEw53d3cub3JhY2xlLmNvbTCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMWDD1iqkoFspX8qhH6HvcEkOtG7
+x5yQQzGO2qr4FzZxzBUsi4fOm33e+wD+H+zRi2u/5GPKr6f6GNT50IQJR6Bg5ReX
+co3nbL0XmqjxMmncM7UkgGwFynzW/AMZ6TobMhtKFiHgCHanXGxMJgZmw9JW0qkf
+pHRsTa/OBnxV16tu4cAIHoVb5ZkJH0qSXKSdhj4KKM6Uajr4QxS7YQQTr51MAlDa
+Xe3tWTaJv3TjMpzD23a8xfhNRDWp6JiN2l/tK30SiOOS68hEByG1oPA3TRRzzixz
+VEf8ANDx6prQfhb+Aior5UcBRFry6IU3h3W4aTSa2k+kBCwGgdNkX0deXqsCAwEA
+AaOCBwwwggcIMB8GA1UdIwQYMBaAFJBY/7CcdahRVHex7fKjQxY4nmzFMB0GA1Ud
+DgQWBBQ7gL1OQ/z3NZBU4xwB7jtfv1rykzCCBH8GA1UdEQSCBHYwggRygg53d3cu
+b3JhY2xlLmNvbYIKb3JhY2xlLmNvbYISc3VwcG9ydC5vcmFjbGUuY29tgg9tYXBz
+Lm9yYWNsZS5jb22CFG9yYWNsZWZvdW5kYXRpb24ub3JnghVmdXNpb25oZWxwLm9y
+YWNsZS5jb22CFHByZXNzcm9vbS5vcmFjbGUuY29tggt3d3cuZ28uamF2YYIIamF2
+YS5jb22CEXNlYXJjaC5vcmFjbGUuY29tghBibG9ncy5vcmFjbGUuY29tghB3aWtp
+cy5vcmFjbGUuY29tggxjbG91ZC5vcmFjbGWCFGNuLmZvcnVtcy5vcmFjbGUuY29t
+ghRteWJ1aWxkZXIub3JhY2xlLmNvbYIXZGlnaXRhbG1lZGlhLm9yYWNsZS5jb22C
+GGZpbi1mdXNpb25jcm0ub3JhY2xlLmNvbYIRZm9ydW1zLm9yYWNsZS5jb22CEGNs
+b3VkLm9yYWNsZS5jb22CG2JpYXBwcy1mdXNpb25jcm0ub3JhY2xlLmNvbYIUY29t
+bXVuaXR5Lm9yYWNsZS5jb22CFGRldmVsb3Blci5vcmFjbGUuY29tghRlbG9jYXRp
+b24ub3JhY2xlLmNvbYIScHJvZmlsZS5vcmFjbGUuY29tghdpYy1mdXNpb25jcm0u
+b3JhY2xlLmNvbYISbXlzaXRlcy5vcmFjbGUuY29tggxtLm9yYWNsZS5jb22CFG15
+cHJvZmlsZS5vcmFjbGUuY29tghFkZXNpZ24ub3JhY2xlLmNvbYILamF2YS5vcmFj
+bGWCGGNybS1mdXNpb25jcm0ub3JhY2xlLmNvbYIUZnVzaW9uY3JtLm9yYWNsZS5j
+b22CEHNpdGVzLm9yYWNsZS5jb22CD2RvY3Mub3JhY2xlLmNvbYIUbXlwcm9jZXNz
+Lm9yYWNsZS5jb22CGGhjbS1mdXNpb25jcm0ub3JhY2xlLmNvbYIRd3d3Lm9yYWNs
+ZWltZy5jb22CG2Nsb3VkbWFya2V0cGxhY2Uub3JhY2xlLmNvbYIUZWRlbGl2ZXJ5
+Lm9yYWNsZS5jb22CEGl0d2ViLm9yYWNsZS5jb22CEXN0YXRpYy5vcmFjbGUuY29t
+ghRrci5mb3J1bXMub3JhY2xlLmNvbYIMd3d3LmphdmEuY29tghpteXZpc3VhbGl6
+YXRpb24ub3JhY2xlLmNvbYIRZXZlbnRzLm9yYWNsZS5jb22CF2JpLWZ1c2lvbmNy
+bS5vcmFjbGUuY29tghh3d3cub3JhY2xlZm91bmRhdGlvbi5vcmeCHHJlc2VsbGVy
+ZWR1Y2F0aW9uLm9yYWNsZS5jb22CFGVkdWNhdGlvbi5vcmFjbGUuY29tghhzY20t
+ZnVzaW9uY3JtLm9yYWNsZS5jb22CGHByai1mdXNpb25jcm0ub3JhY2xlLmNvbYIW
+c2VjdXJlc2l0ZXMub3JhY2xlLmNvbYIYcHJjLWZ1c2lvbmNybS5vcmFjbGUuY29t
+ghFwb3J0YWwub3JhY2xlLmNvbYIHZ28uamF2YTAOBgNVHQ8BAf8EBAMCBaAwHQYD
+VR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMD4GA1UdHwQ3MDUwM6AxoC+GLWh0
+dHA6Ly9jZHAuZ2VvdHJ1c3QuY29tL0dlb1RydXN0UlNBQ0EyMDE4LmNybDBMBgNV
+HSAERTBDMDcGCWCGSAGG/WwBATAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5k
+aWdpY2VydC5jb20vQ1BTMAgGBmeBDAECAjB1BggrBgEFBQcBAQRpMGcwJgYIKwYB
+BQUHMAGGGmh0dHA6Ly9zdGF0dXMuZ2VvdHJ1c3QuY29tMD0GCCsGAQUFBzAChjFo
+dHRwOi8vY2FjZXJ0cy5nZW90cnVzdC5jb20vR2VvVHJ1c3RSU0FDQTIwMTguY3J0
+MAkGA1UdEwQCMAAwggECBgorBgEEAdZ5AgQCBIHzBIHwAO4AdQCkuQmQtBhYFIe7
+E6LMZ3AKPDWYBPkb37jjd80OyA3cEAAAAWJbje2RAAAEAwBGMEQCICYHgjJanZfY
+hZ86nCaMFh4p2qCmO+EUEzsYVbcnihhFAiBTC4OUrYRENk9a3KK3AM3pt8iFfS2j
+X18JZGy1cK2h4QB1AG9Tdqwx8DEZ2JkApFEV/3cVHBHZAsEAKQaNsgiaN9kTAAAB
+YluN7x4AAAQDAEYwRAIgNaXytmedMEeBRSCPDye5C2gV4O6uYsYUYx84aLnXRBsC
+ICnthPukbEzpfhXC0He5IBOW8OOdsF+Wb5yQE8z5I+5hMA0GCSqGSIb3DQEBCwUA
+A4IBAQCC1YtAS4GRpTeQRJnyQfHKkrMemlWCcHIOkUY/d9mIpIlFqMe/jxkhimrX
+uM/AfTv+O9fLuknaQ9fYJGJmKlYk1hsKQy0UBfDDFApaBhjpAOkLjASViLzweVMD
+aBRWn8Qx5ScgTnMjb8FFizcEM2IMpqXetOkJyn6cu5GtYhDthEOmvdkVJIPpC+cL
+i8yesYU8au5Y7ERmHRmJycH7yK6Vl13FYBEUXdR/NoGrc6I3ayiaFiyUf1/HuQEG
+HLW0n2oKDk/SVAI9CZh+MuPwqp4eln4YCBIPBdKnGRrgAbaZCCKIkPKmF9k75wNY
+7wLi08wEQ4LsvmmLN+H3AZ3IBDbF
+-----END CERTIFICATE-----
diff --git a/cadi/aaf/src/test/resources/key.pem b/cadi/aaf/src/test/resources/key.pem new file mode 100644 index 00000000..a5818db0 --- /dev/null +++ b/cadi/aaf/src/test/resources/key.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQC99p0TPyt0jdDo +uwDd7hw9gaRiS3LuDJWYPnYNjFBiyvKQl7AjiKon6NlvaR8RiPPlPDeECLVzDYNv +lWvbIHcYtH2WToSvKPX+0MRVetYMmlc9IQpzIsPO+wsReLplptVltLNfjX9hJy3U +ef+5wejagW0rPoYtVDQlvl2nmxCJXwoN2uPb4oNGRBHpZ4Rjlrz3ADByhgQ6UhfH +r7iO6uFTN2/JGJcXESXYHbQgYNDRvIRSdtcSBwIMElU3JT53FcxIsdzEU0oSOFph +yfXb2cTNK18RXw27GqITtMJk1QLLYEAzF32TiSZnhNvq07LNr58RQG7gEsHLC6zJ +5VsP+a6kfIbor/HIwBWZKoZhWPB4QVf5SQhMey+RlLb4iLOO3XHrnxG4twjxi8Qc +9uaVT7CKs2hz/XA7FHdXndlKOqeCEuqXxNQNUfvKyn6SG2GImevbp9FJk1KQ0Fs2 +FrEWQL1Ri5W6gexxSOxHNfRGQQujg4kbqfXVJTIHQr1YsjpvffJ8FcU6k5cHJq++ +ORIFkzuk04bvsZLP+4JpD+CUTs9KcPHUDMZYZ2/1iooAVTqUQx3DYcKjPNzZpf8Z +c9aG+HYeO+3ZkolzzsNW8ux3JJDCwnTlgXvWTEybGqfCqcPn+HSkyPFOHIObsN9o +Z6T2ADDueoI5Alu9p+XxmcoUqcAj/QIDAQABAoICADRkPuAfDQIhVtvJL60Fzd4c +0lSV0IXdDKknmPGVoFoO9SVx4I98UsmdC9MRYBM6/WFc4UbWDA1GTdjJkiymYJJ8 +vSJmV2vj1SzJMU0OCtkA/EyMv1AP54c/b8cK0AXXJIXfd5VD9jy6TIaMez4lP+57 +wbsqjGEWFyfNwBDI0J/CiYhWtX9gkqofff0sorPA0C8jazk6wxG+sHZPfYxVNX35 +DSieUpV3EkPvtU00xoMCBlCkHB8JtcPUjpIeAINhjK0D+Qpgmk46IptT0y4meoPH +kXm+CJBxAQCEWxTqNtIWor40nVrCecgVOX4jku3toOZmKe483hv9BVPNoPbf+w1C +5PI8eLCVeKp10dhSP9+HsKhwENqac/pF4RISnf5St0hccdyzHlwyRXVY2UJDd8Ik +Hv4zh3iSzuhd5ar4Pgpwvl/9dsJBDQtxf2RgBMLlf9TbIaFTA+Q55Ir/+BsaCxkr +Uz+bk00cF2nrUU7cqu0TXzsOCmCq02Oc4ELZ3zXGu1t2EjeIkAatbrCTigdiGimj +gpB5bSRUNKyu9lQgHP/XIiWeiYmRb1I9j2ICxbvdZm5Kj5o2/6i9vy4ouCvd9qF3 +IdK2/U+sBF6XFKvGMzRC3giID+PYSqMcoBybuUzWgfKLu3WMpuhnPKPtorokc7d7 +M3+Wc7UfSbQUn2JY/2wBAoIBAQDjLLIaFLkbfg6HMQu/JPspLibYzAXbGRw+SJj5 +vkqVmlPFj2pNpEFHLHdN7gmmKxmq3crTL47g3XoOAI4vk5obpO5ICtrsXF/OSL4a +MAm63wvY+KiIUAhRTNzu53xjQ+PwaG1A7VghkPeAtk/HCI2vqJH7UoLWUcR7abUL +gCILuGnxk7QnjJNWoD6pJ6RV4vnkRx/2cZO+rYE6Wm2kBeaNoW/aEKXYYBsAty2E +/dJ1GkEm4x59+R43Lu665GTaDKJPItxTyv7QpKvWbdPUab8g5YdA12p2//HrLCb3 +yMBedxys2VDpaIBSN6INi/6BMCRMtoDdol1gzHm2/dlMWuD9AoIBAQDWES+VNosh +MkLsPcAp1Psq0+ucQCWpyAMgpgkN0SbBJDcMR+xqrmrxunOWuFeWg5A48xiCQNdW +uA8X6X3TWGsFaNyFD5BNPl1WncmzwtqTCjqgn/EDdTWS40eLFZJMxBf0infjPMFS +dkrIcbLOHb56miBf+CnMZ1uEmwo0h2epwkaU6Kk1wm3X8bojUVGzY5O5x8AJzDeP +EC4hmC34FnPu19LRNT/29vzX5X8mLuy7RYcdzCy9ut//G+m+OVoMImvTI6cxLN0v +zcJyJmrYoR9yVfHjcUA43CgkCSqIlVPSYIvBFLL/O9ZZspfZqAERYcCFT38uAtF/ +nPfuTk6mUz8BAoIBABGkzQhdh6rs9W/mjUUBOEiQfw/jeKj1oE3uEYOEFgLcg5ka +dGUnVrKSb4mr7S/stQeiRjh0vyIT0YD45hIn4pY3DxKlVS96VS6OU8Vw6bCL1j25 +wk0j+iFmWNptPCnxgeiQE7wxMuEYg0CJ6FRLA8Yaz4u3ctX2b84t/ZOxFfPXFNNg +Z2OS9XaK55L3sznAcSwbog3f8Fuk2h6QG2fb1XY2jZtgI6FUhYRetbhYhln1+g1t +IlciXAhpKr11M6gDxy9iQ752S6gkwfvbd9JNjDyf1wtgL7KiWkWrnjMsclRj5+Q8 +1J3sMdsw2vM2ZkPeW1Nh8UxFaf80oldmC9R0UnkCggEAbmLwWY6F0jl73xy8shWc +62najnlZsqJsUnKsKo7W4DQPmuqf1CdbCInwPyGSMRBo16Ur10cehB5n0hnag5iN +n32Cca8j52EoepjlQShS1A4rS1cOzoyrcrJ22xblmWZpP/YDeo+C1UYgrBpNbRJT +fh9qYHK1Ay2tOMVGTu4gG58ODI2pbAp14CxLoxi0+792lw+VTLgdUk2yrCowUkUp +xVlP4ggGkxCsM5ypo4QBGVTyJwB5deEezwuSzj/+2lEJrxgsiCQtbxA4m+qJoGn9 +sFT3ZiSpTGji3ipH36S5U7vrdUZ6QzmVAC4jNd73pgH1aAkleRGE/Lxx8VY6InS9 +AQKCAQEAjUxVkW7ei0XOvz3hzEM8s84StZAzz/OOchxxLIDwWtmrnTRlDCDFNgfn +kjWggY8ySvEGeeh/Bq1UjZn46yJEnbBaluSlwtpB9/QNlvVESfi72F6qXxPrAb7w +wvMLFk2abUQk1MUursiC4Xch5Br9wGAQqzPIFNQRlhH3t47ZGyQn9Sc0FONLfPpO +NR+A0BBvfQG7/fg1JLNcmh9AdQr0gxTUJyR4a32An4IcSQZqCyF7Zzr3ERJ3n2tR +0S0NaYmQEs7sqULnG2f8USc53z5/skAo2OXeOZXmCpY7PH7Zfq85ojnsB/d9rAfm +43jbRd3vVTO310fh3QIgNQ3tg+u3mg== +-----END PRIVATE KEY----- diff --git a/cadi/aaf/src/test/resources/log4j.properties b/cadi/aaf/src/test/resources/log4j.properties new file mode 100644 index 00000000..5ec63883 --- /dev/null +++ b/cadi/aaf/src/test/resources/log4j.properties @@ -0,0 +1,32 @@ +# +# 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. +# +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] %m %n + +# General Apache libraries +log4j.rootLogger=WARN,stdout +log4j.logger.org.apache=WARN,stdout +log4j.logger.dme2=WARN,stdout +log4j.logger.init=INFO,stdout +log4j.logger.authz=INFO,stdout +log4j.logger.audit=WARN,stdout + + + diff --git a/cadi/client/.gitignore b/cadi/client/.gitignore new file mode 100644 index 00000000..6028f0a5 --- /dev/null +++ b/cadi/client/.gitignore @@ -0,0 +1,4 @@ +/.classpath +/.settings/ +/target/ +/.project diff --git a/cadi/client/pom.xml b/cadi/client/pom.xml new file mode 100644 index 00000000..b9ba4a1f --- /dev/null +++ b/cadi/client/pom.xml @@ -0,0 +1,204 @@ +<!-- + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * +--> +<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"> + <parent> + <groupId>org.onap.aaf.authz</groupId> + <artifactId>cadiparent</artifactId> + <version>2.1.0-SNAPSHOT</version> + <relativePath>..</relativePath> + </parent> + + <artifactId>aaf-cadi-client</artifactId> + <name>AAF CADI Client</name> + <packaging>jar</packaging> + <modelVersion>4.0.0</modelVersion> + + <properties> + <scijava.jvm.version>1.8</scijava.jvm.version> + <!-- SONAR --> + <!-- <sonar.skip>true</sonar.skip> --> + <jacoco.version>0.7.7.201606060606</jacoco.version> + <sonar-jacoco-listeners.version>3.2</sonar-jacoco-listeners.version> + <sonar.core.codeCoveragePlugin>jacoco</sonar.core.codeCoveragePlugin> + <!-- Default Sonar configuration --> + <sonar.jacoco.reportPaths>target/code-coverage/jacoco-ut.exec</sonar.jacoco.reportPaths> + <sonar.jacoco.itReportPaths>target/code-coverage/jacoco-it.exec</sonar.jacoco.itReportPaths> + <!-- Note: This list should match jacoco-maven-plugin's exclusion list below --> + <sonar.exclusions>**/gen/**,**/generated-sources/**,**/yang-gen**,**/pax/**</sonar.exclusions> + <nexusproxy>https://nexus.onap.org</nexusproxy> + <snapshotNexusPath>/content/repositories/snapshots/</snapshotNexusPath> + <releaseNexusPath>/content/repositories/releases/</releaseNexusPath> + <stagingNexusPath>/content/repositories/staging/</stagingNexusPath> + <sitePath>/content/sites/site/org/onap/aaf/authz/${project.artifactId}/${project.version}</sitePath> + </properties> + + <developers> + <developer> + <name>Jonathan Gathman</name> + <email>jonathan.gathman@att.com</email> + <organization>ATT</organization> + <roles> + <role>Architect</role> + <role>Lead Developer</role> + </roles> + </developer> + <developer> + <name>Gabe Maurer</name> + <email>gabe.maurer@att.com</email> + <organization>ATT</organization> + <roles> + <role>Developer</role> + </roles> + </developer> + <developer> + <name>Ian Howell</name> + <email>ian.howell@att.com</email> + <organization>ATT</organization> + <roles> + <role>Developer</role> + </roles> + </developer> + <developer> + <name>Sai Gandham</name> + <email>sai.gandham@att.com</email> + <organization>ATT</organization> + <roles> + <role>Developer</role> + </roles> + </developer> + </developers> + + <dependencies> + <dependency> + <groupId>org.onap.aaf.authz</groupId> + <artifactId>aaf-misc-rosetta</artifactId> + <version>${project.version}</version> + + </dependency> + <dependency> + <groupId>org.onap.aaf.authz</groupId> + <artifactId>aaf-cadi-core</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>javax.servlet</groupId> + <artifactId>javax.servlet-api</artifactId> + <scope>compile</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jarsigner-plugin</artifactId> + <version>1.4</version> + </plugin> + <plugin> + <groupId>org.sonatype.plugins</groupId> + <artifactId>nexus-staging-maven-plugin</artifactId> + <version>1.6.7</version> + <extensions>true</extensions> + <configuration> + <nexusUrl>${nexusproxy}</nexusUrl> + <stagingProfileId>176c31dfe190a</stagingProfileId> + <serverId>ecomp-staging</serverId> + </configuration> + </plugin> + <plugin> + <groupId>org.jacoco</groupId> + <artifactId>jacoco-maven-plugin</artifactId> + <version>${jacoco.version}</version> + <configuration> + <excludes> + <exclude>**/gen/**</exclude> + <exclude>**/generated-sources/**</exclude> + <exclude>**/yang-gen/**</exclude> + <exclude>**/pax/**</exclude> + </excludes> + </configuration> + <executions> + <execution> + <id>pre-unit-test</id> + <goals> + <goal>prepare-agent</goal> + </goals> + <configuration> + <destFile>${project.build.directory}/code-coverage/jacoco-ut.exec</destFile> + <propertyName>surefireArgLine</propertyName> + </configuration> + </execution> + <execution> + <id>post-unit-test</id> + <phase>test</phase> + <goals> + <goal>report</goal> + </goals> + <configuration> + <dataFile>${project.build.directory}/code-coverage/jacoco-ut.exec</dataFile> + <outputDirectory>${project.reporting.outputDirectory}/jacoco-ut</outputDirectory> + </configuration> + </execution> + <execution> + <id>pre-integration-test</id> + <phase>pre-integration-test</phase> + <goals> + <goal>prepare-agent</goal> + </goals> + <configuration> + <destFile>${project.build.directory}/code-coverage/jacoco-it.exec</destFile> + <propertyName>failsafeArgLine</propertyName> + </configuration> + </execution> + <execution> + <id>post-integration-test</id> + <phase>post-integration-test</phase> + <goals> + <goal>report</goal> + </goals> + <configuration> + <dataFile>${project.build.directory}/code-coverage/jacoco-it.exec</dataFile> + <outputDirectory>${project.reporting.outputDirectory}/jacoco-it</outputDirectory> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + + <distributionManagement> + <repository> + <id>ecomp-releases</id> + <name>AAF Release Repository</name> + <url>${nexusproxy}${releaseNexusPath}</url> + </repository> + <snapshotRepository> + <id>ecomp-snapshots</id> + <name>AAF Snapshot Repository</name> + <url>${nexusproxy}${snapshotNexusPath}</url> + </snapshotRepository> + <site> + <id>ecomp-site</id> + <url>dav:${nexusproxy}${sitePath}</url> + </site> + </distributionManagement> +</project> diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/client/AbsAuthentication.java b/cadi/client/src/main/java/org/onap/aaf/cadi/client/AbsAuthentication.java new file mode 100644 index 00000000..80e6dc40 --- /dev/null +++ b/cadi/client/src/main/java/org/onap/aaf/cadi/client/AbsAuthentication.java @@ -0,0 +1,130 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.client; + +import java.io.IOException; + +import org.onap.aaf.cadi.SecuritySetter; +import org.onap.aaf.cadi.Symm; +import org.onap.aaf.cadi.config.SecurityInfoC; + +/** + * AbsAuthentication is a class representing how to Authenticate onto a Client. + * + * Methods of setting Authentication on a Client vary, so CLIENT is a Generic Type + * This allows the ability to apply security onto Different Client Types, as they come + * into vogue, or change over time. + * + * Password is encrypted at rest. + * + * @author Jonathan + * + * @param <CLIENT> + */ +public abstract class AbsAuthentication<CLIENT> implements SecuritySetter<CLIENT> { + // HTTP Header for Authentication is "Authorization". This was from an early stage of internet where + // Access by Credential "Authorized" you for everything on the site. Since those early days, it became + // clear that "full access" wasn't appropriate, so the split between Authentication and Authorization + // came into being... But the Header remains. + public static final String AUTHORIZATION = "Authorization"; + private static final Symm symm; + + protected static final String REPEAT_OFFENDER = "This call is aborted because of repeated usage of invalid Passwords"; + private static final int MAX_TEMP_COUNT = 10; + private static final int MAX_SPAM_COUNT = 10000; + private static final long WAIT_TIME = 1000*60*4L; + private final byte[] headValue; + private String user; + protected final SecurityInfoC<CLIENT> securityInfo; + protected long lastMiss; + protected int count; + + static { + try { + symm = Symm.encrypt.obtain(); + } catch (IOException e) { + throw new RuntimeException("Cannot create critical internal encryption key",e); + } + + } + + public AbsAuthentication(final SecurityInfoC<CLIENT> securityInfo, final String user, final byte[] headValue) throws IOException { + this.headValue = headValue==null?null:symm.encode(headValue); + this.user = user; + this.securityInfo = securityInfo; + lastMiss=0L; + count=0; + } + + protected String headValue() throws IOException { + if(headValue==null) { + return ""; + } else { + return new String(symm.decode(headValue)); + } + } + + protected void setUser(String id) { + user = id; + } + + @Override + public String getID() { + return user; + } + + public boolean isDenied() { + if(lastMiss>0 && lastMiss>System.currentTimeMillis()) { + return true; + } else { + lastMiss=0L; + return false; + } + } + + public synchronized int setLastResponse(int httpcode) { + if(httpcode == 401) { + ++count; + if(lastMiss==0L && count>MAX_TEMP_COUNT) { + lastMiss=System.currentTimeMillis()+WAIT_TIME; + } + // if(count>MAX_SPAM_COUNT) { + // System.err.printf("Your service has %d consecutive bad service logins to AAF. \nIt will now exit\n", + // count); + // System.exit(401); + // } + if(count%1000==0) { + System.err.printf("Your service has %d consecutive bad service logins to AAF. AAF Access will be disabled after %d\n", + count,MAX_SPAM_COUNT); + } + + } else { + lastMiss=0; + } + return count; + } + + public int count() { + return count; + } + +}
\ No newline at end of file diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/client/AbsTransferSS.java b/cadi/client/src/main/java/org/onap/aaf/cadi/client/AbsTransferSS.java new file mode 100644 index 00000000..3815bc67 --- /dev/null +++ b/cadi/client/src/main/java/org/onap/aaf/cadi/client/AbsTransferSS.java @@ -0,0 +1,76 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.client; + +import java.security.Principal; + +import org.onap.aaf.cadi.SecuritySetter; +import org.onap.aaf.cadi.config.SecurityInfoC; +import org.onap.aaf.cadi.principal.TaggedPrincipal; + +/** + * This client represents the ability to Transfer the Identity of the caller to the authenticated + * user being transferred to. This ability is critical for App-to-App communication to ensure that + * Authorization can happen on the End-Users' credentials when appropriate, even though Authentication + * to App1 by App2 must be by App2's credentials. + * + * @author Jonathan + * + * @param <CLIENT> + */ +public abstract class AbsTransferSS<CLIENT> implements SecuritySetter<CLIENT> { + protected String value; + protected SecurityInfoC<CLIENT> securityInfo; + protected SecuritySetter<CLIENT> defSS; + private Principal principal; + + //Format:<ID>:<APP>:<protocol>[:AS][,<ID>:<APP>:<protocol>]* + public AbsTransferSS(TaggedPrincipal principal, String app) { + init(principal, app); + } + + public AbsTransferSS(TaggedPrincipal principal, String app, SecurityInfoC<CLIENT> si) { + init(principal,app); + securityInfo = si; + this.defSS = si.defSS; + } + + private void init(TaggedPrincipal principal, String app) { + this.principal=principal; + if(principal==null) { + return; + } else { + value = principal.getName() + ':' + + app + ':' + + principal.tag() + ':' + + "AS"; + } + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.SecuritySetter#getID() + */ + @Override + public String getID() { + return principal==null?"":principal.getName(); + } +} diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/client/BasicAuth.java b/cadi/client/src/main/java/org/onap/aaf/cadi/client/BasicAuth.java new file mode 100644 index 00000000..1eb8d7c4 --- /dev/null +++ b/cadi/client/src/main/java/org/onap/aaf/cadi/client/BasicAuth.java @@ -0,0 +1,28 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.client; + +/** + * Basic Auth is a marker Interface, because certain kinds of behaviors apply only to User/Password Combinations + */ +public interface BasicAuth { +} diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/client/EClient.java b/cadi/client/src/main/java/org/onap/aaf/cadi/client/EClient.java new file mode 100644 index 00000000..d5dfebf5 --- /dev/null +++ b/cadi/client/src/main/java/org/onap/aaf/cadi/client/EClient.java @@ -0,0 +1,51 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.client; + +import java.io.IOException; +import java.io.OutputStream; + +import javax.servlet.http.HttpServletResponse; + +import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.misc.env.Data; +import org.onap.aaf.misc.rosetta.env.RosettaDF; + + +public interface EClient<CT> { + public void setMethod(String meth); + public void setPathInfo(String pathinfo); + public void setPayload(Transfer transfer); + public void addHeader(String tag, String value); + public void setQueryParams(String q); + public void setFragment(String f); + public void send() throws APIException; + public<T> Future<T> futureCreate(Class<T> t); + public Future<String> futureReadString(); + public<T> Future<T> futureRead(RosettaDF<T> df,Data.TYPE type); + public<T> Future<T> future(T t); + public Future<Void> future(HttpServletResponse resp, int expected) throws APIException; + + public interface Transfer { + public void transfer(OutputStream os) throws IOException, APIException; + } +} diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/client/Future.java b/cadi/client/src/main/java/org/onap/aaf/cadi/client/Future.java new file mode 100644 index 00000000..2579dc11 --- /dev/null +++ b/cadi/client/src/main/java/org/onap/aaf/cadi/client/Future.java @@ -0,0 +1,33 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.client; + +import org.onap.aaf.cadi.CadiException; + +public abstract class Future<T> { + public T value; + public abstract boolean get(int timeout) throws CadiException; + + public abstract int code(); + public abstract String body(); + public abstract String header(String tag); +}
\ No newline at end of file diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/client/Holder.java b/cadi/client/src/main/java/org/onap/aaf/cadi/client/Holder.java new file mode 100644 index 00000000..c13afc25 --- /dev/null +++ b/cadi/client/src/main/java/org/onap/aaf/cadi/client/Holder.java @@ -0,0 +1,46 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.client; + +/** + * Use to set Variables outside of Anonymous classes. + * @author Jonathan + * + * @param <T> + */ +public class Holder<T> { + private T value; + public Holder(T t) { + value = t; + } + public T set(T t) { + value = t; + return t; + } + + public T get() { + return value; + } + public String toString() { + return value.toString(); + } +} diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/client/Rcli.java b/cadi/client/src/main/java/org/onap/aaf/cadi/client/Rcli.java new file mode 100644 index 00000000..c93d233a --- /dev/null +++ b/cadi/client/src/main/java/org/onap/aaf/cadi/client/Rcli.java @@ -0,0 +1,726 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.client; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.net.URI; +import java.util.Enumeration; + +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.SecuritySetter; +import org.onap.aaf.cadi.client.EClient.Transfer; +import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.misc.env.Data.TYPE; +import org.onap.aaf.misc.env.util.Pool; +import org.onap.aaf.misc.env.util.Pool.Pooled; +import org.onap.aaf.misc.rosetta.env.RosettaDF; + +public abstract class Rcli<CT> { + public static final String FORM_ENCODED = "application/x-www-form-urlencoded"; + public static final String APPL_JSON = "application/json"; + public static final String APPL_XML = "application/xml"; + public static final String BLANK = ""; + public static final String CONTENT_TYPE = "Content-Type"; + public static final String ACCEPT = "Accept"; + + protected static final String POST = "POST"; + protected static final String GET = "GET"; + protected static final String PUT = "PUT"; + protected static final String DELETE = "DELETE"; + protected TYPE type; + protected String apiVersion; + protected int readTimeout = 5000; + protected int connectionTimeout = 3000; + protected URI uri; + private String queryParams, fragment; + public static Pool<byte[]> buffPool = new Pool<byte[]>(new Pool.Creator<byte[]>() { + @Override + public byte[] create() throws APIException { + return new byte[1024]; + } + + @Override + public void destroy(byte[] t) { + } + + @Override + public boolean isValid(byte[] t) { + return true; + } + + @Override + public void reuse(byte[] t) { + } + }); + + + public Rcli() { + super(); + } + + public abstract void setSecuritySetter(SecuritySetter<CT> ss); + public abstract SecuritySetter<CT> getSecuritySetter(); + + + public Rcli<CT> forUser(SecuritySetter<CT> ss) { + Rcli<CT> rv = clone(uri==null?this.uri:uri,ss); + setSecuritySetter(ss); + rv.type = type; + rv.apiVersion = apiVersion; + return rv; + } + + protected abstract Rcli<CT> clone(URI uri, SecuritySetter<CT> ss); + + public abstract void invalidate() throws CadiException; + + public Rcli<CT> readTimeout(int millis) { + readTimeout = millis; + return this; + } + + public Rcli<CT> connectionTimeout(int millis) { + connectionTimeout = millis; + return this; + } + + public Rcli<CT> type(TYPE type) { + this.type=type; + return this; + } + + public Rcli<CT> apiVersion(String apiVersion) { + this.apiVersion = apiVersion; + return this; + } + + public boolean isApiVersion(String prospective) { + return apiVersion.equals(prospective); + } + + + public String typeString(Class<?> cls) { + return "application/"+cls.getSimpleName()+"+"+type.name().toLowerCase()+ + (apiVersion==null?BLANK:";version="+apiVersion); + } + + protected abstract EClient<CT> client() throws CadiException; + + + public<T> Future<T> create(String pathinfo, String contentType, final RosettaDF<T> df, final T t) throws APIException, CadiException { + final String qp = setupParams(pathinfo); + + EClient<CT> client = client(); + client.setMethod(POST); + client.addHeader(CONTENT_TYPE,contentType); + client.setPathInfo(pathinfo); + client.setQueryParams(qp); + client.setFragment(fragment); + client.setPayload(new EClient.Transfer() { + @Override + public void transfer(OutputStream os) throws IOException, APIException { + df.newData().out(type).direct(t,os); + } + }); + client.send(); + queryParams = fragment = null; + return client.futureCreate(df.getTypeClass()); + } + + public<T> Future<T> create(String pathinfo, final RosettaDF<T> df, final T t) throws APIException, CadiException { + final String qp = setupParams(pathinfo); + + EClient<CT> client = client(); + client.setMethod(POST); + client.addHeader(CONTENT_TYPE,typeString(df.getTypeClass())); + client.setPathInfo(pathinfo); + client.setQueryParams(qp); + client.setFragment(fragment); + client.setPayload(new EClient.Transfer() { + @Override + public void transfer(OutputStream os) throws IOException, APIException { + df.newData().out(type).direct(t,os); + } + }); + client.send(); + queryParams = fragment = null; + return client.futureCreate(df.getTypeClass()); + } + + public<T> Future<T> create(String pathinfo, Class<?> cls, final RosettaDF<T> df, final T t) throws APIException, CadiException { + final String qp = setupParams(pathinfo); + + EClient<CT> client = client(); + client.setMethod(POST); + client.addHeader(CONTENT_TYPE,typeString(cls)); + client.setPathInfo(pathinfo); + client.setQueryParams(qp); + client.setFragment(fragment); + client.setPayload(new EClient.Transfer() { + @Override + public void transfer(OutputStream os) throws IOException, APIException { + df.newData().out(type).direct(t,os); + } + }); + client.send(); + queryParams = fragment = null; + return client.futureCreate(df.getTypeClass()); + } + + public<T> Future<T> create(String pathinfo, Class<T> cls) throws APIException, CadiException { + final String qp = setupParams(pathinfo); + + EClient<CT> client = client(); + client.setMethod(POST); + client.addHeader(CONTENT_TYPE,typeString(cls)); + client.setPathInfo(pathinfo); + client.setQueryParams(qp); + client.setFragment(fragment); + client.setPayload(null); + client.send(); + queryParams = fragment = null; + return client.futureCreate(cls); + } + + public Future<Void> create(String pathinfo, String contentType) throws APIException, CadiException { + final String qp = setupParams(pathinfo); + + EClient<CT> client = client(); + client.setMethod(POST); + client.addHeader(CONTENT_TYPE,contentType); + client.setPathInfo(pathinfo); + client.setQueryParams(qp); + client.setFragment(fragment); + client.setPayload(null); + client.send(); + queryParams = fragment = null; + return client.futureCreate(Void.class); + } + + + /** + * Post Data in WWW expected format, with the format tag1=value1&tag2=value2, etc + * Note Shortcut: + * Because typically, you will want to have a variable as value, you can type, as long as tag ends with "=" + * postForm(..., "tag1=value1","tag2=",var2); + * @param pathinfo + * @param df + * @param cls + * @param formParam + * @return + * @throws APIException + * @throws CadiException + */ + public <T> Future<T> postForm(String pathinfo, final RosettaDF<T> df, final String ... formParam) throws APIException, CadiException { + final String qp = setupParams(pathinfo); + + EClient<CT> client = client(); + client.setMethod(POST); + client.addHeader(CONTENT_TYPE,FORM_ENCODED); + switch(type) { + case JSON: + client.addHeader(ACCEPT, APPL_JSON); + break; + case XML: + client.addHeader(ACCEPT, APPL_XML); + break; + default: + break; + } + client.setPathInfo(pathinfo); + client.setQueryParams(qp); + client.setFragment(fragment); + client.setPayload(new Transfer() { + @Override + public void transfer(OutputStream os) throws IOException, APIException { + PrintStream ps; + if(os instanceof PrintStream) { + ps = (PrintStream)os; + } else { + ps = new PrintStream(os); + } + boolean first = true; + for(String fp : formParam) { + if(fp!=null) { + if(first) { + first = false; + } else { + ps.print('&'); + } + if(fp.endsWith("=")) { + first = true; + } + ps.print(fp); + } + } + }}); + client.send(); + queryParams = fragment = null; + return client.futureRead(df,TYPE.JSON); + } + + /** + * Read String, using POST for keyInfo + * + * @param pathinfo + * @param df + * @param t + * @param resp + * @return + * @throws APIException + * @throws CadiException + */ + public<T> Future<String> readPost(String pathinfo, final RosettaDF<T> df, final T t) throws APIException, CadiException { + final String qp = setupParams(pathinfo); + + EClient<CT> client = client(); + client.setMethod(POST); + client.addHeader(CONTENT_TYPE,typeString(df.getTypeClass())); + client.setPathInfo(pathinfo); + client.setQueryParams(qp); + client.setFragment(fragment); + client.setPayload(new EClient.Transfer() { + @Override + public void transfer(OutputStream os) throws IOException, APIException { + df.newData().out(type).direct(t,os); + } + }); + client.send(); + queryParams = fragment = null; + return client.futureReadString(); + } + + /** + * Read using POST for keyInfo, responding with marshaled Objects + * + * @param pathinfo + * @param df + * @param t + * @param resp + * @return + * @throws APIException + * @throws CadiException + */ + public<T,R> Future<R> readPost(String pathinfo, final RosettaDF<T> df, final T t, final RosettaDF<R> resp) throws APIException, CadiException { + final String qp = setupParams(pathinfo); + + EClient<CT> client = client(); + client.setMethod(POST); + client.addHeader(CONTENT_TYPE,typeString(df.getTypeClass())); + client.setPathInfo(pathinfo); + client.setQueryParams(qp); + client.setFragment(fragment); + client.setPayload(new EClient.Transfer() { + @Override + public void transfer(OutputStream os) throws IOException, APIException { + df.newData().out(type).direct(t,os); + } + }); + client.send(); + queryParams = fragment = null; + return client.futureRead(resp,resp.getOutType()); + } + + public Future<String> readPost(String pathinfo, String contentType, String ... headers) throws CadiException, APIException { + final String qp = setupParams(pathinfo); + + EClient<CT> client = client(); + client.setMethod(POST); + client.addHeader(CONTENT_TYPE,contentType); + client.setPathInfo(pathinfo); + client.setQueryParams(qp); + client.setFragment(fragment); + client.setPayload(new EClient.Transfer() { + @Override + public void transfer(OutputStream os) throws IOException, APIException { + }}); + client.send(); + queryParams = fragment = null; + return client.futureReadString(); + } + + public Future<String> read(String pathinfo, String accept, String ... headers) throws APIException, CadiException { + final String qp = setupParams(pathinfo); + + EClient<CT> client = client(); + client.setMethod(GET); + client.addHeader(ACCEPT, accept); + + for(int i=1;i<headers.length;i=i+2) { + client.addHeader(headers[i-1],headers[i]); + } + client.setQueryParams(qp); + client.setFragment(fragment); + + client.setPathInfo(pathinfo); + + client.setPayload(null); + client.send(); + queryParams = fragment = null; + return client.futureReadString(); + } + + public<T> Future<T> read(String pathinfo, String accept, RosettaDF<T> df, String ... headers) throws APIException, CadiException { + final String qp = setupParams(pathinfo); + + EClient<CT> client = client(); + client.setMethod(GET); + client.addHeader(ACCEPT, accept); + for(int i=1;i<headers.length;i=i+2) { + client.addHeader(headers[i-1],headers[i]); + } + client.setQueryParams(qp); + client.setFragment(fragment); + client.setPathInfo(pathinfo); + + client.setPayload(null); + client.send(); + queryParams = fragment = null; + return client.futureRead(df,type); + } + + public<T> Future<T> read(String pathinfo, RosettaDF<T> df,String ... headers) throws APIException, CadiException { + final String qp = setupParams(pathinfo); + + EClient<CT> client = client(); + client.setMethod(GET); + client.addHeader(ACCEPT, typeString(df.getTypeClass())); + for(int i=1;i<headers.length;i=i+2) { + client.addHeader(headers[i-1],headers[i]); + } + client.setQueryParams(qp); + client.setFragment(fragment); + client.setPathInfo(pathinfo); + + client.setPayload(null); + client.send(); + queryParams = fragment = null; + return client.futureRead(df,type); + } + + public<T> Future<T> read(String pathinfo, Class<?> cls, RosettaDF<T> df) throws APIException, CadiException { + final String qp = setupParams(pathinfo); + + EClient<CT> client = client(); + client.setMethod(GET); + client.addHeader(ACCEPT, typeString(cls)); + client.setQueryParams(qp); + client.setFragment(fragment); + client.setPathInfo(pathinfo); + + client.setPayload(null); + client.send(); + queryParams = fragment = null; + return client.futureRead(df,type); + } + + public<T> Future<T> update(String pathinfo, String contentType, final RosettaDF<T> df, final T t) throws APIException, CadiException { + final String qp = setupParams(pathinfo); + + EClient<CT> client = client(); + client.setMethod(PUT); + client.addHeader(CONTENT_TYPE,contentType); + client.setQueryParams(qp); + client.setFragment(fragment); + client.setPathInfo(pathinfo); + client.setPayload(new EClient.Transfer() { + @Override + public void transfer(OutputStream os) throws IOException, APIException { + df.newData().out(type).direct(t,os); + } + }); + client.send(); + queryParams = fragment = null; + return client.future(t); + } + + public<T> Future<String> updateRespondString(String pathinfo, final RosettaDF<T> df, final T t) throws APIException, CadiException { + final String qp = setupParams(pathinfo); + + EClient<CT> client = client(); + client.setMethod(PUT); + client.addHeader(CONTENT_TYPE, typeString(df.getTypeClass())); + client.setQueryParams(qp); + client.setFragment(fragment); + client.setPathInfo(pathinfo); + client.setPayload(new EClient.Transfer() { + @Override + public void transfer(OutputStream os) throws IOException, APIException { + df.newData().out(type).direct(t,os); + } + }); + client.send(); + queryParams = fragment = null; + return client.futureReadString(); + } + + + public<T> Future<T> update(String pathinfo, final RosettaDF<T> df, final T t) throws APIException, CadiException { + final String qp = setupParams(pathinfo); + + EClient<CT> client = client(); + client.setMethod(PUT); + client.addHeader(CONTENT_TYPE, typeString(df.getTypeClass())); + client.setQueryParams(qp); + client.setFragment(fragment); + client.setPathInfo(pathinfo); + client.setPayload(new EClient.Transfer() { + @Override + public void transfer(OutputStream os) throws IOException, APIException { + df.newData().out(type).direct(t,os); + } + }); + client.send(); + queryParams = fragment = null; + return client.future(t); + } + + public<T> Future<T> update(String pathinfo, Class<?> cls, final RosettaDF<T> df, final T t) throws APIException, CadiException { + final String qp = setupParams(pathinfo); + + EClient<CT> client = client(); + client.setMethod(PUT); + client.addHeader(CONTENT_TYPE, typeString(cls)); + client.setQueryParams(qp); + client.setFragment(fragment); + client.setPathInfo(pathinfo); + client.setPayload(new EClient.Transfer() { + @Override + public void transfer(OutputStream os) throws IOException, APIException { + df.newData().out(type).direct(t,os); + } + }); + client.send(); + queryParams = fragment = null; + return client.future(t); + } + + /** + * A method to update with a VOID + * @param pathinfo + * @param resp + * @param expected + * @return + * @throws APIException + * @throws CadiException + */ + public<T> Future<Void> update(String pathinfo) throws APIException, CadiException { + final String qp = setupParams(pathinfo); + + EClient<CT> client = client(); + client.setMethod(PUT); + client.addHeader(CONTENT_TYPE, typeString(Void.class)); + client.setQueryParams(qp); + client.setFragment(fragment); + client.setPathInfo(pathinfo); +// client.setPayload(new EClient.Transfer() { +// @Override +// public void transfer(OutputStream os) throws IOException, APIException { +// } +// }); + client.send(); + queryParams = fragment = null; + return client.future(null); + } + + public<T> Future<T> delete(String pathinfo, String contentType, final RosettaDF<T> df, final T t) throws APIException, CadiException { + final String qp = setupParams(pathinfo); + + EClient<CT> client = client(); + client.setMethod(DELETE); + client.addHeader(CONTENT_TYPE, contentType); + client.setQueryParams(qp); + client.setFragment(fragment); + client.setPathInfo(pathinfo); + client.setPayload(new EClient.Transfer() { + @Override + public void transfer(OutputStream os) throws IOException, APIException { + df.newData().out(type).direct(t,os); + } + }); + client.send(); + queryParams = fragment = null; + return client.future(t); + } + + public<T> Future<T> delete(String pathinfo, Class<?> cls, final RosettaDF<T> df, final T t) throws APIException, CadiException { + final String qp = setupParams(pathinfo); + + EClient<CT> client = client(); + client.setMethod(DELETE); + client.addHeader(CONTENT_TYPE, typeString(cls)); + client.setQueryParams(qp); + client.setFragment(fragment); + client.setPathInfo(pathinfo); + client.setPayload(new EClient.Transfer() { + @Override + public void transfer(OutputStream os) throws IOException, APIException { + df.newData().out(type).direct(t,os); + } + }); + client.send(); + queryParams = fragment = null; + return client.future(t); + } + + public<T> Future<T> delete(String pathinfo, final RosettaDF<T> df, final T t) throws APIException, CadiException { + final String qp = setupParams(pathinfo); + + EClient<CT> client = client(); + client.setMethod(DELETE); + client.addHeader(CONTENT_TYPE, typeString(df.getTypeClass())); + client.setQueryParams(qp); + client.setFragment(fragment); + client.setPathInfo(pathinfo); + client.setPayload(new EClient.Transfer() { + @Override + public void transfer(OutputStream os) throws IOException, APIException { + df.newData().out(type).direct(t,os); + } + }); + + client.send(); + queryParams = fragment = null; + return client.future(t); + } + + + public<T> Future<T> delete(String pathinfo, Class<T> cls) throws APIException, CadiException { + final String qp = setupParams(pathinfo); + + EClient<CT> client = client(); + client.setMethod(DELETE); + client.addHeader(CONTENT_TYPE, typeString(cls)); + client.setQueryParams(qp); + client.setFragment(fragment); + client.setPathInfo(pathinfo); + client.setPayload(null); + client.send(); + queryParams = fragment = null; + return client.future((T)null); + } + + public Future<Void> delete(String pathinfo, String contentType) throws APIException, CadiException { + final String qp = setupParams(pathinfo); + + EClient<CT> client = client(); + client.setMethod(DELETE); + client.addHeader(CONTENT_TYPE, contentType); + client.setQueryParams(qp); + client.setFragment(fragment); + client.setPathInfo(pathinfo); + client.setPayload(null); + client.send(); + queryParams = fragment = null; + return client.future(null); + } + + public Future<Void> transfer(final HttpServletRequest req, final HttpServletResponse resp, final String pathParam, final int expected) throws CadiException, APIException { + EClient<CT> client = client(); + URI uri; + try { + uri = new URI(req.getRequestURI()); + } catch (Exception e) { + throw new CadiException("Invalid incoming URI",e); + } + String name; + for(Enumeration<String> en = req.getHeaderNames();en.hasMoreElements();) { + name = en.nextElement(); + client.addHeader(name,req.getHeader(name)); + } + client.setQueryParams(req.getQueryString()); + client.setFragment(uri.getFragment()); + client.setPathInfo(pathParam); + String meth = req.getMethod(); + client.setMethod(meth); + if(!"GET".equals(meth)) { + client.setPayload(new EClient.Transfer() { + @Override + public void transfer(OutputStream os) throws IOException, APIException { + final ServletInputStream is = req.getInputStream(); + int read; + // reuse Buffers + Pooled<byte[]> pbuff = buffPool.get(); + try { + while((read=is.read(pbuff.content))>=0) { + os.write(pbuff.content,0,read); + } + } finally { + pbuff.done(); + } + } + }); + } + client.send(); + return client.future(resp, expected); + } + + private String setupParams(String pathinfo) { + final String qp; + if(pathinfo==null) { + qp=queryParams; + } else { + final int idx = pathinfo.indexOf('?'); + if(idx>=0) { + qp=pathinfo.substring(idx+1); + pathinfo=pathinfo.substring(0,idx); + } else { + qp=queryParams; + } + } + return qp; + } + + public String toString() { + return uri.toString(); + } + + /** + * @param queryParams the queryParams to set + * @return + */ + public Rcli<CT> setQueryParams(String queryParams) { + this.queryParams = queryParams; + return this; + } + + + /** + * @param fragment the fragment to set + * @return + */ + public Rcli<CT> setFragment(String fragment) { + this.fragment = fragment; + return this; + } + + public URI getURI() { + return uri; + } + +}
\ No newline at end of file diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/client/Result.java b/cadi/client/src/main/java/org/onap/aaf/cadi/client/Result.java new file mode 100644 index 00000000..fecb847b --- /dev/null +++ b/cadi/client/src/main/java/org/onap/aaf/cadi/client/Result.java @@ -0,0 +1,60 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.client; + +public class Result<T> { + public final int code; + public final T value; + public final String error; + + private Result(int code, T value, String error) { + this.code = code; + this.value = value; + this.error = error; + } + + public static<T> Result<T> ok(int code,T t) { + return new Result<T>(code,t,null); + } + + public static<T> Result<T> err(int code,String body) { + return new Result<T>(code,null,body); + } + + public static<T> Result<T> err(Result<?> r) { + return new Result<T>(r.code,null,r.error); + } + + public boolean isOK() { + return error==null; + } + + public String toString() { + StringBuilder sb = new StringBuilder("Code: "); + sb.append(code); + if(error!=null) { + sb.append(" = "); + sb.append(error); + } + return sb.toString(); + } +}
\ No newline at end of file diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/client/Retryable.java b/cadi/client/src/main/java/org/onap/aaf/cadi/client/Retryable.java new file mode 100644 index 00000000..8208efe1 --- /dev/null +++ b/cadi/client/src/main/java/org/onap/aaf/cadi/client/Retryable.java @@ -0,0 +1,71 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.client; + +import java.net.ConnectException; + +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.Locator; +import org.onap.aaf.misc.env.APIException; + +/** + * + * @author Jonathan + * + * @param <RT> + * @param <RET> + */ +public abstract class Retryable<RET> { + // be able to hold state for consistent Connections. Not required for all connection types. + public Rcli<?> lastClient; + private Locator.Item item; + + public Retryable() { + lastClient = null; + item = null; + } + + public Retryable(Retryable<?> ret) { + lastClient = ret.lastClient; + item = ret.item; + } + + public Locator.Item item(Locator.Item item) { + lastClient = null; + this.item = item; + return item; + } + public Locator.Item item() { + return item; + } + + public abstract RET code(Rcli<?> client) throws CadiException, ConnectException, APIException; + + /** + * Note, Retryable is tightly coupled to the Client Utilizing. It will not be the wrong type. + * @return + */ + @SuppressWarnings("unchecked") + public <CLIENT> Rcli<CLIENT> lastClient() { + return (Rcli<CLIENT>)lastClient; + } +} diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/http/HAuthorizationHeader.java b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HAuthorizationHeader.java new file mode 100644 index 00000000..787c5c29 --- /dev/null +++ b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HAuthorizationHeader.java @@ -0,0 +1,54 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.http; + +import java.io.IOException; +import java.net.HttpURLConnection; + +import javax.net.ssl.HttpsURLConnection; + +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.client.AbsAuthentication; +import org.onap.aaf.cadi.config.SecurityInfoC; + +public class HAuthorizationHeader extends AbsAuthentication<HttpURLConnection> { + + public HAuthorizationHeader(SecurityInfoC<HttpURLConnection> si, String user, String headValue) throws IOException { + super(si,user,headValue==null?null:headValue.getBytes()); + } + + @Override + public void setSecurity(HttpURLConnection huc) throws CadiException { + if(isDenied()) { + throw new CadiException(REPEAT_OFFENDER); + } + try { + huc.addRequestProperty(AUTHORIZATION , headValue()); + } catch (IOException e) { + throw new CadiException(e); + } + if(securityInfo!=null && huc instanceof HttpsURLConnection) { + securityInfo.setSocketFactoryOn((HttpsURLConnection)huc); + } + } + +} diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/http/HBasicAuthSS.java b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HBasicAuthSS.java new file mode 100644 index 00000000..9e86c7fb --- /dev/null +++ b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HBasicAuthSS.java @@ -0,0 +1,68 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.http; + +import java.io.IOException; +import java.net.HttpURLConnection; + +import org.onap.aaf.cadi.Symm; +import org.onap.aaf.cadi.client.BasicAuth; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.config.SecurityInfoC; +import org.onap.aaf.cadi.principal.BasicPrincipal; + +public class HBasicAuthSS extends HAuthorizationHeader implements BasicAuth { + public HBasicAuthSS(SecurityInfoC<HttpURLConnection> si, String user, String password) throws IOException { + super(si, user, "Basic " + Symm.base64noSplit.encode(user + ':' + password)); + } + + public HBasicAuthSS(SecurityInfoC<HttpURLConnection> si) throws IOException { + this(si,si.access.getProperty(Config.AAF_APPID, null), + si.access.decrypt(si.access.getProperty(Config.AAF_APPPASS, null), false)); + } + + public HBasicAuthSS(SecurityInfoC<HttpURLConnection> si, boolean setDefault) throws IOException { + this(si,si.access.getProperty(Config.AAF_APPID, null), + si.access.decrypt(si.access.getProperty(Config.AAF_APPPASS, null), false),setDefault); + } + + + public HBasicAuthSS(SecurityInfoC<HttpURLConnection> si, String user, String pass, boolean asDefault) throws IOException { + this(si, user,pass); + if(asDefault) { + si.set(this); + } + } + + public HBasicAuthSS(BasicPrincipal bp, SecurityInfoC<HttpURLConnection> si) throws IOException { + this(si, bp.getName(),new String(bp.getCred())); + } + + public HBasicAuthSS(BasicPrincipal bp, SecurityInfoC<HttpURLConnection> si, boolean asDefault) throws IOException { + this(si, bp.getName(),new String(bp.getCred())); + if(asDefault) { + si.set(this); + } + } + + +} diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/http/HClient.java b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HClient.java new file mode 100644 index 00000000..456184c3 --- /dev/null +++ b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HClient.java @@ -0,0 +1,436 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.http; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.Reader; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; + +import javax.servlet.http.HttpServletResponse; + +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.SecuritySetter; +import org.onap.aaf.cadi.client.EClient; +import org.onap.aaf.cadi.client.Future; +import org.onap.aaf.cadi.client.Rcli; +import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.misc.env.Data; +import org.onap.aaf.misc.env.Data.TYPE; +import org.onap.aaf.misc.env.util.Pool.Pooled; +import org.onap.aaf.misc.rosetta.env.RosettaDF; + +/** + * Low Level Http Client Mechanism. Chances are, you want the high level "HRcli" + * for Rosetta Object Translation + * + * @author Jonathan + * + */ +public class HClient implements EClient<HttpURLConnection> { + private URI uri; + private ArrayList<Header> headers; + private String meth; + private String pathinfo; + private String query; + private String fragment; + private Transfer transfer; + private SecuritySetter<HttpURLConnection> ss; + private HttpURLConnection huc; + private int connectTimeout; + + public HClient(SecuritySetter<HttpURLConnection> ss, URI uri,int connectTimeout) throws LocatorException { + if (uri == null) { + throw new LocatorException("No Service available to call"); + } + this.uri = uri; + this.ss = ss; + this.connectTimeout = connectTimeout; + pathinfo = query = fragment = null; + } + + @Override + public void setMethod(String meth) { + this.meth = meth; + } + + @Override + public void setPathInfo(String pathinfo) { + this.pathinfo = pathinfo; + } + + @Override + public void setPayload(Transfer transfer) { + this.transfer = transfer; + } + + @Override + public void addHeader(String tag, String value) { + if (headers == null) + headers = new ArrayList<Header>(); + headers.add(new Header(tag, value)); + } + + @Override + public void setQueryParams(String q) { + query = q; + } + + @Override + public void setFragment(String f) { + fragment = f; + } + + @Override + public void send() throws APIException { + try { + // Build URL from given URI plus current Settings + if(uri.getPath()==null) { + throw new APIException("Invalid URL entered for HClient"); + } + StringBuilder pi=null; + if(pathinfo!=null) { // additional pathinfo + pi = new StringBuilder(uri.getPath()); + if(!pathinfo.startsWith("/")) { + pi.append('/'); + } + pi.append(pathinfo); + } + pathinfo=null; + query=null; + fragment=null; + //huc = (HttpURLConnection) url.openConnection(); + huc = getConnection(uri, pi); + huc.setRequestMethod(meth); + if(ss!=null) { + ss.setSecurity(huc); + } + if (headers != null) + for (Header d : headers) { + huc.addRequestProperty(d.tag, d.value); + } + huc.setDoInput(true); + huc.setDoOutput(true); + huc.setUseCaches(false); + huc.setConnectTimeout(connectTimeout); + huc.connect(); + if (transfer != null) { + transfer.transfer(huc.getOutputStream()); + } + // TODO other settings? There's a bunch here. + } catch (Exception e) { + throw new APIException(e); + } finally { // ensure all these are reset after sends + meth=pathinfo=null; + if(headers!=null) { + headers.clear(); + } + pathinfo = query = fragment = ""; + } + } + + public URI getURI() { + return uri; + } + + public int timeout() { + return connectTimeout; + } + + protected HttpURLConnection getConnection(URI uri, StringBuilder pi) throws IOException, URISyntaxException { + URL url = new URI( + uri.getScheme(), + uri.getUserInfo(), + uri.getHost(), + uri.getPort(), + pi==null?uri.getPath():pi.toString(), + query, + fragment).toURL(); + return (HttpURLConnection) url.openConnection(); + } + + public abstract class HFuture<T> extends Future<T> { + protected HttpURLConnection huc; + protected int respCode; + protected IOException exception; + protected StringBuilder errContent; + + public HFuture(final HttpURLConnection huc) { + this.huc = huc; + } + + protected boolean evalInfo(HttpURLConnection huc) throws APIException, IOException{ + return respCode == 200; + }; + + @Override + public final boolean get(int timeout) throws CadiException { + try { + huc.setReadTimeout(timeout); + respCode = huc.getResponseCode(); + ss.setLastResponse(respCode); + if(evalInfo(huc)) { + return true; + } else { + extractError(); + return false; + } + } catch (IOException | APIException e) { + throw new CadiException(e); + } finally { + close(); + } + } + + private void extractError() { + InputStream is = huc.getErrorStream(); + try { + if(is==null) { + is = huc.getInputStream(); + } + if(is!=null) { + errContent = new StringBuilder(); + int c; + while((c=is.read())>=0) { + errContent.append((char)c); + } + } + } catch (IOException e) { + exception = e; + } + } + + // Typically only used by Read + public StringBuilder inputStreamToString(InputStream is) { + // Avoids Carriage returns, and is reasonably efficient, given + // the buffer reads. + try { + StringBuilder sb = new StringBuilder(); + Reader rdr = new InputStreamReader(is); + try { + char[] buf = new char[256]; + int read; + while ((read = rdr.read(buf)) >= 0) { + sb.append(buf, 0, read); + } + } finally { + rdr.close(); + } + return sb; + } catch (IOException e) { + exception = e; + return null; + } + } + + + @Override + public int code() { + return respCode; + } + + public HttpURLConnection huc() { + return huc; + } + + public IOException exception() { + return exception; + } + + @Override + public String header(String tag) { + return huc.getHeaderField(tag); + } + + public void close() { + if(huc!=null) { + huc.disconnect(); + } + } + } + + @Override + public <T> Future<T> futureCreate(Class<T> t) { + return new HFuture<T>(huc) { + public boolean evalInfo(HttpURLConnection huc) { + return respCode==201; + } + + @Override + public String body() { + if (errContent != null) { + return errContent.toString(); + } + return ""; + } + }; + } + + @Override + public Future<String> futureReadString() { + return new HFuture<String>(huc) { + public boolean evalInfo(HttpURLConnection huc) throws IOException { + if (respCode == 200) { + StringBuilder sb = inputStreamToString(huc.getInputStream()); + if (sb != null) { + value = sb.toString(); + } + return true; + } + return false; + } + + @Override + public String body() { + if (value != null) { + return value; + } else if (errContent != null) { + return errContent.toString(); + } + return ""; + } + + }; + } + + @Override + public <T> Future<T> futureRead(final RosettaDF<T> df, final TYPE type) { + return new HFuture<T>(huc) { + private Data<T> data; + + public boolean evalInfo(HttpURLConnection huc) throws APIException, IOException { + if (respCode == 200) { + data = df.newData().in(type).load(huc.getInputStream()); + value = data.asObject(); + return true; + } + return false; + } + + @Override + public String body() { + if (data != null) { + try { + return data.asString(); + } catch (APIException e) { + } + } else if (errContent != null) { + return errContent.toString(); + } + return ""; + } + }; + } + + @Override + public <T> Future<T> future(final T t) { + return new HFuture<T>(huc) { + public boolean evalInfo(HttpURLConnection huc) { + if (respCode == 200) { + value = t; + return true; + } + return false; + } + + @Override + public String body() { + if (errContent != null) { + return errContent.toString(); + } + return Integer.toString(respCode); + } + }; + } + + @Override + public Future<Void> future(final HttpServletResponse resp, final int expected) throws APIException { + return new HFuture<Void>(huc) { + public boolean evalInfo(HttpURLConnection huc) throws IOException, APIException { + resp.setStatus(respCode); + int read; + InputStream is; + OutputStream os = resp.getOutputStream(); + if(respCode==expected) { + is = huc.getInputStream(); + // reuse Buffers + Pooled<byte[]> pbuff = Rcli.buffPool.get(); + try { + while((read=is.read(pbuff.content))>=0) { + os.write(pbuff.content,0,read); + } + } finally { + pbuff.done(); + } + return true; + } else { + is = huc.getErrorStream(); + if(is==null) { + is = huc.getInputStream(); + } + if(is!=null) { + errContent = new StringBuilder(); + Pooled<byte[]> pbuff = Rcli.buffPool.get(); + try { + while((read=is.read(pbuff.content))>=0) { + os.write(pbuff.content,0,read); + } + } finally { + pbuff.done(); + } + } + } + return false; + } + + @Override + public String body() { + return errContent==null?null:errContent.toString(); + } + }; + } + + private static class Header { + public final String tag; + public final String value; + + public Header(String t, String v) { + this.tag = t; + this.value = v; + } + + public String toString() { + return tag + '=' + value; + } + } + + public String toString() { + return "HttpURLConnection Client configured to " + uri.toString(); + } +} diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/http/HMangr.java b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HMangr.java new file mode 100644 index 00000000..772a499c --- /dev/null +++ b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HMangr.java @@ -0,0 +1,242 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.http; + +import java.net.ConnectException; +import java.net.HttpURLConnection; +import java.net.SocketException; +import java.net.URI; +import java.net.URISyntaxException; + +import javax.net.ssl.SSLHandshakeException; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.Locator; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.SecuritySetter; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.Locator.Item; +import org.onap.aaf.cadi.client.Rcli; +import org.onap.aaf.cadi.client.Retryable; +import org.onap.aaf.misc.env.APIException; + +public class HMangr { + private String apiVersion; + private int readTimeout, connectionTimeout; + public final Locator<URI> loc; + private Access access; + + public HMangr(Access access, Locator<URI> loc) throws LocatorException { + readTimeout = 10000; + connectionTimeout=3000; + if(loc == null) { + throw new LocatorException("Null Locator passed"); + } + this.loc = loc; + this.access = access; + } + + /** + * Reuse the same service. This is helpful for multiple calls that change service side cached data so that + * there is not a speed issue. + * + * If the service goes down, another service will be substituted, if available. + * + * @param access + * @param loc + * @param ss + * @param item + * @param retryable + * @return + * @throws URISyntaxException + * @throws Exception + */ + public<RET> RET same(SecuritySetter<HttpURLConnection> ss, Retryable<RET> retryable) throws APIException, CadiException, LocatorException { + RET ret = null; + boolean retry = true; + Rcli<HttpURLConnection> client = retryable.lastClient(); + try { + do { + Item item; + // if no previous state, get the best + if(retryable.item()==null) { + item = loc.best(); + if(item==null) { + throw new LocatorException("No Services Found for " + loc); + } + retryable.item(item); + retryable.lastClient = null; + } + if(client==null) { + item = retryable.item(); + URI uri=loc.get(item); + if(uri==null) { + loc.invalidate(retryable.item()); + if(loc.hasItems()) { + retryable.item(loc.next(retryable.item())); + continue; + } else { + throw new LocatorException("No clients available for " + loc.toString()); + } + } + client = new HRcli(this, uri,item,ss) + .connectionTimeout(connectionTimeout) + .readTimeout(readTimeout) + .apiVersion(apiVersion); + } else { + client.setSecuritySetter(ss); + } + + retry = false; + try { + ret = retryable.code(client); + } catch (APIException | CadiException e) { + item = retryable.item(); + loc.invalidate(item); + retryable.item(loc.next(item)); + try { + Throwable ec = e.getCause(); + if(ec instanceof java.net.ConnectException) { + if(client!=null && loc.hasItems()) { + access.log(Level.WARN,"Connection refused, trying next available service"); + retry = true; + } else { + throw new CadiException("Connection refused, no more services to try"); + } + } else if(ec instanceof java.net.SocketException) { + if(client!=null && loc.hasItems()) { + access.log(Level.WARN,"Socket prematurely closed, trying next available service"); + retry = true; + } else { + throw new CadiException("Socket prematurely closed, no more services to try"); + } + } else if(ec instanceof SocketException) { + if("java.net.SocketException: Connection reset".equals(ec.getMessage())) { + access.log(Level.ERROR, ec.getMessage(), " can mean Certificate Expiration or TLS Protocol issues"); + } + retryable.item(null); + throw e; + } else { + retryable.item(null); + throw e; + } + } finally { + client = null; + } + } catch (ConnectException e) { + item = retryable.item(); + loc.invalidate(item); + retryable.item(loc.next(item)); + } + } while(retry); + } finally { + retryable.lastClient = client; + } + return ret; + } + + + public<RET> RET best(SecuritySetter<HttpURLConnection> ss, Retryable<RET> retryable) throws LocatorException, CadiException, APIException { + retryable.item(loc.best()); + return same(ss,retryable); + } + public<RET> RET all(SecuritySetter<HttpURLConnection> ss, Retryable<RET> retryable) throws LocatorException, CadiException, APIException { + return oneOf(ss,retryable,true,null); + } + + public<RET> RET all(SecuritySetter<HttpURLConnection> ss, Retryable<RET> retryable,boolean notify) throws LocatorException, CadiException, APIException { + return oneOf(ss,retryable,notify,null); + } + + public<RET> RET oneOf(SecuritySetter<HttpURLConnection> ss, Retryable<RET> retryable,boolean notify,String host) throws LocatorException, CadiException, APIException { + RET ret = null; + // make sure we have all current references: + loc.refresh(); + for(Item li=loc.first();li!=null;li=loc.next(li)) { + URI uri=loc.get(li); + if(host!=null && !host.equals(uri.getHost())) { + break; + } + try { + ret = retryable.code(new HRcli(this,uri,li,ss)); + access.log(Level.DEBUG,"Success calling",uri,"during call to all services"); + } catch (APIException | CadiException e) { + Throwable t = e.getCause(); + if(t!=null && t instanceof ConnectException) { + loc.invalidate(li); + access.log(Level.ERROR,"Connection to",uri,"refused during call to all services"); + } else if(t instanceof SSLHandshakeException) { + access.log(Level.ERROR,t.getMessage()); + loc.invalidate(li); + } else if(t instanceof SocketException) { + if("java.net.SocketException: Connection reset".equals(t.getMessage())) { + access.log(Level.ERROR, t.getMessage(), " can mean Certificate Expiration or TLS Protocol issues"); + } + retryable.item(null); + throw e; + } else { + throw e; + } + } catch (ConnectException e) { + loc.invalidate(li); + access.log(Level.ERROR,"Connection to",uri,"refused during call to all services"); + } + } + + if(ret == null && notify) + throw new LocatorException("No available clients to call"); + return ret; + } + + + public void close() { + // TODO Anything here? + } + + public HMangr readTimeout(int timeout) { + this.readTimeout = timeout; + return this; + } + + public int readTimeout() { + return readTimeout; + } + + public void connectionTimeout(int t) { + connectionTimeout = t; + } + + public int connectionTimeout() { + return connectionTimeout; + } + + public HMangr apiVersion(String version) { + apiVersion = version; + return this; + } + + public String apiVersion() { + return apiVersion; + } + +} diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/http/HNoAuthSS.java b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HNoAuthSS.java new file mode 100644 index 00000000..b857f3ad --- /dev/null +++ b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HNoAuthSS.java @@ -0,0 +1,45 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.http; + +import java.io.IOException; +import java.net.HttpURLConnection; + +import javax.net.ssl.HttpsURLConnection; + +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.client.AbsAuthentication; +import org.onap.aaf.cadi.config.SecurityInfoC; + +public class HNoAuthSS extends AbsAuthentication<HttpURLConnection> { + public HNoAuthSS(SecurityInfoC<HttpURLConnection> si) throws IOException { + super(si,"noauth",null); + } + + @Override + public void setSecurity(HttpURLConnection client) throws CadiException { + if(securityInfo!=null && client instanceof HttpsURLConnection) { + securityInfo.setSocketFactoryOn((HttpsURLConnection)client); + } + } + +} diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/http/HRcli.java b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HRcli.java new file mode 100644 index 00000000..908b895b --- /dev/null +++ b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HRcli.java @@ -0,0 +1,130 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.http; + +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URISyntaxException; + +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.SecuritySetter; +import org.onap.aaf.cadi.Locator.Item; +import org.onap.aaf.cadi.client.EClient; +import org.onap.aaf.cadi.client.Rcli; +import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.misc.env.Data.TYPE; + +/** + * Rosetta Client + * + * JAXB defined JSON or XML over HTTP/S + * + * @author Jonathan + * + * @param <T> + */ +public class HRcli extends Rcli<HttpURLConnection> { + private HMangr hman; + private Item item; + private SecuritySetter<HttpURLConnection> ss; + + public HRcli(HMangr hman, Item locItem, SecuritySetter<HttpURLConnection> secSet) throws URISyntaxException, LocatorException { + item=locItem; + uri=hman.loc.get(locItem); + this.hman = hman; + ss=secSet; + type = TYPE.JSON; + apiVersion = hman.apiVersion(); + } + + public HRcli(HMangr hman, URI uri, Item locItem, SecuritySetter<HttpURLConnection> secSet) { + item=locItem; + this.uri = uri; + this.hman = hman; + ss=secSet; + type = TYPE.JSON; + apiVersion = hman.apiVersion(); + } + + @Override + protected HRcli clone(URI uri, SecuritySetter<HttpURLConnection> ss) { + return new HRcli(hman,uri,item,ss); + } + + + + /** + * + * @return + * @throws APIException + * @throws DME2Exception + */ + protected EClient<HttpURLConnection> client() throws CadiException { + try { + if(uri==null) { + Item item = hman.loc.best(); + if(item==null) { + throw new CadiException("No service available for " + hman.loc.toString()); + } + uri = hman.loc.get(item); + } + return new HClient(ss,uri,connectionTimeout); + } catch (Exception e) { + throw new CadiException(e); + } + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.client.Rcli#setSecuritySetter(org.onap.aaf.cadi.SecuritySetter) + */ + @Override + public void setSecuritySetter(SecuritySetter<HttpURLConnection> ss) { + this.ss = ss; + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.client.Rcli#getSecuritySetter() + */ + @Override + public SecuritySetter<HttpURLConnection> getSecuritySetter() { + return ss; + } + + public void invalidate() throws CadiException { + try { + hman.loc.invalidate(item); + } catch (Exception e) { + throw new CadiException(e); + } + } + + public HRcli setManager(HMangr hman) { + this.hman = hman; + return this; + } + + public String toString() { + return uri.toString(); + } + +} diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/http/HTokenSS.java b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HTokenSS.java new file mode 100644 index 00000000..873e0fe7 --- /dev/null +++ b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HTokenSS.java @@ -0,0 +1,34 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.http; + +import java.io.IOException; +import java.net.HttpURLConnection; + +import org.onap.aaf.cadi.config.SecurityInfoC; + +public class HTokenSS extends HAuthorizationHeader { + public HTokenSS(final SecurityInfoC<HttpURLConnection> si, final String client_id, final String token) throws IOException { + super(si, client_id,"Bearer " + token); + } + +} diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/http/HTransferSS.java b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HTransferSS.java new file mode 100644 index 00000000..d19c42e9 --- /dev/null +++ b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HTransferSS.java @@ -0,0 +1,64 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.http; + +import java.io.IOException; +import java.net.HttpURLConnection; + +import javax.net.ssl.HttpsURLConnection; + +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.client.AbsTransferSS; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.config.SecurityInfoC; +import org.onap.aaf.cadi.principal.TaggedPrincipal; + + +public class HTransferSS extends AbsTransferSS<HttpURLConnection> { + public HTransferSS(TaggedPrincipal principal, String app) throws IOException { + super(principal, app); + } + + public HTransferSS(TaggedPrincipal principal, String app, SecurityInfoC<HttpURLConnection> si) { + super(principal, app, si); + } + + @Override + public void setSecurity(HttpURLConnection huc) throws CadiException { + if(defSS==null) { + throw new CadiException("Need App Credentials to send message"); + } + defSS.setSecurity(huc); + if(value!=null) { + huc.addRequestProperty(Config.CADI_USER_CHAIN, value); + } + if(securityInfo!=null) { + securityInfo.setSocketFactoryOn((HttpsURLConnection)huc); + } + } + + @Override + public int setLastResponse(int respCode) { + return 0; + } + +} diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/http/HX509SS.java b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HX509SS.java new file mode 100644 index 00000000..9d555f62 --- /dev/null +++ b/cadi/client/src/main/java/org/onap/aaf/cadi/http/HX509SS.java @@ -0,0 +1,152 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.http; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.security.PrivateKey; +import java.security.SecureRandom; +import java.security.Signature; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.X509KeyManager; + +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.SecuritySetter; +import org.onap.aaf.cadi.Symm; +import org.onap.aaf.cadi.client.AbsAuthentication; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.config.SecurityInfoC; +import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.misc.env.util.Chrono; + + +public class HX509SS implements SecuritySetter<HttpURLConnection> { + private static final byte[] X509 = "x509 ".getBytes(); + private PrivateKey priv; + private byte[] pub; + private String cert; + private SecurityInfoC<HttpURLConnection> securityInfo; + private String algo; + private String alias; + private static int count = new SecureRandom().nextInt(); + + public HX509SS(SecurityInfoC<HttpURLConnection> si) throws APIException, CadiException { + this(null,si,false); + } + + public HX509SS(SecurityInfoC<HttpURLConnection> si, boolean asDefault) throws APIException, CadiException { + this(null,si,asDefault); + } + + public HX509SS(final String sendAlias, SecurityInfoC<HttpURLConnection> si) throws APIException, CadiException { + this(sendAlias, si, false); + } + + public HX509SS(final String sendAlias, SecurityInfoC<HttpURLConnection> si, boolean asDefault) throws APIException, CadiException { + securityInfo = si; + if((alias=sendAlias) == null) { + if(si.default_alias == null) { + throw new APIException("JKS Alias is required to use X509SS Security. Use " + Config.CADI_ALIAS +" to set default alias"); + } else { + alias = si.default_alias; + } + } + + priv=null; + X509KeyManager[] xkms = si.getKeyManagers(); + if(xkms==null || xkms.length==0) { + throw new APIException("There are no valid keys available in given Keystores. Wrong Keypass? Expired?"); + } + for(int i=0;priv==null&&i<xkms.length;++i) { + priv = xkms[i].getPrivateKey(alias); + } + try { + for(int i=0;cert==null&&i<xkms.length;++i) { + X509Certificate[] chain = xkms[i].getCertificateChain(alias); + if(chain!=null&&chain.length>0) { + algo = chain[0].getSigAlgName(); + pub = chain[0].getEncoded(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(pub.length*2); + ByteArrayInputStream bais = new ByteArrayInputStream(pub); + Symm.base64noSplit.encode(bais,baos,X509); + cert = baos.toString(); + } + } + } catch (CertificateEncodingException | IOException e) { + throw new CadiException(e); + } + if(algo==null) { + throw new APIException("X509 Security Setter not configured"); + } + } + + @Override + public void setSecurity(HttpURLConnection huc) throws CadiException { + if(huc instanceof HttpsURLConnection) { + securityInfo.setSocketFactoryOn((HttpsURLConnection)huc); + } + if(alias==null) { // must be a one-way + huc.setRequestProperty(AbsAuthentication.AUTHORIZATION, cert); + + // Test Signed content + try { + String data = "SignedContent["+ inc() + ']' + Chrono.dateTime(); + huc.setRequestProperty("Data", data); + + Signature sig = Signature.getInstance(algo); + sig.initSign(priv); + sig.update(data.getBytes()); + byte[] signature = sig.sign(); + + ByteArrayOutputStream baos = new ByteArrayOutputStream((int)(signature.length*1.3)); + ByteArrayInputStream bais = new ByteArrayInputStream(signature); + Symm.base64noSplit.encode(bais, baos); + huc.setRequestProperty("Signature", new String(baos.toByteArray())); + + } catch (Exception e) { + throw new CadiException(e); + } + } + } + + private synchronized int inc() { + return ++count; + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.SecuritySetter#getID() + */ + @Override + public String getID() { + return alias; + } + + @Override + public int setLastResponse(int respCode) { + return 0; + } +} diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/locator/DNSLocator.java b/cadi/client/src/main/java/org/onap/aaf/cadi/locator/DNSLocator.java new file mode 100644 index 00000000..ed60b877 --- /dev/null +++ b/cadi/client/src/main/java/org/onap/aaf/cadi/locator/DNSLocator.java @@ -0,0 +1,223 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.locator; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.URI; +import java.net.URISyntaxException; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.Locator; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.Access.Level; + +public class DNSLocator implements Locator<URI> { + private static enum Status {UNTRIED, OK, INVALID, SLOW}; + private static final int CHECK_TIME = 3000; + + private String host, protocol; + private Access access; + private Host[] hosts; + private int startPort, endPort; + private String suffix; + + public DNSLocator(Access access, String protocol, String host, String range) { + this.host = host; + this.protocol = protocol; + this.access = access; + int dash = range.indexOf('-'); + if(dash<0) { + startPort = endPort = Integer.parseInt(range); + } else { + startPort = Integer.parseInt(range.substring(0,dash)); + endPort = Integer.parseInt(range.substring(dash + 1)); + } + refresh(); + } + + public DNSLocator(Access access, String aaf_locate) throws LocatorException { + this.access = access; + if(aaf_locate==null) { + throw new LocatorException("Null passed into DNSLocator constructor"); + } + int start, port; + if(aaf_locate.startsWith("https:")) { + protocol = "https:"; + start = 9; // https:// + port = 443; + } else if(aaf_locate.startsWith("http:")) { + protocol = "http:"; + start = 8; // http:// + port = 80; + } else { + throw new LocatorException("DNSLocator accepts only https or http protocols. (requested URL " + aaf_locate + ')'); + } + + parsePorts(aaf_locate.substring(start), port); + } + + @Override + public URI get(Item item) throws LocatorException { + return hosts[((DLItem)item).cnt].uri; + } + + @Override + public boolean hasItems() { + for(Host h : hosts) { + if(h.status==Status.OK) { + return true; + } + } + return false; + } + + @Override + public void invalidate(Item item) { + DLItem di = (DLItem)item; + hosts[di.cnt].status = Status.INVALID; + } + + @Override + public Item best() throws LocatorException { + // not a good "best" + for(int i=0;i<hosts.length;++i) { + switch(hosts[i].status) { + case OK: + return new DLItem(i); + case INVALID: + break; + case SLOW: + break; + case UNTRIED: + try { + if(hosts[i].ia.isReachable(CHECK_TIME)) { + hosts[i].status = Status.OK; + return new DLItem(i); + } + } catch (IOException e) { + throw new LocatorException(e); + } + break; + default: + break; + } + } + throw new LocatorException("No Available URIs for " + host); + } + + @Override + public Item first() throws LocatorException { + return new DLItem(0); + } + + @Override + public Item next(Item item) throws LocatorException { + DLItem di = (DLItem)item; + if(++di.cnt<hosts.length) { + return di; + } else { + return null; + } + } + + @Override + public boolean refresh() { + try { + InetAddress[] ias = InetAddress.getAllByName(host); + Host[] temp = new Host[ias.length * (1 + endPort - startPort)]; + int cnt = -1; + for(int j=startPort; j<=endPort; ++j) { + for(int i=0;i<ias.length;++i) { + temp[++cnt] = new Host(ias[i], j, suffix); + } + } + hosts = temp; + return true; + } catch (Exception e) { + access.log(Level.ERROR, e); + } + return false; + } + + private void parsePorts(String aaf_locate, int defaultPort) throws LocatorException { + int slash, start; + int colon = aaf_locate.indexOf(':'); + if(colon > 0) { + start = colon + 1; + int left = aaf_locate.indexOf('[', start); + if(left > 0) { + int right = aaf_locate.indexOf(']', left + 1); + if (right < 0) { + throw new LocatorException("Missing closing bracket in DNSLocator constructor. (requested URL " + aaf_locate + ')'); + } else if (right == (left + 1)) { + throw new LocatorException("Missing ports in brackets in DNSLocator constructor. (requested URL " + aaf_locate + ')'); + } + int dash = aaf_locate.indexOf('-', left + 1); + if (dash == (right - 1) || dash == (left + 1)) { + throw new LocatorException("Missing ports in brackets in DNSLocator constructor. (requested URL " + aaf_locate + ')'); + } + if(dash < 0) { + startPort = endPort = Integer.parseInt(aaf_locate.substring(left + 1, right)); + } else { + startPort = Integer.parseInt(aaf_locate.substring(left + 1, dash)); + endPort = Integer.parseInt(aaf_locate.substring(dash + 1, right)); + } + + } else { + slash = aaf_locate.indexOf('/', start); + if (slash == start) { + throw new LocatorException("Missing port before '/' in DNSLocator constructor. (requested URL " + aaf_locate + ')'); + } + if(slash < 0) { + startPort = endPort = Integer.parseInt(aaf_locate.substring(start)); + } else { + startPort = endPort = Integer.parseInt(aaf_locate.substring(start, slash)); + } + } + } else { + startPort = endPort = defaultPort; + } + } + + private class Host { + private URI uri; + private InetAddress ia; + private Status status; + + public Host(InetAddress inetAddress, int port, String suffix) throws URISyntaxException { + ia = inetAddress; + uri = new URI(protocol,null,inetAddress.getHostAddress(),port,suffix,null,null); + status = Status.UNTRIED; + } + } + + private class DLItem implements Item { + public DLItem(int i) { + cnt = i; + } + + private int cnt; + } + + public void destroy() {} +} diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/locator/HClientHotPeerLocator.java b/cadi/client/src/main/java/org/onap/aaf/cadi/locator/HClientHotPeerLocator.java new file mode 100644 index 00000000..b97768a6 --- /dev/null +++ b/cadi/client/src/main/java/org/onap/aaf/cadi/locator/HClientHotPeerLocator.java @@ -0,0 +1,60 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.locator; + +import java.net.URI; +import java.net.URISyntaxException; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.http.HClient; +import org.onap.aaf.cadi.http.HX509SS; + +public class HClientHotPeerLocator extends HotPeerLocator<HClient> { + private final HX509SS ss; + + public HClientHotPeerLocator(Access access, String urlstr, long invalidateTime, String localLatitude, + String localLongitude, HX509SS ss) throws LocatorException { + super(access, urlstr, invalidateTime, localLatitude, localLongitude); + + this.ss = ss; + } + + @Override + protected HClient _newClient(String clientInfo) throws LocatorException { + try { + int idx = clientInfo.indexOf('/'); + return new HClient(ss,new URI("https://"+(idx<0?clientInfo:clientInfo.substring(0, idx))),3000); + } catch (URISyntaxException e) { + throw new LocatorException(e); + } + } + + @Override + protected HClient _invalidate(HClient client) { + return null; + } + + @Override + protected void _destroy(HClient client) { + } +}
\ No newline at end of file diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/locator/HotPeerLocator.java b/cadi/client/src/main/java/org/onap/aaf/cadi/locator/HotPeerLocator.java new file mode 100644 index 00000000..fd8e99dc --- /dev/null +++ b/cadi/client/src/main/java/org/onap/aaf/cadi/locator/HotPeerLocator.java @@ -0,0 +1,301 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.locator; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.Locator; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.routing.GreatCircle; +import org.onap.aaf.misc.env.util.Split; + +/** + * This Locator is to handle Hot Peer load protection, when the Servers are + * 1) Static + * 2) Well known client URL + * + * The intention is to change traffic over to the Hot Peer, if a server goes down, and reinstate + * when it is back up. + * + * Example of this kind of Service is a MS Certificate Server + * + * @author Jonathan + * + * @param <CLIENT> + */ +public abstract class HotPeerLocator<CLIENT> implements Locator<CLIENT> { + private final String[] urlstrs; + private final CLIENT[] clients; + private final long[] failures; + private final double[] distances; + private int preferred; + private long invalidateTime; + private Thread refreshThread; + protected Access access; + + /** + * Construct: Expect one or more Strings in the form: + * 192.555.112.223:39/38.88087/-77.30122 + * separated by commas + * + * @param trans + * @param urlstr + * @param invalidateTime + * @param localLatitude + * @param localLongitude + * @throws LocatorException + */ + @SuppressWarnings("unchecked") + protected HotPeerLocator(Access access, final String urlstr, final long invalidateTime, final String localLatitude, final String localLongitude) throws LocatorException { + this.access = access; + urlstrs = Split.split(',', urlstr); + clients = (CLIENT[])new Object[urlstrs.length]; + failures = new long[urlstrs.length]; + distances= new double[urlstrs.length]; + this.invalidateTime = invalidateTime; + + double distance = Double.MAX_VALUE; + for(int i=0;i<urlstrs.length;++i) { + String[] info = Split.split('/', urlstrs[i]); + if(info.length<3) { + throw new LocatorException("Configuration needs LAT and LONG, i.e. ip:port/lat/long"); + } + try { + clients[i] = _newClient(urlstrs[i]); + failures[i] = 0L; + } catch(LocatorException le) { + failures[i] = System.currentTimeMillis()+invalidateTime; + } + + double d = GreatCircle.calc(info[1],info[2],localLatitude,localLongitude); + distances[i]=d; + + // find preferred server + if(d<distance) { + preferred = i; + distance=d; + } + } + + access.printf(Level.INIT,"Preferred Client is %s",urlstrs[preferred]); + for(int i=0;i<urlstrs.length;++i) { + if(i!=preferred) { + access.printf(Level.INIT,"Alternate Client is %s",urlstrs[i]); + } + } + } + + protected abstract CLIENT _newClient(String hostInfo) throws LocatorException; + /** + * If client can reconnect, then return. Otherwise, destroy and return null; + * @param client + * @return + * @throws LocatorException + */ + protected abstract CLIENT _invalidate(CLIENT client); + + protected abstract void _destroy(CLIENT client); + + @Override + public Item best() throws LocatorException { + if(failures[preferred]==0L) { + return new HPItem(preferred); + } else { + long now = System.currentTimeMillis(); + double d = Double.MAX_VALUE; + int best = -1; + boolean tickle = false; + // try for best existing client + for(int i=0;i<urlstrs.length;++i) { + if(failures[i]<now && distances[i]<d) { + if(clients[i]!=null) { + best = i; + break; + } else { + tickle = true; // There's some failed clients which can be restored + } + } + } + if(best<0 && tickle) { + tickle=false; + if(refresh()) { + // try again + for(int i=0;i<urlstrs.length;++i) { + if(failures[i]==0L && distances[i]<d) { + if(clients[i]!=null) { + best = i; + break; + } + } + } + } + } + + /* + * If a valid client is available, but there are some that can refresh, return the client immediately + * but start a Thread to do the background Client setup. + */ + if(tickle) { + synchronized(clients) { + if(refreshThread==null) { + refreshThread = new Thread(new Runnable(){ + @Override + public void run() { + refresh(); + refreshThread = null; + } + }); + refreshThread.setDaemon(true); + refreshThread.start(); + } + } + } + + if(best<0) { + throw new LocatorException("No Clients available"); + } + + return new HPItem(best); + } + } + + + @Override + public CLIENT get(Item item) throws LocatorException { + HPItem hpi = (HPItem)item; + CLIENT c = clients[hpi.idx]; + if(c==null) { + if(failures[hpi.idx]>System.currentTimeMillis()) { + throw new LocatorException("Client requested is invalid"); + } else { + synchronized(clients) { + c = _newClient(urlstrs[hpi.idx]); + failures[hpi.idx]=0L; + } + } + } else if(failures[hpi.idx]>0){ + throw new LocatorException("Client requested is invalid"); + } + return c; + } + + public String info(Item item) { + HPItem hpi = (HPItem)item; + if(hpi!=null && hpi.idx<urlstrs.length) { + return urlstrs[hpi.idx]; + } else { + return "Invalid Item"; + } + } + + @Override + public boolean hasItems() { + for(int i=0;i<clients.length;++i) { + if(clients[i]!=null && failures[i]==0L) { + return true; + } + } + return false; + } + + @Override + public synchronized void invalidate(Item item) throws LocatorException { + HPItem hpi = (HPItem)item; + failures[hpi.idx] = System.currentTimeMillis() + invalidateTime; + CLIENT c = clients[hpi.idx]; + clients[hpi.idx] = _invalidate(c); + } + + @Override + public Item first() throws LocatorException { + return new HPItem(0); + } + + @Override + public Item next(Item item) throws LocatorException { + HPItem hpi = (HPItem)item; + if(++hpi.idx>=clients.length) { + return null; + } + return hpi; + } + + @Override + public boolean refresh() { + boolean force = !hasItems(); // If no Items at all, reset + boolean rv = true; + long now = System.currentTimeMillis(); + for(int i=0;i<clients.length;++i) { + if(failures[i]>0L && (failures[i]<now || force)) { // retry + try { + synchronized(clients) { + if(clients[i]==null) { + clients[i]=_newClient(urlstrs[i]); + } + failures[i]=0L; + } + } catch (LocatorException e) { + failures[i]=now+invalidateTime; + rv = false; + } + } + } + return rv; + } + + @Override + public void destroy() { + for(int i=0;i<clients.length;++i) { + if(clients[i]!=null) { + _destroy(clients[i]); + clients[i] = null; + } + } + } + + private static class HPItem implements Item { + private int idx; + + public HPItem(int i) { + idx = i; + } + } + + + /* + * Convenience Functions + */ + public CLIENT bestClient() throws LocatorException { + return get(best()); + } + + public boolean invalidate(CLIENT client) throws LocatorException { + for(int i=0;i<clients.length;++i) { + if(clients[i]==client) { // yes, "==" is appropriate here.. Comparing Java Object Reference + invalidate(new HPItem(i)); + return true; + } + } + return false; + } + +}
\ No newline at end of file diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/locator/PropertyLocator.java b/cadi/client/src/main/java/org/onap/aaf/cadi/locator/PropertyLocator.java new file mode 100644 index 00000000..4591122c --- /dev/null +++ b/cadi/client/src/main/java/org/onap/aaf/cadi/locator/PropertyLocator.java @@ -0,0 +1,298 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.locator; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.UnknownHostException; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.List; +import java.util.Timer; +import java.util.TimerTask; + +import org.onap.aaf.cadi.Locator; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.misc.env.util.Split; + +public class PropertyLocator implements Locator<URI> { + private final URI [] orig; + private PLItem[] current; + private int end; + private final SecureRandom random; + private URI[] resolved; + private long lastRefreshed; + private long minRefresh; + private long backgroundRefresh; + + public PropertyLocator(String locList) throws LocatorException { + this(locList,10000L, 1000*60*20L); // defaults, do not refresh more than once in 10 seconds, Refresh Locator every 20 mins. + } + /** + * comma delimited root url list + * + * @param locList + * @throws LocatorException + */ + public PropertyLocator(String locList, long minRefreshMillis, long backgroundRefreshMillis) throws LocatorException { + minRefresh = minRefreshMillis; + backgroundRefresh = backgroundRefreshMillis; + lastRefreshed=0L; + if(locList==null) { + throw new LocatorException("No Location List given for PropertyLocator"); + } + String[] locarray = Split.split(',',locList); + List<URI> uriList = new ArrayList<URI>(); + + random = new SecureRandom(); + + for(int i=0;i<locarray.length;++i) { + try { + int range = locarray[i].indexOf(":["); + if(range<0) { + uriList.add(new URI(locarray[i])); + } else { + String mach_colon = locarray[i].substring(0, range+1); + int dash = locarray[i].indexOf('-',range+2); + int brac = locarray[i].indexOf(']',dash+1); + int slash = locarray[i].indexOf('/',brac); + int start = Integer.parseInt(locarray[i].substring(range+2, dash)); + int end = Integer.parseInt(locarray[i].substring(dash+1, brac)); + for(int port=start;port<=end;++port) { + uriList.add(new URI(mach_colon+port + (slash>=0?locarray[i].substring(slash):""))); + } + } + } catch (NumberFormatException nf) { + throw new LocatorException("Invalid URI format: " + locarray[i]); + } catch (URISyntaxException e) { + throw new LocatorException(e); + } + } + orig = new URI[uriList.size()]; + uriList.toArray(orig); + + refresh(); + new Timer("PropertyLocator Refresh Timer",true).scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + refresh(); + } + }, backgroundRefresh,backgroundRefresh); + } + + @Override + public URI get(Item item) throws LocatorException { + synchronized(orig) { + if(item==null) { + return null; + } else { + return resolved[((PLItem)item).idx]; + } + } + } + + @Override + public Item first() throws LocatorException { + return end>0?current[0]:null; + } + + @Override + public boolean hasItems() { + return end>0; + } + + @Override + public Item next(Item item) throws LocatorException { + if(item==null) { + return null; + } else { + int spot; + if((spot=(((PLItem)item).order+1))>=end)return null; + return current[spot]; + } + } + + @Override + public synchronized void invalidate(Item item) throws LocatorException { + if(--end<0) { + refresh(); + return; + } + if(item==null) { + return; + } + PLItem pli = (PLItem)item; + int i,order; + for(i=0;i<end;++i) { + if(pli==current[i])break; + } + order = current[i].order; + for(;i<end;++i) { + current[i]=current[i+1]; + current[i].order=order++; + } + current[end]=pli; + } + + @Override + public Item best() throws LocatorException { + if(current.length==0) { + refresh(); + } + switch(current.length) { + case 0: + return null; + case 1: + return current[0]; + default: + int rand = random.nextInt(); // sonar driven syntax + return current[Math.abs(rand)%current.length]; + } + } + + @Override + public synchronized boolean refresh() { + if(System.currentTimeMillis()>lastRefreshed) { + // Build up list + List<URI> resolve = new ArrayList<URI>(); + String realname; + for(int i = 0; i < orig.length ; ++i) { + try { + InetAddress ia[] = InetAddress.getAllByName(orig[i].getHost()); + + URI o,n; + for(int j=0;j<ia.length;++j) { + o = orig[i]; + Socket socket = createSocket(); + try { + realname=ia[j].getHostAddress().equals(ia[j].getHostName())?ia[j].getCanonicalHostName():ia[j].getHostName(); + int port = o.getPort(); + if(port<0) { // default + port = "https".equalsIgnoreCase(o.getScheme())?443:80; + } + socket.connect(new InetSocketAddress(realname,port),3000); + try { + if(socket.isConnected()) { + n = new URI( + o.getScheme(), + o.getUserInfo(), + realname, + o.getPort(), + o.getPath(), + o.getQuery(), + o.getFragment() + ); + resolve.add(n); + } + } finally { + socket.close(); + } + } catch (IOException e) { + } finally { + if(!socket.isClosed()) { + try { + socket.close(); + } catch (IOException e) { + // nothing to do. + } + } + } + } + } catch (UnknownHostException | URISyntaxException e) { + // Note: Orig Name already known as valid, based on constructor + } + } + end=resolve.size(); + PLItem[] newCurrent; + if(current==null || current.length!=end) { + newCurrent = new PLItem[end]; + } else { + newCurrent = current; + } + + for(int i=0; i< end; ++i) { + if(newCurrent[i]==null){ + newCurrent[i]=new PLItem(i); + } else { + newCurrent[i].idx=newCurrent[i].order=i; + } + } + synchronized(orig) { + resolved = new URI[end]; + resolve.toArray(resolved); + current = newCurrent; + } + lastRefreshed = System.currentTimeMillis()+minRefresh; + return !resolve.isEmpty(); + } else { + return false; + } + } + + protected Socket createSocket() { + return new Socket(); + } + + private class PLItem implements Item { + public int idx,order; + + public PLItem(int i) { + idx = order =i; + } + + public String toString() { + return "Item: " + idx + " order: " + order; + } + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + boolean first = true; + for(URI uri : orig) { + boolean isResolved=false; + if(uri!=null) { + if(first) { + first = false; + } else { + sb.append(", "); + } + sb.append(uri.toString()); + sb.append(" ["); + for(URI u2 : resolved) { + if(uri.equals(u2)) { + isResolved = true; + break; + } + } + sb.append(isResolved?"X]\n":" ]"); + } + } + return sb.toString(); + } + + public void destroy() { + } +} diff --git a/cadi/client/src/main/java/org/onap/aaf/cadi/routing/GreatCircle.java b/cadi/client/src/main/java/org/onap/aaf/cadi/routing/GreatCircle.java new file mode 100644 index 00000000..36906188 --- /dev/null +++ b/cadi/client/src/main/java/org/onap/aaf/cadi/routing/GreatCircle.java @@ -0,0 +1,188 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.routing; + +import org.onap.aaf.misc.env.util.Split; + +public class GreatCircle { + // Note: multiplying by this constant is faster than calling Math equivalent function + private static final double DEGREES_2_RADIANS = Math.PI/180.0; + + public static final double DEGREES_2_NM = 60; + public static final double DEGREES_2_KM = DEGREES_2_NM * 1.852; // 1.852 is exact ratio per 1929 Standard Treaty, adopted US 1954 + public static final double DEGREES_2_MI = DEGREES_2_NM * 1.1507795; + + /** + * + * Calculate the length of an arc on a perfect sphere based on Latitude and Longitudes of two points + * Parameters are in Degrees (i.e. the coordinate system you get from GPS, Mapping WebSites, Phones, etc) + * + * L1 = Latitude of point A + * G1 = Longitude of point A + * L2 = Latitude of point B + * G2 = Longitude of point B + * + * d = acos (sin(L1)*sin(L2) + cos(L1)*cos(L2)*cos(G1 - G2)) + * + * Returns answer in Degrees + * + * Since there are 60 degrees per nautical miles, you can convert to NM by multiplying by 60 + * + * Essential formula from a Princeton website, the "Law of Cosines" method. + * + * Refactored cleaned up for speed Jonathan 3/8/2013 + * + * @param latA + * @param lonA + * @param latB + * @param lonB + * @return + */ + public static double calc(double latA, double lonA, double latB, double lonB) { + // Formula requires Radians. Expect Params to be Coordinates (Degrees) + // Simple ratio, quicker than calling Math.toRadians() + latA *= DEGREES_2_RADIANS; + lonA *= DEGREES_2_RADIANS; + latB *= DEGREES_2_RADIANS; + lonB *= DEGREES_2_RADIANS; + + return Math.acos( + Math.sin(latA) * Math.sin(latB) + + Math.cos(latA) * Math.cos(latB) * Math.cos(lonA-lonB) + ) + / DEGREES_2_RADIANS; + } + + /** + * Convert from "Lat,Long Lat,Long" String format + * "Lat,Long,Lat,Long" Format + * or all four entries "Lat Long Lat Long" + * + * (Convenience function) + * + * Since Distance is positive, a "-1" indicates an error in String formatting + */ + public static double calc(String ... coords) { + try { + String [] array; + switch(coords.length) { + case 1: + array = Split.split(',',coords[0]); + if(array.length!=4)return -1; + return calc( + Double.parseDouble(array[0]), + Double.parseDouble(array[1]), + Double.parseDouble(array[2]), + Double.parseDouble(array[3]) + ); + case 2: + array = Split.split(',',coords[0]); + String [] array2 = Split.split(',',coords[1]); + if(array.length!=2 || array2.length!=2)return -1; + return calc( + Double.parseDouble(array[0]), + Double.parseDouble(array[1]), + Double.parseDouble(array2[0]), + Double.parseDouble(array2[1]) + ); + case 4: + return calc( + Double.parseDouble(coords[0]), + Double.parseDouble(coords[1]), + Double.parseDouble(coords[2]), + Double.parseDouble(coords[3]) + ); + + default: + return -1; + } + } catch (NumberFormatException e) { + return -1; + } + } + +} + +///** +//* Haverside method, from Princeton +//* +//* @param alat +//* @param alon +//* @param blat +//* @param blon +//* @return +//*/ +//public static double calc3(double alat, double alon, double blat, double blon) { +// alat *= DEGREES_2_RADIANS; +// alon *= DEGREES_2_RADIANS; +// blat *= DEGREES_2_RADIANS; +// blon *= DEGREES_2_RADIANS; +// return 2 * Math.asin( +// Math.min(1, Math.sqrt( +// Math.pow(Math.sin((blat-alat)/2), 2) + +// (Math.cos(alat)*Math.cos(blat)* +// Math.pow( +// Math.sin((blon-alon)/2),2) +// ) +// ) +// ) +// ) +// / DEGREES_2_RADIANS; +//} +// + + + +//This is a MEAN radius. The Earth is not perfectly spherical +// public static final double EARTH_RADIUS_KM = 6371.0; +// public static final double EARTH_RADIUS_NM = 3440.07; +// public static final double KM_2_MILES_RATIO = 0.621371192; +///** +//* Code on Internet based on Unknown book. Lat/Long is in Degrees +//* @param alat +//* @param alon +//* @param blat +//* @param blon +//* @return +//*/ +//public static double calc1(double alat, double alon, double blat, double blon) { +// alat *= DEGREES_2_RADIANS; +// alon *= DEGREES_2_RADIANS; +// blat *= DEGREES_2_RADIANS; +// blon *= DEGREES_2_RADIANS; +// +// // Reused values +// double cosAlat,cosBlat; +// +// return Math.acos( +// ((cosAlat=Math.cos(alat))*Math.cos(alon)*(cosBlat=Math.cos(blat))*Math.cos(blon)) + +// (cosAlat*Math.sin(alon)*cosBlat*Math.sin(blon)) + +// (Math.sin(alat)*Math.sin(blat)) +// )/DEGREES_2_RADIANS; +// +//} + +/* +* This method was 50% faster than calculation 1, and 75% than the Haverside method +* Also, since it's based off of Agree standard Degrees of the Earth, etc, the calculations are more exact, +* at least for Nautical Miles and Kilometers +*/ diff --git a/cadi/client/src/test/java/org/onap/aaf/cadi/client/test/JU_AbsAuthentication.java b/cadi/client/src/test/java/org/onap/aaf/cadi/client/test/JU_AbsAuthentication.java new file mode 100644 index 00000000..cc67946e --- /dev/null +++ b/cadi/client/src/test/java/org/onap/aaf/cadi/client/test/JU_AbsAuthentication.java @@ -0,0 +1,103 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.client.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import org.junit.*; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.client.AbsAuthentication; +import org.onap.aaf.cadi.config.SecurityInfoC; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.net.HttpURLConnection; + +public class JU_AbsAuthentication { + + private final static String ID = "id"; + private final static String PASSWORD = "password"; + private final static String WARNING = "Your service has 1000 consecutive bad service " + + "logins to AAF. AAF Access will be disabled after 10000\n"; + + private static ByteArrayOutputStream errStream; + + @Before + public void setup() { + errStream = new ByteArrayOutputStream(); + System.setErr(new PrintStream(errStream)); + } + + @After + public void tearDown() { + System.setErr(System.err); + } + + @Test + public void test() throws IOException, InterruptedException { + AuthStub stub = new AuthStub(null, null, null); + assertThat(stub.getID(), is(nullValue())); + assertThat(stub.headValue(), is("")); + assertThat(stub.count(), is(0)); + + stub.setUser(ID); + assertThat(stub.getID(), is(ID)); + + stub = new AuthStub(null, ID, PASSWORD.getBytes()); + assertThat(stub.getID(), is(ID)); + assertThat(stub.headValue(), is(PASSWORD)); + assertThat(stub.count(), is(0)); + + assertThat(stub.setLastResponse(200), is(0)); + assertThat(stub.isDenied(), is(false)); + + for (int i = 1; i <= 10; i++) { + assertThat(stub.setLastResponse(401), is(i)); + assertThat(stub.isDenied(), is(false)); + } + assertThat(stub.setLastResponse(401), is(11)); + assertThat(stub.isDenied(), is(true)); + + stub.setCount(999); + assertThat(stub.setLastResponse(401), is(1000)); + assertThat(errStream.toString(), is(WARNING)); + + // coverage... + stub.setLastMiss(1); + assertThat(stub.isDenied(), is(false)); + } + + private class AuthStub extends AbsAuthentication<HttpURLConnection> { + + public AuthStub(SecurityInfoC<HttpURLConnection> securityInfo, String user, byte[] headValue) + throws IOException { super(securityInfo, user, headValue); } + + @Override public void setSecurity(HttpURLConnection client) throws CadiException { } + @Override public void setUser(String id) { super.setUser(id); } + @Override public String headValue() throws IOException { return super.headValue(); } + + public void setLastMiss(long lastMiss) { this.lastMiss = lastMiss; } + public void setCount(int count) { this.count = count; } + } + +} diff --git a/cadi/client/src/test/java/org/onap/aaf/cadi/client/test/JU_AbsTransferSS.java b/cadi/client/src/test/java/org/onap/aaf/cadi/client/test/JU_AbsTransferSS.java new file mode 100644 index 00000000..507f90f4 --- /dev/null +++ b/cadi/client/src/test/java/org/onap/aaf/cadi/client/test/JU_AbsTransferSS.java @@ -0,0 +1,72 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.client.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import static org.mockito.Mockito.*; +import org.junit.*; +import org.mockito.*; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.client.AbsTransferSS; +import org.onap.aaf.cadi.config.SecurityInfoC; +import org.onap.aaf.cadi.principal.TaggedPrincipal; + +import java.net.HttpURLConnection; + +public class JU_AbsTransferSS { + + @Mock TaggedPrincipal princMock; + @Mock SecurityInfoC<HttpURLConnection> siMock; + + private static final String princName = "name"; + private static final String princTag = "tag"; + private static final String app = "app"; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + + when(princMock.getName()).thenReturn(princName); + when(princMock.tag()).thenReturn(princTag); + } + + @Test + public void test() { + TransferSSStub stub = new TransferSSStub(princMock, app); + assertThat(stub.getID(), is(princName)); + assertThat(stub.getValue(), is(princName + ':' + app + ':' + princTag + ':' + "AS")); + + stub = new TransferSSStub(null, app, siMock); + assertThat(stub.getID(), is("")); + assertThat(stub.getValue(), is(nullValue())); + } + + private class TransferSSStub extends AbsTransferSS<HttpURLConnection> { + public TransferSSStub(TaggedPrincipal principal, String app) { super(principal, app); } + public TransferSSStub(TaggedPrincipal principal, String app, SecurityInfoC<HttpURLConnection> si) { super(principal, app, si); } + @Override public void setSecurity(HttpURLConnection client) throws CadiException { } + @Override public int setLastResponse(int respCode) { return 0; } + public String getValue() { return value; } + } + +} diff --git a/cadi/client/src/test/java/org/onap/aaf/cadi/client/test/JU_Future.java b/cadi/client/src/test/java/org/onap/aaf/cadi/client/test/JU_Future.java new file mode 100644 index 00000000..ad0bd535 --- /dev/null +++ b/cadi/client/src/test/java/org/onap/aaf/cadi/client/test/JU_Future.java @@ -0,0 +1,45 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.client.test; + +import org.junit.Test; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.client.Future; + +// This class exists purely to instantiate (and therefore attain coverage of) the Future class + +public class JU_Future { + + @Test + public void test() { + @SuppressWarnings("unused") + Future<Integer> f = new FutureStub(); + } + + private class FutureStub extends Future<Integer> { + @Override public boolean get(int timeout) throws CadiException { return false; } + @Override public int code() { return 0; } + @Override public String body() { return null; } + @Override public String header(String tag) { return null; } + } + +} diff --git a/cadi/client/src/test/java/org/onap/aaf/cadi/client/test/JU_Holder.java b/cadi/client/src/test/java/org/onap/aaf/cadi/client/test/JU_Holder.java new file mode 100644 index 00000000..079951f5 --- /dev/null +++ b/cadi/client/src/test/java/org/onap/aaf/cadi/client/test/JU_Holder.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.client.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import org.junit.Test; + +import org.onap.aaf.cadi.client.Holder; + +public class JU_Holder { + + @Test + public void test() { + String str1 = "a string"; + String str2 = "another string"; + Holder<String> holder = new Holder<String>(str1); + assertThat(holder.get(), is(str1)); + assertThat(holder.toString(), is(str1)); + + holder.set(str2); + assertThat(holder.get(), is(str2)); + assertThat(holder.toString(), is(str2)); + } + +} diff --git a/cadi/client/src/test/java/org/onap/aaf/cadi/client/test/JU_Rcli.java b/cadi/client/src/test/java/org/onap/aaf/cadi/client/test/JU_Rcli.java new file mode 100644 index 00000000..f957878b --- /dev/null +++ b/cadi/client/src/test/java/org/onap/aaf/cadi/client/test/JU_Rcli.java @@ -0,0 +1,284 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.client.test; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; +import static org.hamcrest.CoreMatchers.*; +import org.junit.*; +import org.mockito.*; + +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.SecuritySetter; +import org.onap.aaf.cadi.client.EClient; +import org.onap.aaf.cadi.client.Future; +import org.onap.aaf.cadi.client.Rcli; +import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.misc.env.Data; +import org.onap.aaf.misc.env.Data.TYPE; +import org.onap.aaf.misc.rosetta.env.RosettaDF; +import org.onap.aaf.misc.rosetta.env.RosettaData; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Enumeration; + +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class JU_Rcli { + + @Mock RosettaDF<HttpURLConnection> dfMock; + @Mock RosettaData<HttpURLConnection> dataMock; + @Mock HttpURLConnection conMock; + @Mock HttpServletRequest reqMock; + @Mock HttpServletResponse respMock; + @Mock ServletInputStream isMock; + + private final static String uriString = "example.com"; + private final static String apiVersion = "v1.0"; + private final static String fragment = "framgent"; + private final static String queryParams = "queryParams"; + private final static String contentType = "contentType"; + + private static URI uri; + private static Enumeration<String> enumeration; + + private Client client; + + @Before + public void setup() throws URISyntaxException, IOException { + MockitoAnnotations.initMocks(this); + + when(dfMock.getTypeClass()).thenReturn(HttpURLConnection.class); + when(dfMock.newData()).thenReturn(dataMock); + when(dataMock.out((TYPE) any())).thenReturn(dataMock); + + when(reqMock.getInputStream()).thenReturn(isMock); + when(isMock.read((byte[]) any())).thenReturn(-1); + + uri = new URI(uriString); + enumeration = new CustomEnumeration(); + client = new Client(); + } + + @Test + public void createTest() throws APIException, CadiException { + RcliStub rcli = new RcliStub(uri); + rcli.type(Data.TYPE.XML); + + rcli.create(null, contentType, dfMock, conMock); + rcli.create("No question mark", contentType, dfMock, conMock); + rcli.create("question?mark", contentType, dfMock, conMock); + + rcli.create(null, dfMock, conMock); + rcli.create("No question mark", dfMock, conMock); + rcli.create("question?mark", dfMock, conMock); + + rcli.create(null, HttpURLConnection.class, dfMock, conMock); + rcli.create("No question mark", HttpURLConnection.class, dfMock, conMock); + rcli.create("question?mark", HttpURLConnection.class, dfMock, conMock); + + rcli.create(null, HttpURLConnection.class); + rcli.create("No question mark", HttpURLConnection.class); + rcli.create("question?mark", HttpURLConnection.class); + + rcli.create(null, contentType); + rcli.create("No question mark", contentType); + rcli.create("question?mark", contentType); + } + + @Test + public void postFormTest() throws APIException, CadiException { + RcliStub rcli = new RcliStub(uri); + + rcli.type(Data.TYPE.DEFAULT); + rcli.postForm(null, dfMock); + rcli.postForm("No question mark", dfMock); + rcli.postForm("question?mark", dfMock); + + rcli.type(Data.TYPE.JSON); + rcli.postForm("question?mark", dfMock); + + rcli.type(Data.TYPE.XML); + rcli.postForm("question?mark", dfMock); + + } + + @Test + public void readPostTest() throws APIException, CadiException { + RcliStub rcli = new RcliStub(uri); + rcli.type(Data.TYPE.DEFAULT); + + rcli.readPost(null, dfMock, conMock); + rcli.readPost("No question mark", dfMock, conMock); + rcli.readPost("question?mark", dfMock, conMock); + + rcli.readPost(null, dfMock, conMock, dfMock); + rcli.readPost("No question mark", dfMock, conMock, dfMock); + rcli.readPost("question?mark", dfMock, conMock, dfMock); + + rcli.readPost("First string", "Second string"); + } + + @Test + public void readTest() throws APIException, CadiException { + RcliStub rcli = new RcliStub(uri); + rcli.type(Data.TYPE.DEFAULT); + + rcli.read("First string", "Second string", "Third string", "Fourth string"); + rcli.read("First string", "Second string", dfMock, "Third string", "Fourth string"); + rcli.read("First string", dfMock, "Third string", "Fourth string"); + rcli.read("First string", HttpURLConnection.class ,dfMock); + } + + @Test + public void updateTest() throws APIException, CadiException { + RcliStub rcli = new RcliStub(uri); + rcli.type(Data.TYPE.DEFAULT); + + rcli.update("First string", "Second string", dfMock, conMock); + rcli.update("First string", dfMock, conMock); + rcli.update("First string", HttpURLConnection.class, dfMock, conMock); + rcli.update("First string"); + rcli.updateRespondString("First string", dfMock, conMock); + } + + @Test + public void deleteTest() throws APIException, CadiException { + RcliStub rcli = new RcliStub(uri); + rcli.type(Data.TYPE.DEFAULT); + + rcli.delete("First string", "Second string", dfMock, conMock); + rcli.delete("First string", dfMock, conMock); + rcli.delete("First string", HttpURLConnection.class, dfMock, conMock); + rcli.delete("First string", HttpURLConnection.class); + rcli.delete("First string", "Second string"); + } + + @Test + public void transferTest() throws APIException, CadiException { + RcliStub rcli = new RcliStub(uri); + rcli.type(Data.TYPE.DEFAULT); + + when(reqMock.getRequestURI()).thenReturn(uriString); + when(reqMock.getHeaderNames()).thenReturn(enumeration); + rcli.transfer(reqMock, respMock, "string", 200); + + // coverage... + when(reqMock.getMethod()).thenReturn("GET"); + rcli.transfer(reqMock, respMock, "string", 200); + } + + @Test(expected = CadiException.class) + public void transferThrowsTest() throws APIException, CadiException { + RcliStub rcli = new RcliStub(uri); + rcli.type(Data.TYPE.DEFAULT); + + rcli.transfer(reqMock, respMock, "string", 200); + } + + @Test + public void accessorMutatorTest() throws URISyntaxException { + RcliStub rcli = new RcliStub(); + Rcli<?> rcliClone = rcli.forUser(null); + + rcli = new RcliStub(uri); + assertThat(rcli.toString(), is(uriString)); + assertThat(rcli.getURI(), is(uri)); + assertThat(rcli.getReadTimeout(), is(5000)); + assertThat(rcli.getConnectionTimeout(), is(3000)); + + rcli.connectionTimeout(3001); + assertThat(rcli.getConnectionTimeout(), is(3001)); + rcli.readTimeout(5001); + assertThat(rcli.getReadTimeout(), is(5001)); + rcli.apiVersion(apiVersion); + assertThat(rcli.isApiVersion(apiVersion), is(true)); + rcli.type(Data.TYPE.XML); + assertThat(rcli.typeString(HttpURLConnection.class), is("application/HttpURLConnection+xml;version=" + apiVersion)); + rcli.apiVersion(null); + assertThat(rcli.typeString(HttpURLConnection.class), is("application/HttpURLConnection+xml")); + + rcli.setFragment(fragment); + rcli.setQueryParams(queryParams); + + rcliClone = rcli.forUser(null); + assertThat(rcliClone.toString(), is(uriString)); + } + + private class RcliStub extends Rcli<HttpURLConnection> { + public RcliStub() { super(); } + public RcliStub(URI uri) { this.uri = uri; } + @Override public void setSecuritySetter(SecuritySetter<HttpURLConnection> ss) { } + @Override public SecuritySetter<HttpURLConnection> getSecuritySetter() { return null; } + @Override protected Rcli<HttpURLConnection> clone(URI uri, SecuritySetter<HttpURLConnection> ss) { return this; } + @Override public void invalidate() throws CadiException { } + @Override protected EClient<HttpURLConnection> client() throws CadiException { return client; } + public int getReadTimeout() { return readTimeout; } + public int getConnectionTimeout() { return connectionTimeout; } + } + + private class CustomEnumeration implements Enumeration<String> { + private int idx = 0; + private final String[] elements = {"This", "is", "a", "test"}; + @Override + public String nextElement() { + return idx >= elements.length ? null : elements[idx++]; + } + @Override + public boolean hasMoreElements() { + return idx < elements.length; + } + } + + private class Client implements EClient<HttpURLConnection> { + private Transfer transfer; + @Override public void setPayload(Transfer transfer) { this.transfer = transfer; } + @Override public void setMethod(String meth) { } + @Override public void setPathInfo(String pathinfo) { } + @Override public void addHeader(String tag, String value) { } + @Override public void setQueryParams(String q) { } + @Override public void setFragment(String f) { } + @Override public void send() throws APIException { + try { + if (transfer != null) { + transfer.transfer(new PrintStream(new ByteArrayOutputStream())); + } + } catch (IOException e) { + } + } + @Override public <T> Future<T> futureCreate(Class<T> t) { return null; } + @Override public Future<String> futureReadString() { return null; } + @Override public <T> Future<T> futureRead(RosettaDF<T> df, TYPE type) { return null; } + @Override public <T> Future<T> future(T t) { return null; } + @Override public Future<Void> future(HttpServletResponse resp, int expected) throws APIException { return null; } + } + + //private class FutureStub implements Future<String> { + //} +} diff --git a/cadi/client/src/test/java/org/onap/aaf/cadi/client/test/JU_Result.java b/cadi/client/src/test/java/org/onap/aaf/cadi/client/test/JU_Result.java new file mode 100644 index 00000000..05ca27f3 --- /dev/null +++ b/cadi/client/src/test/java/org/onap/aaf/cadi/client/test/JU_Result.java @@ -0,0 +1,51 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.client.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import org.junit.*; + +import org.onap.aaf.cadi.client.Result; + +public class JU_Result { + + private static final int OK = 200; + private static final int NOT_FOUND = 404; + + @Test + public void test() { + Result<Integer> result; + result = Result.ok(OK, 10); + assertThat(result.toString(), is("Code: 200")); + assertThat(result.isOK(), is(true)); + + result = Result.err(NOT_FOUND, "File not found"); + assertThat(result.toString(), is("Code: 404 = File not found")); + assertThat(result.isOK(), is(false)); + + result = Result.err(result); + assertThat(result.toString(), is("Code: 404 = File not found")); + assertThat(result.isOK(), is(false)); + } + +} diff --git a/cadi/client/src/test/java/org/onap/aaf/cadi/client/test/JU_Retryable.java b/cadi/client/src/test/java/org/onap/aaf/cadi/client/test/JU_Retryable.java new file mode 100644 index 00000000..84863744 --- /dev/null +++ b/cadi/client/src/test/java/org/onap/aaf/cadi/client/test/JU_Retryable.java @@ -0,0 +1,59 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.client.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import org.junit.*; + +import java.net.ConnectException; + +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.Locator; +import org.onap.aaf.cadi.client.Rcli; +import org.onap.aaf.cadi.client.Retryable; +import org.onap.aaf.misc.env.APIException; + +public class JU_Retryable { + + @Test + public void test() { + RetryableStub retry = new RetryableStub(); + assertThat(retry.item(), is(nullValue())); + assertThat(retry.lastClient(), is(nullValue())); + + Locator.Item item = null; + assertThat(retry.item(item), is(item)); + + retry = new RetryableStub(retry); + assertThat(retry.item(), is(nullValue())); + assertThat(retry.lastClient(), is(nullValue())); + assertThat(retry.item(item), is(item)); + } + + private class RetryableStub extends Retryable<Integer> { + public RetryableStub() { super(); } + public RetryableStub(Retryable<?> ret) { super(ret); } + @Override public Integer code(Rcli<?> client) throws CadiException, ConnectException, APIException { return null; } + } + +} diff --git a/cadi/client/src/test/java/org/onap/aaf/cadi/http/test/JU_HAuthorizationHeader.java b/cadi/client/src/test/java/org/onap/aaf/cadi/http/test/JU_HAuthorizationHeader.java new file mode 100644 index 00000000..be12e7ac --- /dev/null +++ b/cadi/client/src/test/java/org/onap/aaf/cadi/http/test/JU_HAuthorizationHeader.java @@ -0,0 +1,79 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.http.test; + +import java.io.IOException; +import java.net.HttpURLConnection; + +import javax.net.ssl.HttpsURLConnection; + +import org.junit.*; +import org.mockito.*; + +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.config.SecurityInfoC; + +import org.onap.aaf.cadi.http.HAuthorizationHeader; + +public class JU_HAuthorizationHeader { + + @Mock + SecurityInfoC<HttpURLConnection> siMock; + + @Mock + HttpsURLConnection hucsMock; + + @Mock + HttpURLConnection hucMock; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void test() throws IOException, CadiException { + HAuthorizationHeader header = new HAuthorizationHeader(siMock, null, null); + header.setSecurity(hucsMock); + header.setSecurity(hucMock); + + header = new HAuthorizationHeader(null, null, null); + header.setSecurity(hucsMock); + } + + @Test(expected = CadiException.class) + public void throwsWhenDeniedTest() throws CadiException, IOException { + HAuthorizationHeader header = new HAuthorizationHeader(siMock, "string1", "string2") { + @Override public boolean isDenied() { return true; } + }; + header.setSecurity(null); + } + + @Test(expected = CadiException.class) + public void throwsTest() throws CadiException, IOException { + HAuthorizationHeader header = new HAuthorizationHeader(siMock, "string1", "string2") { + @Override public String headValue() throws IOException { throw new IOException(); } + }; + header.setSecurity(null); + } + +} diff --git a/cadi/client/src/test/java/org/onap/aaf/cadi/http/test/JU_HBasicAuthSS.java b/cadi/client/src/test/java/org/onap/aaf/cadi/http/test/JU_HBasicAuthSS.java new file mode 100644 index 00000000..1b9f6c3a --- /dev/null +++ b/cadi/client/src/test/java/org/onap/aaf/cadi/http/test/JU_HBasicAuthSS.java @@ -0,0 +1,94 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.http.test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.net.HttpURLConnection; + +import static org.mockito.Mockito.*; + +import org.junit.*; +import org.mockito.*; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.config.SecurityInfoC; +import org.onap.aaf.cadi.http.HBasicAuthSS; +import org.onap.aaf.cadi.principal.BasicPrincipal; + +public class JU_HBasicAuthSS { + + @Mock + BasicPrincipal bpMock; + + private SecurityInfoC<HttpURLConnection> si; + private PropAccess access; + + private final static String id = "id"; + private final static String password = "password"; + + @Before + public void setup() throws CadiException, IOException { + MockitoAnnotations.initMocks(this); + + when(bpMock.getName()).thenReturn(id); + when(bpMock.getCred()).thenReturn(password.getBytes()); + + access = new PropAccess(new PrintStream(new ByteArrayOutputStream()), new String[0]); + access.setProperty(Config.AAF_APPID, id); + access.setProperty(Config.AAF_APPPASS, access.encrypt(password)); + + si = SecurityInfoC.instance(access, HttpURLConnection.class); + } + + @Test + public void test() throws IOException { + // All the constructors accomplish the same thing + @SuppressWarnings("unused") + HBasicAuthSS auth = new HBasicAuthSS(si); + + // TODO: While these test _should_ pass, and they _do_ pass on my local machine, they won't + // pass when then onap jobbuilder runs them. Good luck! +// assertThat(auth.getID(), is(id)); + + auth = new HBasicAuthSS(si, false); +// assertThat(auth.getID(), is(id)); + + auth = new HBasicAuthSS(si, id, password, false); +// assertThat(auth.getID(), is(id)); + + auth = new HBasicAuthSS(si, id, password, true); +// assertThat(auth.getID(), is(id)); + + auth = new HBasicAuthSS(bpMock, si); +// assertThat(auth.getID(), is(id)); + + auth = new HBasicAuthSS(bpMock, si, false); +// assertThat(auth.getID(), is(id)); + + auth = new HBasicAuthSS(bpMock, si, true); +// assertThat(auth.getID(), is(id)); + } + +} diff --git a/cadi/client/src/test/java/org/onap/aaf/cadi/http/test/JU_HClient.java b/cadi/client/src/test/java/org/onap/aaf/cadi/http/test/JU_HClient.java new file mode 100644 index 00000000..646d63fa --- /dev/null +++ b/cadi/client/src/test/java/org/onap/aaf/cadi/http/test/JU_HClient.java @@ -0,0 +1,320 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.http.test; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URISyntaxException; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletResponse; + +import static org.hamcrest.CoreMatchers.*; +import org.junit.*; +import org.mockito.*; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.SecuritySetter; +import org.onap.aaf.cadi.client.EClient.Transfer; +import org.onap.aaf.cadi.client.Future; +import org.onap.aaf.cadi.http.HClient; +import org.onap.aaf.cadi.http.HClient.HFuture; +import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.misc.rosetta.env.RosettaDF; +import org.onap.aaf.misc.rosetta.env.RosettaData; + +public class JU_HClient { + + @Mock private SecuritySetter<HttpURLConnection> ssMock; + @Mock private Transfer transferMock; + @Mock private HttpURLConnection hucMock; + @Mock private HttpServletResponse respMock; + @Mock private RosettaDF<HttpURLConnection> dfMock; + @Mock private RosettaData<HttpURLConnection> dataMock; + + private static final String uriString = "http://example.com:8080/path/to/a/file.txt"; + private static final String fragment = "fragment"; + private static final String method = "method"; + private static final String pathinfo = "pathinfo"; + private static final String queryParams = "queryParams"; + + private static final String errorString = "error string"; + private static final String successString = "success string"; + + private static final String tag1 = "tag1"; + private static final String tag2 = "tag2"; + private static final String value1 = "value1"; + private static final String value2 = "value2"; + + private URI uri; + + @Before + public void setup() throws URISyntaxException { + MockitoAnnotations.initMocks(this); + + uri = new URI(uriString); + } + + @Test + public void accessorsMutatorsTest() throws LocatorException { + HClient client = new HClient(ssMock, uri, 0); + client.setFragment(fragment); + client.setMethod(method); + client.setPathInfo(pathinfo); + client.setPayload(transferMock); + client.setQueryParams(queryParams); + assertThat(client.getURI(), is(uri)); + assertThat(client.timeout(), is(0)); + assertThat(client.toString(), is("HttpURLConnection Client configured to " + uri.toString())); + } + + @Test + public void sendTest() throws LocatorException, APIException, URISyntaxException { + HClientStub client; + client = new HClientStub(ssMock, uri, 0, null); + client.send(); + + client.setPathInfo("/pathinfo"); + client.send(); + + client.setPathInfo("pathinfo"); + client.send(); + + client = new HClientStub(null, uri, 0, null); + client.send(); + + client.addHeader(tag1, value1); + client.addHeader(tag2, value2); + client.send(); + + client.setPayload(transferMock); + client.send(); + } + + @Test(expected = APIException.class) + public void sendThrows1Test() throws APIException, LocatorException, URISyntaxException { + HClientStub client = new HClientStub(ssMock, new URI("mailto:me@domain.com"), 0, null); + client.send(); + } + + @Test(expected = APIException.class) + public void sendThrows2Test() throws APIException, LocatorException, URISyntaxException { + HClientStub client = new HClientStub(ssMock, new URI("mailto:me@domain.com"), 0, null); + client.addHeader(tag1, value1); + client.addHeader(tag2, value2); + client.send(); + } + + @Test + public void futureCreateTest() throws LocatorException, CadiException, IOException { + HClient client = new HClientStub(ssMock, uri, 0, hucMock); + HFuture<HttpURLConnection> future = (HFuture<HttpURLConnection>) client.futureCreate(HttpURLConnection.class); + + // Test a bad response code (default 0) without output + assertThat(future.get(0), is(false)); + assertThat(future.body().length(), is(0)); + + // Test a bad response code (default 0) with output + ByteArrayInputStream bais = new ByteArrayInputStream(errorString.getBytes()); + when(hucMock.getInputStream()).thenReturn(bais); + assertThat(future.get(0), is(false)); + assertThat(future.body(), is(errorString)); + + // Test a good response code + when(hucMock.getResponseCode()).thenReturn(201); + assertThat(future.get(0), is(true)); + } + + @Test + public void futureReadStringTest() throws LocatorException, CadiException, IOException { + HClient client = new HClientStub(ssMock, uri, 0, hucMock); + Future<String> future = client.futureReadString(); + + // Test a bad response code (default 0) without output + assertThat(future.get(0), is(false)); + assertThat(future.body().length(), is(0)); + + // Test a bad response code (default 0) with output + when(hucMock.getInputStream()).thenReturn(new ByteArrayInputStream(errorString.getBytes())); + assertThat(future.get(0), is(false)); + assertThat(future.body(), is(errorString)); + + // Test a good response code + when(hucMock.getInputStream()).thenReturn(new ByteArrayInputStream(successString.getBytes())); + when(hucMock.getResponseCode()).thenReturn(200); + assertThat(future.get(0), is(true)); + assertThat(future.body(), is(successString)); + } + + @Test + public void futureReadTest() throws LocatorException, CadiException, IOException, APIException { + HClient client = new HClientStub(ssMock, uri, 0, hucMock); + Future<HttpURLConnection> future = client.futureRead(dfMock, null); + + // Test a bad response code (default 0) without output + assertThat(future.get(0), is(false)); + assertThat(future.body().length(), is(0)); + + // Test a bad response code (default 0) with output + when(hucMock.getInputStream()).thenReturn(new ByteArrayInputStream(errorString.getBytes())); + assertThat(future.get(0), is(false)); + assertThat(future.body(), is(errorString)); + + // Test a good response code + when(hucMock.getInputStream()).thenReturn(new ByteArrayInputStream(successString.getBytes())); + when(dfMock.newData()).thenReturn(dataMock); + when(dataMock.in(null)).thenReturn(dataMock); + when(dataMock.load((InputStream)any())).thenReturn(dataMock); + when(dataMock.asObject()).thenReturn(hucMock); + when(dataMock.asString()).thenReturn(successString); + when(hucMock.getResponseCode()).thenReturn(200); + assertThat(future.get(0), is(true)); + assertThat(future.body(), is(successString)); + } + + @Test + public void future1Test() throws LocatorException, CadiException, IOException, APIException { + HClient client = new HClientStub(ssMock, uri, 0, hucMock); + Future<HttpURLConnection> future = client.future(hucMock); + + // Test a good response code + when(hucMock.getInputStream()).thenReturn(new ByteArrayInputStream(successString.getBytes())); + when(hucMock.getResponseCode()).thenReturn(200); + assertThat(future.get(0), is(true)); + assertThat(future.body(), is("200")); + + // Test a bad response code + when(hucMock.getResponseCode()).thenReturn(0); + when(hucMock.getInputStream()).thenReturn(new ByteArrayInputStream(errorString.getBytes())); + assertThat(future.get(0), is(false)); + assertThat(future.body(), is(errorString)); + } + + @Test + public void future2Test() throws LocatorException, CadiException, IOException, APIException { + HClient client = new HClientStub(ssMock, uri, 0, hucMock); + Future<Void> future = client.future(respMock, 200); + + ServletOutputStream sos = new ServletOutputStream() { + @Override public void write(int arg0) throws IOException { } + }; + when(respMock.getOutputStream()).thenReturn(sos); + + // Test a good response code + when(hucMock.getInputStream()).thenReturn(new ByteArrayInputStream(successString.getBytes())); + when(hucMock.getResponseCode()).thenReturn(200); + assertThat(future.get(0), is(true)); + assertThat(future.body(), is(nullValue())); + + // Test a bad response code + when(hucMock.getResponseCode()).thenReturn(0); + when(hucMock.getInputStream()).thenReturn(new ByteArrayInputStream(errorString.getBytes())); + assertThat(future.get(0), is(false)); + assertThat(future.body(), is("")); + } + + @Test + public void hfutureTest() throws CadiException, IOException, LocatorException { + HClient client = new HClientStub(ssMock, uri, 0, hucMock); + HFutureStub future = new HFutureStub(client, hucMock); + assertThat(future.get(0), is(false)); + + // Test a bad response code (default 0) with output + when(hucMock.getInputStream()).thenReturn(new ByteArrayInputStream(errorString.getBytes())); + assertThat(future.get(0), is(false)); + + assertThat(future.get(0), is(false)); + + when(hucMock.getResponseCode()).thenReturn(200); + assertThat(future.get(0), is(true)); + + StringBuilder sb = future.inputStreamToString(new ByteArrayInputStream(errorString.getBytes())); + assertThat(sb.toString(), is(errorString)); + + assertThat(future.code(), is(200)); + assertThat(future.huc(), is(hucMock)); + + assertThat(future.exception(), is(nullValue())); + assertThat(future.header("string"), is(nullValue())); + + // coverage... + future.setHuc(null); + future.close(); + } + + @Test + public void headerTest() throws LocatorException { + HClient client = new HClientStub(ssMock, uri, 0, hucMock); + String tag1 = "tag1"; + String tag2 = "tag2"; + String value1 = "value1"; + String value2 = "value2"; + client.addHeader(tag1, value1); + client.addHeader(tag2, value2); + } + + @Test(expected = LocatorException.class) + public void throws1Test() throws LocatorException { + @SuppressWarnings("unused") + HClient client = new HClient(ssMock, null, 0); + } + + private class HClientStub extends HClient { + public HClientStub(SecuritySetter<HttpURLConnection> ss, URI uri, int connectTimeout, HttpURLConnection huc) throws LocatorException { + super(ss, uri, connectTimeout); + setHuc(huc); + } + public void setHuc(HttpURLConnection huc) { + Field field; + try { + field = HClient.class.getDeclaredField("huc"); + field.setAccessible(true); + field.set(this, huc); + field.setAccessible(false); + } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { + e.printStackTrace(); + fail("Caught an exception: " + e.getMessage()); + } + } + @Override + public HttpURLConnection getConnection(URI uri, StringBuilder pi) throws IOException { + return hucMock; + } + } + + private class HFutureStub extends HFuture<HttpURLConnection> { + public HFutureStub(HClient hClient, HttpURLConnection huc) { + hClient.super(huc); + } + + @Override public String body() { return null; } + public void setHuc(HttpURLConnection huc) { this.huc = huc; } + } + +} diff --git a/cadi/client/src/test/java/org/onap/aaf/cadi/http/test/JU_HMangr.java b/cadi/client/src/test/java/org/onap/aaf/cadi/http/test/JU_HMangr.java new file mode 100644 index 00000000..b7415a52 --- /dev/null +++ b/cadi/client/src/test/java/org/onap/aaf/cadi/http/test/JU_HMangr.java @@ -0,0 +1,265 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.http.test; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.net.ConnectException; +import java.net.HttpURLConnection; +import java.net.SocketException; +import java.net.URI; +import java.net.URISyntaxException; + +import javax.net.ssl.SSLHandshakeException; + +import static org.hamcrest.CoreMatchers.*; + +import org.junit.*; +import org.mockito.*; +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.Locator; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.SecuritySetter; +import org.onap.aaf.cadi.client.Rcli; +import org.onap.aaf.cadi.client.Retryable; +import org.onap.aaf.cadi.http.HMangr; +import org.onap.aaf.misc.env.APIException; + +public class JU_HMangr { + + @Mock Locator<URI> locMock; + @Mock SecuritySetter<HttpURLConnection> ssMock; + @Mock Retryable<Void> retryableMock; + @Mock Retryable<Integer> goodRetry; + @Mock Locator.Item itemMock; + @Mock Rcli<Object> clientMock; + + private PropAccess access; + private URI uri; + private final static String uriString = "http://example.com"; + + @Before + public void setup() throws URISyntaxException { + MockitoAnnotations.initMocks(this); + + access = new PropAccess(new PrintStream(new ByteArrayOutputStream()), new String[0]); + uri = new URI(uriString); + } + + @Test + public void sameTest() throws LocatorException, APIException, CadiException, ConnectException { + HMangr hman = new HMangr(access, locMock); + when(retryableMock.item()).thenReturn(itemMock); + when(locMock.get(itemMock)).thenReturn(uri); + assertThat(hman.same(ssMock, retryableMock), is(nullValue())); + + //coverage... + when(retryableMock.lastClient()).thenReturn(clientMock); + assertThat(hman.same(ssMock, retryableMock), is(nullValue())); + + CadiException cadiException; + + ConnectException connectException = new ConnectException(); + cadiException = new CadiException(connectException); + doThrow(cadiException).when(retryableMock).code(clientMock); + when(locMock.hasItems()).thenReturn(true).thenReturn(false); + assertThat(hman.same(ssMock, retryableMock), is(nullValue())); + + SocketException socketException = new SocketException(); + cadiException = new CadiException(socketException); + doThrow(cadiException).when(retryableMock).code(clientMock); + when(locMock.hasItems()).thenReturn(true).thenReturn(false); + assertThat(hman.same(ssMock, retryableMock), is(nullValue())); + + doThrow(connectException).when(retryableMock).code(clientMock); + assertThat(hman.same(ssMock, retryableMock), is(nullValue())); + + } + + @Test(expected = LocatorException.class) + public void throwsLocatorException1Test() throws LocatorException { + @SuppressWarnings("unused") + HMangr hman = new HMangr(access, null); + } + + @Test(expected = LocatorException.class) + public void throwsLocatorException2Test() throws LocatorException, APIException, CadiException { + HMangr hman = new HMangr(access, locMock); + hman.same(ssMock, retryableMock); + } + + @Test(expected = LocatorException.class) + public void throwsLocatorException3Test() throws LocatorException, APIException, CadiException { + HMangr hman = new HMangr(access, locMock); + when(locMock.best()).thenReturn(itemMock); + when(locMock.hasItems()).thenReturn(true).thenReturn(false); + hman.same(ssMock, retryableMock); + } + + @SuppressWarnings("unchecked") + @Test(expected = CadiException.class) + public void throwsCadiException1Test() throws LocatorException, APIException, CadiException, ConnectException { + HMangr hman = new HMangr(access, locMock); + when(retryableMock.item()).thenReturn(itemMock); + when(locMock.get(itemMock)).thenReturn(uri); + when(retryableMock.lastClient()).thenReturn(clientMock); + when(retryableMock.code(clientMock)).thenThrow(CadiException.class); + hman.same(ssMock, retryableMock); + } + + @Test(expected = CadiException.class) + public void throwsCadiException2Test() throws LocatorException, APIException, CadiException, ConnectException { + HMangr hman = new HMangr(access, locMock); + when(retryableMock.item()).thenReturn(itemMock); + when(locMock.get(itemMock)).thenReturn(uri); + when(retryableMock.lastClient()).thenReturn(clientMock); + + ConnectException connectException = new ConnectException(); + CadiException cadiException = new CadiException(connectException); + doThrow(cadiException).when(retryableMock).code(clientMock); + hman.same(ssMock, retryableMock); + } + + @Test(expected = CadiException.class) + public void throwsCadiException3Test() throws LocatorException, APIException, CadiException, ConnectException { + HMangr hman = new HMangr(access, locMock); + when(retryableMock.item()).thenReturn(itemMock); + when(locMock.get(itemMock)).thenReturn(uri); + when(retryableMock.lastClient()).thenReturn(clientMock); + + SocketException socketException = new SocketException(); + CadiException cadiException = new CadiException(socketException); + doThrow(cadiException).when(retryableMock).code(clientMock); + hman.same(ssMock, retryableMock); + } + + @Test(expected = CadiException.class) + public void throwsCadiException4Test() throws LocatorException, APIException, CadiException, ConnectException { + HMangr hman = new HMangr(access, locMock); + when(retryableMock.item()).thenReturn(itemMock); + when(locMock.get(itemMock)).thenReturn(uri); + when(retryableMock.lastClient()).thenReturn(clientMock); + + Exception e = new Exception(); + CadiException cadiException = new CadiException(e); + doThrow(cadiException).when(retryableMock).code(clientMock); + hman.same(ssMock, retryableMock); + } + + @Test + public void allTest() throws LocatorException, CadiException, APIException { + HManagerStub hman = new HManagerStub(access, locMock); + assertThat(hman.best(ssMock, retryableMock), is(nullValue())); + assertThat(hman.all(ssMock, retryableMock), is(nullValue())); + assertThat(hman.all(ssMock, retryableMock, true), is(nullValue())); + } + + @Test + public void oneOfTest() throws LocatorException, CadiException, APIException, ConnectException { + HMangr hman = new HMangr(access, locMock); + assertThat(hman.oneOf(ssMock, retryableMock, false, "host"), is(nullValue())); + + try { + hman.oneOf(ssMock, retryableMock, true, "host"); + fail("Should've thrown an exception"); + } catch (LocatorException e) { + } + + when(locMock.first()).thenReturn(itemMock); + when(locMock.get(itemMock)).thenReturn(uri); + + // Branching coverage... + assertThat(hman.oneOf(ssMock, retryableMock, false, null), is(nullValue())); + assertThat(hman.oneOf(ssMock, retryableMock, false, "host"), is(nullValue())); + + assertThat(hman.oneOf(ssMock, retryableMock, false, uriString.substring(7)), is(nullValue())); + + CadiException cadiException; + + cadiException = new CadiException(new ConnectException()); + doThrow(cadiException).when(retryableMock).code((Rcli<?>) any()); + assertThat(hman.oneOf(ssMock, retryableMock, false, uriString.substring(7)), is(nullValue())); + + cadiException = new CadiException(new SSLHandshakeException(null)); + doThrow(cadiException).when(retryableMock).code((Rcli<?>) any()); + assertThat(hman.oneOf(ssMock, retryableMock, false, uriString.substring(7)), is(nullValue())); + + cadiException = new CadiException(new SocketException()); + doThrow(cadiException).when(retryableMock).code((Rcli<?>) any()); + try { + hman.oneOf(ssMock, retryableMock, false, uriString.substring(7)); + fail("Should've thrown an exception"); + } catch (CadiException e) { + } + + cadiException = new CadiException(new SocketException("java.net.SocketException: Connection reset")); + doThrow(cadiException).when(retryableMock).code((Rcli<?>) any()); + try { + hman.oneOf(ssMock, retryableMock, false, uriString.substring(7)); + fail("Should've thrown an exception"); + } catch (CadiException e) { + } + + cadiException = new CadiException(); + doThrow(cadiException).when(retryableMock).code((Rcli<?>) any()); + try { + hman.oneOf(ssMock, retryableMock, false, uriString.substring(7)); + fail("Should've thrown an exception"); + } catch (CadiException e) { + } + + doThrow(new ConnectException()).when(retryableMock).code((Rcli<?>) any()); + assertThat(hman.oneOf(ssMock, retryableMock, false, uriString.substring(7)), is(nullValue())); + + when(goodRetry.code((Rcli<?>) any())).thenReturn(5); + assertThat(hman.oneOf(ssMock, goodRetry, false, uriString.substring(7)), is(5)); + } + + @Test + public void coverageTest() throws LocatorException { + HMangr hman = new HMangr(access, locMock); + hman.readTimeout(5); + assertThat(hman.readTimeout(), is(5)); + hman.connectionTimeout(5); + assertThat(hman.connectionTimeout(), is(5)); + hman.apiVersion("v1.0"); + assertThat(hman.apiVersion(), is("v1.0")); + hman.close(); + + } + + private class HManagerStub extends HMangr { + public HManagerStub(Access access, Locator<URI> loc) throws LocatorException { super(access, loc); } + @Override public<RET> RET same(SecuritySetter<HttpURLConnection> ss, Retryable<RET> retryable) { + return null; + } + @Override public<RET> RET oneOf(SecuritySetter<HttpURLConnection> ss, Retryable<RET> retryable, boolean notify, String host) { + return null; + } + } + +}
\ No newline at end of file diff --git a/cadi/client/src/test/java/org/onap/aaf/cadi/http/test/JU_HNoAuthSS.java b/cadi/client/src/test/java/org/onap/aaf/cadi/http/test/JU_HNoAuthSS.java new file mode 100644 index 00000000..9179aa36 --- /dev/null +++ b/cadi/client/src/test/java/org/onap/aaf/cadi/http/test/JU_HNoAuthSS.java @@ -0,0 +1,60 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.http.test; + +import java.io.IOException; +import java.net.HttpURLConnection; + +import javax.net.ssl.HttpsURLConnection; + +import org.junit.*; +import org.mockito.*; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.config.SecurityInfoC; +import org.onap.aaf.cadi.http.HNoAuthSS; + +public class JU_HNoAuthSS { + + @Mock + SecurityInfoC<HttpURLConnection> siMock; + + @Mock + HttpURLConnection httpMock; + + @Mock + HttpsURLConnection httpsMock; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void test() throws IOException, CadiException { + HNoAuthSS noAuth = new HNoAuthSS(null); + noAuth.setSecurity(httpMock); + noAuth = new HNoAuthSS(siMock); + noAuth.setSecurity(httpMock); + noAuth.setSecurity(httpsMock); + } + +} diff --git a/cadi/client/src/test/java/org/onap/aaf/cadi/http/test/JU_HRcli.java b/cadi/client/src/test/java/org/onap/aaf/cadi/http/test/JU_HRcli.java new file mode 100644 index 00000000..06055ad6 --- /dev/null +++ b/cadi/client/src/test/java/org/onap/aaf/cadi/http/test/JU_HRcli.java @@ -0,0 +1,121 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.http.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import static org.mockito.Mockito.*; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URISyntaxException; + +import org.junit.*; +import org.mockito.*; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.Locator; +import org.onap.aaf.cadi.Locator.Item; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.SecuritySetter; +import org.onap.aaf.cadi.client.EClient; +import org.onap.aaf.cadi.http.HMangr; +import org.onap.aaf.cadi.http.HRcli; + +public class JU_HRcli { + + @Mock + SecuritySetter<HttpURLConnection> ssMock; + + @Mock + Locator<URI> locMock; + + @Mock + Locator.Item itemMock; + + private HMangr hman; + private PropAccess access; + private static URI uri; + + private static final String uriString = "example.com"; + + @Before + public void setup() throws LocatorException, URISyntaxException { + MockitoAnnotations.initMocks(this); + + access = new PropAccess(new PrintStream(new ByteArrayOutputStream()), new String[0]); + hman = new HMangr(access, locMock); + uri = new URI(uriString); + + when(locMock.get(itemMock)).thenReturn(uri); + } + + @Test(expected = CadiException.class) + public void publicInterfaceTest() throws URISyntaxException, LocatorException, CadiException { + HRcli hrcli = new HRcli(hman, itemMock, ssMock); + assertThat(hrcli.setManager(hman), is(hrcli)); + assertThat(hrcli.toString(), is(uriString)); + + hrcli.setSecuritySetter(ssMock); + assertThat(hrcli.getSecuritySetter(), is(ssMock)); + + // No throw + hrcli.invalidate(); + // Throw + doThrow(CadiException.class).when(locMock).invalidate(itemMock); + hrcli.invalidate(); + } + + @Test(expected = CadiException.class) + public void protectedInterfaceTest() throws CadiException, LocatorException { + HRcliStub hrcli = new HRcliStub(hman, uri, itemMock, ssMock); + HRcli clone = hrcli.clone(uri, ssMock); + assertThat(clone.toString(), is(hrcli.toString())); + + EClient<HttpURLConnection> eclient = hrcli.client(); + assertThat(eclient, is(not(nullValue()))); + + hrcli = new HRcliStub(hman, null, itemMock, ssMock); + when(locMock.best()).thenReturn(itemMock); + eclient = hrcli.client(); + assertThat(eclient, is(not(nullValue()))); + + hrcli = new HRcliStub(hman, null, itemMock, ssMock); + when(locMock.best()).thenReturn(null); + eclient = hrcli.client(); + } + + private class HRcliStub extends HRcli { + public HRcliStub(HMangr hman, URI uri, Item locItem, SecuritySetter<HttpURLConnection> secSet) { + super(hman, uri, locItem, secSet); + } + public HRcli clone(URI uri, SecuritySetter<HttpURLConnection> ss) { + return super.clone(uri, ss); + } + public EClient<HttpURLConnection> client() throws CadiException { + return super.client(); + } + } + +} diff --git a/cadi/client/src/test/java/org/onap/aaf/cadi/http/test/JU_HTokenSS.java b/cadi/client/src/test/java/org/onap/aaf/cadi/http/test/JU_HTokenSS.java new file mode 100644 index 00000000..0fb695ec --- /dev/null +++ b/cadi/client/src/test/java/org/onap/aaf/cadi/http/test/JU_HTokenSS.java @@ -0,0 +1,51 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.http.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; + +import java.io.IOException; +import java.net.HttpURLConnection; + +import org.junit.*; +import org.mockito.*; +import org.onap.aaf.cadi.config.SecurityInfoC; +import org.onap.aaf.cadi.http.HTokenSS; + +public class JU_HTokenSS { + + @Mock + SecurityInfoC<HttpURLConnection> siMock; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void test() throws IOException { + HTokenSS token = new HTokenSS(siMock, "string1", "string2"); + assertThat(token, is(not(nullValue()))); + } + +} diff --git a/cadi/client/src/test/java/org/onap/aaf/cadi/http/test/JU_HTransferSS.java b/cadi/client/src/test/java/org/onap/aaf/cadi/http/test/JU_HTransferSS.java new file mode 100644 index 00000000..fcb25d4e --- /dev/null +++ b/cadi/client/src/test/java/org/onap/aaf/cadi/http/test/JU_HTransferSS.java @@ -0,0 +1,92 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.http.test; + +import java.io.IOException; +import java.net.HttpURLConnection; + +import javax.net.ssl.HttpsURLConnection; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; +import static org.hamcrest.CoreMatchers.*; + +import org.junit.*; +import org.mockito.*; + +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.SecuritySetter; +import org.onap.aaf.cadi.config.SecurityInfoC; + +import org.onap.aaf.cadi.http.HTransferSS; +import org.onap.aaf.cadi.principal.TaggedPrincipal; + +public class JU_HTransferSS { + + @Mock + TaggedPrincipal princMock; + + @Mock + HttpURLConnection hucMock; + + @Mock + HttpsURLConnection hucsMock; + + @Mock + SecurityInfoC<HttpURLConnection> siMock; + + @Mock + SecurityInfoC<HttpURLConnection> siMockNoDefSS; + + @Mock + SecuritySetter<HttpURLConnection> ssMock; + + private static final String princName = "name"; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + when(princMock.getName()).thenReturn(princName); + siMock.defSS = ssMock; + } + + @Test + public void test() throws IOException, CadiException { + HTransferSS transfer = new HTransferSS(princMock, "string1"); + assertThat(transfer.setLastResponse(0), is(0)); + + transfer = new HTransferSS(princMock, "string1", siMock); + transfer.setSecurity(hucsMock); + assertThat(transfer.getID(), is(princName)); + + transfer = new HTransferSS(null, "string1", siMock); + transfer.setSecurity(hucsMock); + assertThat(transfer.getID(), is("")); + } + + @Test(expected = CadiException.class) + public void testThrows() throws CadiException { + HTransferSS transfer = new HTransferSS(princMock, "string1", siMockNoDefSS); + transfer.setSecurity(hucMock); + } + +} diff --git a/cadi/client/src/test/java/org/onap/aaf/cadi/http/test/JU_HX509SS.java b/cadi/client/src/test/java/org/onap/aaf/cadi/http/test/JU_HX509SS.java new file mode 100644 index 00000000..0c086e4b --- /dev/null +++ b/cadi/client/src/test/java/org/onap/aaf/cadi/http/test/JU_HX509SS.java @@ -0,0 +1,121 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.http.test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.net.HttpURLConnection; +import java.security.PrivateKey; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.X509KeyManager; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; +import static org.hamcrest.CoreMatchers.*; + +import org.junit.*; +import org.mockito.*; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.config.SecurityInfoC; +import org.onap.aaf.cadi.http.HX509SS; +import org.onap.aaf.misc.env.APIException; + +public class JU_HX509SS { + + @Mock X509Certificate x509Mock; + @Mock X509KeyManager keyManagerMock; + @Mock PrivateKey privateKeyMock; + @Mock SecurityInfoC<HttpURLConnection> siMock; + @Mock HttpURLConnection hucMock; + @Mock HttpsURLConnection hucsMock; + + private final static String alias = "Some alias"; + private final static String algName = "Some algName"; + private final static byte[] publicKeyBytes = "a public key".getBytes(); + + private PropAccess access; + private SecurityInfoC<HttpURLConnection> si; + + @Before + public void setup() throws IOException, CadiException, CertificateEncodingException { + MockitoAnnotations.initMocks(this); + + when(x509Mock.getSigAlgName()).thenReturn(algName); + when(x509Mock.getEncoded()).thenReturn(publicKeyBytes); + + when(keyManagerMock.getCertificateChain(alias)).thenReturn(new X509Certificate[] {x509Mock}); + when(keyManagerMock.getPrivateKey(alias)).thenReturn(privateKeyMock); + + when(siMock.getKeyManagers()).thenReturn(new X509KeyManager[] {keyManagerMock}); + + access = new PropAccess(new PrintStream(new ByteArrayOutputStream()), new String[0]); + access.setProperty(Config.CADI_ALIAS, alias); + si = SecurityInfoC.instance(access, HttpURLConnection.class); + } + + @Test + public void test() throws APIException, CadiException { + HX509SS x509 = new HX509SS(alias, siMock); + assertThat(x509.getID(), is(alias)); + assertThat(x509.setLastResponse(0), is(0)); + assertThat(x509.setLastResponse(1), is(0)); + assertThat(x509.setLastResponse(2), is(0)); + + // coverage... + x509.setSecurity(hucMock); + x509.setSecurity(hucsMock); + } + + // TODO: Test the setSecurity method - Ian + // @Test + // public void test2() throws APIException, CadiException { + // HX509SS x509 = new HX509SS(si, false); + // x509.setSecurity(hucMock); + // x509.setSecurity(hucsMock); + // } + + @Test(expected = APIException.class) + public void throws1Test() throws APIException, CadiException { + @SuppressWarnings("unused") + HX509SS x509 = new HX509SS(siMock); + } + + @Test(expected = APIException.class) + public void throws2Test() throws APIException, CadiException { + @SuppressWarnings("unused") + HX509SS x509 = new HX509SS(si, false); + } + + @Test(expected = APIException.class) + public void throws3Test() throws APIException, CadiException { + when(keyManagerMock.getCertificateChain(alias)).thenReturn(new X509Certificate[0]); + @SuppressWarnings("unused") + HX509SS x509 = new HX509SS(alias, siMock); + } + +} diff --git a/cadi/client/src/test/java/org/onap/aaf/cadi/locator/test/JU_DNSLocator.java b/cadi/client/src/test/java/org/onap/aaf/cadi/locator/test/JU_DNSLocator.java new file mode 100644 index 00000000..a80e52f7 --- /dev/null +++ b/cadi/client/src/test/java/org/onap/aaf/cadi/locator/test/JU_DNSLocator.java @@ -0,0 +1,125 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.locator.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import org.junit.*; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.net.URI; + +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.Locator.Item; +import org.onap.aaf.cadi.locator.DNSLocator; + +public class JU_DNSLocator { + + private PropAccess access; + + @Before + public void setup() { + access = new PropAccess(new PrintStream(new ByteArrayOutputStream()), new String[0]); + } + + @Test + public void test() throws LocatorException { + DNSLocator dl; + Item item; + URI uri; + + dl = new DNSLocator(access, "https", "localhost", "8100-8101"); + + item = dl.best(); + uri = dl.get(item); + assertThat(uri.toString(), is("https://127.0.0.1:8100")); + item = dl.best(); + assertThat(uri.toString(), is("https://127.0.0.1:8100")); + + assertThat(dl.hasItems(), is(true)); + for (item = dl.first(); item != null; item = dl.next(item)) { + dl.invalidate(item); + } + assertThat(dl.hasItems(), is(false)); + + // This doesn't actually do anything besides increase coverage + dl.destroy(); + } + + @Test + public void constructorTest() throws LocatorException { + // For coverage + new DNSLocator(access, "https", "localhost", "8100"); + new DNSLocator(access, "https", "localhost", "8100-8101"); + + new DNSLocator(access, "http:localhost"); + new DNSLocator(access, "https:localhost"); + new DNSLocator(access, "https:localhost:8100"); + new DNSLocator(access, "https:localhost:[8100]"); + new DNSLocator(access, "https:localhost:[8100-8101]"); + new DNSLocator(access, "https:localhost:8000/"); + } + + @Test + public void refreshTest() throws LocatorException { + DNSLocator dl = new DNSLocator(access, "https", "bogushost", "8100-8101"); + assertThat(dl.refresh(), is(false)); + } + + @Test(expected = LocatorException.class) + public void throws1Test() throws LocatorException { + new DNSLocator(access, null); + } + + @Test(expected = LocatorException.class) + public void throws2Test() throws LocatorException { + new DNSLocator(access, "ftp:invalid"); + } + + @Test(expected = LocatorException.class) + public void throws3Test() throws LocatorException { + new DNSLocator(access, "https:localhost:[8100"); + } + + @Test(expected = LocatorException.class) + public void throws4Test() throws LocatorException { + new DNSLocator(access, "https:localhost:[]"); + } + + @Test(expected = LocatorException.class) + public void throws5Test() throws LocatorException { + new DNSLocator(access, "https:localhost:[8100-]"); + } + + @Test(expected = LocatorException.class) + public void throws6Test() throws LocatorException { + new DNSLocator(access, "https:localhost:[-8101]"); + } + + @Test(expected = LocatorException.class) + public void throws7Test() throws LocatorException { + new DNSLocator(access, "https:localhost:/"); + } + +} diff --git a/cadi/client/src/test/java/org/onap/aaf/cadi/locator/test/JU_HClientHotPeerLocator.java b/cadi/client/src/test/java/org/onap/aaf/cadi/locator/test/JU_HClientHotPeerLocator.java new file mode 100644 index 00000000..1478cafe --- /dev/null +++ b/cadi/client/src/test/java/org/onap/aaf/cadi/locator/test/JU_HClientHotPeerLocator.java @@ -0,0 +1,150 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.locator.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import org.junit.*; +import org.mockito.*; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.Locator; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.http.HClient; +import org.onap.aaf.cadi.http.HX509SS; +import org.onap.aaf.cadi.locator.HClientHotPeerLocator; + +public class JU_HClientHotPeerLocator { + + @Mock private HX509SS ssMock; + + private PropAccess access; + private ByteArrayOutputStream outStream; + + // Note: - The IP and port are irrelevant for these tests + private static final String goodURL1 = "fakeIP1:fakePort1/38/-90"; // Approx St Louis + private static final String goodURL2 = "fakeIP2:fakePort2/33/-96"; // Approx Dallas + private static final String badURL = "~%$!@#$//"; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + + outStream = new ByteArrayOutputStream(); + access = new PropAccess(new PrintStream(outStream), new String[0]); + } + + @Test + public void test() throws LocatorException { + HClientHotPeerLocator loc; + String urlStr = goodURL1 + ',' + goodURL2; + loc = new HClientHotPeerLocator(access, urlStr, 0, "38.627", "-90.199", ssMock); + assertThat(loc.hasItems(), is(true)); + + String[] messages = outStream.toString().split("\n"); + String preffered = messages[0].split(" ", 4)[3]; + String alternate = messages[1].split(" ", 4)[3]; + assertThat(preffered, is("Preferred Client is " + goodURL1)); + assertThat(alternate, is("Alternate Client is " + goodURL2)); + + HClient firstClient = loc.get(loc.first()); + HClient bestClient = loc.bestClient(); + assertThat(bestClient, is(firstClient)); + + Locator.Item item = loc.first(); + assertThat(loc.info(item), is(goodURL1)); + + item = loc.next(item); + assertThat(loc.info(item), is(goodURL2)); + + item = loc.next(item); + assertThat(item, is(nullValue())); + assertThat(loc.info(item), is("Invalid Item")); + + item = loc.first(); + loc.invalidate(item); + + loc.invalidate(loc.bestClient()); + loc.invalidate(loc.get(loc.next(item))); + loc.destroy(); + } + + @Test(expected = LocatorException.class) + public void failuresTest() throws LocatorException { + HClientHotPeerLocator loc; + String urlStr = goodURL1 + ',' + goodURL2 + ',' + badURL; + loc = new HClientHotPeerLocator(access, urlStr, 1000000, "38.627", "-90.199", ssMock); + String[] messages = outStream.toString().split("\n"); + String preffered = messages[0].split(" ", 4)[3]; + String alternate1 = messages[1].split(" ", 4)[3]; + String alternate2 = messages[2].split(" ", 4)[3]; + assertThat(preffered, is("Preferred Client is " + badURL)); + assertThat(alternate1, is("Alternate Client is " + goodURL1)); + assertThat(alternate2, is("Alternate Client is " + goodURL2)); + + outStream.reset(); + + loc.invalidate(loc.first()); + + loc.destroy(); + loc.best(); + } + + @Test + public void hasNoItemTest() throws LocatorException { + HClientHotPeerLocator loc; + loc = new HClientHotPeerLocator(access, badURL, 0, "38.627", "-90.199", ssMock); + assertThat(loc.hasItems(), is(false)); + loc.invalidate(loc.first()); + } + + @Test(expected = LocatorException.class) + public void invalidClientTest() throws LocatorException { + @SuppressWarnings("unused") + HClientHotPeerLocator loc = new HClientHotPeerLocator(access, "InvalidClient", 0, "38.627", "-90.199", ssMock); + } + + @Test(expected = LocatorException.class) + public void coverageTest() throws LocatorException { + CoverageLocator loc; + String urlStr = goodURL1 + ',' + goodURL2; + loc = new CoverageLocator(access, urlStr, 0, "38.627", "-90.199", ssMock); + assertThat(loc._invalidate(null), is(nullValue())); + loc._destroy(null); + + loc._newClient("bad string"); + } + + private class CoverageLocator extends HClientHotPeerLocator { + public CoverageLocator(Access access, String urlstr, long invalidateTime, String localLatitude, + String localLongitude, HX509SS ss) throws LocatorException { + super(access, urlstr, invalidateTime, localLatitude, localLongitude, ss); + } + public HClient _newClient(String clientInfo) throws LocatorException { return super._newClient(clientInfo); } + public HClient _invalidate(HClient client) { return super._invalidate(client); } + public void _destroy(HClient client) { super._destroy(client); } + } +} diff --git a/cadi/client/src/test/java/org/onap/aaf/cadi/locator/test/JU_PropertyLocator.java b/cadi/client/src/test/java/org/onap/aaf/cadi/locator/test/JU_PropertyLocator.java new file mode 100644 index 00000000..b7558c02 --- /dev/null +++ b/cadi/client/src/test/java/org/onap/aaf/cadi/locator/test/JU_PropertyLocator.java @@ -0,0 +1,114 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.locator.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import static org.mockito.Mockito.*; +import org.junit.*; +import org.mockito.*; + +import java.net.Socket; +import java.net.URI; + +import org.onap.aaf.cadi.Locator.Item; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.locator.PropertyLocator; + +public class JU_PropertyLocator { + + @Mock + Socket socketMock; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + + when(socketMock.isConnected()).thenReturn(true); + when(socketMock.isClosed()).thenReturn(true).thenReturn(false); + } + + @Test + public void test() throws Exception { + String uris = "https://fred.wilma.com:26444,https://tom.jerry.com:[534-535]"; + PropertyLocator pl = new PropertyLocator(uris, 0L, 1000*60*20L) { + @Override protected Socket createSocket() { return socketMock; } + }; + String str = pl.toString(); + assertThat(str.contains("https://fred.wilma.com:26444"), is(true)); + assertThat(str.contains("https://tom.jerry.com:534"), is(true)); + assertThat(str.contains("https://tom.jerry.com:535"), is(true)); + + Item item = pl.first(); + assertThat(item.toString(), is("Item: 0 order: 0")); + + URI uri = pl.get(item); + assertThat(uri.toString(), is("https://fred.wilma.com:26444")); + + assertThat(pl.get(null), is(nullValue())); + + assertThat(pl.hasItems(), is(true)); + + assertThat(countItems(pl), is(3)); + pl.invalidate(pl.best()); + + assertThat(countItems(pl), is(2)); + pl.invalidate(pl.best()); + + assertThat(countItems(pl), is(1)); + + pl.invalidate(pl.best()); + + assertThat(pl.hasItems(), is(false)); + assertThat(countItems(pl), is(0)); + + pl.refresh(); + + assertThat(pl.hasItems(), is(true)); + + assertThat(pl.next(null), is(nullValue())); + + // coverage... + pl.invalidate(null); + pl.invalidate(null); + pl.invalidate(null); + pl.invalidate(null); + + pl.destroy(); + + pl = new PropertyLocator(uris); + } + + @Test(expected=LocatorException.class) + public void exceptionTest() throws LocatorException { + new PropertyLocator(null); + } + + private int countItems(PropertyLocator pl) throws LocatorException { + int count = 0; + for(Item i = pl.first(); i != null; i = pl.next(i)) { + ++count; + } + return count; + } + +} diff --git a/cadi/client/src/test/java/org/onap/aaf/cadi/routing/test/JU_GreatCircle.java b/cadi/client/src/test/java/org/onap/aaf/cadi/routing/test/JU_GreatCircle.java new file mode 100644 index 00000000..f492d4a2 --- /dev/null +++ b/cadi/client/src/test/java/org/onap/aaf/cadi/routing/test/JU_GreatCircle.java @@ -0,0 +1,79 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.routing.test; + +import static org.junit.Assert.*; +import org.junit.*; + +import org.onap.aaf.cadi.routing.GreatCircle; + +public class JU_GreatCircle { + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + } + + @AfterClass + public static void tearDownAfterClass() throws Exception { + } + + @Before + public void setUp() throws Exception { + } + + @After + public void tearDown() throws Exception { + } + + @Test + public void calc1Test() { + assertEquals(7.803062505568182, GreatCircle.calc(38.627345, -90.193774, 35.252234, -81.384929), 0.000000001); + assertEquals(0.0, GreatCircle.calc(38.627345, -90.193774, 38.627345, -90.193774), 0.000000001); + assertEquals(7.803062505568182, GreatCircle.calc(35.252234, -81.384929, 38.627345, -90.193774), 0.000000001); + assertEquals(7.803062505568182, GreatCircle.calc(38.627345, -90.193774, 35.252234, -81.384929), 0.000000001); + assertEquals(7.803062505568182, GreatCircle.calc(-38.627345, 90.193774, -35.252234, 81.384929), 0.000000001); + assertEquals(105.71060033936052, GreatCircle.calc(-38.627345, 90.193774, -35.252234, -81.384929), 0.000000001); + assertEquals(105.71060033936052, GreatCircle.calc(38.627345, -90.193774, 35.252234, 81.384929), 0.000000001); + assertEquals(74.32786874922931, GreatCircle.calc(-38.627345, 90.193774, 35.252234, 81.384929), 0.000000001); + } + + @Test + public void calc2Test() { + assertEquals(7.803062505568182, GreatCircle.calc(new String[] {"38.627345", "-90.193774", "35.252234", "-81.384929"}), 0.000000001); + assertEquals(7.803062505568182, GreatCircle.calc(new String[] {"38.627345,-90.193774", "35.252234,-81.384929"}), 0.000000001); + assertEquals(7.803062505568182, GreatCircle.calc(new String[] {"38.627345,-90.193774,35.252234,-81.384929"}), 0.000000001); + + assertEquals(-1, GreatCircle.calc(new String[0]), 0.000000001); + assertEquals(-1, GreatCircle.calc(new String[] {"38.627345;-90.193774", "35.252234,-81.384929"}), 0.000000001); + assertEquals(-1, GreatCircle.calc(new String[] {"38.627345,-90.193774", "35.252234;-81.384929"}), 0.000000001); + assertEquals(-1, GreatCircle.calc(new String[] {"38.627345,-90.193774;35.252234,-81.384929"}), 0.000000001); + + assertEquals(-1, GreatCircle.calc(new String[] {"Invalid input", "Invalid input", "Invalid input", "Invalid input"}), 0.000000001); + } + + @Test + public void coverageTest() { + @SuppressWarnings("unused") + GreatCircle gc = new GreatCircle(); + } + +} diff --git a/cadi/client/src/test/java/org/onap/aaf/client/test/JU_ResultTest.java b/cadi/client/src/test/java/org/onap/aaf/client/test/JU_ResultTest.java new file mode 100644 index 00000000..b0ac5a0c --- /dev/null +++ b/cadi/client/src/test/java/org/onap/aaf/client/test/JU_ResultTest.java @@ -0,0 +1,108 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.client.test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.*; + +import org.junit.Before; +import org.junit.Test; +import org.onap.aaf.cadi.client.Result; + +public class JU_ResultTest { + + @Before + public void setUp() throws Exception { + } + + @Test + public void testOk() { + Result<String> t = Result.ok(1, "Ok"); + assertNotNull(t); + assertThat(t.code, is(1)); + assertTrue(t.isOK()); + assertThat(t.toString(), is("Code: 1")); + } + + @Test + public void testErr() { + Result<String> t = Result.err(1, "Error Body"); + assertNotNull(t); + assertThat(t.error, is("Error Body")); + assertFalse(t.isOK()); + assertThat(t.toString(), is("Code: 1 = Error Body")); + } + + @Test + public void testOk1() { + Result<String> t = Result.ok(1, "Ok"); + assertNotNull(t); + assertThat(t.code, is(1)); + assertTrue(t.isOK()); + assertThat(t.toString(), is("Code: 1")); + } + + @Test + public void testErr1() { + Result<String> t = Result.err(1, "Error Body"); + assertNotNull(t); + assertThat(t.error, is("Error Body")); + assertFalse(t.isOK()); + assertThat(t.toString(), is("Code: 1 = Error Body")); + } + + @Test + public void testOk2() { + Result<String> t = Result.ok(1, "Ok"); + assertNotNull(t); + assertThat(t.code, is(1)); + assertTrue(t.isOK()); + assertThat(t.toString(), is("Code: 1")); + } + + @Test + public void testErr2() { + Result<String> t = Result.err(1, "Error Body"); + assertNotNull(t); + assertThat(t.error, is("Error Body")); + assertFalse(t.isOK()); + assertThat(t.toString(), is("Code: 1 = Error Body")); + } + + @Test + public void testOk3() { + Result<String> t = Result.ok(1, "Ok"); + assertNotNull(t); + assertThat(t.code, is(1)); + assertTrue(t.isOK()); + assertThat(t.toString(), is("Code: 1")); + } + + @Test + public void testErr3() { + Result<String> t = Result.err(1, "Error Body"); + assertNotNull(t); + assertThat(t.error, is("Error Body")); + assertFalse(t.isOK()); + assertThat(t.toString(), is("Code: 1 = Error Body")); + } +} diff --git a/cadi/core/.gitignore b/cadi/core/.gitignore new file mode 100644 index 00000000..6028f0a5 --- /dev/null +++ b/cadi/core/.gitignore @@ -0,0 +1,4 @@ +/.classpath +/.settings/ +/target/ +/.project diff --git a/cadi/core/conf/.gitignore b/cadi/core/conf/.gitignore new file mode 100644 index 00000000..06efae87 --- /dev/null +++ b/cadi/core/conf/.gitignore @@ -0,0 +1 @@ +/keyfile diff --git a/cadi/core/conf/cadi.properties b/cadi/core/conf/cadi.properties new file mode 100644 index 00000000..09c79254 --- /dev/null +++ b/cadi/core/conf/cadi.properties @@ -0,0 +1,34 @@ +# This is a normal Java Properties File +# Comments are with Pound Signs at beginning of lines, +# and multi-line expression of properties can be obtained by backslash at end of line + +# Certain machines have several possible machine names, and +# the right one may not be reported. This is especially +# important for CSP Authorization, which will only +# function on official AT&T domains. +hostname=veeger.mo.sbc.com + +port=2533 + +# CSP has Production mode (active users) or DEVL mode (for +# Testing purposes... Bogus users) +#csp_domain=DEVL +csp_domain=PROD + +# Report all AUTHN and AUTHZ activity +loglevel=AUDIT + +# +# BasicAuth and other User/Password support +# +# The realm reported on BasicAuth callbacks +basic_realm=spiderman.agile.att.com +users=ks%xiVUs_25_1jqGdJ24hqy43Gi; +groups=aaf:Jd8bb3jslg88b@spiderman.agile.att.com%7sZCPBZ_8iWbslqdjWFIDLgTZlm9ung0ym-G,\ + jg1555,lg2384,rd8227,tp007s,pe3617; + + +# Keyfile (with relative path) for encryption. This file +# should be marked as ReadOnly by Only the running process +# for security's sake +keyfile=conf/keyfile diff --git a/cadi/core/pom.xml b/cadi/core/pom.xml new file mode 100644 index 00000000..59513118 --- /dev/null +++ b/cadi/core/pom.xml @@ -0,0 +1,204 @@ +<!-- * ============LICENSE_START==================================================== + * org.onap.aaf * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * --> +<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"> + <parent> + <groupId>org.onap.aaf.authz</groupId> + <artifactId>cadiparent</artifactId> + <relativePath>..</relativePath> + <version>2.1.0-SNAPSHOT</version> + </parent> + + <modelVersion>4.0.0</modelVersion> + <name>AAF CADI Core Framework</name> + <artifactId>aaf-cadi-core</artifactId> + <packaging>jar</packaging> + + <properties> + <!-- SONAR --> + <jacoco.version>0.7.7.201606060606</jacoco.version> + <sonar-jacoco-listeners.version>3.2</sonar-jacoco-listeners.version> + <sonar.core.codeCoveragePlugin>jacoco</sonar.core.codeCoveragePlugin> + <!-- Default Sonar configuration --> + <sonar.jacoco.reportPaths>target/code-coverage/jacoco-ut.exec</sonar.jacoco.reportPaths> + <sonar.jacoco.itReportPaths>target/code-coverage/jacoco-it.exec</sonar.jacoco.itReportPaths> + <!-- Note: This list should match jacoco-maven-plugin's exclusion list below --> + <sonar.exclusions>**/gen/**,**/generated-sources/**,**/yang-gen**,**/pax/**</sonar.exclusions> + <nexusproxy>https://nexus.onap.org</nexusproxy> + <snapshotNexusPath>/content/repositories/snapshots/</snapshotNexusPath> + <releaseNexusPath>/content/repositories/releases/</releaseNexusPath> + <stagingNexusPath>/content/repositories/staging/</stagingNexusPath> + <sitePath>/content/sites/site/org/onap/aaf/authz/${project.artifactId}/${project.version}</sitePath> + </properties> + + <developers> + <developer> + <name>Jonathan Gathman</name> + <email>jonathan.gathman@att.com</email> + <organization>ATT</organization> + <roles> + <role>Architect</role> + <role>Lead Developer</role> + </roles> + </developer> + <developer> + <name>Gabe Maurer</name> + <email>gabe.maurer@att.com</email> + <organization>ATT</organization> + <roles> + <role>Developer</role> + </roles> + </developer> + <developer> + <name>Ian Howell</name> + <email>ian.howell@att.com</email> + <organization>ATT</organization> + <roles> + <role>Developer</role> + </roles> + </developer> + <developer> + <name>Sai Gandham</name> + <email>sai.gandham@att.com</email> + <organization>ATT</organization> + <roles> + <role>Developer</role> + </roles> + </developer> + </developers> + <dependencies> + <dependency> + <groupId>javax.servlet</groupId> + <artifactId>javax.servlet-api</artifactId> + <scope>provided</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <!-- Must put this in to turn on Signing, but Configuration itself is + in Parent --> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jarsigner-plugin</artifactId> + <version>1.4</version> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <configuration> + <archive> + <manifest> + <mainClass>org.onap.aaf.cadi.CmdLine</mainClass> + </manifest> + <manifestEntries> + <Sealed>true</Sealed> + </manifestEntries> + </archive> + </configuration> + <executions> + <execution> + <id>test-jar</id> + <phase>package</phase> + <goals> + <goal>test-jar</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.sonatype.plugins</groupId> + <artifactId>nexus-staging-maven-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <nexusUrl>${nexusproxy}</nexusUrl> + <stagingProfileId>176c31dfe190a</stagingProfileId> + <serverId>ecomp-staging</serverId> + </configuration> + </plugin> + <plugin> + <groupId>org.jacoco</groupId> + <artifactId>jacoco-maven-plugin</artifactId> + <configuration> + <excludes> + <exclude>**/gen/**</exclude> + <exclude>**/generated-sources/**</exclude> + <exclude>**/yang-gen/**</exclude> + <exclude>**/pax/**</exclude> + </excludes> + </configuration> + <executions> + <execution> + <id>pre-unit-test</id> + <goals> + <goal>prepare-agent</goal> + </goals> + <configuration> + <destFile>${project.build.directory}/code-coverage/jacoco-ut.exec</destFile> + <propertyName>surefireArgLine</propertyName> + </configuration> + </execution> + <execution> + <id>post-unit-test</id> + <phase>test</phase> + <goals> + <goal>report</goal> + </goals> + <configuration> + <dataFile>${project.build.directory}/code-coverage/jacoco-ut.exec</dataFile> + <outputDirectory>${project.reporting.outputDirectory}/jacoco-ut</outputDirectory> + </configuration> + </execution> + <execution> + <id>pre-integration-test</id> + <phase>pre-integration-test</phase> + <goals> + <goal>prepare-agent</goal> + </goals> + <configuration> + <destFile>${project.build.directory}/code-coverage/jacoco-it.exec</destFile> + <propertyName>failsafeArgLine</propertyName> + </configuration> + </execution> + <execution> + <id>post-integration-test</id> + <phase>post-integration-test</phase> + <goals> + <goal>report</goal> + </goals> + <configuration> + <dataFile>${project.build.directory}/code-coverage/jacoco-it.exec</dataFile> + <outputDirectory>${project.reporting.outputDirectory}/jacoco-it</outputDirectory> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + + <distributionManagement> + <repository> + <id>ecomp-releases</id> + <name>AAF Release Repository</name> + <url>${nexusproxy}${releaseNexusPath}</url> + </repository> + <snapshotRepository> + <id>ecomp-snapshots</id> + <name>AAF Snapshot Repository</name> + <url>${nexusproxy}${snapshotNexusPath}</url> + </snapshotRepository> + <site> + <id>ecomp-site</id> + <url>dav:${nexusproxy}${sitePath}</url> + </site> + </distributionManagement> +</project> diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/AES.java b/cadi/core/src/main/java/org/onap/aaf/cadi/AES.java new file mode 100644 index 00000000..3ef3355a --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/AES.java @@ -0,0 +1,131 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.CipherInputStream; +import javax.crypto.CipherOutputStream; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.KeyGenerator; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; + +import org.onap.aaf.cadi.Symm.Encryption; +import org.onap.aaf.cadi.util.Chmod; + + +/** + * AES Class wraps Cipher AES, 128 + * NOTE: While not explicitly stated in JavaDocs, Ciphers AND SecretKeySpecs are NOT ThreadSafe + * Ciphers take time to create, therefore, we have pooled them. + * + * @author Jonathan + * + */ +public class AES implements Encryption { + public static final String AES = AES.class.getSimpleName(); + public static final int AES_KEY_SIZE = 128; // 256 isn't supported on all JDKs. + + private SecretKeySpec aeskeySpec; + + public static SecretKey newKey() throws NoSuchAlgorithmException { + KeyGenerator kgen = KeyGenerator.getInstance(AES); + kgen.init(AES_KEY_SIZE); + return kgen.generateKey(); + } + + public AES(byte[] aeskey, int offset, int len) throws IOException, NoSuchAlgorithmException, NoSuchPaddingException { + aeskeySpec = new SecretKeySpec(aeskey,offset,len,AES); + } + + public byte[] encrypt(byte[] in) throws CadiException { + try { + Cipher c = Cipher.getInstance(AES); + c.init(Cipher.ENCRYPT_MODE,aeskeySpec); + return c.doFinal(in); + } catch (InvalidKeyException | IllegalBlockSizeException | BadPaddingException | NoSuchAlgorithmException | NoSuchPaddingException e) { + throw new CadiException(e); + } + } + + public byte[] decrypt(byte[] in) throws CadiException { + try { + Cipher c = Cipher.getInstance(AES); + c.init(Cipher.DECRYPT_MODE,aeskeySpec); + return c.doFinal(in); + } catch (InvalidKeyException | IllegalBlockSizeException | BadPaddingException | NoSuchAlgorithmException | NoSuchPaddingException e) { + throw new CadiException(e); + } + } + + public void save(File keyfile) throws IOException { + FileOutputStream fis = new FileOutputStream(keyfile); + try { + fis.write(aeskeySpec.getEncoded()); + } finally { + fis.close(); + } + Chmod.to400.chmod(keyfile); + } + + public CipherOutputStream outputStream(OutputStream os, boolean encrypt) { + try { + Cipher c = Cipher.getInstance(AES); + if(encrypt) { + c.init(Cipher.ENCRYPT_MODE,aeskeySpec); + } else { + c.init(Cipher.DECRYPT_MODE,aeskeySpec); + } + return new CipherOutputStream(os,c); + } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException e) { + // Cannot add Exception to this API. throw Runtime + System.err.println("Error creating Aes CipherOutputStream"); + return null; // should never get here. + } + } + + public CipherInputStream inputStream(InputStream is, boolean encrypt) { + try { + Cipher c = Cipher.getInstance(AES); + if(encrypt) { + c.init(Cipher.ENCRYPT_MODE,aeskeySpec); + } else { + c.init(Cipher.DECRYPT_MODE,aeskeySpec); + } + return new CipherInputStream(is,c); + } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException e) { + // Cannot add Exception to this API. throw Runtime + System.err.println("Error creating Aes CipherInputStream"); + return null; // should never get here. + } + } +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/AbsUserCache.java b/cadi/core/src/main/java/org/onap/aaf/cadi/AbsUserCache.java new file mode 100644 index 00000000..c65a9b22 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/AbsUserCache.java @@ -0,0 +1,467 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi; + + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.security.Principal; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; +import java.util.TreeMap; +import java.util.concurrent.ConcurrentHashMap; + +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.CachedPrincipal.Resp; +import org.onap.aaf.cadi.principal.CachedBasicPrincipal; + +/** + * Implement Fast lookup and Cache for Local User Info + * + * Include ability to add and remove Users + * + * Also includes a Timer Thread (when necessary) to invoke cleanup on expiring Credentials + * + * @author Jonathan + * + */ +public abstract class AbsUserCache<PERM extends Permission> { + // Need an obvious key for when there is no Authentication Cred + private static final String NO_CRED = "NoCred"; + static final int MIN_INTERVAL = 1000*60; // Min 1 min + static final int MAX_INTERVAL = 1000*60*60*4; // 4 hour max + private static Timer timer; + // Map of userName to User + private final Map<String, User<PERM>> userMap; + private static final Map<String, Miss> missMap = new TreeMap<String,Miss>(); + private final Symm missEncrypt; + + private Clean clean; + protected Access access; + + protected AbsUserCache(Access access, long cleanInterval, int highCount, int usageCount) { + this.access = access; + Symm s; + try { + byte[] gennedKey = Symm.keygen(); + s = Symm.obtain(new ByteArrayInputStream(gennedKey)); + } catch (IOException e) { + access.log(e); + s = Symm.base64noSplit; + } + missEncrypt = s; + + userMap = new ConcurrentHashMap<String, User<PERM>>(); + + + if(cleanInterval>0) { + cleanInterval = Math.max(MIN_INTERVAL, cleanInterval); + synchronized(AbsUserCache.class) { // Lazy instantiate.. in case there is no cleanup needed + if(timer==null) { + timer = new Timer("CADI Cleanup Timer",true); + } + + timer.schedule(clean = new Clean(access, cleanInterval, highCount, usageCount), cleanInterval, cleanInterval); + access.log(Access.Level.INIT, "Cleaning Thread initialized with interval of",cleanInterval, "ms and max objects of", highCount); + } + } + } + + @SuppressWarnings("unchecked") + public AbsUserCache(AbsUserCache<PERM> cache) { + this.access = cache.access; + userMap = cache.userMap; + missEncrypt = cache.missEncrypt; + + synchronized(AbsUserCache.class) { + if(cache.clean!=null && cache.clean.lur==null && this instanceof CachingLur) { + cache.clean.lur=(CachingLur<PERM>)this; + } + } + } + + protected void setLur(CachingLur<PERM> lur) { + if(clean!=null)clean.lur = lur; + + } + + protected void addUser(User<PERM> user) { + Principal p = user.principal; + String key; + try { + if(p instanceof GetCred) { + key = missKey(p.getName(), ((GetCred)p).getCred()); + } else { + byte[] cred; + if((cred=user.getCred())==null) { + key = user.name + NO_CRED; + } else { + key = missKey(user.name,cred); + } + } + } catch (IOException e) { + access.log(e); + return; + } + userMap.put(key, user); + } + + // Useful for looking up by WebToken, etc. + protected void addUser(String key, User<PERM> user) { + userMap.put(key, user); + } + + /** + * Add miss to missMap. If Miss exists, or too many tries, returns false. + * + * otherwise, returns true to allow another attempt. + * + * @param key + * @param bs + * @return + * @throws IOException + */ + protected synchronized boolean addMiss(String key, byte[] bs) { + String mkey; + try { + mkey = missKey(key,bs); + } catch (IOException e) { + access.log(e); + return false; + } + Miss miss = missMap.get(mkey); + if(miss==null) { + missMap.put(mkey, new Miss(bs,clean==null?MIN_INTERVAL:clean.timeInterval,key)); + return true; + } + return miss.mayContinue(); + } + + protected Miss missed(String key, byte[] bs) throws IOException { + return missMap.get(missKey(key,bs)); + } + + protected User<PERM> getUser(Principal principal) { + String key; + if(principal instanceof GetCred) { + GetCred gc = (GetCred)principal; + try { + key = missKey(principal.getName(), gc.getCred()); + } catch (IOException e) { + access.log(e, "Error getting key from Principal"); + key = principal.getName(); + } + } else { + key = principal.getName()+NO_CRED; + } + User<PERM> u = userMap.get(key); + if(u!=null) { + u.incCount(); + } + return u; + } + + protected User<PERM> getUser(CachedBasicPrincipal cbp) { + return getUser(cbp.getName(), cbp.getCred()); + } + + protected User<PERM> getUser(String user, byte[] cred) { + User<PERM> u; + String key=null; + try { + key =missKey(user,cred); + } catch (IOException e) { + access.log(e); + return null; + } + u = userMap.get(key); + if(u!=null) { + if(u.permExpired()) { + userMap.remove(key); + u=null; + } else { + u.incCount(); + } + } + return u; + } + + /** + * Removes User from the Cache + * @param user + */ + protected void remove(User<PERM> user) { + userMap.remove(user.principal.getName()); + } + + /** + * Removes user from the Cache + * + * @param user + */ + public void remove(String user) { + Object o = userMap.remove(user); + if(o!=null) { + access.log(Level.INFO, user,"removed from Client Cache by Request"); + } + } + + /** + * Clear all Users from the Client Cache + */ + public void clearAll() { + userMap.clear(); + } + + public final List<DumpInfo> dumpInfo() { + List<DumpInfo> rv = new ArrayList<DumpInfo>(); + for(User<PERM> user : userMap.values()) { + rv.add(new DumpInfo(user)); + } + return rv; + } + + /** + * The default behavior of a LUR is to not handle something exclusively. + */ + public boolean handlesExclusively(Permission pond) { + return false; + } + + /** + * Container calls when cleaning up... + * + * If overloading in Derived class, be sure to call "super.destroy()" + */ + public void destroy() { + if(timer!=null) { + timer.purge(); + timer.cancel(); + } + } + + + + // Simple map of Group name to a set of User Names + // private Map<String, Set<String>> groupMap = new HashMap<String, Set<String>>(); + + /** + * Class to hold a small subset of the data, because we don't want to expose actual Permission or User Objects + */ + public final class DumpInfo { + public String user; + public List<String> perms; + + public DumpInfo(User<PERM> user) { + this.user = user.principal.getName(); + perms = new ArrayList<String>(user.perms.keySet()); + } + } + + /** + * Clean will examine resources, and remove those that have expired. + * + * If "highs" have been exceeded, then we'll expire 10% more the next time. This will adjust after each run + * without checking contents more than once, making a good average "high" in the minimum speed. + * + * @author Jonathan + * + */ + private final class Clean extends TimerTask { + private final Access access; + private CachingLur<PERM> lur; + + // The idea here is to not be too restrictive on a high, but to Expire more items by + // shortening the time to expire. This is done by judiciously incrementing "advance" + // when the "highs" are exceeded. This effectively reduces numbers of cached items quickly. + private final int high; + private long advance; + private final long timeInterval; + private final int usageTriggerCount; + + public Clean(Access access, long cleanInterval, int highCount, int usageTriggerCount) { + this.access = access; + lur = null; + high = highCount; + timeInterval = cleanInterval; + advance = 0; + this.usageTriggerCount=usageTriggerCount; + } + public void run() { + int renewed = 0; + int count = 0; + int total = 0; + try { + // look at now. If we need to expire more by increasing "now" by "advance" + ArrayList<User<PERM>> al = new ArrayList<User<PERM>>(userMap.values().size()); + al.addAll(0, userMap.values()); + long now = System.currentTimeMillis() + advance; + for(User<PERM> user : al) { + ++total; + if(user.count>usageTriggerCount) { + boolean touched = false, removed=false; + if(user.principal instanceof CachedPrincipal) { + CachedPrincipal cp = (CachedPrincipal)user.principal; + if(cp.expires() < now) { + switch(cp.revalidate(null)) { + case INACCESSIBLE: + access.log(Level.AUDIT, "AAF Inaccessible. Keeping credentials"); + break; + case REVALIDATED: + user.resetCount(); + touched = true; + break; + default: + user.resetCount(); + remove(user); + ++count; + removed = true; + break; + } + } + } + + if(!removed && lur!=null && user.permExpires<= now ) { + if(lur.reload(user).equals(Resp.REVALIDATED)) { + user.renewPerm(); + access.log(Level.DEBUG, "Reloaded Perms for",user); + touched = true; + } + } + user.resetCount(); + if(touched) { + ++renewed; + } + + } else { + if(user.permExpired()) { + remove(user); + ++count; + } + } + } + + // Clean out Misses + int missTotal = missMap.keySet().size(); + int miss = 0; + if(missTotal>0) { + ArrayList<String> keys = new ArrayList<String>(missTotal); + keys.addAll(missMap.keySet()); + for(String key : keys) { + Miss m = missMap.get(key); + if(m!=null) { + long timeLeft = m.timestamp - System.currentTimeMillis(); + if(timeLeft<0) { + synchronized(missMap) { + missMap.remove(key); + } + access.log(Level.INFO, m.name, " has been removed from Missed Credential Map (" + m.tries + " invalid tries)"); + ++miss; + } else { + access.log(Level.INFO, m.name, " remains in Missed Credential Map (" + m.tries + " invalid tries) for " + (timeLeft/1000) + " more seconds"); + } + } + } + } + + if(count+renewed+miss>0) { + access.log(Level.INFO, (lur==null?"Cache":lur.getClass().getSimpleName()), "removed",count, + "and renewed",renewed,"expired Permissions out of", total,"and removed", miss, "password misses out of",missTotal); + } + + // If High (total) is reached during this period, increase the number of expired services removed for next time. + // There's no point doing it again here, as there should have been cleaned items. + if(total>high) { + // advance cleanup by 10%, without getting greater than timeInterval. + advance = Math.min(timeInterval, advance+(timeInterval/10)); + } else { + // reduce advance by 10%, without getting lower than 0. + advance = Math.max(0, advance-(timeInterval/10)); + } + } catch (Exception e) { + access.log(Level.ERROR,e.getMessage()); + } + } + } + + + private String missKey(String name, byte[] bs) throws IOException { + return name + Hash.toHex(missEncrypt.encode(bs)); + } + + protected static class Miss { + private static final int MAX_TRIES = 3; + + long timestamp; + + private long timetolive; + + private long tries; + + private final String name; + + public Miss(final byte[] first, final long timeInterval, final String name) { + timestamp = System.currentTimeMillis() + timeInterval; + this.timetolive = timeInterval; + tries = 0L; + this.name = name; + } + + + public synchronized boolean mayContinue() { + long ts = System.currentTimeMillis(); + if(ts>timestamp) { + tries = 0; + timestamp = ts + timetolive; + } else if(MAX_TRIES <= ++tries) { + return false; + } + return true; + } + + } + + /** + * Report on state + */ + public String toString() { + return getClass().getSimpleName() + + " Cache:\n Users Cached: " + + userMap.size() + + "\n Misses Saved: " + + missMap.size() + + '\n'; + + } + + public void clear(Principal p, StringBuilder sb) { + sb.append(toString()); + userMap.clear(); + missMap.clear(); + access.log(Level.AUDIT, p.getName(),"has cleared User Cache in",getClass().getSimpleName()); + sb.append("Now cleared\n"); + } + +}
\ No newline at end of file diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/Access.java b/cadi/core/src/main/java/org/onap/aaf/cadi/Access.java new file mode 100644 index 00000000..83127238 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/Access.java @@ -0,0 +1,180 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +/** + * Various Environments require different logging mechanisms, or at least allow + * for different ones. We need the Framework to be able to hook into any particular instance of logging + * mechanism, whether it be a Logging Object within a Servlet Context, or a direct library like log4j. + * This interface, therefore, allows maximum pluggability in a variety of different app styles. + * + * @author Jonathan + * + */ +public interface Access { + // levels to use + public enum Level { + DEBUG(0x1), INFO(0x10), AUDIT(0x100), WARN(0x2000), ERROR(0x4000), INIT(0x8000),TRACE(0x10000),NONE(0XFFFF); + private final int bit; + + Level(int ord) { + bit = ord; + } + + public boolean inMask(int mask) { + return (mask & bit) == bit; + } + + public int addToMask(int mask) { + return mask | bit; + } + + public int delFromMask(int mask) { + return mask & ~bit; + } + + public int toggle(int mask) { + if(inMask(mask)) { + return delFromMask(mask); + } else { + return addToMask(mask); + } + } + + + public int maskOf() { + int mask=0; + for(Level l : values()) { + if(ordinal()<=l.ordinal() && l!=NONE) { + mask|=l.bit; + } + } + return mask; + } + } + + /** + * Write a variable list of Object's text via the toString() method with appropriate space, etc. + * @param elements + */ + public void log(Level level, Object ... elements); + + /** + * Printf mechanism for Access + * @param level + * @param fmt + * @param elements + */ + public void printf(Level level, String fmt, Object ... elements); + + /** + * Check if message will log before constructing + * @param level + * @return + */ + public boolean willLog(Level level); + + /** + * Write the contents of an exception, followed by a variable list of Object's text via the + * toString() method with appropriate space, etc. + * + * The Loglevel is always "ERROR" + * + * @param elements + */ + public void log(Exception e, Object ... elements); + + /** + * Set the Level to compare logging too + */ + public void setLogLevel(Level level); + + /** + * It is important in some cases to create a class from within the same Classloader that created + * Security Objects. Specifically, it's pretty typical for Web Containers to separate classloaders + * so as to allow Apps with different dependencies. + * @return + */ + public ClassLoader classLoader(); + + public String getProperty(String string, String def); + + public Properties getProperties(); + + public void load(InputStream is) throws IOException; + + /** + * if "anytext" is true, then decryption will always be attempted. Otherwise, only if starts with + * Symm.ENC + * @param encrypted + * @param anytext + * @return + * @throws IOException + */ + public String decrypt(String encrypted, boolean anytext) throws IOException; + + public static final Access NULL = new Access() { + public void log(Level level, Object... elements) { + } + + @Override + public void printf(Level level, String fmt, Object... elements) { + } + + public void log(Exception e, Object... elements) { + } + + public ClassLoader classLoader() { + return ClassLoader.getSystemClassLoader(); + } + + public String getProperty(String string, String def) { + return null; + } + + public void load(InputStream is) throws IOException { + } + + public void setLogLevel(Level level) { + } + + public String decrypt(String encrypted, boolean anytext) throws IOException { + return encrypted; + } + + @Override + public boolean willLog(Level level) { + return false; + } + + @Override + public Properties getProperties() { + return new Properties(); + } + }; + + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/BasicCred.java b/cadi/core/src/main/java/org/onap/aaf/cadi/BasicCred.java new file mode 100644 index 00000000..b80cda89 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/BasicCred.java @@ -0,0 +1,36 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi; + +/** + * An Interface for testing on Requests to see if we can get a User and Password + * It works for CadiWrap, but also, Container Specific Wraps (aka Tomcat) should also + * implement. + * + * @author Jonathan + * + */ +public interface BasicCred extends GetCred { + public void setUser(String user); + public void setCred(byte[] passwd); + public String getUser(); +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/BufferedServletInputStream.java b/cadi/core/src/main/java/org/onap/aaf/cadi/BufferedServletInputStream.java new file mode 100644 index 00000000..2df01cda --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/BufferedServletInputStream.java @@ -0,0 +1,200 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi; + +import java.io.IOException; +import java.io.InputStream; + +import javax.servlet.ServletInputStream; + +/** + * BufferedServletInputStream + * + * There are cases in brain-dead middleware (SOAP) where they store routing information in the content. + * + * In HTTP, this requires reading the content from the InputStream which, of course, cannot be re-read. + * + * BufferedInputStream exists to implement the "Mark" protocols for Streaming, which will enable being + * re-read. Unfortunately, J2EE chose to require a "ServletInputStream" as an abstract class, rather than + * an interface, which requires we create a delegating pattern, rather than the preferred inheriting pattern. + * + * Unfortunately, the standard "BufferedInputStream" cannot be used, because it simply creates a byte array + * in the "mark(int)" method of that size. This is not appropriate for this application, because the Header + * can be potentially huge, and if a buffer was allocated to accommodate all possibilities, the cost of memory + * allocation would be too large for high performance transactions. + * + * + * @author Jonathan + * + */ +public class BufferedServletInputStream extends ServletInputStream { + private static final int NONE = 0; + private static final int STORE = 1; + private static final int READ = 2; + + private InputStream is; + private int state = NONE; + private Capacitor capacitor; + + public BufferedServletInputStream(InputStream is) { + this.is = is; + capacitor = null; + } + + + public int read() throws IOException { + int value=-1; + if(capacitor==null) { + value=is.read(); + } else { + switch(state) { + case STORE: + value = is.read(); + if(value>=0) { + capacitor.put((byte)value); + } + break; + case READ: + value = capacitor.read(); + if(value<0) { + capacitor.done(); + capacitor=null; // all done with buffer + value = is.read(); + } + } + } + return value; + } + + public int read(byte[] b) throws IOException { + return read(b,0,b.length); + } + + + public int read(byte[] b, int off, int len) throws IOException { + int count = -1; + if(capacitor==null) { + count = is.read(b,off,len); + } else { + switch(state) { + case STORE: + count = is.read(b, off, len); + if(count>0) { + capacitor.put(b, off, count); + } + break; + case READ: + count = capacitor.read(b, off, len); + if(count<=0) { + capacitor.done(); + capacitor=null; // all done with buffer + } + if(count<len) { + int temp = is.read(b, count, len-count); + if(temp>0) { // watch for -1 + count+=temp; + } else if(count<=0) { + count = temp; // must account for Stream coming back -1 + } + } + break; + } + } + return count; + } + + public long skip(long n) throws IOException { + long skipped = capacitor.skip(n); + if(skipped<n) { + skipped += is.skip(n-skipped); + } + return skipped; + } + + + public int available() throws IOException { + int count = is.available(); + if(capacitor!=null)count+=capacitor.available(); + return count; + } + + /** + * Return just amount buffered (for debugging purposes, mostly) + * @return + */ + public int buffered() { + return capacitor.available(); + } + + + public void close() throws IOException { + if(capacitor!=null) { + capacitor.done(); + capacitor=null; + } + is.close(); + } + + + /** + * Note: Readlimit is ignored in this implementation, because the need was for unknown buffer size which wouldn't + * require allocating and dumping huge chunks of memory every use, or risk overflow. + */ + public synchronized void mark(int readlimit) { + switch(state) { + case NONE: + capacitor = new Capacitor(); + break; + case READ: + capacitor.done(); + break; + } + state = STORE; + } + + + /** + * Reset Stream + * + * Calling this twice is not supported in typical Stream situations, but it is allowed in this service. The caveat is that it can only reset + * the data read in since Mark has been called. The data integrity is only valid if you have not continued to read past what is stored. + * + */ + public synchronized void reset() throws IOException { + switch(state) { + case STORE: + capacitor.setForRead(); + state = READ; + break; + case READ: + capacitor.reset(); + break; + case NONE: + throw new IOException("InputStream has not been marked"); + } + } + + + public boolean markSupported() { + return true; + } +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/CachedPrincipal.java b/cadi/core/src/main/java/org/onap/aaf/cadi/CachedPrincipal.java new file mode 100644 index 00000000..2bb3db32 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/CachedPrincipal.java @@ -0,0 +1,47 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi; + +import java.security.Principal; + +/** + * Cached Principals need to be able to revalidate in the background. + * + * @author Jonathan + * + */ +public interface CachedPrincipal extends Principal { + public enum Resp {NOT_MINE,UNVALIDATED,REVALIDATED,INACCESSIBLE,DENIED}; + + /** + * Re-validate with Creator + * + * @return + */ + public abstract Resp revalidate(Object state); + + /** + * Store when last updated. + * @return + */ + public abstract long expires(); +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/CachingLur.java b/cadi/core/src/main/java/org/onap/aaf/cadi/CachingLur.java new file mode 100644 index 00000000..e083f4ed --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/CachingLur.java @@ -0,0 +1,34 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi; + +import java.security.Principal; + +import org.onap.aaf.cadi.CachedPrincipal.Resp; + + +public interface CachingLur<PERM extends Permission> extends Lur { + public abstract void remove(String user); + public abstract Resp reload(User<PERM> user); + public abstract void setDebug(String commaDelimIDsOrNull); + public abstract void clear(Principal p, StringBuilder sb); +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/CadiException.java b/cadi/core/src/main/java/org/onap/aaf/cadi/CadiException.java new file mode 100644 index 00000000..0f250b36 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/CadiException.java @@ -0,0 +1,50 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi; + +/** + * CADI Specific Exception + * @author Jonathan + */ +public class CadiException extends Exception { + /** + * Generated ID + */ + private static final long serialVersionUID = -4180145363107742619L; + + public CadiException() { + super(); + } + + public CadiException(String message) { + super(message); + } + + public CadiException(Throwable cause) { + super(cause); + } + + public CadiException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/CadiWrap.java b/cadi/core/src/main/java/org/onap/aaf/cadi/CadiWrap.java new file mode 100644 index 00000000..49572f4c --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/CadiWrap.java @@ -0,0 +1,198 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi; + +import java.security.Principal; +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; + +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.filter.NullPermConverter; +import org.onap.aaf.cadi.filter.PermConverter; +import org.onap.aaf.cadi.lur.EpiLur; +import org.onap.aaf.cadi.principal.TaggedPrincipal; +import org.onap.aaf.cadi.taf.TafResp; + + + +/** + * Inherit the HttpServletRequestWrapper, which calls methods of delegate it's created with, but + * overload the key security mechanisms with CADI mechanisms + * + * This works with mechanisms working strictly with HttpServletRequest (i.e. Servlet Filters) + * + * Specialty cases, i.e. Tomcat, which for their containers utilize their own mechanisms and Wrappers, you may + * need something similar. See AppServer specific code (i.e. tomcat) for these. + * + * @author Jonathan + * + */ +public class CadiWrap extends HttpServletRequestWrapper implements HttpServletRequest, BasicCred { + private TaggedPrincipal principal; + private Lur lur; + private String user; // used to set user/pass from brain-dead protocols like WSSE + private byte[] password; + private PermConverter pconv; + private Access access; + + /** + * Standard Wrapper constructor for Delegate pattern + * @param request + */ + public CadiWrap(HttpServletRequest request, TafResp tafResp, Lur lur) { + super(request); + principal = tafResp.getPrincipal(); + access = tafResp.getAccess(); + this.lur = lur; + pconv = NullPermConverter.singleton(); + } + + /** + * Standard Wrapper constructor for Delegate pattern, with PermConverter + * @param request + */ + public CadiWrap(HttpServletRequest request, TafResp tafResp, Lur lur, PermConverter pc) { + super(request); + principal = tafResp.getPrincipal(); + access = tafResp.getAccess(); + this.lur = lur; + pconv = pc; + } + + + /** + * Part of the HTTP Security API. Declare the User associated with this HTTP Transaction. + * CADI does this by reporting the name associated with the Principal obtained, if any. + */ + @Override + public String getRemoteUser() { + return principal==null?null:principal.getName(); + } + + /** + * Part of the HTTP Security API. Return the User Principal associated with this HTTP + * Transaction. + */ + @Override + public Principal getUserPrincipal() { + return principal; + } + + /** + * This is the key API call for AUTHZ in J2EE. Given a Role (String passed in), is the user + * associated with this HTTP Transaction allowed to function in this Role? + * + * For CADI, we pass the responsibility for determining this to the "LUR", which may be + * determined by the Enterprise. + * + * Note: Role check is also done in "CadiRealm" in certain cases... + * + * + */ + @Override + public boolean isUserInRole(String perm) { + return perm==null?false:checkPerm(access,"(HttpRequest)",principal,pconv,lur,perm); + } + + public static boolean checkPerm(Access access, String caller, Principal principal, PermConverter pconv, Lur lur, String perm) { + if(principal== null) { + access.log(Level.AUDIT,caller, "No Principal in Transaction"); + return false; + } else { + perm = pconv.convert(perm); + if(lur.fish(principal,lur.createPerm(perm))) { + access.log(Level.DEBUG,caller, principal.getName(), "has", perm); + return true; + } else { + access.log(Level.DEBUG,caller, principal.getName(), "does not have", perm); + return false; + } + } + + } + + /** + * CADI Function (Non J2EE standard). GetPermissions will read the Permissions from AAF (if configured) and Roles from Local Lur, etc + * as implemented with lur.fishAll + * + * To utilize, the Request must be a "CadiWrap" object, then call. + */ + public List<Permission> getPermissions(Principal p) { + List<Permission> perms = new ArrayList<Permission>(); + lur.fishAll(p, perms); + return perms; + } + /** + * Allow setting of tafResp and lur after construction + * + * This can happen if the CadiWrap is constructed in a Valve other than CadiValve + */ + public void set(TafResp tafResp, Lur lur) { + principal = tafResp.getPrincipal(); + access = tafResp.getAccess(); + this.lur = lur; + } + + public String getUser() { + if(user==null && principal!=null) { + user = principal.getName(); + } + return user; + } + + public byte[] getCred() { + return password; + } + + public void setUser(String user) { + this.user = user; + } + + public void setCred(byte[] passwd) { + password = passwd; + } + + public CadiWrap setPermConverter(PermConverter pc) { + pconv = pc; + return this; + } + + // Add a feature + public void invalidate(String id) { + if(lur instanceof EpiLur) { + ((EpiLur)lur).remove(id); + } else if(lur instanceof CachingLur) { + ((CachingLur<?>)lur).remove(id); + } + } + + public Lur getLur() { + return lur; + } + + public Access access() { + return access; + } +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/Capacitor.java b/cadi/core/src/main/java/org/onap/aaf/cadi/Capacitor.java new file mode 100644 index 00000000..00383851 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/Capacitor.java @@ -0,0 +1,241 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi; + +import java.nio.ByteBuffer; +import java.util.ArrayList; + +/** + * Capacitor + * + * Storage mechanism for read data, specifically designed for InputStreams. + * + * The Standard BufferedInputStream requires a limit to be set for buffered reading, which is + * impractical for reading SOAP headers, which can be quite large. + * @author Jonathan + * + */ +public class Capacitor { + private static final int DEFAULT_CHUNK = 256; + private ArrayList<ByteBuffer> bbs = new ArrayList<ByteBuffer>(); + private ByteBuffer curr = null; + private int idx; + + // Maintain a private RingBuffer for Memory, for efficiency + private static ByteBuffer[] ring = new ByteBuffer[16]; + private static int start, end; + + + public void put(byte b) { + if(curr == null || curr.remaining()==0) { // ensure we have a "curr" buffer ready for data + curr = ringGet(); + bbs.add(curr); + } + curr.put(b); + } + + public int read() { + if(curr!=null) { + if(curr.remaining()>0) { // have a buffer, use it! + return curr.get(); + } else if(idx<bbs.size()){ // Buffer not enough, get next one from array + curr=bbs.get(idx++); + return curr.get(); + } + } // if no curr buffer, treat as end of stream + return -1; + } + + /** + * read into an array like Streams + * + * @param array + * @param offset + * @param length + * @return + */ + public int read(byte[] array, int offset, int length) { + if(curr==null)return -1; + int len; + int count=0; + while(length>0) { // loop through while there's data needed + if((len=curr.remaining())>length) { // if enough data in curr buffer, use this code + curr.get(array,offset,length); + count+=length; + length=0; + } else { // get data from curr, mark how much is needed to fulfil, and loop for next curr. + curr.get(array,offset,len); + count+=len; + offset+=len; + length-=len; + if(idx<bbs.size()) { + curr=bbs.get(idx++); + } else { + length=0; // stop, and return the count of how many we were able to load + } + } + } + return count; + } + + /** + * Put an array of data into Capacitor + * + * @param array + * @param offset + * @param length + */ + public void put(byte[] array, int offset, int length) { + if(curr == null || curr.remaining()==0) { + curr = ringGet(); + bbs.add(curr); + } + + int len; + while(length>0) { + if((len=curr.remaining())>length) { + curr.put(array,offset,length); + length=0; + } else { +// System.out.println(new String(array)); + curr.put(array,offset,len); + length-=len; + offset+=len; + curr = ringGet(); + bbs.add(curr); + } + } + } + + /** + * Move state from Storage mode into Read mode, changing all internal buffers to read mode, etc + */ + public void setForRead() { + for(ByteBuffer bb : bbs) { + bb.flip(); + } + if(bbs.isEmpty()) { + curr = null; + idx = 0; + } else { + curr=bbs.get(0); + idx=1; + } + } + + /** + * reuse all the buffers + */ + public void done() { + for(ByteBuffer bb : bbs) { + ringPut(bb); + } + bbs.clear(); + curr = null; + } + + /** + * Declare amount of data available to be read at once. + * + * @return + */ + public int available() { + int count = 0; + for(ByteBuffer bb : bbs) { + count+=bb.remaining(); + } + return count; + } + + /** + * Returns how many are left that were not skipped + * @param n + * @return + */ + public long skip(long n) { + long skipped=0L; + int skip; + if(curr==null) { + return 0; + } + while(n>0) { + if(n<(skip=curr.remaining())) { + curr.position(curr.position()+(int)n); + skipped+=skip; + n=0; + } else { + curr.position(curr.limit()); + + skipped-=skip; + if(idx<bbs.size()) { + curr=bbs.get(idx++); + n-=skip; + } else { + n=0; + } + } + } + return skipped > 0 ? skipped : 0; + } + /** + * Be able to re-read data that is stored that has already been re-read. This is not a standard Stream behavior, but can be useful + * in a standalone mode. + */ + public void reset() { + for(ByteBuffer bb : bbs) { + bb.position(0); + } + if(bbs.isEmpty()) { + curr = null; + idx = 0; + } else { + curr=bbs.get(0); + idx=1; + } + } + + /* + * Ring Functions. Reuse allocated memory + */ + private ByteBuffer ringGet() { + ByteBuffer bb = null; + synchronized(ring) { + bb=ring[start]; + ring[start]=null; + if(bb!=null && ++start>15)start=0; + } + if(bb==null) { + bb=ByteBuffer.allocate(DEFAULT_CHUNK); + } else { + bb.clear();// refresh reused buffer + } + return bb; + } + + private void ringPut(ByteBuffer bb) { + synchronized(ring) { + ring[end]=bb; // if null or not, BB will just be Garbage collected + if(++end>15)end=0; + } + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/CmdLine.java b/cadi/core/src/main/java/org/onap/aaf/cadi/CmdLine.java new file mode 100644 index 00000000..ea126f54 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/CmdLine.java @@ -0,0 +1,360 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.security.NoSuchAlgorithmException; + +import org.onap.aaf.cadi.util.Chmod; +import org.onap.aaf.cadi.util.JsonOutputStream; + + + +/** + * A Class to run on command line to determine suitability of environment for certain TAFs. + * + * For instance, CSP supports services only in certain domains, and while dynamic host + * lookups on the machine work in most cases, sometimes, names and IPs are unexpected (and + * invalid) for CSP because of multiple NetworkInterfaces, etc + * + * @author Jonathan + * + */ +public class CmdLine { + + private static boolean systemExit = true; + /** + * @param args + */ + public static void main(String[] args) { + if(args.length>0) { + if("digest".equalsIgnoreCase(args[0]) && (args.length>2 || (args.length>1 && System.console()!=null))) { + String keyfile; + String password; + if(args.length>2) { + password = args[1]; + keyfile = args[2]; + if("-i".equals(password)) { + int c; + StringBuilder sb = new StringBuilder(); + try { + while((c=System.in.read())>=0) { + sb.append((char)c); + } + } catch (IOException e) { + e.printStackTrace(); + } + password = sb.toString(); + } + } else { + keyfile = args[1]; + password = new String(System.console().readPassword("Type here (keystrokes hidden): ")); + } + + try { + Symm symm; + FileInputStream fis = new FileInputStream(keyfile); + try { + symm = Symm.obtain(fis); + } finally { + fis.close(); + } + symm.enpass(password, System.out); + System.out.println(); + System.out.flush(); + return; + /* testing code... don't want it exposed + System.out.println(" ******** Testing *********"); + for(int i=0;i<100000;++i) { + System.out.println(args[1]); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + b64.enpass(args[1], baos); + String pass; + System.out.println(pass=new String(baos.toByteArray())); + ByteArrayOutputStream reconstituted = new ByteArrayOutputStream(); + b64.depass(pass, reconstituted); + String r = reconstituted.toString(); + System.out.println(r); + if(!r.equals(args[1])) { + System.err.println("!!!!! STOP - ERROR !!!!!"); + return; + } + System.out.println(); + } + System.out.flush(); + */ + + } catch (IOException e) { + System.err.println("Cannot digest password"); + System.err.println(" \""+ e.getMessage() + '"'); + } +// DO NOT LEAVE THIS METHOD Compiled IN CODE... Do not want looking at passwords on disk too easy +// Jonathan. Oh, well, Deployment services need this behavior. I will put this code in, but leave it undocumented. +// One still needs access to the keyfile to read. +// July 2016 - thought of a tool "CMPass" to regurgitate from properties, but only if allowed. + } else if("regurgitate".equalsIgnoreCase(args[0]) && args.length>2) { + try { + Symm symm; + FileInputStream fis = new FileInputStream(args[2]); + try { + symm = Symm.obtain(fis); + } finally { + fis.close(); + } + boolean isFile = false; + if("-i".equals(args[1]) || (isFile="-f".equals(args[1]))) { + BufferedReader br; + if(isFile) { + if(args.length<4) { + System.err.println("Filename in 4th position"); + return; + } + br = new BufferedReader(new FileReader(args[3])); + } else { + br = new BufferedReader(new InputStreamReader(System.in)); + } + try { + String line; + boolean cont = false; + StringBuffer sb = new StringBuffer(); + JsonOutputStream jw = new JsonOutputStream(System.out); + while((line=br.readLine())!=null) { + if(cont) { + int end; + if((end=line.indexOf('"'))>=0) { + sb.append(line,0,end); + cont=false; + } else { + sb.append(line); + } + } else { + int idx; + if((idx = line.indexOf(' '))>=0 + && (idx = line.indexOf(' ',++idx))>0 + && (idx = line.indexOf('=',++idx))>0 + ) { + System.out.println(line.substring(0, idx-5)); + int start = idx+2; + int end; + if((end=line.indexOf('"',start))<0) { + end = line.length(); + cont = true; + } + sb.append(line,start,end); + } + } + if(sb.length()>0) { + symm.depass(sb.toString(),jw); + if(!cont) { + System.out.println(); + } + } + System.out.flush(); + sb.setLength(0); + if(!cont) { + jw.resetIndent(); + } + } + } finally { + if(isFile) { + br.close(); + } + } + } else { + symm.depass(args[1], System.out); + } + System.out.println(); + System.out.flush(); + return; + } catch (IOException e) { + System.err.println("Cannot regurgitate password"); + System.err.println(" \""+ e.getMessage() + '"'); + } + } else if("encode64".equalsIgnoreCase(args[0]) && args.length>1) { + try { + Symm.base64.encode(args[1], System.out); + System.out.println(); + System.out.flush(); + return; + } catch (IOException e) { + System.err.println("Cannot encode Base64 with " + args[1]); + System.err.println(" \""+ e.getMessage() + '"'); + } + } else if("decode64".equalsIgnoreCase(args[0]) && args.length>1) { + try { + Symm.base64.decode(args[1], System.out); + System.out.println(); + System.out.flush(); + return; + } catch (IOException e) { + System.err.println("Cannot decode Base64 text from " + args[1]); + System.err.println(" \""+ e.getMessage() + '"'); + } + } else if("encode64url".equalsIgnoreCase(args[0]) && args.length>1) { + try { + Symm.base64url.encode(args[1], System.out); + System.out.println(); + System.out.flush(); + return; + } catch (IOException e) { + System.err.println("Cannot encode Base64url with " + args[1]); + System.err.println(" \""+ e.getMessage() + '"'); + } + } else if("decode64url".equalsIgnoreCase(args[0]) && args.length>1) { + try { + Symm.base64url.decode(args[1], System.out); + System.out.println(); + System.out.flush(); + return; + } catch (IOException e) { + System.err.println("Cannot decode Base64url text from " + args[1]); + System.err.println(" \""+ e.getMessage() + '"'); + } + } else if("md5".equalsIgnoreCase(args[0]) && args.length>1) { + try { + System.out.println(Hash.hashMD5asStringHex(args[1])); + System.out.flush(); + } catch (NoSuchAlgorithmException e) { + System.err.println("Cannot hash MD5 from " + args[1]); + System.err.println(" \""+ e.getMessage() + '"'); + } + return; + } else if("sha256".equalsIgnoreCase(args[0]) && args.length>1) { + try { + if(args.length>2) { + int max = args.length>7?7:args.length; + for(int i=2;i<max;++i) { + int salt = Integer.parseInt(args[i]); + System.out.println(Hash.hashSHA256asStringHex(args[1],salt)); + } + } else { + System.out.println(Hash.hashSHA256asStringHex(args[1])); + } + } catch (NoSuchAlgorithmException e) { + System.err.println("Cannot hash SHA256 text from " + args[1]); + System.err.println(" \""+ e.getMessage() + '"'); + } + System.out.flush(); + return; + } else if("keygen".equalsIgnoreCase(args[0])) { + try { + if(args.length>1) { + File f = new File(args[1]); + FileOutputStream fos = new FileOutputStream(f); + try { + fos.write(Symm.keygen()); + fos.flush(); + } finally { + fos.close(); + Chmod.to400.chmod(f); + } + } else { + // create a Symmetric Key out of same characters found in base64 + System.out.write(Symm.keygen()); + System.out.flush(); + } + return; + } catch (IOException e) { + System.err.println("Cannot create a key " + args[0]); + System.err.println(" \""+ e.getMessage() + '"'); + } + + } else if("passgen".equalsIgnoreCase(args[0])) { + int numDigits; + if(args.length <= 1) { + numDigits = 24; + } else { + numDigits = Integer.parseInt(args[1]); + if(numDigits<8)numDigits = 8; + } + String pass; + boolean noLower,noUpper,noDigits,noSpecial,repeatingChars,missingChars; + do { + pass = Symm.randomGen(numDigits); + missingChars=noLower=noUpper=noDigits=noSpecial=true; + repeatingChars=false; + int c=-1,last; + for(int i=0;i<numDigits;++i) { + last = c; + c = pass.charAt(i); + if(c==last) { + repeatingChars=true; + break; + } + if(noLower) { + noLower=!(c>=0x61 && c<=0x7A); + } + if(noUpper) { + noUpper=!(c>=0x41 && c<=0x5A); + } + if(noDigits) { + noDigits=!(c>=0x30 && c<=0x39); + } + if(noSpecial) { + noSpecial = "+!@#$%^&*(){}[]?:;,.".indexOf(c)<0; + } + + missingChars = (noLower || noUpper || noDigits || noSpecial); + } + } while(missingChars || repeatingChars); + System.out.println(pass.substring(0,numDigits)); + } else if("urlgen".equalsIgnoreCase(args[0])) { + int numDigits; + if(args.length <= 1) { + numDigits = 24; + } else { + numDigits = Integer.parseInt(args[1]); + } + System.out.println(Symm.randomGen(Symm.base64url.codeset, numDigits).substring(0,numDigits)); + } + } else { + System.out.println("Usage: java -jar <this jar> ..."); + System.out.println(" keygen [<keyfile>] (Generates Key on file, or Std Out)"); + System.out.println(" digest [<passwd>|-i|] <keyfile> (Encrypts Password with \"keyfile\""); + System.out.println(" if passwd = -i, will read StdIin"); + System.out.println(" if passwd is blank, will ask securely)"); + System.out.println(" passgen <digits> (Generate Password of given size)"); + System.out.println(" urlgen <digits> (Generate URL field of given size)"); + System.out.println(" csptest (Tests for CSP compatibility)"); + System.out.println(" encode64 <your text> (Encodes to Base64)"); + System.out.println(" decode64 <base64 encoded text> (Decodes from Base64)"); + System.out.println(" encode64url <your text> (Encodes to Base64 URL charset)"); + System.out.println(" decode64url <base64url encoded text> (Decodes from Base64 URL charset)"); + System.out.println(" sha256 <text> <salts(s)> (Digest String into SHA256 Hash)"); + System.out.println(" md5 <text> (Digest String into MD5 Hash)"); + } + if (systemExit) { + System.exit(1); + } + } + + public static void setSystemExit(boolean shouldExit) { + systemExit = shouldExit; + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/Connector.java b/cadi/core/src/main/java/org/onap/aaf/cadi/Connector.java new file mode 100644 index 00000000..7f47ce78 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/Connector.java @@ -0,0 +1,26 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi; + +public interface Connector { + public Lur newLur() throws CadiException; +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/CredVal.java b/cadi/core/src/main/java/org/onap/aaf/cadi/CredVal.java new file mode 100644 index 00000000..4e42a5fb --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/CredVal.java @@ -0,0 +1,42 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi; + + +/** + * UserPass + * + * The essential interface required by BasicAuth to determine if a given User/Password combination is + * valid. This is done as an interface. + * + * @author Jonathan + */ +public interface CredVal { + public enum Type{PASSWORD}; + /** + * Validate if the User/Password combination matches records + * @param user + * @param pass + * @return + */ + public boolean validate(String user, Type type, byte[] cred, Object state); +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/GetCred.java b/cadi/core/src/main/java/org/onap/aaf/cadi/GetCred.java new file mode 100644 index 00000000..e64f0dd4 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/GetCred.java @@ -0,0 +1,26 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi; + +public interface GetCred { + byte[] getCred(); +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/Hash.java b/cadi/core/src/main/java/org/onap/aaf/cadi/Hash.java new file mode 100644 index 00000000..6babb4c9 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/Hash.java @@ -0,0 +1,258 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi; + +import java.nio.ByteBuffer; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public class Hash { + private static char hexDigit[] = "0123456789abcdef".toCharArray(); + +///////////////////////////////// +// MD5 +///////////////////////////////// + /** + * Encrypt MD5 from Byte Array to Byte Array + * @param input + * @return + * @throws NoSuchAlgorithmException + */ + public static byte[] hashMD5 (byte[] input) throws NoSuchAlgorithmException { + // Note: Protect against Multi-thread issues with new MessageDigest + MessageDigest md = MessageDigest.getInstance("MD5"); + md.update(input); + return md.digest(); + } + + /** + * Encrypt MD5 from Byte Array to Byte Array + * @param input + * @return + * @throws NoSuchAlgorithmException + */ + public static byte[] hashMD5 (byte[] input, int offset, int length) throws NoSuchAlgorithmException { + // Note: Protect against Multi-thread issues with new MessageDigest + MessageDigest md = MessageDigest.getInstance("MD5"); + md.update(input,offset,length); + return md.digest(); + } + + + + /** + * Convenience Function: Encrypt MD5 from String to String Hex representation + * + * @param input + * @return + * @throws NoSuchAlgorithmException + */ + public static String hashMD5asStringHex(String input) throws NoSuchAlgorithmException { + byte[] output = hashMD5(input.getBytes()); + StringBuilder sb = new StringBuilder("0x"); + for (byte b : output) { + sb.append(hexDigit[(b >> 4) & 0x0f]); + sb.append(hexDigit[b & 0x0f]); + } + return sb.toString(); + } + +///////////////////////////////// +// SHA256 +///////////////////////////////// + /** + * SHA256 Hashing + */ + public static byte[] hashSHA256(byte[] input) throws NoSuchAlgorithmException { + // Note: Protect against Multi-thread issues with new MessageDigest + MessageDigest md = MessageDigest.getInstance("SHA-256"); + md.update(input); + return md.digest(); + } + + /** + * SHA256 Hashing + */ + public static byte[] hashSHA256(byte[] input, int offset, int length) throws NoSuchAlgorithmException { + // Note: Protect against Multi-thread issues with new MessageDigest + MessageDigest md = MessageDigest.getInstance("SHA-256"); + md.update(input,offset,length); + return md.digest(); + } + + /** + * Convenience Function: Hash from String to String Hex representation + * + * @param input + * @return + * @throws NoSuchAlgorithmException + */ + public static String hashSHA256asStringHex(String input) throws NoSuchAlgorithmException { + return toHex(hashSHA256(input.getBytes())); + } + + /** + * Convenience Function: Hash from String to String Hex representation + * + * @param input + * @return + * @throws NoSuchAlgorithmException + */ + public static String hashSHA256asStringHex(String input, int salt) throws NoSuchAlgorithmException { + byte[] in = input.getBytes(); + ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + in.length); + bb.putInt(salt); + bb.put(input.getBytes()); + return toHex(Hash.hashSHA256(bb.array())); + } + + /** + * Compare two byte arrays for equivalency + * @param ba1 + * @param ba2 + * @return + */ + public static boolean isEqual(byte ba1[], byte ba2[]) { + if(ba1.length!=ba2.length)return false; + for(int i = 0;i<ba1.length; ++i) { + if(ba1[i]!=ba2[i])return false; + } + return true; + } + + public static int compareTo(byte[] a, byte[] b) { + int end = Math.min(a.length, b.length); + int compare = 0; + for(int i=0;compare == 0 && i<end;++i) { + compare = a[i]-b[i]; + } + if(compare==0)compare=a.length-b.length; + return compare; + } + + public static String toHexNo0x(byte[] ba) { + StringBuilder sb = new StringBuilder(); + for (byte b : ba) { + sb.append(hexDigit[(b >> 4) & 0x0f]); + sb.append(hexDigit[b & 0x0f]); + } + return sb.toString(); + } + + public static String toHex(byte[] ba) { + StringBuilder sb = new StringBuilder("0x"); + for (byte b : ba) { + sb.append(hexDigit[(b >> 4) & 0x0f]); + sb.append(hexDigit[b & 0x0f]); + } + return sb.toString(); + } + + public static String toHex(byte[] ba, int start, int length) { + StringBuilder sb = new StringBuilder("0x"); + for (int i=start;i<length;++i) { + sb.append(hexDigit[(ba[i] >> 4) & 0x0f]); + sb.append(hexDigit[ba[i] & 0x0f]); + } + return sb.toString(); + } + + + public static byte[] fromHex(String s) throws CadiException{ + if(!s.startsWith("0x")) { + throw new CadiException("HexString must start with \"0x\""); + } + boolean high = true; + int c; + byte b; + byte[] ba = new byte[(s.length()-2)/2]; + int idx; + for(int i=2;i<s.length();++i) { + c = s.charAt(i); + if(c>=0x30 && c<=0x39) { + b=(byte)(c-0x30); + } else if(c>=0x61 && c<=0x66) { + b=(byte)(c-0x57); // account for "A" + } else if(c>=0x41 && c<=0x46) { + b=(byte)(c-0x37); + } else { + throw new CadiException("Invalid char '" + c + "' in HexString"); + } + idx = (i-2)/2; + if(high) { + ba[idx]=(byte)(b<<4); + high = false; + } else { + ba[idx]|=b; + high = true; + } + } + return ba; + } + + /** + * Does not expect to start with "0x" + * if Any Character doesn't match, it returns null; + * + * @param s + * @return + */ + public static byte[] fromHexNo0x(String s) { + int c; + byte b; + byte[] ba; + boolean high; + int start; + if(s.length()%2==0) { + ba = new byte[s.length()/2]; + high=true; + start=0; + } else { + ba = new byte[(s.length()/2)+1]; + high = false; + start=1; + } + int idx; + for(int i=start;i<s.length();++i) { + c = s.charAt((i-start)); + if(c>=0x30 && c<=0x39) { + b=(byte)(c-0x30); + } else if(c>=0x61 && c<=0x66) { + b=(byte)(c-0x57); // account for "A" + } else if(c>=0x41 && c<=0x46) { + b=(byte)(c-0x37); + } else { + return null; + } + idx = i/2; + if(high) { + ba[idx]=(byte)(b<<4); + high = false; + } else { + ba[idx]|=b; + high = true; + } + } + return ba; + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/Locator.java b/cadi/core/src/main/java/org/onap/aaf/cadi/Locator.java new file mode 100644 index 00000000..22258d12 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/Locator.java @@ -0,0 +1,36 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi; + +public interface Locator<T> { + public T get(Locator.Item item) throws LocatorException; + public boolean hasItems(); + public void invalidate(Locator.Item item) throws LocatorException; + public Locator.Item best() throws LocatorException; + public Item first() throws LocatorException; + public Item next(Item item) throws LocatorException; + public boolean refresh(); + public void destroy(); + + public interface Item {} + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/LocatorException.java b/cadi/core/src/main/java/org/onap/aaf/cadi/LocatorException.java new file mode 100644 index 00000000..f14fba70 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/LocatorException.java @@ -0,0 +1,46 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi; + +public class LocatorException extends Exception { + /** + * + */ + private static final long serialVersionUID = -4267929804321134469L; + + public LocatorException(String arg0) { + super(arg0); + } + + public LocatorException(Throwable arg0) { + super(arg0); + } + + public LocatorException(String arg0, Throwable arg1) { + super(arg0, arg1); + } + + public LocatorException(CharSequence cs) { + super(cs.toString()); + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/Lur.java b/cadi/core/src/main/java/org/onap/aaf/cadi/Lur.java new file mode 100644 index 00000000..fd73d00b --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/Lur.java @@ -0,0 +1,92 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi; + +import java.security.Principal; +import java.util.List; + + + +/** + * LUR: Local User Registry + * + * Concept by Robert Garskof, Implementation by Jonathan Gathman + * + * Where we can keep local copies of users and roles for faster Authorization when asked. + * + * Note: Author cannot resist the mental image of using a Fishing Lure to this LUR pattern + * + * @author Jonathan + * + */ +public interface Lur { + /** + * Allow the Lur, which has correct Permission access, to create and hand back. + */ + public Permission createPerm(String p); + + /** + * Fish for Principals in a Pond + * + * or more boringly, is the User identified within a named collection representing permission. + * + * @param principalName + * @return + */ + public boolean fish(Principal bait, Permission pond); + + /** + * Fish all the Principals out a Pond + * + * For additional humor, pronounce the following with a Southern Drawl, "FishOil" + * + * or more boringly, load the List with Permissions found for Principal + * + * @param principalName + * @return + */ + public void fishAll(Principal bait, List<Permission> permissions); + + /** + * Allow implementations to disconnect, or cleanup resources if unneeded + */ + public void destroy(); + + /** + * Does this LUR handle this pond exclusively? Important for EpiLUR to determine whether + * to try another (more expensive) LUR + * @param pond + * @return + */ + public boolean handlesExclusively(Permission pond); + + /** + * Does the LUR support a particular kind of Principal + * This can be used to check name's domain, like above, or Principal type + */ + public boolean handles(Principal principal); + + /** + * Clear: Clear any Caching, if exists + */ + public void clear(Principal p, StringBuilder report); +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/Permission.java b/cadi/core/src/main/java/org/onap/aaf/cadi/Permission.java new file mode 100644 index 00000000..f8061290 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/Permission.java @@ -0,0 +1,28 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi; + +public interface Permission { + public String permType(); + public String getKey(); + public boolean match(Permission p); +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/PropAccess.java b/cadi/core/src/main/java/org/onap/aaf/cadi/PropAccess.java new file mode 100644 index 00000000..c827477f --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/PropAccess.java @@ -0,0 +1,396 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map.Entry; +import java.util.Properties; + +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.config.SecurityInfo; + +public class PropAccess implements Access { + // Sonar says cannot be static... it's ok. not too many PropAccesses created. + private final SimpleDateFormat iso8601 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); + + public static Level DEFAULT = Level.AUDIT; + + private Symm symm; + private int level; + private Properties props; + private List<String> recursionProtection = null; + private LogIt logIt; + private String name; + + public PropAccess() { + logIt = new StreamLogIt(System.out); + init(null); + } + + /** + * This Constructor soly exists to instantiate Servlet Context Based Logging that will call "init" later. + * @param sc + */ + protected PropAccess(Object o) { + logIt = new StreamLogIt(System.out); + props = new Properties(); + } + + public PropAccess(String ... args) { + this(System.out,args); + } + + public PropAccess(PrintStream ps, String[] args) { + logIt = new StreamLogIt(ps==null?System.out:ps); + init(logIt,args); + } + + public PropAccess(LogIt logit, String[] args) { + init(logit, args); + } + + public PropAccess(Properties p) { + this(System.out,p); + } + + public PropAccess(PrintStream ps, Properties p) { + logIt = new StreamLogIt(ps==null?System.out:ps); + init(p); + } + + protected void init(final LogIt logIt, final String[] args) { + this.logIt = logIt; + Properties nprops=new Properties(); + int eq; + for(String arg : args) { + if((eq=arg.indexOf('='))>0) { + nprops.setProperty(arg.substring(0, eq),arg.substring(eq+1)); + } + } + init(nprops); + } + + protected void init(Properties p) { + // Make sure these two are set before any changes in Logging + name = "cadi"; + level=DEFAULT.maskOf(); + + props = new Properties(); + // First, load related System Properties + for(Entry<Object,Object> es : System.getProperties().entrySet()) { + String key = es.getKey().toString(); + for(String start : new String[] {"cadi_","aaf_","cm_"}) { + if(key.startsWith(start)) { + props.put(key, es.getValue()); + } + } + } + // Second, overlay or fill in with Passed in Props + if(p!=null) { + props.putAll(p); + } + + // Third, load any Chained Property Files + load(props.getProperty(Config.CADI_PROP_FILES)); + + String sLevel = props.getProperty(Config.CADI_LOGLEVEL); + if(sLevel!=null) { + level=Level.valueOf(sLevel).maskOf(); + } + // Setup local Symmetrical key encryption + if(symm==null) { + try { + symm = Symm.obtain(this); + } catch (CadiException e) { + System.err.append("FATAL ERROR: Cannot obtain Key Information."); + e.printStackTrace(System.err); + System.exit(1); + } + } + + name = props.getProperty(Config.CADI_LOGNAME, name); + + specialConversions(); + } + + private void specialConversions() { + // Critical - if no Security Protocols set, then set it. We'll just get messed up if not + if(props.get(Config.CADI_PROTOCOLS)==null) { + props.setProperty(Config.CADI_PROTOCOLS, SecurityInfo.HTTPS_PROTOCOLS_DEFAULT); + } + + Object temp; + temp=props.get(Config.CADI_PROTOCOLS); + if(props.get(Config.HTTPS_PROTOCOLS)==null && temp!=null) { + props.put(Config.HTTPS_PROTOCOLS, temp); + } + + if(temp!=null) { + if("1.7".equals(System.getProperty("java.specification.version")) + && (temp==null || (temp instanceof String && ((String)temp).contains("TLSv1.2")))) { + System.setProperty(Config.HTTPS_CIPHER_SUITES, Config.HTTPS_CIPHER_SUITES_DEFAULT); + } + } + } + + private void load(String cadi_prop_files) { + if(cadi_prop_files==null) { + return; + } + String prevKeyFile = props.getProperty(Config.CADI_KEYFILE); + int prev = 0, end = cadi_prop_files.length(); + int idx; + String filename; + while(prev<end) { + idx = cadi_prop_files.indexOf(File.pathSeparatorChar,prev); + if(idx<0) { + idx = end; + } + File file = new File(filename=cadi_prop_files.substring(prev,idx)); + if(file.exists()) { + printf(Level.INIT,"Loading CADI Properties from %s",file.getAbsolutePath()); + try { + FileInputStream fis = new FileInputStream(file); + try { + props.load(fis); + // Recursively Load + String chainProp = props.getProperty(Config.CADI_PROP_FILES); + if(chainProp!=null) { + if(recursionProtection==null) { + recursionProtection = new ArrayList<String>(); + recursionProtection.add(cadi_prop_files); + } + if(!recursionProtection.contains(chainProp)) { + recursionProtection.add(chainProp); + load(chainProp); // recurse + } + } + } finally { + fis.close(); + } + } catch (Exception e) { + log(e,filename,"cannot be opened"); + } + } else { + printf(Level.WARN,"Warning: recursive CADI Property %s does not exist",file.getAbsolutePath()); + } + prev = idx+1; + } + + // Trim + for(Entry<Object, Object> es : props.entrySet()) { + Object value = es.getValue(); + if(value instanceof String) { + String trim = ((String)value).trim(); + if(trim!=value) { // Yes, I want OBJECT equals + props.setProperty((String)es.getKey(), trim); + } + } + } + // Reset Symm if Keyfile Changes: + String newKeyFile = props.getProperty(Config.CADI_KEYFILE); + if((prevKeyFile!=null && newKeyFile!=null) || (newKeyFile!=null && !newKeyFile.equals(prevKeyFile))) { + try { + symm = Symm.obtain(this); + } catch (CadiException e) { + System.err.append("FATAL ERROR: Cannot obtain Key Information."); + e.printStackTrace(System.err); + System.exit(1); + } + + prevKeyFile=newKeyFile; + } + + String loglevel = props.getProperty(Config.CADI_LOGLEVEL); + if(loglevel!=null) { + try { + level=Level.valueOf(loglevel).maskOf(); + } catch (IllegalArgumentException e) { + printf(Level.ERROR,"%s=%s is an Invalid Log Level",Config.CADI_LOGLEVEL,loglevel); + } + } + + specialConversions(); + } + + @Override + public void load(InputStream is) throws IOException { + props.load(is); + load(props.getProperty(Config.CADI_PROP_FILES)); + } + + @Override + public void log(Level level, Object ... elements) { + if(willLog(level)) { + logIt.push(level,elements); + } + } + + protected StringBuilder buildMsg(Level level, Object[] elements) { + return buildMsg(name,iso8601,level,elements); + } + + public static StringBuilder buildMsg(final String name, final SimpleDateFormat sdf, Level level, Object[] elements) { + StringBuilder sb = new StringBuilder(sdf.format(new Date())); + sb.append(' '); + sb.append(level.name()); + sb.append(" ["); + sb.append(name); + + int end = elements.length; + if(end<=0) { + sb.append("] "); + } else { + int idx = 0; + if(elements[idx] instanceof Integer) { + sb.append('-'); + sb.append(elements[idx]); + ++idx; + } + sb.append("] "); + String s; + boolean first = true; + for(Object o : elements) { + if(o!=null) { + s=o.toString(); + if(first) { + first = false; + } else { + int l = s.length(); + if(l>0) { + switch(s.charAt(l-1)) { + case ' ': + break; + default: + sb.append(' '); + } + } + } + sb.append(s); + } + } + } + return sb; + } + + @Override + public void log(Exception e, Object... elements) { + log(Level.ERROR,e.getMessage(),elements); + e.printStackTrace(System.err); + } + + @Override + public void printf(Level level, String fmt, Object... elements) { + if(willLog(level)) { + log(level,String.format(fmt, elements)); + } + } + + @Override + public void setLogLevel(Level level) { + this.level = level.maskOf(); + } + + @Override + public boolean willLog(Level level) { + return level.inMask(this.level); + } + + @Override + public ClassLoader classLoader() { + return ClassLoader.getSystemClassLoader(); + } + + @Override + public String getProperty(String tag, String def) { + return props.getProperty(tag,def); + } + + @Override + public String decrypt(String encrypted, boolean anytext) throws IOException { + return (encrypted!=null && (anytext==true || encrypted.startsWith(Symm.ENC))) + ? symm.depass(encrypted) + : encrypted; + } + + public String encrypt(String unencrypted) throws IOException { + return Symm.ENC+symm.enpass(unencrypted); + } + + ////////////////// + // Additional + ////////////////// + public String getProperty(String tag) { + return props.getProperty(tag); + } + + + public Properties getProperties() { + return props; + } + + public void setProperty(String tag, String value) { + if(value!=null) { + props.put(tag, value); + if(Config.CADI_KEYFILE.equals(tag)) { + // reset decryption too + try { + symm = Symm.obtain(this); + } catch (CadiException e) { + System.err.append("FATAL ERROR: Cannot obtain Key Information."); + e.printStackTrace(System.err); + System.exit(1); + } + } + } + } + + public interface LogIt { + public void push(Level level, Object ... elements) ; + } + + private class StreamLogIt implements LogIt { + private PrintStream ps; + + public StreamLogIt(PrintStream ps) { + this.ps = ps; + } + @Override + public void push(Level level, Object ... elements) { + ps.println(buildMsg(level,elements)); + ps.flush(); + } + + } + + public void set(LogIt logit) { + logIt = logit; + } +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/Revalidator.java b/cadi/core/src/main/java/org/onap/aaf/cadi/Revalidator.java new file mode 100644 index 00000000..125ac24c --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/Revalidator.java @@ -0,0 +1,34 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi; + + +public interface Revalidator<TRANS> { + /** + * Re-Validate Credential + * + * @param prin + * @return + */ + public CachedPrincipal.Resp revalidate(TRANS trans, CachedPrincipal prin); + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/SecuritySetter.java b/cadi/core/src/main/java/org/onap/aaf/cadi/SecuritySetter.java new file mode 100644 index 00000000..31563017 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/SecuritySetter.java @@ -0,0 +1,44 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi; + + +/** + * Apply any particular security mechanism + * + * This allows the definition of various mechanisms involved outside of DRcli jars + * + * @author Jonathan + * + */ +public interface SecuritySetter<CT> { + public String getID(); + + public void setSecurity(CT client) throws CadiException; + + /** + * Returns number of bad logins registered + * @param respCode + * @return + */ + public int setLastResponse(int respCode); +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/ServletContextAccess.java b/cadi/core/src/main/java/org/onap/aaf/cadi/ServletContextAccess.java new file mode 100644 index 00000000..38a01a09 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/ServletContextAccess.java @@ -0,0 +1,67 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi; + +import java.util.Enumeration; + +import javax.servlet.FilterConfig; +import javax.servlet.ServletContext; + +public class ServletContextAccess extends PropAccess { + + private ServletContext context; + + public ServletContextAccess(FilterConfig filterConfig) { + super(filterConfig); // protected constructor... does not have "init" called. + context = filterConfig.getServletContext(); + + for(Enumeration<?> en = filterConfig.getInitParameterNames();en.hasMoreElements();) { + String name = (String)en.nextElement(); + setProperty(name, filterConfig.getInitParameter(name)); + } + init(getProperties()); + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.PropAccess#log(org.onap.aaf.cadi.Access.Level, java.lang.Object[]) + */ + @Override + public void log(Level level, Object... elements) { + if(willLog(level)) { + StringBuilder sb = buildMsg(level, elements); + context.log(sb.toString()); + } + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.PropAccess#log(java.lang.Exception, java.lang.Object[]) + */ + @Override + public void log(Exception e, Object... elements) { + StringBuilder sb = buildMsg(Level.ERROR, elements); + context.log(sb.toString(),e); + } + + public ServletContext context() { + return context; + } +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/Symm.java b/cadi/core/src/main/java/org/onap/aaf/cadi/Symm.java new file mode 100644 index 00000000..82645c31 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/Symm.java @@ -0,0 +1,858 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Random; + +import javax.crypto.CipherInputStream; +import javax.crypto.CipherOutputStream; + +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.config.Config; + +/** + * Key Conversion, primarily "Base64" + * + * Base64 is required for "Basic Authorization", which is an important part of the overall CADI Package. + * + * Note: This author found that there is not a "standard" library for Base64 conversion within Java. + * The source code implementations available elsewhere were surprisingly inefficient, requiring, for + * instance, multiple string creation, on a transaction pass. Integrating other packages that might be + * efficient enough would put undue Jar File Dependencies given this Framework should have none-but-Java + * dependencies. + * + * The essential algorithm is good for a symmetrical key system, as Base64 is really just + * a symmetrical key that everyone knows the values. + * + * This code is quite fast, taking about .016 ms for encrypting, decrypting and even .08 for key + * generation. The speed quality, especially of key generation makes this a candidate for a short term token + * used for identity. + * + * It may be used to easily avoid placing Clear-Text passwords in configurations, etc. and contains + * supporting functions such as 2048 keyfile generation (see keygen). This keyfile should, of course, + * be set to "400" (Unix) and protected as any other mechanism requires. + * + * However, this algorithm has not been tested against hackers. Until such a time, utilize more tested + * packages to protect Data, especially sensitive data at rest (long term). + * + * @author Jonathan + * + */ +public class Symm { + private static final byte[] DOUBLE_EQ = new byte[] {'=','='}; + public static final String ENC = "enc:"; + private static final Object LOCK = new Object(); + private static final SecureRandom random = new SecureRandom(); + + public final char[] codeset; + private final int splitLinesAt; + private final String encoding; + private final Convert convert; + private final boolean endEquals; + private byte[] keyBytes = null; + //Note: AES Encryption is not Thread Safe. It is Synchronized + //private AES aes = null; // only initialized from File, and only if needed for Passwords + + /** + * This is the standard base64 Key Set. + * RFC 2045 + */ + public static final Symm base64 = new Symm( + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray() + ,76, Config.UTF_8,true); + + public static final Symm base64noSplit = new Symm( + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray() + ,Integer.MAX_VALUE, Config.UTF_8,true); + + /** + * This is the standard base64 set suitable for URLs and Filenames + * RFC 4648 + */ + public static final Symm base64url = new Symm( + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".toCharArray() + ,76, Config.UTF_8,true); + + /** + * A Password set, using US-ASCII + * RFC 4648 + */ + public static final Symm encrypt = new Symm(base64url.codeset,1024, "US-ASCII", false); + private static final byte[] EMPTY = new byte[0]; + + /** + * A typical set of Password Chars + * Note, this is too large to fit into the algorithm. Only use with PassGen + */ + private static char passChars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+!@#$%^&*(){}[]?:;,.".toCharArray(); + + + + /** + * Use this to create special case Case Sets and/or Line breaks + * + * If you don't know why you need this, use the Singleton Method + * + * @param codeset + * @param split + */ + public Symm(char[] codeset, int split, String charset, boolean useEndEquals) { + this.codeset = codeset; + splitLinesAt = split; + encoding = charset; + endEquals = useEndEquals; + char prev = 0, curr=0, first = 0; + int offset=Integer.SIZE; // something that's out of range for integer array + + // There can be time efficiencies gained when the underlying keyset consists mainly of ordered + // data (i.e. abcde...). Therefore, we'll quickly analyze the keyset. If it proves to have + // too much entropy, the "Unordered" algorithm, which is faster in such cases is used. + ArrayList<int[]> la = new ArrayList<int[]>(); + for(int i=0;i<codeset.length;++i) { + curr = codeset[i]; + if(prev+1==curr) { // is next character in set + prev = curr; + } else { + if(offset!=Integer.SIZE) { // add previous range + la.add(new int[]{first,prev,offset}); + } + first = prev = curr; + offset = curr-i; + } + } + la.add(new int[]{first,curr,offset}); + if(la.size()>codeset.length/3) { + convert = new Unordered(codeset); + } else { // too random to get speed enhancement from range algorithm + int[][] range = new int[la.size()][]; + la.toArray(range); + convert = new Ordered(range); + } + } + + public Symm copy(int lines) { + return new Symm(codeset,lines,encoding,endEquals); + } + + // Only used by keygen, which is intentionally randomized. Therefore, always use unordered + private Symm(char[] codeset, Symm parent) { + this.codeset = codeset; + splitLinesAt = parent.splitLinesAt; + endEquals = parent.endEquals; + encoding = parent.encoding; + convert = new Unordered(codeset); + } + + /** + * Obtain the base64() behavior of this class, for use in standard BASIC AUTH mechanism, etc. + * @return + */ + @Deprecated + public static final Symm base64() { + return base64; + } + + /** + * Obtain the base64() behavior of this class, for use in standard BASIC AUTH mechanism, etc. + * No Line Splitting + * @return + */ + @Deprecated + public static final Symm base64noSplit() { + return base64noSplit; + } + + /** + * Obtain the base64 "URL" behavior of this class, for use in File Names, etc. (no "/") + */ + @Deprecated + public static final Symm base64url() { + return base64url; + } + + /** + * Obtain a special ASCII version for Scripting, with base set of base64url use in File Names, etc. (no "/") + */ + public static final Symm baseCrypt() { + return encrypt; + } + + public <T> T exec(SyncExec<T> exec) throws Exception { + synchronized(LOCK) { + if(keyBytes == null) { + keyBytes = new byte[AES.AES_KEY_SIZE/8]; + int offset = (Math.abs(codeset[0])+47)%(codeset.length-keyBytes.length); + for(int i=0;i<keyBytes.length;++i) { + keyBytes[i] = (byte)codeset[i+offset]; + } + } + } + return exec.exec(new AES(keyBytes,0,keyBytes.length)); + } + + public interface Encryption { + public CipherOutputStream outputStream(OutputStream os, boolean encrypt); + public CipherInputStream inputStream(InputStream is, boolean encrypt); + } + + public static interface SyncExec<T> { + public T exec(Encryption enc) throws IOException, Exception; + } + + public byte[] encode(byte[] toEncrypt) throws IOException { + if(toEncrypt==null) { + return EMPTY; + } else { + ByteArrayOutputStream baos = new ByteArrayOutputStream((int)(toEncrypt.length*1.25)); + encode(new ByteArrayInputStream(toEncrypt),baos); + return baos.toByteArray(); + } + } + + public byte[] decode(byte[] encrypted) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream((int)(encrypted.length*1.25)); + decode(new ByteArrayInputStream(encrypted),baos); + return baos.toByteArray(); + } + + /** + * Helper function for String API of "Encode" + * use "getBytes" with appropriate char encoding, etc. + * + * @param str + * @return + * @throws IOException + */ + public String encode(String str) throws IOException { + byte[] array; + boolean useDefaultEncoding = false; + try { + array = str.getBytes(encoding); + } catch (IOException e) { + array = str.getBytes(); // take default + useDefaultEncoding = true; + } + // Calculate expected size to avoid any buffer expansion copies within the ByteArrayOutput code + ByteArrayOutputStream baos = new ByteArrayOutputStream((int)(array.length*1.363)); // account for 4 bytes for 3 and a byte or two more + + encode(new ByteArrayInputStream(array),baos); + if (useDefaultEncoding) { + return baos.toString(); + } + return baos.toString(encoding); + } + + /** + * Helper function for the String API of "Decode" + * use "getBytes" with appropriate char encoding, etc. + * @param str + * @return + * @throws IOException + */ + public String decode(String str) throws IOException { + byte[] array; + boolean useDefaultEncoding = false; + try { + array = str.getBytes(encoding); + } catch (IOException e) { + array = str.getBytes(); // take default + useDefaultEncoding = true; + } + // Calculate expected size to avoid any buffer expansion copies within the ByteArrayOutput code + ByteArrayOutputStream baos = new ByteArrayOutputStream((int)(array.length*.76)); // Decoding is 3 bytes for 4. Allocate slightly more than 3/4s + decode(new ByteArrayInputStream(array), baos); + if (useDefaultEncoding) { + return baos.toString(); + } + return baos.toString(encoding); + } + + /** + * Convenience Function + * + * encode String into InputStream and call encode(InputStream, OutputStream) + * + * @param string + * @param out + * @throws IOException + */ + public void encode(String string, OutputStream out) throws IOException { + encode(new ByteArrayInputStream(string.getBytes()),out); + } + + /** + * Convenience Function + * + * encode String into InputStream and call decode(InputStream, OutputStream) + * + * @param string + * @param out + * @throws IOException + */ + public void decode(String string, OutputStream out) throws IOException { + decode(new ByteArrayInputStream(string.getBytes()),out); + } + + public void encode(InputStream is, OutputStream os, byte[] prefix) throws IOException { + os.write(prefix); + encode(is,os); + } + + /** + * encode InputStream onto Output Stream + * + * @param is + * @param estimate + * @return + * @throws IOException + */ + public void encode(InputStream is, OutputStream os) throws IOException { + // StringBuilder sb = new StringBuilder((int)(estimate*1.255)); // try to get the right size of StringBuilder from start.. slightly more than 1.25 times + int prev=0; + int read, idx=0, line=0; + boolean go; + do { + read = is.read(); + if(go = read>=0) { + if(line>=splitLinesAt) { + os.write('\n'); + line = 0; + } + switch(++idx) { // 1 based reading, slightly faster ++ + case 1: // ptr is the first 6 bits of read + os.write(codeset[read>>2]); + prev = read; + break; + case 2: // ptr is the last 2 bits of prev followed by the first 4 bits of read + os.write(codeset[((prev & 0x03)<<4) | (read>>4)]); + prev = read; + break; + default: //(3+) + // Char 1 is last 4 bits of prev plus the first 2 bits of read + // Char 2 is the last 6 bits of read + os.write(codeset[(((prev & 0xF)<<2) | (read>>6))]); + if(line==splitLinesAt) { // deal with line splitting for two characters + os.write('\n'); + line=0; + } + os.write(codeset[(read & 0x3F)]); + ++line; + idx = 0; + prev = 0; + } + ++line; + } else { // deal with any remaining bits from Prev, then pad + switch(idx) { + case 1: // just the last 2 bits of prev + os.write(codeset[(prev & 0x03)<<4]); + if(endEquals)os.write(DOUBLE_EQ); + break; + case 2: // just the last 4 bits of prev + os.write(codeset[(prev & 0xF)<<2]); + if(endEquals)os.write('='); + break; + } + idx = 0; + } + + } while(go); + } + + public void decode(InputStream is, OutputStream os, int skip) throws IOException { + if(is.skip(skip)!=skip) { + throw new IOException("Error skipping on IOStream in Symm"); + } + decode(is,os); + } + + /** + * Decode InputStream onto OutputStream + * @param is + * @param os + * @throws IOException + */ + public void decode(InputStream is, OutputStream os) throws IOException { + int read, idx=0; + int prev=0, index; + while((read = is.read())>=0) { + index = convert.convert(read); + if(index>=0) { + switch(++idx) { // 1 based cases, slightly faster ++ + case 1: // index goes into first 6 bits of prev + prev = index<<2; + break; + case 2: // write second 2 bits of into prev, write byte, last 4 bits go into prev + os.write((byte)(prev|(index>>4))); + prev = index<<4; + break; + case 3: // first 4 bits of index goes into prev, write byte, last 2 bits go into prev + os.write((byte)(prev|(index>>2))); + prev = index<<6; + break; + default: // (3+) | prev and last six of index + os.write((byte)(prev|(index&0x3F))); + idx = prev = 0; + } + } + }; + os.flush(); + } + + /** + * Interface to allow this class to choose which algorithm to find index of character in Key + * @author Jonathan + * + */ + private interface Convert { + public int convert(int read) throws IOException; + } + + /** + * Ordered uses a range of orders to compare against, rather than requiring the investigation + * of every character needed. + * @author Jonathan + * + */ + private static final class Ordered implements Convert { + private int[][] range; + public Ordered(int[][] range) { + this.range = range; + } + public int convert(int read) throws IOException { + switch(read) { + case -1: + case '=': + case '\n': + case '\r': + return -1; + } + for(int i=0;i<range.length;++i) { + if(read >= range[i][0] && read<=range[i][1]) { + return read-range[i][2]; + } + } + throw new IOException("Unacceptable Character in Stream"); + } + } + + /** + * Unordered, i.e. the key is purposely randomized, simply has to investigate each character + * until we find a match. + * @author Jonathan + * + */ + private static final class Unordered implements Convert { + private char[] codec; + public Unordered(char[] codec) { + this.codec = codec; + } + public int convert(int read) throws IOException { + switch(read) { + case -1: + case '=': + case '\n': + return -1; + } + for(int i=0;i<codec.length;++i) { + if(codec[i]==read)return i; + } + // don't give clue in Encryption mode + throw new IOException("Unacceptable Character in Stream"); + } + } + + /** + * Generate a 2048 based Key from which we extract our code base + * + * @return + * @throws IOException + */ + public static byte[] keygen() throws IOException { + byte inkey[] = new byte[0x600]; + new SecureRandom().nextBytes(inkey); + ByteArrayOutputStream baos = new ByteArrayOutputStream(0x800); + base64url.encode(new ByteArrayInputStream(inkey), baos); + return baos.toByteArray(); + } + + // A class allowing us to be less predictable about significant digits (i.e. not picking them up from the + // beginning, and not picking them up in an ordered row. Gives a nice 2048 with no visible patterns. + private class Obtain { + private int last; + private int skip; + private int length; + private byte[] key; + + private Obtain(Symm b64, byte[] key) { + skip = Math.abs(key[key.length-13]%key.length); + if((key.length&0x1) == (skip&0x1)) { // if both are odd or both are even + ++skip; + } + length = b64.codeset.length; + last = 17+length%59; // never start at beginning + this.key = key; + } + + private int next() { + return Math.abs(key[(++last*skip)%key.length])%length; + } + }; + + /** + * Obtain a Symm from "keyfile" (Config.KEYFILE) property + * + * @param acesss + * @return + * @throws IOException + * @throws CadiException + */ + public static Symm obtain(Access access) throws CadiException { + Symm symm = Symm.baseCrypt(); + + String keyfile = access.getProperty(Config.CADI_KEYFILE,null); + if(keyfile!=null) { + File file = new File(keyfile); + try { + access.log(Level.INIT, Config.CADI_KEYFILE,"points to",file.getCanonicalPath()); + } catch (IOException e1) { + access.log(Level.INIT, Config.CADI_KEYFILE,"points to",file.getAbsolutePath()); + } + if(file.exists()) { + try { + FileInputStream fis = new FileInputStream(file); + try { + symm = Symm.obtain(fis); + } finally { + try { + fis.close(); + } catch (IOException e) { + } + } + } catch (IOException e) { + access.log(e, "Cannot load keyfile"); + } + } else { + String filename; + try { + filename = file.getCanonicalPath(); + } catch (IOException e) { + filename = file.getAbsolutePath(); + } + throw new CadiException("ERROR: " + filename + " does not exist!"); + } + } + return symm; + } + /** + * Create a new random key + */ + public Symm obtain() throws IOException { + byte inkey[] = new byte[0x800]; + new SecureRandom().nextBytes(inkey); + return obtain(inkey); + } + + /** + * Obtain a Symm from 2048 key from a String + * + * @param key + * @return + * @throws IOException + */ + public static Symm obtain(String key) throws IOException { + return obtain(new ByteArrayInputStream(key.getBytes())); + } + + /** + * Obtain a Symm from 2048 key from a Stream + * + * @param is + * @return + * @throws IOException + */ + public static Symm obtain(InputStream is) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + base64url.decode(is, baos); + } catch (IOException e) { + // don't give clue + throw new IOException("Invalid Key"); + } + byte[] bkey = baos.toByteArray(); + if(bkey.length<0x88) { // 2048 bit key + throw new IOException("Invalid key"); + } + return baseCrypt().obtain(bkey); + } + + /** + * Convenience for picking up Keyfile + * + * @param f + * @return + * @throws IOException + */ + public static Symm obtain(File f) throws IOException { + FileInputStream fis = new FileInputStream(f); + try { + return obtain(fis); + } finally { + fis.close(); + } + } + /** + * Decrypt into a String + * + * Convenience method + * + * @param password + * @return + * @throws IOException + */ + public String enpass(String password) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + enpass(password,baos); + return new String(baos.toByteArray()); + } + + /** + * Create an encrypted password, making sure that even short passwords have a minimum length. + * + * @param password + * @param os + * @throws IOException + */ + public void enpass(final String password, final OutputStream os) throws IOException { + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(baos); + byte[] bytes = password.getBytes(); + if(this.getClass().getSimpleName().startsWith("base64")) { // don't expose randomization + dos.write(bytes); + } else { + + Random r = new SecureRandom(); + int start = 0; + byte b; + for(int i=0;i<3;++i) { + dos.writeByte(b=(byte)r.nextInt()); + start+=Math.abs(b); + } + start%=0x7; + for(int i=0;i<start;++i) { + dos.writeByte(r.nextInt()); + } + dos.writeInt((int)System.currentTimeMillis()); + int minlength = Math.min(0x9,bytes.length); + dos.writeByte(minlength); // expect truncation + if(bytes.length<0x9) { + for(int i=0;i<bytes.length;++i) { + dos.writeByte(r.nextInt()); + dos.writeByte(bytes[i]); + } + // make sure it's long enough + for(int i=bytes.length;i<0x9;++i) { + dos.writeByte(r.nextInt()); + } + } else { + dos.write(bytes); + } + } + + // 7/21/2016 Jonathan add AES Encryption to the mix + try { + exec(new SyncExec<Void>() { + @Override + public Void exec(Encryption enc) throws Exception { + CipherInputStream cis = enc.inputStream(new ByteArrayInputStream(baos.toByteArray()), true); + try { + encode(cis,os); + } finally { + os.flush(); + cis.close(); + } + return null; + } + }); + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new IOException(e); + } + } + + /** + * Decrypt a password into a String + * + * Convenience method + * + * @param password + * @return + * @throws IOException + */ + public String depass(String password) throws IOException { + if(password==null)return null; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + depass(password,baos); + return new String(baos.toByteArray()); + } + + /** + * Decrypt a password + * + * Skip Symm.ENC + * + * @param password + * @param os + * @return + * @throws IOException + */ + public long depass(final String password, final OutputStream os) throws IOException { + int offset = password.startsWith(ENC)?4:0; + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + final ByteArrayInputStream bais = new ByteArrayInputStream(password.getBytes(),offset,password.length()-offset); + try { + exec(new SyncExec<Void>() { + @Override + public Void exec(Encryption enc) throws IOException { + CipherOutputStream cos = enc.outputStream(baos, false); + decode(bais,cos); + cos.close(); // flush + return null; + } + }); + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new IOException(e); + } + + byte[] bytes = baos.toByteArray(); + DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bytes)); + long time; + if(this.getClass().getSimpleName().startsWith("base64")) { // don't expose randomization + os.write(bytes); + time = 0L; + } else { + int start=0; + for(int i=0;i<3;++i) { + start+=Math.abs(dis.readByte()); + } + start%=0x7; + for(int i=0;i<start;++i) { + dis.readByte(); + } + time = (dis.readInt() & 0xFFFF)|(System.currentTimeMillis()&0xFFFF0000); + int minlength = dis.readByte(); + if(minlength<0x9){ + DataOutputStream dos = new DataOutputStream(os); + for(int i=0;i<minlength;++i) { + dis.readByte(); + dos.writeByte(dis.readByte()); + } + } else { + int pre =((Byte.SIZE*3+Integer.SIZE+Byte.SIZE)/Byte.SIZE)+start; + os.write(bytes, pre, bytes.length-pre); + } + } + return time; + } + + public static String randomGen(int numBytes) { + return randomGen(passChars,numBytes); + } + + public static String randomGen(char[] chars ,int numBytes) { + int rint; + StringBuilder sb = new StringBuilder(numBytes); + for(int i=0;i<numBytes;++i) { + rint = random.nextInt(chars.length); + sb.append(chars[rint]); + } + return sb.toString(); + } + // Internal mechanism for helping to randomize placement of characters within a Symm codeset + // Based on an incoming data stream (originally created randomly, but can be recreated within + // 2048 key), go after a particular place in the new codeset. If that codeset spot is used, then move + // right or left (depending on iteration) to find the next available slot. In this way, key generation + // is speeded up by only enacting N iterations, but adds a spreading effect of the random number stream, so that keyset is also + // shuffled for a good spread. It is, however, repeatable, given the same number set, allowing for + // quick recreation when the official stream is actually obtained. + public Symm obtain(byte[] key) throws IOException { + int filled = codeset.length; + char[] seq = new char[filled]; + int end = filled--; + + boolean right = true; + int index; + Obtain o = new Obtain(this,key); + + while(filled>=0) { + index = o.next(); + if(index<0 || index>=codeset.length) { + System.out.println("uh, oh"); + } + if(right) { // alternate going left or right to find the next open slot (keeps it from taking too long to hit something) + for(int j=index;j<end;++j) { + if(seq[j]==0) { + seq[j]=codeset[filled]; + --filled; + break; + } + } + right = false; + } else { + for(int j=index;j>=0;--j) { + if(seq[j]==0) { + seq[j]=codeset[filled]; + --filled; + break; + } + } + right = true; + } + } + Symm newSymm = new Symm(seq,this); + // Set the KeyBytes + try { + newSymm.keyBytes = new byte[AES.AES_KEY_SIZE/8]; + int offset = (Math.abs(key[(47%key.length)])+137)%(key.length-newSymm.keyBytes.length); + for(int i=0;i<newSymm.keyBytes.length;++i) { + newSymm.keyBytes[i] = key[i+offset]; + } + } catch (Exception e) { + throw new IOException(e); + } + + return newSymm; + } +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/Taf.java b/cadi/core/src/main/java/org/onap/aaf/cadi/Taf.java new file mode 100644 index 00000000..1767258c --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/Taf.java @@ -0,0 +1,57 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi; + +import org.onap.aaf.cadi.taf.TafResp; + + +/** + * TAF - Transmutative Assertion Framework. + * + * This main Interface embodies the essential of the assertion, where a number of different TAFs might be used to authenticate + * and that authentication to be recognized through other elements. + * + * Concept by Robert Garskof. Implemented by Jonathan Gathman + * + * @author Jonathan + * + */ +public interface Taf { + enum LifeForm {CBLF, SBLF, LFN}; + /** + * The lifeForm param is a humorous way of describing whether the interaction is proceeding from direct Human Interaction via a browser + * or App which can directly query a memorized password, key sequence, bio-feedback, from that user, or a machine mechanism for which identity + * can more easily be determined by Certificate, Mechanical ID/Password etc. Popularized in modern culture and Science Fiction (especially + * Star Trek), we (starting with Robert Garskof) use the terms "Carbon Based Life Form" (CBLF) for mechanisms with people at the end of them, or + * "Silicon Based Life Forms" (SBLF) to indicate machine only interactions. I have added "LFN" for (Life-Form Neutral) to aid identifying + * processes for which it doesn't matter whether there is a human at the immediate end of the chain, or cannot be determined mechanically. + * + * The variable parameter is not necessarily ideal, but with too many unknown Tafs to be created, flexibility, + * is unfortunately required at this point. Future versions could lock this down more. Jonathan 10/18/2012 + * + * @param lifeForm + * @param info + * @return + */ + public TafResp validate(LifeForm reading, String ... info); + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/Transmutate.java b/cadi/core/src/main/java/org/onap/aaf/cadi/Transmutate.java new file mode 100644 index 00000000..63722253 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/Transmutate.java @@ -0,0 +1,45 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi; + +import java.security.Principal; + +/** + * The unique element of TAF is that we establish the relationship/mechanism to mutate the Principal derived from + * one Authentication mechanism into a trustable Principal of another. The mechanism needs to be decided by system + * trusting. + * + * The Generic "T" is used so that the code used will be very specific for the implementation, enforced by Compiler + * + * This interface will allow differences of trusting Transmutation of Authentication + * @author Jonathan + * + */ +public interface Transmutate<T> { + /** + * Mutate the (assumed validated) Principal into the expected Principal name to be used to construct + * + * @param p + * @return + */ + public T mutate(Principal p); +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/TrustChecker.java b/cadi/core/src/main/java/org/onap/aaf/cadi/TrustChecker.java new file mode 100644 index 00000000..fabec0b0 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/TrustChecker.java @@ -0,0 +1,52 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi; + +import javax.servlet.http.HttpServletRequest; + +import org.onap.aaf.cadi.taf.TafResp; + +/** + * Change to another Principal based on Trust of caller and User Chain (if desired) + * + * @author Jonathan + * + */ +public interface TrustChecker { + public TafResp mayTrust(TafResp tresp, HttpServletRequest req); + + /** + * A class that trusts no-one else, so just return same TResp + */ + public static TrustChecker NOTRUST = new TrustChecker() { + @Override + public TafResp mayTrust(TafResp tresp, HttpServletRequest req) { + return tresp; + } + + @Override + public void setLur(Lur lur) { + } + }; + + public void setLur(Lur lur); +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/User.java b/cadi/core/src/main/java/org/onap/aaf/cadi/User.java new file mode 100644 index 00000000..5e9f8a58 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/User.java @@ -0,0 +1,177 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi; + +import java.security.Principal; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.onap.aaf.cadi.lur.LocalPermission; + +/** + * Class to hold info from the User Perspective. + * + * @author Jonathan + * + */ +public final class User<PERM extends Permission> { + private static final Map<String,Permission> NULL_MAP = new HashMap<String,Permission>(); + public String name; + private byte[] cred; + public Principal principal; + Map<String, Permission> perms ; + long permExpires; + private final long interval; + int count; + + // Note: This should only be used for Local RBAC (in memory) + public User(Principal principal) { + this.principal = principal; + name = principal.getName(); + perms = NULL_MAP; + permExpires = Long.MAX_VALUE; // Never. Well, until 64 bits of millis since 1970 expires... + interval = 0L; + count = 0; + } + + public User(String name, byte[] cred) { + this.principal = null; + this.name = name; + this.cred = cred; + perms = NULL_MAP; + permExpires = Long.MAX_VALUE; // Never. Well, until 64 bits of millis since 1970 expires... + interval = 0L; + count = 0; + } + + public User(Principal principal, long expireInterval) { + this.principal = principal; + this.name = principal.getName(); + perms = NULL_MAP; + expireInterval = Math.max(expireInterval, 0); // avoid < 1 + interval = Math.max(AbsUserCache.MIN_INTERVAL,Math.min(expireInterval,AbsUserCache.MAX_INTERVAL)); + count = 0; + renewPerm(); + renewPerm(); + } + + public User(String name, byte[] cred, long expireInterval) { + this.principal = null; + this.name = name; + this.cred = cred; + perms = NULL_MAP; + expireInterval = Math.max(expireInterval, 0); // avoid < 1 + interval = Math.max(AbsUserCache.MIN_INTERVAL,Math.min(expireInterval,AbsUserCache.MAX_INTERVAL)); + count = 0; + renewPerm(); + } + + public void renewPerm() { + permExpires = System.currentTimeMillis()+interval; + } + + public long permExpires() { + return permExpires; + } + + public boolean permExpired() { + return System.currentTimeMillis() > permExpires; + } + + public boolean noPerms() { + return perms==null || perms==NULL_MAP || perms.values().size()==0; + } + + public synchronized void setNoPerms() { + perms=NULL_MAP; + renewPerm(); + } + + public boolean permsUnloaded() { + return perms==null || perms==NULL_MAP; + } + + public synchronized void incCount() { + ++count; + } + + public synchronized void resetCount() { + count=0; + } + + public Map<String,Permission> newMap() { + return new ConcurrentHashMap<String,Permission>(); + } + + public void add(LocalPermission permission) { + if(perms==NULL_MAP) { + perms=newMap(); + } + perms.put(permission.getKey(),permission); + } + + public void add(Map<String, Permission> newMap, PERM permission) { + newMap.put(permission.getKey(),permission); + } + + public synchronized void setMap(Map<String, Permission> newMap) { + perms = newMap; + renewPerm(); + } + + public boolean contains(Permission perm) { + for (Permission p : perms.values()) { + if (p.match(perm)) return true; + } + return false; + } + + public void copyPermsTo(List<Permission> sink) { + sink.addAll(perms.values()); + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(principal.getName()); + sb.append('|'); + boolean first = true; + synchronized(perms) { + for(Permission gp : perms.values()) { + if(first) { + first = false; + sb.append(':'); + } else { + sb.append(','); + } + sb.append(gp.getKey()); + } + } + return sb.toString(); + } + + public byte[] getCred() { + return cred; + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/UserChain.java b/cadi/core/src/main/java/org/onap/aaf/cadi/UserChain.java new file mode 100644 index 00000000..e423b8b1 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/UserChain.java @@ -0,0 +1,43 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi; + +/** + * Interface to add a User Chain String to Principal + * + * + * + * Where + * APP is name suitable for Logging (i.e. official App Acronym) + * ID is official User or MechID, best if includes Identity Source (i.e. ab1234@csp.att.com) + * Protocol is the Security protocol, + * + * Format:<ID>:<APP>:<protocol>[:AS][,<ID>:<APP>:<protocol>]* + * + * + * @author Jonathan + * + */ +public interface UserChain { + public enum Protocol {BasicAuth,Cookie,Cert,OAuth}; + public String userChain(); +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/config/Config.java b/cadi/core/src/main/java/org/onap/aaf/cadi/config/Config.java new file mode 100644 index 00000000..d7c7526f --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/config/Config.java @@ -0,0 +1,760 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.config; + +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.HttpURLConnection; +import java.net.InetAddress; +import java.net.URI; +import java.net.UnknownHostException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.util.ArrayList; +import java.util.List; + +import org.onap.aaf.cadi.AbsUserCache; +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.CachingLur; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.Connector; +import org.onap.aaf.cadi.CredVal; +import org.onap.aaf.cadi.Locator; +import org.onap.aaf.cadi.Lur; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.Symm; +import org.onap.aaf.cadi.TrustChecker; +import org.onap.aaf.cadi.lur.EpiLur; +import org.onap.aaf.cadi.lur.LocalLur; +import org.onap.aaf.cadi.lur.NullLur; +import org.onap.aaf.cadi.taf.HttpEpiTaf; +import org.onap.aaf.cadi.taf.HttpTaf; +import org.onap.aaf.cadi.taf.basic.BasicHttpTaf; +import org.onap.aaf.cadi.taf.cert.X509Taf; +import org.onap.aaf.cadi.taf.dos.DenialOfServiceTaf; + +/** + * Create a Consistent Configuration mechanism, even when configuration styles are as vastly different as + * Properties vs JavaBeans vs FilterConfigs... + * + * @author Jonathan + * + */ +public class Config { + + private static final String AAF_V2_0 = "org.onap.aaf.cadi.aaf.v2_0"; + private static final String AAF_V2_0_AAFCON = AAF_V2_0+".AAFCon"; + private static final String AAF_V2_0_AAF_LUR_PERM = AAF_V2_0+".AAFLurPerm"; + private static final String OAUTH = "org.onap.auth.oauth"; + private static final String OAUTH_TOKEN_MGR = OAUTH+".TokenMgr"; + private static final String OAUTH_HTTP_TAF = OAUTH+".OAuth2HttpTaf"; + private static final String OAUTH_DIRECT_TAF = OAUTH+".OAuthDirectTAF"; + + public static final String UTF_8 = "UTF-8"; + + // Property Names associated with configurations. + // As of 1.0.2, these have had the dots removed so as to be compatible with JavaBean style + // configurations as well as property list style. + public static final String HOSTNAME = "hostname"; + public static final String CADI_REGISTRATION_HOSTNAME = "cadi_registration_hostname"; + public static final String CADI_PROP_FILES = "cadi_prop_files"; // Additional Properties files (separate with ;) + public static final String CADI_LOGLEVEL = "cadi_loglevel"; + public static final String CADI_LOGDIR = "cadi_log_dir"; + public static final String CADI_ETCDIR = "cadi_etc_dir"; + public static final String CADI_LOGNAME = "cadi_logname"; + public static final String CADI_KEYFILE = "cadi_keyfile"; + public static final String CADI_KEYSTORE = "cadi_keystore"; + public static final String CADI_KEYSTORE_PASSWORD = "cadi_keystore_password"; + public static final String CADI_ALIAS = "cadi_alias"; + public static final String CADI_LOGINPAGE_URL = "cadi_loginpage_url"; + public static final String CADI_LATITUDE = "cadi_latitude"; + public static final String CADI_LONGITUDE = "cadi_longitude"; + + + public static final String CADI_KEY_PASSWORD = "cadi_key_password"; + public static final String CADI_TRUSTSTORE = "cadi_truststore"; + public static final String CADI_TRUSTSTORE_PASSWORD = "cadi_truststore_password"; + public static final String CADI_X509_ISSUERS = "cadi_x509_issuers"; + public static final String CADI_TRUST_MASKS="cadi_trust_masks"; + public static final String CADI_TRUST_PERM="cadi_trust_perm"; // IDs with this perm can utilize the "AS " user concept + public static final String CADI_PROTOCOLS = "cadi_protocols"; + public static final String CADI_NOAUTHN = "cadi_noauthn"; + public static final String CADI_LOC_LIST = "cadi_loc_list"; + + public static final String CADI_USER_CHAIN_TAG = "cadi_user_chain"; + public static final String CADI_USER_CHAIN = "USER_CHAIN"; + + public static final String CADI_OAUTH2_URL="cadi_oauth2_url"; + public static final String CADI_TOKEN_DIR = "cadi_token_dir"; + + public static final String CSP_DOMAIN = "csp_domain"; + public static final String CSP_HOSTNAME = "csp_hostname"; + public static final String CSP_DEVL_LOCALHOST = "csp_devl_localhost"; + public static final String CSP_USER_HEADER = "CSP_USER"; + public static final String CSP_SYSTEMS_CONF = "CSPSystems.conf"; + public static final String CSP_SYSTEMS_CONF_FILE = "csp_systems_conf_file"; + + public static final String HTTPS_PROTOCOLS = "https.protocols"; + public static final String HTTPS_CIPHER_SUITES = "https.cipherSuites"; + public static final String HTTPS_CLIENT_PROTOCOLS="jdk.tls.client.protocols"; + public static final String HTTPS_CIPHER_SUITES_DEFAULT="TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA," + + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA," + + "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_DSS_WITH_AES_128_CBC_SHA," + + "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,TLS_ECDHE_RSA_WITH_RC4_128_SHA,TLS_ECDH_ECDSA_WITH_RC4_128_SHA," + + "TLS_ECDH_RSA_WITH_RC4_128_SHA,TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA," + + "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA,TLS_EMPTY_RENEGOTIATION_INFO_SCSV"; + + + public static final String LOCALHOST_ALLOW = "localhost_allow"; + public static final String LOCALHOST_DENY = "localhost_deny"; + + public static final String BASIC_REALM = "basic_realm"; // what is sent to the client + public static final String BASIC_WARN = "basic_warn"; // Warning of insecure channel + public static final String USERS = "local_users"; + public static final String GROUPS = "local_groups"; + public static final String WRITE_TO = "local_writeto"; // dump RBAC to local file in Tomcat Style (some apps use) + + public static final String OAUTH_CLIENT_ID="client_id"; + public static final String OAUTH_CLIENT_SECRET="client_secret"; + + public static final String AAF_ENV = "aaf_env"; + public static final String AAF_URL = "aaf_url"; //URL for AAF... Use to trigger AAF configuration + public static final String AAF_ROOT_NS = "aaf_root_ns"; + public static final String AAF_ROOT_COMPANY = "aaf_root_company"; + public static final String AAF_LOCATE_URL = "aaf_locate_url"; //URL for AAF locator + private static final String AAF_LOCATE_URL_TAG = "AAF_LOCATE_URL"; // Name of Above for use in Config Variables. + public static final String AAF_APPID = "aaf_id"; + public static final String AAF_APPPASS = "aaf_password"; + public static final String AAF_LUR_CLASS = "aaf_lur_class"; + public static final String AAF_TAF_CLASS = "aaf_taf_class"; + public static final String AAF_CONNECTOR_CLASS = "aaf_connector_class"; + public static final String AAF_LOCATOR_CLASS = "aaf_locator_class"; + public static final String AAF_CONN_TIMEOUT = "aaf_conn_timeout"; + public static final String AAF_CONN_TIMEOUT_DEF = "3000"; + public static final String AAF_CONN_IDLE_TIMEOUT = "aaf_conn_idle_timeout"; // only for Direct Jetty Access. + public static final String AAF_CONN_IDLE_TIMEOUT_DEF = "10000"; // only for Direct Jetty Access. + + // Default Classes: These are for Class loading to avoid direct compile links + public static final String AAF_TAF_CLASS_DEF = "org.onap.aaf.cadi.aaf.v2_0.AAFTaf"; + public static final String AAF_LOCATOR_CLASS_DEF = "org.onap.aaf.cadi.aaf.v2_0.AAFLocator"; + public static final String CADI_OLUR_CLASS_DEF = "org.onap.aaf.cadi.olur.OLur"; + public static final String CADI_OBASIC_HTTP_TAF_DEF = "org.onap.aaf.cadi.obasic.OBasicHttpTaf"; + public static final String CADI_AAF_CON_DEF = "org.onap.aaf.cadi.aaf.v2_0.AAFCon"; + + public static final String AAF_CALL_TIMEOUT = "aaf_timeout"; + public static final String AAF_CALL_TIMEOUT_DEF = "5000"; + public static final String AAF_USER_EXPIRES = "aaf_user_expires"; + public static final String AAF_USER_EXPIRES_DEF = "600000"; // Default is 10 mins + public static final String AAF_CLEAN_INTERVAL = "aaf_clean_interval"; + public static final String AAF_CLEAN_INTERVAL_DEF = "30000"; // Default is 30 seconds + public static final String AAF_REFRESH_TRIGGER_COUNT = "aaf_refresh_trigger_count"; + public static final String AAF_REFRESH_TRIGGER_COUNT_DEF = "3"; // Default is 10 mins + + public static final String AAF_HIGH_COUNT = "aaf_high_count"; + public static final String AAF_HIGH_COUNT_DEF = "1000"; // Default is 1000 entries + public static final String AAF_PERM_MAP = "aaf_perm_map"; + public static final String AAF_COMPONENT = "aaf_component"; + public static final String AAF_CERT_IDS = "aaf_cert_ids"; + public static final String AAF_DEBUG_IDS = "aaf_debug_ids"; // comma delimited + public static final String AAF_DEFAULT_VERSION = "2.0"; + public static final String AAF_DATA_DIR = "aaf_data_dir"; // AAF processes and Components only. + + + + public static final String GW_URL = "gw_url"; + public static final String CM_URL = "cm_url"; + public static final String CM_TRUSTED_CAS = "cm_trusted_cas"; + + public static final String PATHFILTER_URLPATTERN = "pathfilter_urlpattern"; + public static final String PATHFILTER_STACK = "pathfilter_stack"; + public static final String PATHFILTER_NS = "pathfilter_ns"; + public static final String PATHFILTER_NOT_AUTHORIZED_MSG = "pathfilter_not_authorized_msg"; + + // This one should go unpublic + public static final String AAF_DEFAULT_REALM = "aaf_default_realm"; + private static String defaultRealm="none"; + + public static final String AAF_DOMAIN_SUPPORT = "aaf_domain_support"; + public static final String AAF_DOMAIN_SUPPORT_DEF = ".com:.org"; + + // OAUTH2 + public static final String AAF_OAUTH2_TOKEN_URL = "aaf_oauth2_token_url"; + public static final String AAF_OAUTH2_INTROSPECT_URL = "aaf_oauth2_introspect_url"; + public static final String AAF_ALT_OAUTH2_TOKEN_URL = "aaf_alt_oauth2_token_url"; + public static final String AAF_ALT_OAUTH2_INTROSPECT_URL = "aaf_alt_oauth2_introspect_url"; + public static final String AAF_ALT_OAUTH2_DOMAIN = "aaf_alt_oauth2_domain"; + public static final String AAF_ALT_CLIENT_ID = "aaf_alt_oauth2_client_id"; + public static final String AAF_ALT_CLIENT_SECRET = "aaf_alt_oauth2_client_secret"; + public static final String AAF_OAUTH2_HELLO_URL = "aaf_oauth2_hello_url"; + + + + public static void setDefaultRealm(Access access) throws CadiException { + try { + defaultRealm = logProp(access,Config.AAF_DEFAULT_REALM, + logProp(access,Config.BASIC_REALM, + logProp(access,HOSTNAME,InetAddress.getLocalHost().getHostName()) + ) + ); + } catch (UnknownHostException e) { + //defaultRealm="none"; + } + } + + public static HttpTaf configHttpTaf(Connector con, SecurityInfoC<HttpURLConnection> si, TrustChecker tc, CredVal up, Lur lur, Object ... additionalTafLurs) throws CadiException { + Access access = si.access; + ///////////////////////////////////////////////////// + // Setup AAFCon for any following + ///////////////////////////////////////////////////// + Class<?> aafConClass = loadClass(access,CADI_AAF_CON_DEF); + Object aafcon = null; + if(con!=null && aafConClass!=null && aafConClass.isAssignableFrom(con.getClass())) { + aafcon = con; + } else if(lur != null) { + Field f = null; + try { + f = lur.getClass().getField("aaf"); + aafcon = f.get(lur); + } catch (Exception nsfe) { + } + } + + + boolean hasDirectAAF = hasDirect("DirectAAFLur",additionalTafLurs); + // IMPORTANT! Don't attempt to load AAF Connector if there is no AAF URL + String aafURL = access.getProperty(AAF_URL,null); + if(!hasDirectAAF && aafcon==null && aafURL!=null) { + aafcon = loadAAFConnector(si, aafURL); + } + + HttpTaf taf; + // Setup Host, in case Network reports an unusable Hostname (i.e. VTiers, VPNs, etc) + String hostname = logProp(access, HOSTNAME,null); + if(hostname==null) { + try { + hostname = InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e1) { + throw new CadiException("Unable to determine Hostname",e1); + } + } + + access.log(Level.INIT, "Hostname set to",hostname); + // Get appropriate TAFs + ArrayList<HttpTaf> htlist = new ArrayList<HttpTaf>(); + + ///////////////////////////////////////////////////// + // Add a Denial of Service TAF + // Note: how IPs and IDs are added are up to service type. + // They call "DenialOfServiceTaf.denyIP(String) or denyID(String) + ///////////////////////////////////////////////////// + htlist.add(new DenialOfServiceTaf(access)); + + ///////////////////////////////////////////////////// + // Configure Client Cert TAF + ///////////////////////////////////////////////////// + + String truststore = logProp(access, CADI_TRUSTSTORE,null); + if(truststore!=null) { + String truststore_pwd = access.getProperty(CADI_TRUSTSTORE_PASSWORD,null); + if(truststore_pwd!=null) { + if(truststore_pwd.startsWith(Symm.ENC)) { + try { + truststore_pwd = access.decrypt(truststore_pwd,false); + } catch (IOException e) { + throw new CadiException(CADI_TRUSTSTORE_PASSWORD + " cannot be decrypted",e); + } + } + try { + htlist.add(new X509Taf(access,lur)); + access.log(Level.INIT,"Certificate Authorization enabled"); + } catch (SecurityException e) { + access.log(Level.INIT,"AAFListedCertIdentity cannot be instantiated. Certificate Authorization is now disabled",e); + } catch (IllegalArgumentException e) { + access.log(Level.INIT,"AAFListedCertIdentity cannot be instantiated. Certificate Authorization is now disabled",e); + } catch (CertificateException e) { + access.log(Level.INIT,"Certificate Authorization failed, it is disabled",e); + } catch (NoSuchAlgorithmException e) { + access.log(Level.INIT,"Certificate Authorization failed, wrong Security Algorithm",e); + } + } + } else { + access.log(Level.INIT,"Certificate Authorization not enabled"); + } + + ///////////////////////////////////////////////////// + // Configure Basic Auth (local content) + ///////////////////////////////////////////////////// + boolean hasOAuthDirectTAF = hasDirect("DirectOAuthTAF", additionalTafLurs); + String basic_realm = logProp(access, BASIC_REALM,null); + String aafCleanup = logProp(access, AAF_USER_EXPIRES,AAF_USER_EXPIRES_DEF); // Default is 10 mins + long userExp = Long.parseLong(aafCleanup); + boolean basic_warn = "TRUE".equals(access.getProperty(BASIC_WARN,"FALSE")); + + if(!hasDirectAAF) { + HttpTaf aaftaf=null; + if(!hasOAuthDirectTAF) { + if(basic_realm!=null) { + @SuppressWarnings("unchecked") + Class<HttpTaf> obasicCls = (Class<HttpTaf>)loadClass(access,CADI_OBASIC_HTTP_TAF_DEF); + if(obasicCls!=null) { + try { + String tokenurl = logProp(access,Config.AAF_OAUTH2_TOKEN_URL, null); + String introspecturl = logProp(access,Config.AAF_OAUTH2_INTROSPECT_URL, null); + if(tokenurl==null || introspecturl==null) { + access.log(Level.INIT,"Both tokenurl and introspecturl are required. Oauth Authorization is disabled."); + } + Constructor<HttpTaf> obasicConst = obasicCls.getConstructor(PropAccess.class,String.class, String.class, String.class); + htlist.add(obasicConst.newInstance(access,basic_realm,tokenurl,introspecturl)); + access.log(Level.INIT,"Oauth supported Basic Authorization is enabled"); + } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + } + } else if(up!=null) { + access.log(Level.INIT,"Basic Authorization is enabled using realm",basic_realm); + // Allow warning about insecure channel to be turned off + if(!basic_warn)access.log(Level.INIT,"WARNING! The basic_warn property has been set to false.", + " There will be no additional warning if Basic Auth is used on an insecure channel" + ); + htlist.add(new BasicHttpTaf(access, up, basic_realm, userExp, basic_warn)); + access.log(Level.INIT,"Basic Authorization is enabled"); + } + } else { + access.log(Level.INIT,"Local Basic Authorization is disabled. Enable by setting basic_realm=<appropriate realm, i.e. my.att.com>"); + } + + ///////////////////////////////////////////////////// + // Configure AAF Driven Basic Auth + ///////////////////////////////////////////////////// + if(aafcon==null) { + access.log(Level.INIT,"AAF Connection (AAFcon) is null. Cannot create an AAF TAF"); + } else if(aafURL==null) { + access.log(Level.INIT,"No AAF URL in properties, Cannot create an AAF TAF"); + } else {// There's an AAF_URL... try to configure an AAF + String aafTafClassName = logProp(access, AAF_TAF_CLASS,AAF_TAF_CLASS_DEF); + // Only 2.0 available at this time + if(AAF_TAF_CLASS_DEF.equals(aafTafClassName)) { + try { + Class<?> aafTafClass = loadClass(access,aafTafClassName); + if(aafTafClass!=null) { + Constructor<?> cstr = aafTafClass.getConstructor(Connector.class,boolean.class,AbsUserCache.class); + if(cstr!=null) { + if(lur instanceof AbsUserCache) { + aaftaf = (HttpTaf)cstr.newInstance(aafcon,basic_warn,lur); + } else { + cstr = aafTafClass.getConstructor(Connector.class,boolean.class); + if(cstr!=null) { + aaftaf = (HttpTaf)cstr.newInstance(aafcon,basic_warn); + } + } + if(aaftaf==null) { + access.log(Level.INIT,"ERROR! AAF TAF Failed construction. NOT Configured"); + } else { + access.log(Level.INIT,"AAF TAF Configured to ",aafURL); + // Note: will add later, after all others configured + } + } + } else { + access.log(Level.INIT, "There is no AAF TAF class available: %s. AAF TAF not configured.",aafTafClassName); + } + } catch(Exception e) { + access.log(Level.INIT,"ERROR! AAF TAF Failed construction. NOT Configured",e); + } + } + } + } + + ///////////////////////////////////////////////////// + // Configure OAuth TAF + ///////////////////////////////////////////////////// + if(!hasOAuthDirectTAF) { + String oauth_token_url = logProp(access,Config.AAF_OAUTH2_TOKEN_URL,null); + Class<?> oadtClss; + try { + oadtClss = Class.forName(OAUTH_DIRECT_TAF); + } catch (ClassNotFoundException e1) { + oadtClss = null; + } + if(additionalTafLurs!=null && additionalTafLurs.length>0 && (oadtClss!=null && additionalTafLurs[0].getClass().isAssignableFrom(oadtClss))) { + htlist.add((HttpTaf)additionalTafLurs[0]); + String array[] = new String[additionalTafLurs.length-1]; + if(array.length>0) { + System.arraycopy(htlist, 1, array, 0, array.length); + } + additionalTafLurs = array; + access.log(Level.INIT,"OAuth2 Direct is enabled"); + } else if(oauth_token_url!=null) { + String oauth_introspect_url = logProp(access,Config.AAF_OAUTH2_INTROSPECT_URL,null); + @SuppressWarnings("unchecked") + Class<HttpTaf> oaTCls = (Class<HttpTaf>)loadClass(access,OAUTH_HTTP_TAF); + if(oaTCls!=null) { + Class<?> oaTTmgrCls = loadClass(access, OAUTH_TOKEN_MGR); + if(oaTTmgrCls!=null) { + try { + Method oaTTmgrGI = oaTTmgrCls.getMethod("getInstance",PropAccess.class,String.class,String.class); + Object oaTTmgr = oaTTmgrGI.invoke(null /*this is static method*/,access,oauth_token_url,oauth_introspect_url); + Constructor<HttpTaf> oaTConst = oaTCls.getConstructor(Access.class,oaTTmgrCls); + htlist.add(oaTConst.newInstance(access,oaTTmgr)); + access.log(Level.INIT,"OAuth2 TAF is enabled"); + } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | InstantiationException e) { + access.log(Level.INIT,"OAuth2HttpTaf cannot be instantiated. OAuth2 is disabled",e); + } + } + } + } else { + access.log(Level.INIT,"OAuth TAF is not configured"); + } + } + + ///////////////////////////////////////////////////// + // Adding BasicAuth (AAF) last, after other primary Cookie Based + // Needs to be before Cert... see below + ///////////////////////////////////////////////////// + if(aaftaf!=null) { + htlist.add(aaftaf); + } + } + + ///////////////////////////////////////////////////// + // Any Additional Lurs passed in Constructor + ///////////////////////////////////////////////////// + if(additionalTafLurs!=null) { + for(Object additional : additionalTafLurs) { + if(additional instanceof HttpTaf) { + htlist.add((HttpTaf)additional); + access.printf(Level.INIT,"%s Authentication is enabled",additional.getClass().getSimpleName()); + } else if(hasOAuthDirectTAF) { + Class<?> daupCls; + try { + daupCls = Class.forName("org.onap.aaf.auth.direct.DirectAAFUserPass"); + } catch (ClassNotFoundException e) { + daupCls = null; + } + if(daupCls != null && additional.getClass().isAssignableFrom(daupCls)) { + htlist.add(new BasicHttpTaf(access, (CredVal)additional , basic_realm, userExp, basic_warn)); + access.printf(Level.INIT,"Direct BasicAuth Authentication is enabled",additional.getClass().getSimpleName()); + } + } + } + } + + ///////////////////////////////////////////////////// + // Create EpiTaf from configured TAFs + ///////////////////////////////////////////////////// + if(htlist.size()==1) { + // just return the one + taf = htlist.get(0); + } else { + HttpTaf[] htarray = new HttpTaf[htlist.size()]; + htlist.toArray(htarray); + Locator<URI> locator = loadLocator(si, logProp(access, AAF_LOCATE_URL, null)); + + taf = new HttpEpiTaf(access,locator, tc, htarray); // ok to pass locator == null + String level = logProp(access, CADI_LOGLEVEL, null); + if(level!=null) { + access.setLogLevel(Level.valueOf(level)); + } + } + + return taf; + } + + public static String logProp(Access access,String tag, String def) { + String rv = access.getProperty(tag, def); + if(rv == null) { + access.log(Level.INIT,tag,"is not explicitly set"); + } else { + access.log(Level.INIT,tag,"is set to",rv); + } + return rv; + } + + public static Lur configLur(SecurityInfoC<HttpURLConnection> si, Connector con, Object ... additionalTafLurs) throws CadiException { + Access access = si.access; + List<Lur> lurs = new ArrayList<Lur>(); + + ///////////////////////////////////////////////////// + // Configure a Local Property Based RBAC/LUR + ///////////////////////////////////////////////////// + try { + String users = access.getProperty(USERS,null); + String groups = access.getProperty(GROUPS,null); + + if(groups!=null || users!=null) { + LocalLur ll; + lurs.add(ll = new LocalLur(access, users, groups)); // note b64==null is ok.. just means no encryption. + + String writeto = access.getProperty(WRITE_TO,null); + if(writeto!=null) { + String msg = UsersDump.updateUsers(writeto, ll); + if(msg!=null) access.log(Level.INIT,"ERROR! Error Updating ",writeto,"with roles and users:",msg); + } + } + } catch (IOException e) { + throw new CadiException(e); + } + + ///////////////////////////////////////////////////// + // Configure the OAuth Lur (if any) + ///////////////////////////////////////////////////// + String token_url = logProp(access,AAF_OAUTH2_TOKEN_URL, null); + String introspect_url = logProp(access,AAF_OAUTH2_INTROSPECT_URL, null); + if(token_url!=null && introspect_url !=null) { + try { + Class<?> olurCls = loadClass(access, CADI_OLUR_CLASS_DEF); + if(olurCls!=null) { + Constructor<?> olurCnst = olurCls.getConstructor(PropAccess.class,String.class,String.class); + Lur olur = (Lur)olurCnst.newInstance(access,token_url,introspect_url); + lurs.add(olur); + access.log(Level.INIT, "OAuth2 LUR enabled"); + } else { + access.log(Level.INIT,"AAF/OAuth LUR plugin is not available."); + } + } catch (NoSuchMethodException| SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + access.log(e,"AAF/OAuth LUR could not be constructed with given Constructors."); + } + } else { + access.log(Level.INIT, "OAuth2 Lur disabled"); + } + + if(con!=null) { // try to reutilize connector + lurs.add(con.newLur()); + } else { + ///////////////////////////////////////////////////// + // Configure the AAF Lur (if any) + ///////////////////////////////////////////////////// + String aafURL = logProp(access,AAF_URL,null); // Trigger Property + String aaf_env = access.getProperty(AAF_ENV,null); + if(aaf_env == null && aafURL!=null && access instanceof PropAccess) { // set AAF_ENV from AAF_URL + int ec = aafURL.indexOf("envContext="); + if(ec>0) { + ec += 11; // length of envContext= + int slash = aafURL.indexOf('/', ec); + if(slash>0) { + aaf_env = aafURL.substring(ec, slash); + ((PropAccess)access).setProperty(AAF_ENV, aaf_env); + access.printf(Level.INIT, "Setting aaf_env to %s from aaf_url value",aaf_env); + } + } + } + + // Don't configure AAF if it is using DirectAccess + if(!hasDirect("DirectAAFLur",additionalTafLurs)) { + if(aafURL==null) { + access.log(Level.INIT,"No AAF LUR properties, AAF will not be loaded"); + } else {// There's an AAF_URL... try to configure an AAF + String aafLurClassStr = logProp(access,AAF_LUR_CLASS,AAF_V2_0_AAF_LUR_PERM); + ////////////AAF Lur 2.0 ///////////// + if(aafLurClassStr!=null && aafLurClassStr.startsWith(AAF_V2_0)) { + try { + Object aafcon = loadAAFConnector(si, aafURL); + if(aafcon==null) { + access.log(Level.INIT,"AAF LUR class,",aafLurClassStr,"cannot be constructed without valid AAFCon object."); + } else { + Class<?> aafAbsAAFCon = loadClass(access, AAF_V2_0_AAFCON); + if(aafAbsAAFCon!=null) { + Method mNewLur = aafAbsAAFCon.getMethod("newLur"); + Object aaflur = mNewLur.invoke(aafcon); + + if(aaflur==null) { + access.log(Level.INIT,"ERROR! AAF LUR Failed construction. NOT Configured"); + } else { + access.log(Level.INIT,"AAF LUR Configured to ",aafURL); + lurs.add((Lur)aaflur); + String debugIDs = logProp(access,Config.AAF_DEBUG_IDS, null); + if(debugIDs !=null && aaflur instanceof CachingLur) { + ((CachingLur<?>)aaflur).setDebug(debugIDs); + } + } + } + } + } catch (Exception e) { + access.log(e,"AAF LUR class,",aafLurClassStr,"could not be constructed with given Constructors."); + } + } + } + } + } + + ///////////////////////////////////////////////////// + // Any Additional passed in Constructor + ///////////////////////////////////////////////////// + if(additionalTafLurs!=null) { + for(Object additional : additionalTafLurs) { + if(additional instanceof Lur) { + lurs.add((Lur)additional); + access.log(Level.INIT, additional); + } + } + } + + ///////////////////////////////////////////////////// + // Return a Lur based on how many there are... + ///////////////////////////////////////////////////// + switch(lurs.size()) { + case 0: + access.log(Level.INIT,"WARNING! No CADI LURs configured"); + // Return a NULL Lur that does nothing. + return new NullLur(); + case 1: + return lurs.get(0); // Only one, just return it, save processing + default: + // Multiple Lurs, use EpiLUR to handle + Lur[] la = new Lur[lurs.size()]; + lurs.toArray(la); + return new EpiLur(la); + } + } + + private static boolean hasDirect(String simpleClassName, Object[] additionalTafLurs) { + if(additionalTafLurs!=null) { + for(Object tf : additionalTafLurs) { + if(tf.getClass().getSimpleName().equals(simpleClassName)) { + return true; + } + } + } + return false; + } + + private static final String AAF_V2_0_AAF_CON_HTTP = "org.onap.aaf.cadi.aaf.v2_0.AAFConHttp"; + + public static Object loadAAFConnector(SecurityInfoC<HttpURLConnection> si, String aafURL) { + Access access = si.access; + Object aafcon = null; + Class<?> aafConClass = null; + + try { + if (aafURL!=null) { + String aafConnector = access.getProperty(AAF_CONNECTOR_CLASS, AAF_V2_0_AAF_CON_HTTP); + if (AAF_V2_0_AAF_CON_HTTP.equals(aafConnector)) { + aafConClass = loadClass(access, AAF_V2_0_AAF_CON_HTTP); + if (aafConClass != null) { + for (Constructor<?> c : aafConClass.getConstructors()) { + List<Object> lo = new ArrayList<Object>(); + for (Class<?> pc : c.getParameterTypes()) { + if (pc.equals(Access.class)) { + lo.add(access); + } else if (pc.equals(Locator.class)) { + lo.add(loadLocator(si, aafURL)); + } else { + continue; + } + } + if (c.getParameterTypes().length != lo.size()) { + continue; // back to another Constructor + } else { + aafcon = c.newInstance(lo.toArray()); + } + break; + } + } + } + if (aafcon != null) { + String mechid = logProp(access, Config.AAF_APPID, null); + String pass = access.getProperty(Config.AAF_APPPASS, null); + if (mechid != null && pass != null) { + try { + Method basicAuth = aafConClass.getMethod("basicAuth", String.class, String.class); + basicAuth.invoke(aafcon, mechid, pass); + } catch (NoSuchMethodException nsme) { + // it's ok, don't use + } + } + } + } + } catch (Exception e) { + access.log(e, "AAF Connector could not be constructed with given Constructors."); + } + + return aafcon; + } + + public static Class<?> loadClass(Access access, String className) { + Class<?> cls=null; + try { + cls = access.classLoader().loadClass(className); + } catch (ClassNotFoundException cnfe) { + try { + cls = access.getClass().getClassLoader().loadClass(className); + } catch (ClassNotFoundException cnfe2) { + // just return null + } + } + return cls; + } + + + @SuppressWarnings("unchecked") + public static Locator<URI> loadLocator(SecurityInfoC<HttpURLConnection> si, final String _url) { + Access access = si.access; + Locator<URI> locator = null; + if(_url==null) { + access.log(Level.INIT,"No URL passed to 'loadLocator'. Disabled"); + } else { + String url = _url, replacement; + int idxAAF_LOCATE_URL; + if((idxAAF_LOCATE_URL=_url.indexOf(AAF_LOCATE_URL_TAG))>0 && ((replacement=access.getProperty(AAF_LOCATE_URL, null))!=null)) { + url = replacement + "/locate" + _url.substring(idxAAF_LOCATE_URL+AAF_LOCATE_URL_TAG.length()); + } + + try { + Class<?> lcls = loadClass(access,AAF_LOCATOR_CLASS_DEF); + if(lcls==null) { + throw new CadiException("Need to include aaf-cadi-aaf jar for AAFLocator"); + } + // First check for preloaded + try { + Method meth = lcls.getMethod("create",String.class); + locator = (Locator<URI>)meth.invoke(null,url); + } catch (Exception e) { + locator = null; + } + if(locator==null) { + URI locatorURI = new URI(url); + Constructor<?> cnst = lcls.getConstructor(new Class[] {SecurityInfoC.class,URI.class}); + locator = (Locator<URI>)cnst.newInstance(new Object[] {si,locatorURI}); + int port = locatorURI.getPort(); + String portS = port<0?"":(":"+locatorURI.getPort()); + + access.log(Level.INFO, "AAFLocator enabled using " + locatorURI.getScheme() +"://"+locatorURI.getHost() + portS); + } else { + access.log(Level.INFO, "AAFLocator enabled using preloaded " + locator.getClass().getSimpleName()); + } + } catch (InvocationTargetException e) { + access.log(Level.INIT,e.getTargetException().getMessage(),"AAFLocator for",url,"could not be created.",e); + } catch (Exception e) { + access.log(Level.INIT,"AAFLocator for",url,"could not be created.",e); + } + } + return locator; + } + + // Set by CSP, or is hostname. + public static String getDefaultRealm() { + return defaultRealm; + } + +} + diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/config/Get.java b/cadi/core/src/main/java/org/onap/aaf/cadi/config/Get.java new file mode 100644 index 00000000..dfb7b4d3 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/config/Get.java @@ -0,0 +1,97 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.config; + +import java.lang.reflect.Method; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.Access.Level; + +public interface Get { + public String get(String name, String def, boolean print); + + + /** + * A class for Getting info out of "JavaBean" format + * @author Jonathan + * + */ + public static class Bean implements Get { + private Object bean; + private Class<?> bc; + private Class<?>[] params; + private Object[] args; + + public Bean(Object bean) { + this.bean = bean; + bc = bean.getClass(); + params = new Class<?>[0]; // note, this will allow to go out of scope after config + args = new Object[0]; + } + + public String get(String name, String def, boolean print) { + String str = null; + String gname = "get"+Character.toUpperCase(name.charAt(0))+name.substring(1); + try { + Method meth = bc.getMethod(gname, params); + Object obj = meth.invoke(bean, args); + str = obj==null?null:obj.toString(); // easy string convert... + } catch (Exception e) { + } + + // Take def if nothing else + if(str==null) { + str = def; + // don't log defaults + } else { + str = str.trim(); // this is vital in Property File based values, as spaces can hide easily + } + // Note: Can't log during configuration + return str; + } + } + + public static Get NULL = new Get() { + public String get(String name, String def, boolean print) { + return def; + } + }; + + public static class AccessGet implements Get { + private Access access; + public AccessGet(Access access) { + this.access = access; + } + public String get(String name, String def, boolean print) { + String gotten = access.getProperty(name, def); + if(print) { + if(gotten == null) { + access.log(Level.INIT,name, "is not set"); + } else { + access.log(Level.INIT,name, "is set to", gotten); + } + } + return gotten; + } + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/config/GetAccess.java b/cadi/core/src/main/java/org/onap/aaf/cadi/config/GetAccess.java new file mode 100644 index 00000000..b44de05f --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/config/GetAccess.java @@ -0,0 +1,57 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.config; + +import org.onap.aaf.cadi.PropAccess; + +public class GetAccess extends PropAccess { + private final Get getter; + + public GetAccess(Get getter) { + super(new String[]{"cadi_prop_files="+getter.get("cadi_prop_files", null, true)}); + this.getter = getter; + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.PropAccess#getProperty(java.lang.String, java.lang.String) + */ + @Override + public String getProperty(String tag, String def) { + String rv; + rv = super.getProperty(tag, null); + if(rv==null && getter!=null) { + rv = getter.get(tag, null, true); + } + return rv==null?def:rv; + } + /* (non-Javadoc) + * @see org.onap.aaf.cadi.PropAccess#getProperty(java.lang.String) + */ + @Override + public String getProperty(String tag) { + return getProperty(tag, null); + } + + public Get get() { + return getter; + } +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/config/MultiGet.java b/cadi/core/src/main/java/org/onap/aaf/cadi/config/MultiGet.java new file mode 100644 index 00000000..a73df14e --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/config/MultiGet.java @@ -0,0 +1,42 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.config; + +public class MultiGet implements Get { + private Get[] getters; + + public MultiGet(Get ... getters) { + this.getters = getters; + } + + @Override + public String get(String name, String def, boolean print) { + String str; + for(Get getter : getters) { + str = getter.get(name, null, print); + if(str!=null) + return str; + } + return def; + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/config/SecurityInfo.java b/cadi/core/src/main/java/org/onap/aaf/cadi/config/SecurityInfo.java new file mode 100644 index 00000000..b34d096d --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/config/SecurityInfo.java @@ -0,0 +1,278 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.config; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.rmi.AccessException; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509KeyManager; +import javax.net.ssl.X509TrustManager; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.util.MaskFormatException; +import org.onap.aaf.cadi.util.NetMask; + +public class SecurityInfo { + private static final String SECURITY_ALGO = "RSA"; + private static final String HTTPS_PROTOCOLS = "https.protocols"; + private static final String JDK_TLS_CLIENT_PROTOCOLS = "jdk.tls.client.protocols"; + + public static final String HTTPS_PROTOCOLS_DEFAULT = "TLSv1.1,TLSv1.2"; + public static final String REGEX_COMMA = "\\s*,\\s*"; + public static final String SslKeyManagerFactoryAlgorithm; + + private SSLSocketFactory scf; + private X509KeyManager[] km; + private X509TrustManager[] tm; + public final String default_alias; + private NetMask[] trustMasks; + private SSLContext ctx; + private HostnameVerifier maskHV; + public final Access access; + + // Change Key Algorithms for IBM's VM. Could put in others, if needed. + static { + if(System.getProperty("java.vm.vendor").equalsIgnoreCase("IBM Corporation")) { + SslKeyManagerFactoryAlgorithm = "IbmX509"; + } else { + SslKeyManagerFactoryAlgorithm = "SunX509"; + } + } + + + public SecurityInfo(final Access access) throws CadiException { + try { + this.access = access; + // reuse DME2 Properties for convenience if specific Properties don't exist + + initializeKeyManager(); + + initializeTrustManager(); + + default_alias = access.getProperty(Config.CADI_ALIAS, null); + + initializeTrustMasks(); + + String https_protocols = Config.logProp(access, Config.CADI_PROTOCOLS, + access.getProperty(HTTPS_PROTOCOLS, HTTPS_PROTOCOLS_DEFAULT) + ); + System.setProperty(HTTPS_PROTOCOLS, https_protocols); + System.setProperty(JDK_TLS_CLIENT_PROTOCOLS, https_protocols); + if("1.7".equals(System.getProperty("java.specification.version")) && https_protocols.contains("TLSv1.2")) { + System.setProperty(Config.HTTPS_CIPHER_SUITES, Config.HTTPS_CIPHER_SUITES_DEFAULT); + } + + ctx = SSLContext.getInstance("TLS"); + ctx.init(km, tm, null); + SSLContext.setDefault(ctx); + scf = ctx.getSocketFactory(); + } catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException | CertificateException | UnrecoverableKeyException | IOException e) { + throw new CadiException(e); + } + } + + /** + * @return the scf + */ + public SSLSocketFactory getSSLSocketFactory() { + return scf; + } + + public SSLContext getSSLContext() { + return ctx; + } + + /** + * @return the km + */ + public X509KeyManager[] getKeyManagers() { + return km; + } + + public void checkClientTrusted(X509Certificate[] certarr) throws CertificateException { + for(X509TrustManager xtm : tm) { + xtm.checkClientTrusted(certarr, SECURITY_ALGO); + } + } + + public void checkServerTrusted(X509Certificate[] certarr) throws CertificateException { + for(X509TrustManager xtm : tm) { + xtm.checkServerTrusted(certarr, SECURITY_ALGO); + } + } + + public void setSocketFactoryOn(HttpsURLConnection hsuc) { + hsuc.setSSLSocketFactory(scf); + if(maskHV != null && !maskHV.equals(hsuc.getHostnameVerifier())) { + hsuc.setHostnameVerifier(maskHV); + } + } + + protected void initializeKeyManager() throws CadiException, IOException, NoSuchAlgorithmException, KeyStoreException, CertificateException, UnrecoverableKeyException { + String keyStore = access.getProperty(Config.CADI_KEYSTORE, null); + if(keyStore != null && !new File(keyStore).exists()) { + throw new CadiException(keyStore + " does not exist"); + } + + String keyStorePasswd = access.getProperty(Config.CADI_KEYSTORE_PASSWORD, null); + keyStorePasswd = (keyStorePasswd == null) ? null : access.decrypt(keyStorePasswd, false); + + String keyPasswd = access.getProperty(Config.CADI_KEY_PASSWORD, null); + keyPasswd = (keyPasswd == null) ? keyStorePasswd : access.decrypt(keyPasswd, false); + + KeyManagerFactory kmf = KeyManagerFactory.getInstance(SslKeyManagerFactoryAlgorithm); + if(keyStore == null || keyStorePasswd == null) { + km = new X509KeyManager[0]; + } else { + ArrayList<X509KeyManager> kmal = new ArrayList<X509KeyManager>(); + File file; + for(String ksname : keyStore.split(REGEX_COMMA)) { + file = new File(ksname); + String keystoreFormat; + if(ksname.endsWith(".p12") || ksname.endsWith(".pkcs12")) { + keystoreFormat = "PKCS12"; + } else { + keystoreFormat = "JKS"; + } + if(file.exists()) { + FileInputStream fis = new FileInputStream(file); + try { + KeyStore ks = KeyStore.getInstance(keystoreFormat); + ks.load(fis, keyStorePasswd.toCharArray()); + kmf.init(ks, keyPasswd.toCharArray()); + } finally { + fis.close(); + } + } + } + for(KeyManager km : kmf.getKeyManagers()) { + if(km instanceof X509KeyManager) { + kmal.add((X509KeyManager)km); + } + } + km = new X509KeyManager[kmal.size()]; + kmal.toArray(km); + } + } + + protected void initializeTrustManager() throws NoSuchAlgorithmException, CertificateException, IOException, KeyStoreException, CadiException { + String trustStore = access.getProperty(Config.CADI_TRUSTSTORE, null); + if(trustStore != null && !new File(trustStore).exists()) { + throw new CadiException(trustStore + " does not exist"); + } + + String trustStorePasswd = access.getProperty(Config.CADI_TRUSTSTORE_PASSWORD, null); + trustStorePasswd = (trustStorePasswd == null) ? "changeit"/*defacto Java Trust Pass*/ : access.decrypt(trustStorePasswd, false); + + TrustManagerFactory tmf = TrustManagerFactory.getInstance(SslKeyManagerFactoryAlgorithm); + if(trustStore != null) { + File file; + for(String tsname : trustStore.split(REGEX_COMMA)) { + file = new File(tsname); + if(file.exists()) { + FileInputStream fis = new FileInputStream(file); + try { + KeyStore ts = KeyStore.getInstance("JKS"); + ts.load(fis, trustStorePasswd.toCharArray()); + tmf.init(ts); + } finally { + fis.close(); + } + } + } + + TrustManager tms[] = tmf.getTrustManagers(); + if(tms != null && tms.length>0) { + tm = new X509TrustManager[tms.length]; + for(int i = 0; i < tms.length; ++i) { + try { + tm[i] = (X509TrustManager)tms[i]; + } catch (ClassCastException e) { + access.log(Level.WARN, "Non X509 TrustManager", tm[i].getClass().getName(), "skipped in SecurityInfo"); + } + } + } + } + + } + + protected void initializeTrustMasks() throws AccessException { + String tips = access.getProperty(Config.CADI_TRUST_MASKS, null); + if(tips != null) { + access.log(Level.INIT, "Explicitly accepting valid X509s from", tips); + String[] ipsplit = tips.split(REGEX_COMMA); + trustMasks = new NetMask[ipsplit.length]; + for(int i = 0; i < ipsplit.length; ++i) { + try { + trustMasks[i] = new NetMask(ipsplit[i]); + } catch (MaskFormatException e) { + throw new AccessException("Invalid IP Mask in " + Config.CADI_TRUST_MASKS, e); + } + } + } + + if(trustMasks != null) { + final HostnameVerifier origHV = HttpsURLConnection.getDefaultHostnameVerifier(); + HttpsURLConnection.setDefaultHostnameVerifier(maskHV = new HostnameVerifier() { + @Override + public boolean verify(final String urlHostName, final SSLSession session) { + try { + // This will pick up /etc/host entries as well as DNS + InetAddress ia = InetAddress.getByName(session.getPeerHost()); + for(NetMask tmask : trustMasks) { + if(tmask.isInNet(ia.getHostAddress())) { + return true; + } + } + } catch (UnknownHostException e) { + // It's ok. do normal Verify + } + return origHV.verify(urlHostName, session); + }; + }); + } + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/config/SecurityInfoC.java b/cadi/core/src/main/java/org/onap/aaf/cadi/config/SecurityInfoC.java new file mode 100644 index 00000000..33aef6c9 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/config/SecurityInfoC.java @@ -0,0 +1,72 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.config; + +import java.util.HashMap; +import java.util.Map; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.SecuritySetter; + + +public class SecurityInfoC<CLIENT> extends SecurityInfo { + public static final String DEF_ID = "ID not Set"; + private static Map<Class<?>,SecurityInfoC<?>> sicMap = new HashMap<Class<?>,SecurityInfoC<?>>(); + public SecuritySetter<CLIENT> defSS; + + private SecurityInfoC(Access access) throws CadiException { + super(access); + defSS = new SecuritySetter<CLIENT>() { + @Override + public String getID() { + return DEF_ID; + } + + @Override + public void setSecurity(CLIENT client) throws CadiException { + throw new CadiException("No Client Credentials set."); + } + + @Override + public int setLastResponse(int respCode) { + return 0; + } + }; + } + + @SuppressWarnings("unchecked") + public static synchronized <CLIENT> SecurityInfoC<CLIENT> instance(Access access, Class<CLIENT> cls) throws CadiException { + SecurityInfoC<?> sic = sicMap.get(cls); + if(sic==null) { + sic = new SecurityInfoC<CLIENT>(access); + sicMap.put(cls, sic); + } + return (SecurityInfoC<CLIENT>)sic; + } + + public SecurityInfoC<CLIENT> set(SecuritySetter<CLIENT> defSS) { + this.defSS = defSS; + return this; + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/config/UsersDump.java b/cadi/core/src/main/java/org/onap/aaf/cadi/config/UsersDump.java new file mode 100644 index 00000000..a3e267cd --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/config/UsersDump.java @@ -0,0 +1,162 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.config; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.util.Date; +import java.util.HashSet; + +import org.onap.aaf.cadi.AbsUserCache; +import org.onap.aaf.cadi.lur.LocalLur; + +public class UsersDump { + + /** + * @param args + */ + public static boolean write(OutputStream os, AbsUserCache<?> lur) { + PrintStream ps; + if(os instanceof PrintStream) { + ps = (PrintStream)os; + } else { + ps = new PrintStream(os); + } + try { + ps.println("<?xml version='1.0' encoding='utf-8'?>"); + ps.println("<!--"); + ps.print( " Code Generated Tomcat Users and Roles from AT&T LUR on "); + ps.println(new Date()); + ps.println( "-->"); + ps.println("<tomcat-users>"); + + // We loop through Users, but want to write Groups first... therefore, save off print + StringBuilder sb = new StringBuilder(); + + // Obtain all unique role names + HashSet<String> groups = new HashSet<String>(); + for(AbsUserCache<?>.DumpInfo di : lur.dumpInfo()) { + sb.append("\n <user username=\""); + sb.append(di.user); + sb.append("\" roles=\""); + boolean first = true; + for(String role : di.perms) { + groups.add(role); + if(first)first = false; + else sb.append(','); + sb.append(role); + } + sb.append("\"/>"); + + } + + // Print roles + for(String group : groups) { + ps.print(" <role rolename=\""); + ps.print(group); + ps.println("\"/>"); + } + + ps.println(sb); + + ps.println("</tomcat-users>"); + ps.flush(); + } catch (Throwable t) { + t.printStackTrace(ps); + return false; + } + return true; + } + + /** + * + * Note: This method returns a String if there's an error, or null if ok. + * This unusual style is necessitated by the fact that any Exceptions thrown are likely to + * be unlogged and hidden from view, making debugging almost impossible. + * + * @param writeto + * @param up + * @return + */ + public static String updateUsers(String writeto, LocalLur up) { + // Dump a Tomcat-user.xml lookalike (anywhere) + if(writeto!=null) { + // First read content + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + if(UsersDump.write(baos, up)) { + byte[] postulate = baos.toByteArray(); + // now get contents of file + File file = new File(writeto); + boolean writeIt; + if(file.exists()) { + try { + FileInputStream fis = new FileInputStream(file); + byte[] orig = new byte[(int)file.length()]; + int read; + try { + read = fis.read(orig); + } finally { + fis.close(); + } + if(read<=0) { + writeIt = false; + } else { + // Starting at third "<" (<tomcat-users> line) + int startA=0, startB=0; + for(int i=0;startA<orig.length && i<3;++startA) if(orig[startA]=='<')++i; + for(int i=0;startB<orig.length && i<3;++startB) if(postulate[startB]=='<')++i; + + writeIt=orig.length-startA!=postulate.length-startB; // first, check if remaining length is the same + while(!writeIt && startA<orig.length && startB<postulate.length) { + if(orig[startA++]!=postulate[startB++])writeIt = true; + } + } + } catch (Exception e) { + writeIt = true; + } + } else { + writeIt = true; + } + + if(writeIt) { + try { + FileOutputStream fos = new FileOutputStream(file); + try { + fos.write(postulate); + } finally { + fos.close(); + } + } catch (IOException e) { + return e.getMessage(); + } + } + } + } + return null; // no message means ok. + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/filter/AUTHZ.java b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/AUTHZ.java new file mode 100644 index 00000000..7fd1e93c --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/AUTHZ.java @@ -0,0 +1,36 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.filter; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.servlet.Servlet; + +@Target({TYPE}) +@Retention(RUNTIME) +public @interface AUTHZ { + Class<? extends Servlet> value(); +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/filter/AUTHZServlet.java b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/AUTHZServlet.java new file mode 100644 index 00000000..f72a99bf --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/AUTHZServlet.java @@ -0,0 +1,98 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.filter; + +import java.io.IOException; + +import javax.servlet.Servlet; +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * + * @author Jonathan + * + */ +public class AUTHZServlet<S extends Servlet> implements Servlet { + private String[] roles; + private Servlet delegate; + + protected AUTHZServlet(Class<S> cls) { + try { + delegate = cls.newInstance(); + } catch (Exception e) { + delegate = null; + } + RolesAllowed rolesAllowed = cls.getAnnotation(RolesAllowed.class); + if (rolesAllowed == null) { + roles = null; + } else { + roles = rolesAllowed.value(); + } + } + + public void init(ServletConfig sc) throws ServletException { + if (delegate == null) { + throw new ServletException("Invalid Servlet Delegate"); + } + delegate.init(sc); + } + + public ServletConfig getServletConfig() { + return delegate.getServletConfig(); + } + + public String getServletInfo() { + return delegate.getServletInfo(); + } + + public void service(ServletRequest req, ServletResponse resp) throws ServletException, IOException { + if (roles == null) { + delegate.service(req, resp); + return; + } + + // Validate + try { + HttpServletRequest hreq = (HttpServletRequest)req; + for (String role : roles) { + if (hreq.isUserInRole(role)) { + delegate.service(req, resp); + return; + } + } + + ((HttpServletResponse)resp).sendError(403); // forbidden + } catch (ClassCastException e) { + throw new ServletException("JASPIServlet only supports HTTPServletRequest/HttpServletResponse"); + } + } + + public void destroy() { + delegate.destroy(); + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/filter/AccessGetter.java b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/AccessGetter.java new file mode 100644 index 00000000..ab34a0a4 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/AccessGetter.java @@ -0,0 +1,35 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ +package org.onap.aaf.cadi.filter; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.config.Get; + +public class AccessGetter implements Get { + private final Access access; + public AccessGetter(Access access) { + this.access = access; + } + public String get(String name, String def, boolean print) { + return access.getProperty(name, def); + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/filter/CadiFilter.java b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/CadiFilter.java new file mode 100644 index 00000000..8577d55c --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/CadiFilter.java @@ -0,0 +1,332 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.filter; + +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.CadiWrap; +import org.onap.aaf.cadi.Lur; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.ServletContextAccess; +import org.onap.aaf.cadi.TrustChecker; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.config.Get; +import org.onap.aaf.cadi.taf.TafResp; +import org.onap.aaf.cadi.taf.TafResp.RESP; + +/** + * CadiFilter + * + * This class implements Servlet Filter, and ties together CADI implementations + * + * This class can be used in a standard J2EE Servlet manner. Optimal usage is for POJO operations, where + * one can enforce this Filter being first and primary. Depending on the Container, it + * may be more effective, in some cases, to utilize features that allow earlier determination of + * AUTHN (Authorization). An example would be "Tomcat Valve". These implementations, however, should + * be modeled after the "init" and "doFilter" functions, and be kept up to date as this class changes. + * + * + * @author Jonathan + * + */ +public class CadiFilter implements Filter { + private static CadiHTTPManip httpChecker; + private static String[] pathExceptions; + private static List<Pair> mapPairs; + private Access access; + private Object[] additionalTafLurs; + private Filter oauthFilter; + private static int count=0; + + public Lur getLur() { + return httpChecker.getLur(); + } + + /** + * Construct a viable Filter + * + * Due to the vagaries of many containers, there is a tendency to create Objects and call "Init" on + * them at a later time. Therefore, this object creates with an object that denies all access + * until appropriate Init happens, just in case the container lets something slip by in the meantime. + * + */ + public CadiFilter() { + additionalTafLurs = CadiHTTPManip.noAdditional; + } + + /** + * This constructor to be used when directly constructing and placing in HTTP Engine + * + * @param access + * @param moreTafLurs + * @throws ServletException + */ + public CadiFilter(Access access, Object ... moreTafLurs) throws ServletException { + additionalTafLurs = moreTafLurs; + init(new AccessGetter(this.access = access)); + } + + + /** + * Use this to pass in a PreContructed CADI Filter, but with initializing... let Servlet do it + * @param init + * @param access + * @param moreTafLurs + * @throws ServletException + */ + public CadiFilter(boolean init, PropAccess access, Object ... moreTafLurs) throws ServletException { + this.access = access; + additionalTafLurs = moreTafLurs; + if(init) { + init(new AccessGetter(access)); + } + } + + /** + * Init + * + * Standard Filter "init" call with FilterConfig to obtain properties. POJOs can construct a + * FilterConfig with the mechanism of their choice, and standard J2EE Servlet engines utilize this + * mechanism already. + */ + //TODO Always validate changes against Tomcat AbsCadiValve and Jaspi CadiSAM Init functions + public void init(FilterConfig filterConfig) throws ServletException { + // need the Context for Logging, instantiating ClassLoader, etc + ServletContextAccess sca=new ServletContextAccess(filterConfig); + if(access==null) { + access = sca; + } + + // Set Protected getter with base Access, for internal class instantiations + init(new FCGet(access, sca.context(), filterConfig)); + } + + + @SuppressWarnings("unchecked") + private void init(Get getter) throws ServletException { + // Start with the assumption of "Don't trust anyone". + TrustChecker tc = TrustChecker.NOTRUST; // default position + try { + Class<TrustChecker> ctc = (Class<TrustChecker>) Class.forName("org.onap.aaf.cadi.aaf.v2_0.AAFTrustChecker"); + if(ctc!=null) { + Constructor<TrustChecker> contc = ctc.getConstructor(Access.class); + if(contc!=null) { + tc = contc.newInstance(access); + } + } + } catch (Exception e) { + access.log(Level.INIT, "AAFTrustChecker cannot be loaded",e.getMessage()); + } + + try { + Class<Filter> cf=null; + try { + cf= (Class<Filter>) Class.forName("org.onap.aaf.cadi.oauth.OAuthFilter"); + oauthFilter = cf.newInstance(); + } catch (ClassNotFoundException e) { + oauthFilter = new Filter() { // Null Filter + @Override + public void destroy() { + } + + @Override + public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)throws IOException, ServletException { + chain.doFilter(req, resp); + } + + @Override + public void init(FilterConfig arg0) throws ServletException { + } + }; + } + } catch (Exception e) { + access.log(Level.INIT, "AAFTrustChecker cannot be loaded",e.getMessage()); + } + + + // Synchronize, because some instantiations call init several times on the same object + // In this case, the epiTaf will be changed to a non-NullTaf, and thus not instantiate twice. + synchronized(CadiHTTPManip.noAdditional /*will always remain same Object*/) { + ++count; + if(httpChecker == null) { + if(access==null) { + access = new PropAccess(); + } + try { + httpChecker = new CadiHTTPManip(access,null /*reuseable Con*/,tc, additionalTafLurs); + } catch (CadiException e1) { + throw new ServletException(e1); + } + } else if(access==null) { + access= httpChecker.getAccess(); + } + + /* + * Setup Authn Path Exceptions + */ + if(pathExceptions==null) { + String str = getter.get(Config.CADI_NOAUTHN, null, true); + if(str!=null) { + pathExceptions = str.split("\\s*:\\s*"); + } + } + + /* + * SETUP Permission Converters... those that can take Strings from a Vendor Product, and convert to appropriate AAF Permissions + */ + if(mapPairs==null) { + String str = getter.get(Config.AAF_PERM_MAP, null, true); + if(str!=null) { + String mstr = getter.get(Config.AAF_PERM_MAP, null, true); + if(mstr!=null) { + String map[] = mstr.split("\\s*:\\s*"); + if(map.length>0) { + MapPermConverter mpc=null; + int idx; + mapPairs = new ArrayList<Pair>(); + for(String entry : map) { + if((idx=entry.indexOf('='))<0) { // it's a Path, so create a new converter + access.log(Level.INIT,"Loading Perm Conversions for:",entry); + mapPairs.add(new Pair(entry,mpc=new MapPermConverter())); + } else { + if(mpc!=null) { + mpc.map().put(entry.substring(0,idx),entry.substring(idx+1)); + } else { + access.log(Level.ERROR,"cadi_perm_map is malformed; ",entry, "is skipped"); + } + } + } + } + } + } + } + } + + // Remove Getter + getter = Get.NULL; + } + + /** + * Containers call "destroy" when time to cleanup + */ + public void destroy() { + // Synchronize, in case multiCadiFilters are used. + synchronized(CadiHTTPManip.noAdditional) { + if(--count<=0 && httpChecker!=null) { + httpChecker.destroy(); + httpChecker=null; + access=null; + pathExceptions=null; + } + } + } + + /** + * doFilter + * + * This is the standard J2EE invocation. Analyze the request, modify response as necessary, and + * only call the next item in the filterChain if request is suitably Authenticated. + */ + //TODO Always validate changes against Tomcat AbsCadiValve and Jaspi CadiSAM functions + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + try { + HttpServletRequest hreq = (HttpServletRequest)request; + if(noAuthn(hreq)) { + chain.doFilter(request, response); + } else { + HttpServletResponse hresp = (HttpServletResponse)response; + TafResp tresp = httpChecker.validate(hreq, hresp, hreq); + if(tresp.isAuthenticated()==RESP.IS_AUTHENTICATED) { + CadiWrap cw = new CadiWrap(hreq, tresp, httpChecker.getLur(),getConverter(hreq)); + if(httpChecker.notCadi(cw, hresp)) { + oauthFilter.doFilter(cw,response,chain); + } + } + } + } catch (ClassCastException e) { + throw new ServletException("CadiFilter expects Servlet to be an HTTP Servlet",e); + } + } + + + /** + * If PathExceptions exist, report if these should not have Authn applied. + * @param hreq + * @return + */ + private boolean noAuthn(HttpServletRequest hreq) { + if(pathExceptions!=null) { + String pi = hreq.getPathInfo(); + if(pi==null) return false; // JBoss sometimes leaves null + for(String pe : pathExceptions) { + if(pi.startsWith(pe))return true; + } + } + return false; + } + + /** + * Get Converter by Path + */ + private PermConverter getConverter(HttpServletRequest hreq) { + if(mapPairs!=null) { + String pi = hreq.getPathInfo(); + if(pi !=null) { + for(Pair p: mapPairs) { + if(pi.startsWith(p.name))return p.pc; + } + } + } + return NullPermConverter.singleton(); + } + + /** + * store PermConverters by Path prefix + * @author Jonathan + * + */ + private class Pair { + public Pair(String key, PermConverter pc) { + name = key; + this.pc = pc; + } + public String name; + public PermConverter pc; + } + +} + diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/filter/CadiHTTPManip.java b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/CadiHTTPManip.java new file mode 100644 index 00000000..006d6b4e --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/CadiHTTPManip.java @@ -0,0 +1,212 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.filter; + +import java.io.IOException; +import java.net.HttpURLConnection; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.CadiWrap; +import org.onap.aaf.cadi.Connector; +import org.onap.aaf.cadi.CredVal; +import org.onap.aaf.cadi.Lur; +import org.onap.aaf.cadi.Taf; +import org.onap.aaf.cadi.TrustChecker; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.config.SecurityInfoC; +import org.onap.aaf.cadi.lur.EpiLur; +import org.onap.aaf.cadi.taf.HttpTaf; +import org.onap.aaf.cadi.taf.TafResp; +import org.onap.aaf.cadi.util.UserChainManip; + +/** + * Encapsulate common HTTP Manipulation Behavior. It will appropriately set + * HTTPServletResponse for Redirect or Forbidden, as needed. + * + * Further, this is useful, because it avoids multiple creates of Connections, where some Filters + * are created and destroyed regularly. + * + * @author Jonathan + * + */ +public class CadiHTTPManip { + private static final String ACCESS_CADI_CONTROL = ".access|cadi|control"; + private static final String METH = "OPTIONS"; + private static final String CADI = "/cadi/"; + private static final String CADI_CACHE_PRINT = "/cadi/cache/print"; + private static final String CADI_CACHE_CLEAR = "/cadi/cache/clear"; + private static final String CADI_LOG_SET = "/cadi/log/set/"; + private static final Object LOCK = new Object(); + private Access access; + private HttpTaf taf; + private CredVal up; + private Lur lur; + private String thisPerm,companyPerm,aaf_id; + + public static final Object[] noAdditional = new Object[0]; // CadiFilter can be created each call in some systems + + + public CadiHTTPManip(Access access, Connector con, TrustChecker tc, Object ... additionalTafLurs) throws CadiException { + synchronized(LOCK) { + this.access = access; +// Get getter = new AccessGetter(access); + Config.setDefaultRealm(access); + + aaf_id = access.getProperty(Config.CADI_ALIAS,access.getProperty(Config.AAF_APPID, null)); + if(aaf_id==null) { + access.printf(Level.INIT, "%s is not set. %s can be used instead",Config.AAF_APPID,Config.CADI_ALIAS); + } else { + access.printf(Level.INIT, "%s is set to %s",Config.AAF_APPID,aaf_id); + } + String ns = aaf_id==null?null:UserChainManip.idToNS(aaf_id); + if(ns!=null) { + thisPerm = ns+ACCESS_CADI_CONTROL; + int dot = ns.indexOf('.'); + if(dot>=0) { + int dot2=ns.indexOf('.',dot+1); + if(dot2<0) { + dot2=dot; + } + companyPerm = ns.substring(0, dot2)+ACCESS_CADI_CONTROL; + } else { + companyPerm = "com"+ACCESS_CADI_CONTROL; + } + } else { + thisPerm = companyPerm = "com"+ACCESS_CADI_CONTROL; + } + SecurityInfoC<HttpURLConnection> si; + si = SecurityInfoC.instance(access, HttpURLConnection.class); + + lur = Config.configLur(si, con, additionalTafLurs); + + tc.setLur(lur); + if(lur instanceof EpiLur) { + up = ((EpiLur)lur).getUserPassImpl(); + } else if(lur instanceof CredVal) { + up = (CredVal)lur; + } else { + up = null; + } + taf = Config.configHttpTaf(con,si, tc, up, lur, additionalTafLurs); + } + } + + public TafResp validate(HttpServletRequest hreq, HttpServletResponse hresp, Object state) throws IOException { + TafResp tresp = taf.validate(Taf.LifeForm.LFN, hreq, hresp); + switch(tresp.isAuthenticated()) { + case IS_AUTHENTICATED: + access.printf(Level.INFO,"Authenticated: %s from %s:%d", + tresp.desc(), hreq.getRemoteAddr(), hreq.getRemotePort()); + break; + case TRY_AUTHENTICATING: + switch (tresp.authenticate()) { + case IS_AUTHENTICATED: + access.printf(Level.INFO,"Authenticated: %s from %s:%d", + tresp.desc(), hreq.getRemoteAddr(), hreq.getRemotePort()); + break; + case HTTP_REDIRECT_INVOKED: + access.log(Level.INFO,"Authenticating via redirection: ", tresp.desc()); + break; + case NO_FURTHER_PROCESSING: + access.printf(Level.AUDIT,"Authentication Failure: %s from %s:%d" + , tresp.desc(), hreq.getRemoteAddr(), hreq.getRemotePort()); + hresp.sendError(403, tresp.desc()); // Forbidden + break; + + default: + access.printf(Level.AUDIT,"No TAF will authorize for request from %s:%d" + , hreq.getRemoteAddr(), hreq.getRemotePort()); + hresp.sendError(403, tresp.desc()); // Forbidden + } + break; + case NO_FURTHER_PROCESSING: + access.printf(Level.AUDIT,"Authentication Failure: %s from %s:%d", + tresp.desc(), hreq.getRemoteAddr(), hreq.getRemotePort()); + hresp.sendError(403, "Access Denied"); // FORBIDDEN + break; + default: + access.printf(Level.AUDIT,"No TAF will authorize for request from %s:%d" + , hreq.getRemoteAddr(), hreq.getRemotePort()); + hresp.sendError(403, "Access Denied"); // FORBIDDEN + } + return tresp; + } + + public boolean notCadi(CadiWrap req, HttpServletResponse resp) { + + String pathInfo = req.getPathInfo(); + if(METH.equalsIgnoreCase(req.getMethod()) && pathInfo!=null && pathInfo.contains(CADI)) { + if(req.getUser().equals(aaf_id) || req.isUserInRole(thisPerm) || req.isUserInRole(companyPerm)) { + try { + if(pathInfo.contains(CADI_CACHE_PRINT)) { + resp.getOutputStream().println(lur.toString()); + resp.setStatus(200); + return false; + } else if(pathInfo.contains(CADI_CACHE_CLEAR)) { + StringBuilder report = new StringBuilder(); + lur.clear(req.getUserPrincipal(), report); + resp.getOutputStream().println(report.toString()); + resp.setStatus(200); + return false; + } else if(pathInfo.contains(CADI_LOG_SET)) { + Level l; + int slash = pathInfo.lastIndexOf('/'); + String level = pathInfo.substring(slash+1); + try { + l = Level.valueOf(level); + access.printf(Level.AUDIT, "%s has set CADI Log Level to '%s'",req.getUser(),l.name()); + access.setLogLevel(l); + } catch (IllegalArgumentException e) { + access.printf(Level.AUDIT, "'%s' is not a valid CADI Log Level",level); + } + return false; + } + } catch (IOException e) { + access.log(e); + } + } + } + return true; + } + + public Lur getLur() { + return lur; + } + + public void destroy() { + access.log(Level.INFO,"CadiHttpChecker destroyed."); + if(lur!=null) { + lur.destroy(); + lur=null; + } + } + + public Access getAccess() { + return access; + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/filter/FCGet.java b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/FCGet.java new file mode 100644 index 00000000..9c4cca10 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/FCGet.java @@ -0,0 +1,76 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.filter; + +import javax.servlet.FilterConfig; +import javax.servlet.ServletContext; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.config.Get; + +/* + * A private method to query the Filter config and if not exists, return the default. This + * cleans up the initialization code. + */ +class FCGet implements Get { + /** + * + */ + private final Access access; + private FilterConfig filterConfig; + private ServletContext context; + + public FCGet(Access access, ServletContext context, FilterConfig filterConfig) { + this.access = access; + this.context = context; + this.filterConfig = filterConfig; + } + + public String get(String name, String def, boolean print) { + String str = null; + // Try Server Context First + if(context!=null) { + str = context.getInitParameter(name); + } + + // Try Filter Context next + if(str==null && filterConfig != null) { + str = filterConfig.getInitParameter(name); + } + + if(str==null) { + str = access.getProperty(name, def); + } + // Take def if nothing else + if(str==null) { + str = def; + // don't log defaults + } else { + str = str.trim(); // this is vital in Property File based values, as spaces can hide easily + if(print) { + access.log(Level.INFO,"Setting", name, "to", str); + } + } + return str; + } +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/filter/MapPermConverter.java b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/MapPermConverter.java new file mode 100644 index 00000000..f0786b12 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/MapPermConverter.java @@ -0,0 +1,54 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.filter; + +import java.util.HashMap; +import java.util.Map; + +public class MapPermConverter implements PermConverter { + private HashMap<String,String> map; + + /** + * Create with colon separated name value pairs + * i.e. teAdmin=com.att.myNS.myPerm|*|*:teUser=... + * + * @param value + */ + public MapPermConverter() { + map = new HashMap<>(); + } + + /** + * use to instantiate entries + * + * @return + */ + public Map<String,String> map() { + return map; + } + + public String convert(String minimal) { + String rv = map.get(minimal); + return (rv == null) ? minimal : rv; + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/filter/NullPermConverter.java b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/NullPermConverter.java new file mode 100644 index 00000000..8b70d95d --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/NullPermConverter.java @@ -0,0 +1,45 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.filter; + + +/** + * A NullPermConverter + * + * Obey the PermConverter Interface, but passed in "minimal" String is not converted. + * + * @author Jonathan + * + */ +public class NullPermConverter implements PermConverter { + + private static final NullPermConverter singleton = new NullPermConverter(); + + private NullPermConverter() {} + + public static NullPermConverter singleton() { return singleton; } + + public String convert(String minimal) { + return minimal; + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/filter/PathFilter.java b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/PathFilter.java new file mode 100644 index 00000000..cf87c840 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/PathFilter.java @@ -0,0 +1,180 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.filter; + +import java.io.IOException; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.config.Config; + +/** + * PathFilter + * + * This class implements Servlet Filter, and uses AAF to validate access to a Path. + * + * This class can be used in a standard J2EE Servlet manner. + * + * @author Jonathan, collaborating with Xue Gao + * + */ +public class PathFilter implements Filter { + private final Log log; + + private ServletContext context; + private String aafType; + private String notAuthorizedMsg; + + /** + * Construct a viable Filter for installing in Container WEB.XML, etc. + * + */ + public PathFilter() { + log = new Log() { + public void info(String ... msg) { + context.log(build("INFO:", msg)); + } + public void audit(String ... msg) { + context.log(build("AUDIT:", msg)); + } + private String build(String type, String []msg) { + StringBuilder sb = new StringBuilder(type); + for (String s : msg) { + sb.append(' '); + sb.append(s); + } + return sb.toString(); + } + }; + } + + /** + * Filter that can be constructed within Java + * @param access + */ + public PathFilter(final Access access) { + log = new Log() { + public void info(String ... msg) { + access.log(Level.INFO, (Object[])msg); + } + public void audit(String ... msg) { + access.log(Level.AUDIT, (Object[])msg); + } + }; + } + + /** + * Init + * + * Standard Filter "init" call with FilterConfig to obtain properties. POJOs can construct a + * FilterConfig with the mechanism of their choice, and standard J2EE Servlet engines utilize this + * mechanism already. + */ + public void init(FilterConfig filterConfig) throws ServletException { + // need the Context for Logging, instantiating ClassLoader, etc + context = filterConfig.getServletContext(); + StringBuilder sb = new StringBuilder(); + StringBuilder err = new StringBuilder(); + Object attr = context.getAttribute(Config.PATHFILTER_NS); + if (attr == null) { + err.append("PathFilter - pathfilter_ns is not set"); + } else { + sb.append(attr.toString()); + } + + attr = context.getAttribute(Config.PATHFILTER_STACK); + if (attr == null) { + log.info("PathFilter - No pathfilter_stack set, ignoring"); + } else { + sb.append('.'); + sb.append(attr.toString()); + } + + attr = context.getAttribute(Config.PATHFILTER_URLPATTERN); + if (attr == null) { + log.info("PathFilter - No pathfilter_urlpattern set, defaulting to 'urlpattern'"); + sb.append(".urlpattern"); + } else { + sb.append('.'); + sb.append(attr.toString()); + } + + log.info("PathFilter - AAF Permission Type is", sb.toString()); + + sb.append('|'); + + aafType = sb.toString(); + + attr = context.getAttribute(Config.PATHFILTER_NOT_AUTHORIZED_MSG); + if (attr == null) { + notAuthorizedMsg = "Forbidden - Not Authorized to access this Path"; + } else { + notAuthorizedMsg = attr.toString(); + } + + if (err.length() > 0) { + throw new ServletException(err.toString()); + } + } + + private interface Log { + public void info(String ... msg); + public void audit(String ... msg); + } + + /** + * doFilter + * + * This is the standard J2EE invocation. Analyze the request, modify response as necessary, and + * only call the next item in the filterChain if request is suitably Authenticated. + */ + //TODO Always validate changes against Tomcat AbsCadiValve and Jaspi CadiSAM functions + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + HttpServletRequest hreq = (HttpServletRequest)request; + HttpServletResponse hresp = (HttpServletResponse)response; + String perm = aafType + hreq.getPathInfo() + '|' + hreq.getMethod(); + if (hreq.isUserInRole(perm)) { + chain.doFilter(request, response); + } else { + log.audit("PathFilter has denied", hreq.getUserPrincipal().getName(), "access to", perm); + hresp.sendError(403, notAuthorizedMsg); + } + } + + /** + * Containers call "destroy" when time to cleanup + */ + public void destroy() { + log.info("PathFilter destroyed."); + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/filter/PermConverter.java b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/PermConverter.java new file mode 100644 index 00000000..bb97894b --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/PermConverter.java @@ -0,0 +1,32 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.filter; + +/** + * Convert a simplistic, single string Permission into an Enterprise Scoped Perm + * + * @author Jonathan + * + */ +public interface PermConverter { + public String convert(String minimal); +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/filter/RolesAllowed.java b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/RolesAllowed.java new file mode 100644 index 00000000..5f709f12 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/RolesAllowed.java @@ -0,0 +1,56 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +/** + * RolesAllowed + * + * @author Jonathan + * + * Similar to Java EE's Spec from Annotations 1.1, 2.8 + * + * That Spec, however, was geared towards being able to route calls to Methods on Objects, and thus needed a more refined + * sense of permissions hierarchy. The same mechanism, however, can easily be achieved on single Servlet/Handlers in + * POJOs like Jetty by simply adding the Roles Allowed in a similar Annotation + * + */ +package org.onap.aaf.cadi.filter; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * JASPI Style Annotation of RolesAllowed when the coding style is desired but actually including all + * JEE jars is not. If using actual JASPI, use official @interface classes, not this one... + * + * @author Jonathan + */ +@Target({TYPE}) +@Retention(RUNTIME) +public @interface RolesAllowed { + /** + * Security role of the implementation, which doesn't have to be an EJB or CORBA like object. Can be just a + * Handler + * @return + */ + String[] value(); +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/filter/ServletImpl.java b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/ServletImpl.java new file mode 100644 index 00000000..02c2600f --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/ServletImpl.java @@ -0,0 +1,56 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +/** + * RolesAllowed + * + * @author Jonathan + * + * Similar to Java EE's Spec from Annotations 1.1, 2.8 + * + * That Spec, however, was geared towards being able to route calls to Methods on Objects, and thus needed a more refined + * sense of permissions hierarchy. The same mechanism, however, can easily be achieved on single Servlet/Handlers in + * POJOs like Jetty by simply adding the Roles Allowed in a similar Annotation + * + */ +package org.onap.aaf.cadi.filter; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.servlet.Servlet; + +/** + * + * @author Jonathan + */ +@Target({TYPE}) +@Retention(RUNTIME) +public @interface ServletImpl { + /** + * Security role of the implementation, which doesn't have to be an EJB or CORBA like object. Can be just a + * Handler + * @return + */ + Class<? extends Servlet> value(); +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/lur/ConfigPrincipal.java b/cadi/core/src/main/java/org/onap/aaf/cadi/lur/ConfigPrincipal.java new file mode 100644 index 00000000..43dd1018 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/lur/ConfigPrincipal.java @@ -0,0 +1,69 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.lur; + +import java.io.IOException; +import java.security.Principal; + +import org.onap.aaf.cadi.GetCred; +import org.onap.aaf.cadi.Symm; + +public class ConfigPrincipal implements Principal, GetCred { + private String name; + private byte[] cred; + private String content; + + public ConfigPrincipal(String name, String passwd) { + this.name = name; + this.cred = passwd.getBytes(); + content = null; + } + + public ConfigPrincipal(String name, byte[] cred) { + this.name = name; + this.cred = cred; + content = null; + } + + public String getName() { + return name; + } + + public byte[] getCred() { + return cred; + } + + public String toString() { + return name; + } + + public String getAsBasicAuthHeader() throws IOException { + if(content ==null) { + String s = name + ':' + new String(cred); + content = "Basic " + Symm.base64.encode(s); + } else if(!content.startsWith("Basic ")) { // content is the saved password from construction + String s = name + ':' + content; + content = "Basic " + Symm.base64.encode(s); + } + return content; + } +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/lur/EpiLur.java b/cadi/core/src/main/java/org/onap/aaf/cadi/lur/EpiLur.java new file mode 100644 index 00000000..2813dca8 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/lur/EpiLur.java @@ -0,0 +1,169 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.lur; + +import java.security.Principal; +import java.util.List; + +import org.onap.aaf.cadi.CachingLur; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.CredVal; +import org.onap.aaf.cadi.Lur; +import org.onap.aaf.cadi.Permission; + +/** + * EpiLUR + * + * Short for "Epic LUR". Be able to run through a series of LURs to obtain the validation needed. + * + * The pun is better for the other pattern... "TAF" (aka EpiTaf), but it's still the larger picture of + * LURs that will be accomplished. + * + * FYI, the reason we separate LURs, rather than combine, is that Various User Repository Resources have + * different Caching requirements. For instance, the Local User Repo (with stand alone names), never expire, but might be + * refreshed with a change in Configuration File, while the Remote Service based LURs will need to expire at prescribed intervals + * + * @author Jonathan + * + */ +public final class EpiLur implements Lur { + private final Lur[] lurs; + + /** + * EpiLur constructor + * + * Construct the EpiLur from variable TAF parameters + * @param lurs + * @throws CadiException + */ + public EpiLur(Lur ... lurs) throws CadiException{ + this.lurs = lurs; + if(lurs.length==0) throw new CadiException("Need at least one Lur implementation in constructor"); + } + + public boolean fish(Principal bait, Permission pond) { + if(pond==null) { + return false; + } + boolean rv = false; + Lur lur; + for(int i=0;!rv && i<lurs.length;++i) { + rv = (lur = lurs[i]).fish(bait, pond); + if(!rv && lur.handlesExclusively(pond)) break; + } + return rv; + } + + public void fishAll(Principal bait, List<Permission> permissions) { + for(Lur lur : lurs) { + lur.fishAll(bait, permissions); + } + } + + public void destroy() { + for(Lur lur : lurs) { + lur.destroy(); + } + } + + /** + * Return the first Lur (if any) which also implements UserPass + * @return + */ + public CredVal getUserPassImpl() { + for(Lur lur : lurs) { + if(lur instanceof CredVal) { + return (CredVal)lur; + } + } + return null; + } + + // Never needed... Only EpiLur uses... + public boolean handlesExclusively(Permission pond) { + return false; + } + + /** + * Get Lur for index. Returns null if out of range + * @param idx + * @return + */ + public Lur get(int idx) { + if(idx>=0 && idx<lurs.length) { + return lurs[idx]; + } + return null; + } + + public boolean handles(Principal p) { + for(Lur l : lurs) { + if(l.handles(p)) { + return true; + } + } + return false; + } + + public void remove(String id) { + for(Lur l : lurs) { + if(l instanceof CachingLur) { + ((CachingLur<?>)l).remove(id); + } + } + } + + public Lur subLur(Class<? extends Lur> cls ) { + for(Lur l : lurs) { + if(l.getClass().isAssignableFrom(cls)) { + return l; + } + } + return null; + } + + @Override + public Permission createPerm(String p) { + return new LocalPermission(p); + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.Lur#clear(java.security.Principal, java.lang.StringBuilder) + */ + @Override + public void clear(Principal p, StringBuilder report) { + for(Lur lur : lurs) { + lur.clear(p, report); + } + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + for(Lur lur : lurs) { + sb.append(lur.getClass().getSimpleName()); + sb.append(": Report\n"); + sb.append(lur.toString()); + sb.append('\n'); + } + return sb.toString(); + } +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/lur/LocalLur.java b/cadi/core/src/main/java/org/onap/aaf/cadi/lur/LocalLur.java new file mode 100644 index 00000000..0f9adb94 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/lur/LocalLur.java @@ -0,0 +1,212 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.lur; + +import java.io.IOException; +import java.security.Principal; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import org.onap.aaf.cadi.AbsUserCache; +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.CredVal; +import org.onap.aaf.cadi.Hash; +import org.onap.aaf.cadi.Lur; +import org.onap.aaf.cadi.Permission; +import org.onap.aaf.cadi.User; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.config.Config; + +/** + * An in-memory Lur that can be configured locally with User info via properties, similar to Tomcat-users.xml mechanisms. + * + * @author Jonathan + * + */ +public final class LocalLur extends AbsUserCache<LocalPermission> implements Lur, CredVal { + public static final String SEMI = "\\s*;\\s*"; + public static final String COLON = "\\s*:\\s*"; + public static final String COMMA = "\\s*,\\s*"; + public static final String PERCENT = "\\s*%\\s*"; + + // Use to quickly determine whether any given group is supported by this LUR + private final Set<String> supportingGroups; + private String supportedRealm; + + /** + * Construct by building structure, see "build" + * + * Reconstruct with "build" + * + * @param userProperties + * @param groupProperties + * @param decryptor + * @throws IOException + */ + public LocalLur(Access access, String userProperties, String groupProperties) throws IOException { + super(access, 0, 0, Integer.MAX_VALUE); // data doesn't expire + supportedRealm = access.getProperty(Config.BASIC_REALM, "localized"); + supportingGroups = new TreeSet<>(); + + if (userProperties != null) { + parseUserProperties(userProperties); + } + + if (groupProperties != null) { + parseGroupProperties(groupProperties); + } + } + + public boolean validate(String user, CredVal.Type type, byte[] cred, Object state) { + if (cred == null) { + return false; + } + User<LocalPermission> usr = getUser(user, cred); + if (usr == null) { + return false; + } + // covers null as well as bad pass + if ((type == Type.PASSWORD) && (usr.principal instanceof ConfigPrincipal)) {; + return Hash.isEqual(cred, ((ConfigPrincipal)usr.principal).getCred()); + } + return false; + } + + // @Override + public boolean fish(Principal bait, Permission pond) { + if (pond == null) { + return false; + } + if (handles(bait) && pond instanceof LocalPermission) { // local Users only have LocalPermissions + User<LocalPermission> user = getUser(bait); + if (user != null) { + return user.contains((LocalPermission)pond); + } + } + return false; + } + + // We do not want to expose the actual Group, so make a copy. + public void fishAll(Principal bait, List<Permission> perms) { + if (handles(bait)) { + User<LocalPermission> user = getUser(bait); + if (user != null) { + user.copyPermsTo(perms); + } + } + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.Lur#handles(java.security.Principal) + */ + @Override + public boolean handles(Principal principal) { + if (principal == null) { + return false; + } + return principal.getName().endsWith(supportedRealm); + } + + public boolean handlesExclusively(Permission pond) { + return supportingGroups.contains(pond.getKey()); + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.Lur#createPerm(java.lang.String) + */ + @Override + public Permission createPerm(String p) { + return new LocalPermission(p); + } + + private void parseUserProperties(String userProperties) throws IOException { + // For each User name... + for (String userProperty : userProperties.trim().split(SEMI)) { + String[] userInfo = userProperty.split(COLON, 2); + String[] userPass = userInfo[0].split(PERCENT, 2); + String userName = userPass[0]; + + byte[] password = null; + if (userPass.length > 1) { + password = access.decrypt(userPass[1], true).getBytes(); + if (userName.indexOf('@') < 0) { + userName += '@' + access.getProperty(Config.AAF_DEFAULT_REALM, Config.getDefaultRealm()); + } + } + User<LocalPermission> usr; + usr = new User<>(new ConfigPrincipal(userName, password)); + addUser(usr); + access.log(Level.INIT, "Local User:", usr.principal); + + if (userInfo.length > 1) { + Map<String, Permission> newMap = usr.newMap(); + for (String group : userInfo[1].split(COMMA)) { + supportingGroups.add(group); + usr.add(newMap, new LocalPermission(group)); + } + usr.setMap(newMap); + } + } + } + + + private void parseGroupProperties(String groupProperties) throws IOException { + // For each Group name... + for (String group : groupProperties.trim().split(SEMI)) { + String[] groups = group.split(COLON, 2); + if (groups.length <= 1) { + continue; + } + supportingGroups.add(groups[0]); + LocalPermission p = new LocalPermission(groups[0]); + + // Add all users (known by comma separators) + for (String groupMember : groups[1].split(COMMA)) { + // look for password, if so, put in passMap + String[] userPass = groupMember.split(PERCENT, 2); + String userName = userPass[0]; + if (userName.indexOf('@') < 0) { + userName += '@' + access.getProperty(Config.AAF_DEFAULT_REALM, Config.getDefaultRealm()); + } + + User<LocalPermission> usr = null; + byte[] password = null; + if (userPass.length > 1) { + password = access.decrypt(userPass[1], true).getBytes(); + } + usr = getUser(userName, password); + if (usr == null) { + usr = new User<>(new ConfigPrincipal(userName, password)); + addUser(usr); + } + else { + usr.principal = new ConfigPrincipal(userName, password); + } + usr.add(p); + access.log(Level.INIT, "Local User:", usr.principal); + } + } + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/lur/LocalPermission.java b/cadi/core/src/main/java/org/onap/aaf/cadi/lur/LocalPermission.java new file mode 100644 index 00000000..8d6f9698 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/lur/LocalPermission.java @@ -0,0 +1,50 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.lur; + +import org.onap.aaf.cadi.Permission; + +public class LocalPermission implements Permission { + private String key; + + public LocalPermission(String role) { + this.key = role; + } + + public String getKey() { + return key; + } + + public String toString() { + return key; + } + + public boolean match(Permission p) { + return key.equals(p.getKey()); + } + + public String permType() { + return "LOCAL"; + } + + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/lur/NullLur.java b/cadi/core/src/main/java/org/onap/aaf/cadi/lur/NullLur.java new file mode 100644 index 00000000..1e44726a --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/lur/NullLur.java @@ -0,0 +1,87 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.lur; + +import java.security.Principal; +import java.util.List; + +import org.onap.aaf.cadi.Lur; +import org.onap.aaf.cadi.Permission; + +public class NullLur implements Lur { + private static final Permission NULL = new Permission() { + @Override + public String permType() { + return ""; + } + + @Override + public String getKey() { + return ""; + } + + @Override + public boolean match(Permission p) { + return false; + }}; + + public boolean fish(Principal bait, Permission pond) { + // Well, for Jenkins, this is ok... It finds out it can't do J2EE Security, and then looks at it's own +// System.err.println("CADI's LUR has not been configured, but is still being called. Access is being denied"); + return false; + } + + public void fishAll(Principal bait, List<Permission> permissions) { + } + + public void destroy() { + } + + public boolean handlesExclusively(Permission pond) { + return false; + } + + public boolean handles(Principal p) { + return false; + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.Lur#createPerm(java.lang.String) + */ + @Override + public Permission createPerm(String p) { + return NULL; + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.Lur#clear(java.security.Principal, java.lang.StringBuilder) + */ + @Override + public void clear(Principal p, StringBuilder report) { + report.append(NullLur.class.getSimpleName()); + report.append('\n'); + } + + public String toString() { + return NullLur.class.getSimpleName() + '\n'; + } +}
\ No newline at end of file diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/principal/BasicPrincipal.java b/cadi/core/src/main/java/org/onap/aaf/cadi/principal/BasicPrincipal.java new file mode 100644 index 00000000..22ba702c --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/principal/BasicPrincipal.java @@ -0,0 +1,126 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.principal; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Date; + +import org.onap.aaf.cadi.BasicCred; +import org.onap.aaf.cadi.GetCred; +import org.onap.aaf.cadi.Symm; + +public class BasicPrincipal extends BearerPrincipal implements GetCred { + private static byte[] basic = "Basic ".getBytes(); + + private String name = null; + private String shortName = null; + private byte[] cred = null; + + private long created; + + public BasicPrincipal(String content,String domain) throws IOException { + created = System.currentTimeMillis(); + ByteArrayInputStream bis = new ByteArrayInputStream(content.getBytes()); + // Read past "Basic ", ensuring it starts with it. + for(int i=0;i<basic.length;++i) { + if(bis.read()!=basic[i]) { + name=content; + cred = null; + return; + } + } + BasicOS bos = new BasicOS(content.length()); + Symm.base64.decode(bis,bos); // note: writes directly to name until ':' + if(name==null) throw new IOException("Invalid Coding"); + else cred = bos.toCred(); + int at; + if((at=name.indexOf('@'))>0) { + domain=name.substring(at+1); + shortName=name.substring(0, at); + } else { + shortName = name; + name = name + '@' + domain; + } + } + + public BasicPrincipal(BasicCred bc, String domain) { + name = bc.getUser(); + cred = bc.getCred(); + } + + private class BasicOS extends OutputStream { + private boolean first = true; + private ByteArrayOutputStream baos; + + public BasicOS(int size) { + baos = new ByteArrayOutputStream(size); + } + + @Override + public void write(int b) throws IOException { + if(b==':' && first) { + first = false; + name = new String(baos.toByteArray()); + baos.reset(); // + } else { + baos.write(b); + } + } + + private byte[] toCred() { + return baos.toByteArray(); + } + } + + public String getName() { + return name; + } + + public String getShortName() { + return shortName; + } + + public byte[] getCred() { + return cred; + } + + public long created() { + return created; + } + + public String toString() { + return "Basic Authorization for " + name + " evaluated on " + new Date(created).toString(); + } + + @Override + public String tag() { + return "BAth"; + } + + @Override + public String personalName() { + return name; // personalName not available with Basic Auth + } +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/principal/BearerPrincipal.java b/cadi/core/src/main/java/org/onap/aaf/cadi/principal/BearerPrincipal.java new file mode 100644 index 00000000..ea0ff2fe --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/principal/BearerPrincipal.java @@ -0,0 +1,33 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.principal; + +public abstract class BearerPrincipal extends TaggedPrincipal { + private String bearer = null; + public BearerPrincipal setBearer(String bearer) { + this.bearer = bearer; + return this; + } + public String getBearer() { + return bearer; + } +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/principal/CachedBasicPrincipal.java b/cadi/core/src/main/java/org/onap/aaf/cadi/principal/CachedBasicPrincipal.java new file mode 100644 index 00000000..68229d3d --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/principal/CachedBasicPrincipal.java @@ -0,0 +1,65 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.principal; + +import java.io.IOException; + +import org.onap.aaf.cadi.BasicCred; +import org.onap.aaf.cadi.CachedPrincipal; +import org.onap.aaf.cadi.taf.HttpTaf; + +/** + * Cached Principals need to be able to revalidate in the Background + * + * @author Jonathan + * + */ +public class CachedBasicPrincipal extends BasicPrincipal implements CachedPrincipal { + private final HttpTaf creator; + private long timeToLive; + private long expires; + + public CachedBasicPrincipal(HttpTaf creator, BasicCred bc, String domain, long timeToLive) { + super(bc, domain); + this.creator = creator; + this.timeToLive = timeToLive; + expires = System.currentTimeMillis()+timeToLive; + } + + public CachedBasicPrincipal(HttpTaf creator, String content, String domain, long timeToLive) throws IOException { + super(content, domain); + this.creator = creator; + this.timeToLive = timeToLive; + expires = System.currentTimeMillis()+timeToLive; + } + + public CachedPrincipal.Resp revalidate(Object state) { + Resp resp = creator.revalidate(this, state); + if(resp.equals(Resp.REVALIDATED))expires = System.currentTimeMillis()+timeToLive; + return resp; + } + + public long expires() { + return expires; + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/principal/Kind.java b/cadi/core/src/main/java/org/onap/aaf/cadi/principal/Kind.java new file mode 100644 index 00000000..bb6dc673 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/principal/Kind.java @@ -0,0 +1,53 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.principal; + +import java.security.Principal; + +public class Kind { + public static final char X509 = 'X'; + public static final char OAUTH = 'O'; + public static final char AAF_OAUTH='A'; + public static final char BASIC_AUTH = 'B'; + public static final char UNKNOWN = 'U'; + + + public static char getKind(final Principal principal) { + Principal check; + if(principal instanceof TrustPrincipal) { + check = ((TrustPrincipal)principal).original(); + } else { + check = principal; + } + if(check instanceof X509Principal) { + return X509; + } + if(check instanceof OAuth2FormPrincipal) { + // Note: if AAF, will turn into 'A' + return OAUTH; + } + if(check instanceof BasicPrincipal) { + return BASIC_AUTH; + } + return UNKNOWN; + } +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/principal/OAuth2FormPrincipal.java b/cadi/core/src/main/java/org/onap/aaf/cadi/principal/OAuth2FormPrincipal.java new file mode 100644 index 00000000..1df2bd3e --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/principal/OAuth2FormPrincipal.java @@ -0,0 +1,61 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.principal; + +public class OAuth2FormPrincipal extends TaggedPrincipal { + private final String username; + private final String client_id; + + /* + * Note: client_id and username might be the same, if only authenticating the Client_ID + */ + public OAuth2FormPrincipal(final String client_id, final String username) { + this.username = username; + this.client_id = client_id; + } + + @Override + public String getName() { + return username; + } + + public String client_id() { + return client_id; + } + + @Override + public String tag() { + return "OAuth"; + } + + @Override + public String personalName() { + if(username!=null && username!=client_id) { + StringBuilder sb = new StringBuilder(); + sb.append(username); + sb.append('|'); + sb.append(client_id); + return sb.toString(); + } + return client_id; + } +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/principal/StringTagLookup.java b/cadi/core/src/main/java/org/onap/aaf/cadi/principal/StringTagLookup.java new file mode 100644 index 00000000..a3927168 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/principal/StringTagLookup.java @@ -0,0 +1,35 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ +package org.onap.aaf.cadi.principal; + +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.principal.TaggedPrincipal.TagLookup; + +public class StringTagLookup implements TagLookup { + private String tag; + public StringTagLookup(final String tag) { + this.tag = tag; + } + @Override + public String lookup() throws CadiException { + return tag; + } +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/principal/TaggedPrincipal.java b/cadi/core/src/main/java/org/onap/aaf/cadi/principal/TaggedPrincipal.java new file mode 100644 index 00000000..a3b07c6c --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/principal/TaggedPrincipal.java @@ -0,0 +1,60 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ +package org.onap.aaf.cadi.principal; + +import java.security.Principal; + +import org.onap.aaf.cadi.CadiException; + +public abstract class TaggedPrincipal implements Principal { + + public TaggedPrincipal() { + tagLookup = null; + } + + public TaggedPrincipal(final TagLookup tl) { + tagLookup = tl; + } + + public abstract String tag(); // String representing what kind of Authentication occurred. + + public interface TagLookup { + public String lookup() throws CadiException; + } + + private TagLookup tagLookup; + + public void setTagLookup(TagLookup tl) { + tagLookup = tl; + } + + public String personalName() { + if(tagLookup == null) { + return getName(); + } + try { + return tagLookup.lookup(); + } catch (CadiException e) { + return getName(); + } + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/principal/TrustPrincipal.java b/cadi/core/src/main/java/org/onap/aaf/cadi/principal/TrustPrincipal.java new file mode 100644 index 00000000..09083316 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/principal/TrustPrincipal.java @@ -0,0 +1,70 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.principal; + +import java.security.Principal; + +import org.onap.aaf.cadi.UserChain; + +public class TrustPrincipal extends BearerPrincipal implements UserChain { + private final String name; + private final Principal original; + private String userChain; + + public TrustPrincipal(final Principal actual, final String asName) { + this.original = actual; + name = asName.trim(); + if(actual instanceof UserChain) { + UserChain uc = (UserChain)actual; + userChain = uc.userChain(); + } else if(actual instanceof TaggedPrincipal) { + userChain=((TaggedPrincipal)actual).tag(); + } else { + userChain = actual.getClass().getSimpleName(); + } + } + + @Override + public String getName() { + return name; + } + + @Override + public String userChain() { + return userChain; + } + + public Principal original() { + return original; + } + + @Override + public String tag() { + return userChain; + } + + @Override + public String personalName() { + return original.getName() + '[' + userChain + ']'; + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/principal/UnAuthPrincipal.java b/cadi/core/src/main/java/org/onap/aaf/cadi/principal/UnAuthPrincipal.java new file mode 100644 index 00000000..52f78e80 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/principal/UnAuthPrincipal.java @@ -0,0 +1,37 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.principal; + +import java.security.Principal; + +public class UnAuthPrincipal implements Principal { + private String name; + + public UnAuthPrincipal(final String name) { + this.name = name; + } + @Override + public String getName() { + return name; + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/principal/X509Principal.java b/cadi/core/src/main/java/org/onap/aaf/cadi/principal/X509Principal.java new file mode 100644 index 00000000..16f62171 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/principal/X509Principal.java @@ -0,0 +1,109 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.principal; + +import java.io.IOException; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.util.regex.Pattern; + +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.GetCred; + +public class X509Principal extends BearerPrincipal implements GetCred { + private static final Pattern pattern = Pattern.compile("[a-zA-Z0-9]*\\@[a-zA-Z0-9.]*"); + private final X509Certificate cert; + private final String name; + private TagLookup tagLookup; + private byte[] content; + + public X509Principal(String identity, X509Certificate cert) { + name = identity; + content = null; + this.cert = cert; + tagLookup = null; + } + + public X509Principal(String identity, X509Certificate cert, byte[] content) { + name = identity; + this.content = content; + this.cert = cert; + tagLookup = null; + } + + public X509Principal(X509Certificate cert, byte[] content) throws IOException { + this.content=content; + this.cert = cert; + String _name = null; + String subj = cert.getSubjectDN().getName(); + int cn = subj.indexOf("OU="); + if(cn>=0) { + cn+=3; + int space = subj.indexOf(',',cn); + if(space>=0) { + String id = subj.substring(cn, space); + if(pattern.matcher(id).matches()) { + _name = id; + } + } + } + if(_name==null) { + throw new IOException("X509 does not have Identity as CN"); + } + name = _name; + tagLookup = null; + } + + public String getAsHeader() throws IOException { + try { + if(content==null) { + content=cert.getEncoded(); + } + } catch (CertificateEncodingException e) { + throw new IOException(e); + } + return "X509 " + content; + } + + public String toString() { + return "X509 Authentication for " + name; + } + + + public byte[] getCred() { + try { + return content==null?(content=cert.getEncoded()):content; + } catch (CertificateEncodingException e) { + return null; + } + } + + public String getName() { + return name; + } + + @Override + public String tag() { + return "x509"; + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/taf/AbsTafResp.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/AbsTafResp.java new file mode 100644 index 00000000..c216fb57 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/AbsTafResp.java @@ -0,0 +1,116 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.taf; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.principal.TaggedPrincipal; + +/** + * AbsTafResp + * + * Base class for TafResp (TAF Response Objects) + * + * @author Jonathan + * + */ +public abstract class AbsTafResp implements TafResp { + + protected final String desc; + protected final TaggedPrincipal principal; + protected final Access access; + + /** + * AbsTafResp + * + * Set and hold + * Description (for logging) + * Principal (as created by derived class) + * Access (for access to underlying container, i.e. for Logging, auditing, ClassLoaders, etc) + * + * @param access + * @param principal + * @param description + */ + public AbsTafResp(Access access, TaggedPrincipal principal, String description) { + this.access = access; + this.principal = principal; + this.desc = description; + } + + /** + * isValid() + * + * Respond in the affirmative if the TAF was able to Authenticate + */ + public boolean isValid() { + return principal != null; + } + + /** + * desc() + * + * Respond with description of response as given by the TAF + */ + public String desc() { + return desc; + } + + /** + * isAuthenticated() + * + * Respond with the TAF's code of whether Authenticated, or suggested next steps + * default is either IS_AUTHENTICATED, or TRY_ANOTHER_TAF. The TAF can overload + * and suggest others, such as "NO_FURTHER_PROCESSING", if it can detect that this + * is some sort of security breach (i.e. Denial of Service) + */ + public RESP isAuthenticated() { + return principal==null?RESP.TRY_ANOTHER_TAF:RESP.IS_AUTHENTICATED; + } + + /** + * getPrincipal() + * + * Return the principal created by the TAF based on Authentication. + * + * Returns "null" if Authentication failed (no principal) + */ + public TaggedPrincipal getPrincipal() { + return principal; + } + + /** + * getAccess() + * + * Get the Access object from the TAF, so that appropriate Logging, etc can be coordinated. + */ + public Access getAccess() { + return access; + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.taf.TafResp#isFailedAttempt() + */ + public boolean isFailedAttempt() { + return false; + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/taf/EpiTaf.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/EpiTaf.java new file mode 100644 index 00000000..d772d493 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/EpiTaf.java @@ -0,0 +1,84 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.taf; + +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.Taf; + +/** + * EpiTAF + * + * Short for "Epic TAF". Be able to run through a series of TAFs to obtain the validation needed. + * + * OK, the name could probably be better as "Tafs", like it was originally, but the pun was too + * irresistible for this author to pass up. + * + * @author Jonathan + * + */ +public class EpiTaf implements Taf { + private Taf[] tafs; + + /** + * EpiTaf constructor + * + * Construct the EpiTaf from variable TAF parameters + * @param tafs + * @throws CadiException + */ + public EpiTaf(Taf ... tafs) throws CadiException{ + this.tafs = tafs; + if(tafs.length==0) throw new CadiException("Need at least one Taf implementation in constructor"); + } + + /** + * validate + * + * Respond with the first TAF to authenticate user based on variable info and "LifeForm" (is it + * a human behind an interface, or a server behind a protocol). + * + * If there is no TAF that can authenticate, respond with the first TAF that suggests it can + * establish an Authentication conversation (TRY_AUTHENTICATING). + * + * If no TAF declares either, respond with NullTafResp (which denies all questions) + */ + public TafResp validate(LifeForm reading, String... info) { + TafResp tresp,firstTryAuth=null; + for(Taf taf : tafs) { + tresp = taf.validate(reading, info); + switch(tresp.isAuthenticated()) { + case TRY_ANOTHER_TAF: + break; + case TRY_AUTHENTICATING: + if(firstTryAuth==null)firstTryAuth=tresp; + break; + default: + return tresp; + } + } + + // No TAFs configured, at this point. It is safer at this point to be "not validated", + // rather than "let it go" + return firstTryAuth == null?NullTafResp.singleton():firstTryAuth; + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/taf/HttpEpiTaf.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/HttpEpiTaf.java new file mode 100644 index 00000000..5cd6323d --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/HttpEpiTaf.java @@ -0,0 +1,207 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.taf; + +import java.net.URI; +import java.security.Principal; +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.CachedPrincipal; +import org.onap.aaf.cadi.CachedPrincipal.Resp; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.Locator; +import org.onap.aaf.cadi.Taf.LifeForm; +import org.onap.aaf.cadi.TrustChecker; + +/** + * HttpEpiTaf + * + * An extension of the basic "EpiTAF" concept, check known HTTP Related TAFs for valid credentials + * + * @author Jonathan + * + */ +public class HttpEpiTaf implements HttpTaf { + private HttpTaf[] tafs; + private Access access; + private Locator<URI> locator; + private TrustChecker trustChecker; + + /** + * HttpEpiTaf constructor + * + * Construct the HttpEpiTaf from variable Http specific TAF parameters + + * @param tafs + * @throws CadiException + */ + public HttpEpiTaf(Access access, Locator<URI> locator, TrustChecker tc, HttpTaf ... tafs) throws CadiException{ + this.tafs = tafs; + this.access = access; + this.locator = locator; + this.trustChecker = tc; + // Establish what Header Property to look for UserChain/Trust Props + + if (tafs.length == 0) { + throw new CadiException("Need at least one HttpTaf implementation in constructor"); + } + } + + /** + * validate + * + * Respond with the first Http specific TAF to authenticate user based on variable info + * and "LifeForm" (is it a human behind a browser, or a server utilizing HTTP Protocol). + * + * If there is no HttpTAF that can authenticate, respond with the first TAF that suggests it can + * establish an Authentication conversation (TRY_AUTHENTICATING) (Examples include a redirect to CSP + * Servers for CSP Cookie, or BasicAuth 401 response, suggesting User/Password for given Realm + * submission + * + * If no TAF declares either, respond with NullTafResp (which denies all questions) + */ + public TafResp validate(LifeForm reading, HttpServletRequest req, HttpServletResponse resp) { + // Given a LifeForm Neutral, for HTTP, we need to discover true Life-Form Readings + if (reading == LifeForm.LFN) { + reading = tricorderScan(req); + } + TafResp tresp = null; + TafResp firstTry = null; + List<Redirectable> redirectables = null; + List<TafResp> log = (access.willLog(Level.DEBUG)) ? new ArrayList<TafResp>() : null; + try { + for (HttpTaf taf : tafs) { + tresp = taf.validate(reading, req, resp); + addToLog(log, tresp); + switch(tresp.isAuthenticated()) { + case TRY_ANOTHER_TAF: + break; // and loop + case TRY_AUTHENTICATING: + if (tresp instanceof Redirectable) { + if (redirectables == null) { + redirectables = new ArrayList<>(); + } + redirectables.add((Redirectable)tresp); + } else if (firstTry == null) { + firstTry = tresp; + } + break; + case IS_AUTHENTICATED: + tresp = trustChecker.mayTrust(tresp, req); + return tresp; + default: + return tresp; + } + } + } finally { + printLog(log); + } + + // If No TAFs configured, at this point. It is safer at this point to be "not validated", + // rather than "let it go" + // Note: if exists, there will always be more than 0 entries, according to above code + if (redirectables == null) { + return (firstTry != null) ? firstTry : NullTafResp.singleton(); + } + + // If there is one Tryable entry then return it + if (redirectables.size() > 1) { + return LoginPageTafResp.create(access, locator, resp, redirectables); + } else { + return redirectables.get(0); + } + } + + public boolean revalidate(Principal prin) throws Exception { + return false; + } + + /* + * Since this is internal, we use a little Star Trek humor to indicate looking in the HTTP Request to see if we can determine what kind + * of "LifeForm" reading we can determine, i.e. is there a Human (CarbonBasedLifeForm) behind a browser, or is it mechanical + * id (SiliconBasedLifeForm)? This makes a difference in some Authentication, i.e CSP, which doesn't work well for SBLFs + */ + private LifeForm tricorderScan(HttpServletRequest req) { + // For simplicity's sake, we'll say Humans use FQDNs, not IPs. + + // Current guess that only Browsers bother to set "Agent" codes that identify the kind of browser they are. + // If mechanical frameworks are found that populate this, then more advanced analysis may be required + // Jonathan 1/22/2013 + String agent = req.getHeader("User-Agent"); + if (agent != null && agent.startsWith("Mozilla")) { // covers I.E./Firefox/Safari/probably any other "advanced" Browser see http://en.wikipedia.org/wiki/User_agent + return LifeForm.CBLF; + } + return LifeForm.SBLF; // notably skips "curl","wget", (which is desired behavior. We don't want to try CSP, etc on these) + } + + public Resp revalidate(CachedPrincipal prin, Object state) { + Resp resp; + for (HttpTaf taf : tafs) { + resp = taf.revalidate(prin, state); + if (resp != Resp.NOT_MINE) { + return resp; + } +// switch(resp) { +// case NOT_MINE: +// break; +// default: +// return resp; +// } + } + return Resp.NOT_MINE; + } + + private void addToLog(List<TafResp> log, TafResp tresp) { + if (log == null) { + return; + } + log.add(tresp); + } + + private void printLog(List<TafResp> log) { + if (log == null) { + return; + } + for (TafResp tresp : log) { + access.log(Level.DEBUG, tresp.desc()); + } + } + + /** + * List HttpTafs with their "toString" representations... primarily useful for Debugging in an IDE + * like Eclipse. + */ + public String toString() { + StringBuilder sb = new StringBuilder(); + for (HttpTaf ht : tafs) { + sb.append(ht.toString()); + sb.append(". "); + } + return sb.toString(); + } +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/taf/HttpTaf.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/HttpTaf.java new file mode 100644 index 00000000..9484458c --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/HttpTaf.java @@ -0,0 +1,60 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.taf; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.onap.aaf.cadi.CachedPrincipal; +import org.onap.aaf.cadi.Taf.LifeForm; + +/** + * A TAF which is in a specific HTTP environment in which the engine implements + * javax Servlet. + * + * Using the Http Request and Response interfaces takes the effort out of implementing in almost any kind of + * HTTP Container or Engine. + * + * @author Jonathan + * + */ +public interface HttpTaf { + /** + * validate + * + * Validate the Request, and respond with created TafResp object. + * + * @param reading + * @param req + * @param resp + * @return + */ + public TafResp validate(LifeForm reading, HttpServletRequest req, HttpServletResponse resp); + + /** + * Re-Validate Credential + * + * @param prin + * @return + */ + public CachedPrincipal.Resp revalidate(CachedPrincipal prin,Object state); +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/taf/LoginPageTafResp.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/LoginPageTafResp.java new file mode 100644 index 00000000..3f80170e --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/LoginPageTafResp.java @@ -0,0 +1,94 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.taf; + +import java.io.IOException; +import java.net.URI; +import java.util.List; + +import javax.servlet.http.HttpServletResponse; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.Locator; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.Locator.Item; + +public class LoginPageTafResp extends AbsTafResp { + private final HttpServletResponse httpResp; + private final String loginPageURL; + + private LoginPageTafResp(Access access, final HttpServletResponse resp, String loginPageURL) { + super(access, null, "Multiple Possible HTTP Logins available. Redirecting to Login Choice Page"); + httpResp = resp; + this.loginPageURL = loginPageURL; + } + + @Override + public RESP authenticate() throws IOException { + httpResp.sendRedirect(loginPageURL); + return RESP.HTTP_REDIRECT_INVOKED; + } + + @Override + public RESP isAuthenticated() { + return RESP.TRY_AUTHENTICATING; + } + + public static TafResp create(Access access, Locator<URI> locator, final HttpServletResponse resp, List<Redirectable> redirectables) { + if (locator == null) { + if (!redirectables.isEmpty()) { + access.log(Level.DEBUG,"LoginPage Locator is not configured. Taking first Redirectable Taf"); + return redirectables.get(0); + } + return NullTafResp.singleton(); + } + + try { + Item item = locator.best(); + URI uri = locator.get(item); + if (uri == null) { + return NullTafResp.singleton(); + } + + StringBuilder sb = new StringBuilder(uri.toString()); + String query = uri.getQuery(); + boolean first = ((query == null) || (query.length() == 0)); + for (Redirectable redir : redirectables) { + if (first) { + sb.append('?'); + first = false; + } + else { + sb.append('&'); + } + sb.append(redir.get()); + } + if (!redirectables.isEmpty()) { + return new LoginPageTafResp(access, resp, sb.toString()); + } + } catch (Exception e) { + access.log(e, "Error deriving Login Page location"); + } + + return NullTafResp.singleton(); + } +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/taf/NullTaf.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/NullTaf.java new file mode 100644 index 00000000..e8293faa --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/NullTaf.java @@ -0,0 +1,64 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.taf; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.onap.aaf.cadi.CachedPrincipal; +import org.onap.aaf.cadi.Taf; +import org.onap.aaf.cadi.CachedPrincipal.Resp; + + +/** + * This TAF is set at the very beginning of Filters and Valves so that if any configuration issues hit while + * starting, the default behavior is to shut down traffic rather than leaving an open hole + * + * @author Jonathan + * + */ +public class NullTaf implements Taf, HttpTaf { + // Singleton Pattern + public NullTaf() {} + + /** + * validate + * + * Always Respond with a NullTafResp, which declares it is unauthenticated, and unauthorized + */ + public TafResp validate(LifeForm reading, String... info) { + return NullTafResp.singleton(); + } + + /** + * validate + * + * Always Respond with a NullTafResp, which declares it is unauthenticated, and unauthorized + */ + public TafResp validate(LifeForm reading, HttpServletRequest req, HttpServletResponse resp) { + return NullTafResp.singleton(); + } + + public Resp revalidate(CachedPrincipal prin, Object state) { + return Resp.NOT_MINE; + } +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/taf/NullTafResp.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/NullTafResp.java new file mode 100644 index 00000000..20fc944a --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/NullTafResp.java @@ -0,0 +1,73 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.taf; + +import java.io.IOException; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.principal.TaggedPrincipal; + +/** + * A Null Pattern for setting responses to "Deny" before configuration is setup. + * @author Jonathan + * + */ +class NullTafResp implements TafResp { + private NullTafResp(){} + + private static TafResp singleton = new NullTafResp(); + + public static TafResp singleton() { + return singleton; + } + + public boolean isValid() { + return false; + } + + public RESP isAuthenticated() { + return RESP.NO_FURTHER_PROCESSING; + } + + public String desc() { + return "All Authentication denied"; + } + + public RESP authenticate() throws IOException { + return RESP.NO_FURTHER_PROCESSING; + } + + public TaggedPrincipal getPrincipal() { + return null; + } + + public Access getAccess() { + return Access.NULL; + } + + /* (non-Javadoc) + * @see org.onap.aaf.cadi.taf.TafResp#isFailedAttempt() + */ + public boolean isFailedAttempt() { + return true; + } +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/taf/PuntTafResp.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/PuntTafResp.java new file mode 100644 index 00000000..f496581b --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/PuntTafResp.java @@ -0,0 +1,69 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.taf; + +import java.io.IOException; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.principal.TaggedPrincipal; + +/** + * A Punt Resp to make it fast and easy for a Taf to respond that it cannot handle a particular kind of + * request. It is always the same object, so there is no cost for memory, etc. + * @author Jonathan + * + */ +public class PuntTafResp implements TafResp { + private final String desc; + + public PuntTafResp(String name, String explanation) { + desc = name + " is not processing this transaction: " + explanation; + } + + public boolean isValid() { + return false; + } + + public RESP isAuthenticated() { + return RESP.TRY_ANOTHER_TAF; + } + + public String desc() { + return desc; + } + + public RESP authenticate() throws IOException { + return RESP.TRY_ANOTHER_TAF; + } + + public TaggedPrincipal getPrincipal() { + return null; + } + + public Access getAccess() { + return NullTafResp.singleton().getAccess(); + } + + public boolean isFailedAttempt() { + return false; + } +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/taf/Redirectable.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/Redirectable.java new file mode 100644 index 00000000..8dc5c118 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/Redirectable.java @@ -0,0 +1,31 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.taf; + +public interface Redirectable extends TafResp { + /** + * Create a Redirectable URL entry prefaced by a URLEncoder.String for a Menu + * example: + * "Global Login=https://xxxx....." + */ + public String get(); +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/taf/TafResp.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/TafResp.java new file mode 100644 index 00000000..a679d994 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/TafResp.java @@ -0,0 +1,94 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.taf; + +import java.io.IOException; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.principal.TaggedPrincipal; + +/** + * Response from Taf objects, which inform users what has happened and/or what should be done + * + * @author Jonathan + * + */ +public interface TafResp { + public static enum RESP { + IS_AUTHENTICATED, + NO_FURTHER_PROCESSING, + TRY_AUTHENTICATING, + TRY_ANOTHER_TAF, + FAIL, + // A note was made to avoid the response REDIRECT. However, I have deemed that it is + // unavoidable when the underlying TAF did do a REDIRECT, because it requires a HTTP + // Service code to exit without modifying the Response any further. + // Therefore, I have changed this to indicate what HAS happened, with should accommodate + // both positions. Jonathan 10/18/2012 +// public static final int HTTP_REDIRECT_INVOKED = 11; + HTTP_REDIRECT_INVOKED, + HAS_PROCESSED}; + + /** + * Basic success check + * @return + */ + public boolean isValid(); + + /** + * String description of what has occurred (for logging/exceptions) + * @return + */ + public String desc(); + + /** + * Check Response + * @return + */ + public RESP isAuthenticated(); + + /** + * Authenticate, returning FAIL or Other Valid indication + * + * HTTP implementations should watch for "HTTP_REDIRECT_INVOKED", and end the HTTP call appropriately. + * @return + * @throws CadiException + */ + public RESP authenticate() throws IOException; + + /** + * Once authenticated, this object should hold a Principal created from the authorization + * @return + */ + public TaggedPrincipal getPrincipal(); + + /** + * get the Access object which created this object, allowing the responder to appropriate Log, etc + */ + public Access getAccess(); + + /** + * Be able to check if part of a Failed attempt + */ + public boolean isFailedAttempt(); +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/taf/TrustNotTafResp.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/TrustNotTafResp.java new file mode 100644 index 00000000..24a79cf3 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/TrustNotTafResp.java @@ -0,0 +1,76 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.taf; + +import java.io.IOException; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.principal.TaggedPrincipal; + +public class TrustNotTafResp implements TafResp { + private final TafResp delegate; + private final String desc; + + public TrustNotTafResp(final TafResp delegate, final String desc) { + this.delegate = delegate; + this.desc = desc; + } + + @Override + public boolean isValid() { + return false; + } + + @Override + public String desc() { + return desc; + } + + @Override + public RESP isAuthenticated() { + return RESP.NO_FURTHER_PROCESSING; + } + + @Override + public RESP authenticate() throws IOException { + return RESP.NO_FURTHER_PROCESSING; + } + + @Override + public TaggedPrincipal getPrincipal() { + return delegate.getPrincipal(); + } + + @Override + public Access getAccess() { + return delegate.getAccess(); + } + + @Override + public boolean isFailedAttempt() { + return true; + } + + public String toString() { + return desc(); + } +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/taf/TrustTafResp.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/TrustTafResp.java new file mode 100644 index 00000000..bc5e8db6 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/TrustTafResp.java @@ -0,0 +1,78 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.taf; + +import java.io.IOException; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.principal.TaggedPrincipal; + +public class TrustTafResp implements TafResp { + private final TafResp delegate; + private final TaggedPrincipal principal; + private final String desc; + + public TrustTafResp(final TafResp delegate, final TaggedPrincipal principal, final String desc) { + this.delegate = delegate; + this.principal = principal; + this.desc = desc + ' ' + delegate.desc(); + } + + @Override + public boolean isValid() { + return delegate.isValid(); + } + + @Override + public String desc() { + return desc; + } + + @Override + public RESP isAuthenticated() { + return delegate.isAuthenticated(); + } + + @Override + public RESP authenticate() throws IOException { + return delegate.authenticate(); + } + + @Override + public TaggedPrincipal getPrincipal() { + return principal; + } + + @Override + public Access getAccess() { + return delegate.getAccess(); + } + + @Override + public boolean isFailedAttempt() { + return delegate.isFailedAttempt(); + } + + public String toString() { + return principal.getName() + " by trust of " + desc(); + } +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/taf/basic/BasicHttpTaf.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/basic/BasicHttpTaf.java new file mode 100644 index 00000000..6d516f00 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/basic/BasicHttpTaf.java @@ -0,0 +1,165 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.taf.basic; + +import java.io.IOException; +import java.security.Principal; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.BasicCred; +import org.onap.aaf.cadi.CachedPrincipal; +import org.onap.aaf.cadi.CredVal; +import org.onap.aaf.cadi.Taf; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.CachedPrincipal.Resp; +import org.onap.aaf.cadi.CredVal.Type; +import org.onap.aaf.cadi.principal.BasicPrincipal; +import org.onap.aaf.cadi.principal.CachedBasicPrincipal; +import org.onap.aaf.cadi.taf.HttpTaf; +import org.onap.aaf.cadi.taf.TafResp; +import org.onap.aaf.cadi.taf.TafResp.RESP; +import org.onap.aaf.cadi.taf.dos.DenialOfServiceTaf; + +/** + * BasicHttpTaf + * + * This TAF implements the "Basic Auth" protocol. + * + * WARNING! It is true for any implementation of "Basic Auth" that the password is passed unencrypted. + * This is because the expectation, when designed years ago, was that it would only be used in + * conjunction with SSL (https). It is common, however, for users to ignore this on the assumption that + * their internal network is secure, or just ignorance. Therefore, a WARNING will be printed + * when the HTTP Channel is not encrypted (unless explicitly turned off). + * + * @author Jonathan + * + */ +public class BasicHttpTaf implements HttpTaf { + private Access access; + private String realm; + private CredVal rbac; + private boolean warn; + private long timeToLive; + + public BasicHttpTaf(Access access, CredVal rbac, String realm, long timeToLive, boolean turnOnWarning) { + this.access = access; + this.realm = realm; + this.rbac = rbac; + this.warn = turnOnWarning; + this.timeToLive = timeToLive; + } + + /** + * Note: BasicHttp works for either Carbon Based (Humans) or Silicon Based (machine) Lifeforms. + * @see Taf + */ + public TafResp validate(Taf.LifeForm reading, HttpServletRequest req, HttpServletResponse resp) { + // See if Request implements BasicCred (aka CadiWrap or other), and if User/Pass has already been set separately + if(req instanceof BasicCred) { + BasicCred bc = (BasicCred)req; + if(bc.getUser()!=null) { // CadiWrap, if set, makes sure User & Password are both valid, or both null + if(DenialOfServiceTaf.isDeniedID(bc.getUser())!=null) { + return DenialOfServiceTaf.respDenyID(access,bc.getUser()); + } + CachedBasicPrincipal bp = new CachedBasicPrincipal(this,bc,realm,timeToLive); + // ONLY FOR Last Ditch DEBUGGING... + // access.log(Level.WARN,bp.getName() + ":" + new String(bp.getCred())); + + if(rbac.validate(bp.getName(),Type.PASSWORD,bp.getCred(),req)) { + return new BasicHttpTafResp(access,bp,bp.getName()+" authenticated by password",RESP.IS_AUTHENTICATED,resp,realm,false); + } else { + //TODO may need timed retries in a given time period + return new BasicHttpTafResp(access,null,buildMsg(bp,req,"user/pass combo invalid for ",bc.getUser(),"from",req.getRemoteAddr()), + RESP.TRY_AUTHENTICATING,resp,realm,true); + } + } + } + // Get User/Password from Authorization Header value + String authz = req.getHeader("Authorization"); + if(authz != null && authz.startsWith("Basic ")) { + if(warn&&!req.isSecure()) { + access.log(Level.WARN,"WARNING! BasicAuth has been used over an insecure channel"); + } + try { + CachedBasicPrincipal ba = new CachedBasicPrincipal(this,authz,realm,timeToLive); + if(DenialOfServiceTaf.isDeniedID(ba.getName())!=null) { + return DenialOfServiceTaf.respDenyID(access,ba.getName()); + } + + // ONLY FOR Last Ditch DEBUGGING... + // access.log(Level.WARN,ba.getName() + ":" + new String(ba.getCred())); + if(rbac.validate(ba.getName(), Type.PASSWORD, ba.getCred(), req)) { + return new BasicHttpTafResp(access,ba, ba.getName()+" authenticated by BasicAuth password",RESP.IS_AUTHENTICATED,resp,realm,false); + } else { + //TODO may need timed retries in a given time period + return new BasicHttpTafResp(access,null,buildMsg(ba,req,"user/pass combo invalid"), + RESP.TRY_AUTHENTICATING,resp,realm,true); + } + } catch (IOException e) { + String msg = buildMsg(null,req,"Failed HTTP Basic Authorization (", e.getMessage(), ')'); + access.log(Level.INFO,msg); + return new BasicHttpTafResp(access,null,msg, RESP.TRY_AUTHENTICATING, resp, realm,true); + } + } + return new BasicHttpTafResp(access,null,"Requesting HTTP Basic Authorization",RESP.TRY_AUTHENTICATING,resp,realm,false); + } + + protected String buildMsg(Principal pr, HttpServletRequest req, Object ... msg) { + StringBuilder sb = new StringBuilder(); + if(pr!=null) { + sb.append("user="); + sb.append(pr.getName()); + sb.append(','); + } + sb.append("ip="); + sb.append(req.getRemoteAddr()); + sb.append(",port="); + sb.append(req.getRemotePort()); + if(msg.length>0) { + sb.append(",msg=\""); + for(Object s : msg) { + sb.append(s.toString()); + } + sb.append('"'); + } + return sb.toString(); + } + + @Override + public Resp revalidate(CachedPrincipal prin, Object state) { + if(prin instanceof BasicPrincipal) { + BasicPrincipal ba = (BasicPrincipal)prin; + if(DenialOfServiceTaf.isDeniedID(ba.getName())!=null) { + return Resp.UNVALIDATED; + } + return rbac.validate(ba.getName(), Type.PASSWORD, ba.getCred(), state)?Resp.REVALIDATED:Resp.UNVALIDATED; + } + return Resp.NOT_MINE; + } + + public String toString() { + return "Basic Auth enabled on realm: " + realm; + } +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/taf/basic/BasicHttpTafResp.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/basic/BasicHttpTafResp.java new file mode 100644 index 00000000..c17797b8 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/basic/BasicHttpTafResp.java @@ -0,0 +1,62 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.taf.basic; + +import java.io.IOException; + +import javax.servlet.http.HttpServletResponse; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.principal.TaggedPrincipal; +import org.onap.aaf.cadi.taf.AbsTafResp; +import org.onap.aaf.cadi.taf.TafResp; + +public class BasicHttpTafResp extends AbsTafResp implements TafResp { + private HttpServletResponse httpResp; + private String realm; + private RESP status; + private final boolean wasFailed; + + public BasicHttpTafResp(Access access, TaggedPrincipal principal, String description, RESP status, HttpServletResponse resp, String realm, boolean wasFailed) { + super(access,principal, description); + httpResp = resp; + this.realm = realm; + this.status = status; + this.wasFailed = wasFailed; + } + + public RESP authenticate() throws IOException { + httpResp.setStatus(401); // Unauthorized + httpResp.setHeader("WWW-Authenticate", "Basic realm=\""+realm+'"'); + return RESP.HTTP_REDIRECT_INVOKED; + } + + public RESP isAuthenticated() { + return status; + } + + public boolean isFailedAttempt() { + return wasFailed; + } + + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/taf/cert/CertIdentity.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/cert/CertIdentity.java new file mode 100644 index 00000000..0da41b81 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/cert/CertIdentity.java @@ -0,0 +1,46 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.taf.cert; + +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import javax.servlet.http.HttpServletRequest; + +import org.onap.aaf.cadi.principal.TaggedPrincipal; + +public interface CertIdentity { + /** + * identity from X509Certificate Object and/or certBytes + * + * If you have both, include them. If you only have one, leave the other null, and it will be generated if needed + * + * The Request is there to obtain Header or Attribute info of ultimate user + * + * @param req + * @param cert + * @param certBytes + * @return + * @throws CertificateException + */ + public TaggedPrincipal identity(HttpServletRequest req, X509Certificate cert, byte[] certBytes) throws CertificateException; +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/taf/cert/X509HttpTafResp.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/cert/X509HttpTafResp.java new file mode 100644 index 00000000..b7f63b8e --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/cert/X509HttpTafResp.java @@ -0,0 +1,51 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.taf.cert; + +import java.io.IOException; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.principal.TaggedPrincipal; +import org.onap.aaf.cadi.taf.AbsTafResp; +import org.onap.aaf.cadi.taf.TafResp; + +public class X509HttpTafResp extends AbsTafResp implements TafResp { + private RESP status; + + public X509HttpTafResp(Access access, TaggedPrincipal principal, String description, RESP status) { + super(access, principal, description); + this.status = status; + } + + public RESP authenticate() throws IOException { + return RESP.TRY_ANOTHER_TAF; + } + + public RESP isAuthenticated() { + return status; + } + + public String toString() { + return status.name(); + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/taf/cert/X509Taf.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/cert/X509Taf.java new file mode 100644 index 00000000..4411a859 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/cert/X509Taf.java @@ -0,0 +1,262 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.taf.cert; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.Signature; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.ArrayList; + +import javax.net.ssl.TrustManagerFactory; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.CachedPrincipal; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.Lur; +import org.onap.aaf.cadi.Symm; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.CachedPrincipal.Resp; +import org.onap.aaf.cadi.Taf.LifeForm; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.config.SecurityInfo; +import org.onap.aaf.cadi.config.SecurityInfoC; +import org.onap.aaf.cadi.principal.TaggedPrincipal; +import org.onap.aaf.cadi.principal.X509Principal; +import org.onap.aaf.cadi.taf.HttpTaf; +import org.onap.aaf.cadi.taf.TafResp; +import org.onap.aaf.cadi.taf.TafResp.RESP; +import org.onap.aaf.cadi.util.Split; + +public class X509Taf implements HttpTaf { + + private static final String CERTIFICATE_NOT_VALID_FOR_AUTHENTICATION = "Certificate NOT valid for Authentication"; + public static final CertificateFactory certFactory; + public static final MessageDigest messageDigest; + public static final TrustManagerFactory tmf; + private Access access; + private CertIdentity[] certIdents; +// private Lur lur; + private ArrayList<String> cadiIssuers; + private String env; + private SecurityInfo si; + + static { + try { + certFactory = CertificateFactory.getInstance("X.509"); + messageDigest = MessageDigest.getInstance("SHA-256"); // use this to clone + tmf = TrustManagerFactory.getInstance(SecurityInfoC.SslKeyManagerFactoryAlgorithm); + } catch (Exception e) { + throw new RuntimeException("X.509 and SHA-256 are required for X509Taf",e); + } + } + + public X509Taf(Access access, Lur lur, CertIdentity ... cis) throws CertificateException, NoSuchAlgorithmException, CadiException { + this.access = access; + env = access.getProperty(Config.AAF_ENV,null); + if(env==null) { + throw new CadiException("X509Taf requires Environment ("+Config.AAF_ENV+") to be set."); + } +// this.lur = lur; + this.cadiIssuers = new ArrayList<String>(); + for(String ci : access.getProperty(Config.CADI_X509_ISSUERS, "").split(":")) { + access.printf(Level.INIT, "Trusting Identity for Certificates signed by \"%s\"",ci); + cadiIssuers.add(ci); + } + try { + Class<?> dci = access.classLoader().loadClass("org.onap.aaf.auth.direct.DirectCertIdentity"); + if(dci==null) { + certIdents = cis; + } else { + CertIdentity temp[] = new CertIdentity[cis.length+1]; + System.arraycopy(cis, 0, temp, 1, cis.length); + temp[0] = (CertIdentity) dci.newInstance(); + certIdents=temp; + } + } catch (Exception e) { + certIdents = cis; + } + + si = new SecurityInfo(access); + } + + public static final X509Certificate getCert(byte[] certBytes) throws CertificateException { + ByteArrayInputStream bais = new ByteArrayInputStream(certBytes); + return (X509Certificate)certFactory.generateCertificate(bais); + } + + public static final byte[] getFingerPrint(byte[] ba) { + MessageDigest md; + try { + md = (MessageDigest)messageDigest.clone(); + } catch (CloneNotSupportedException e) { + // should never get here + return new byte[0]; + } + md.update(ba); + return md.digest(); + } + + @Override + public TafResp validate(LifeForm reading, HttpServletRequest req, HttpServletResponse resp) { + // Check for Mutual SSL + try { + X509Certificate[] certarr = (X509Certificate[])req.getAttribute("javax.servlet.request.X509Certificate"); + if(certarr!=null && certarr.length>0) { + si.checkClientTrusted(certarr); + // Note: If the Issuer is not in the TrustStore, it's not added to the Cert list + String issuer = certarr[0].getIssuerDN().toString(); + if(cadiIssuers.contains(issuer)) { + String subject = certarr[0].getSubjectDN().getName(); + // avoiding extra object creation, since this is validated EVERY transaction with a Cert + int at = subject.indexOf('@'); + if(at>=0) { + int start = subject.lastIndexOf(',', at); + if(start<0) { + start = 0; + } + int end = subject.indexOf(',', at); + if(end<0) { + end=subject.length(); + } + int temp; + if(((temp=subject.indexOf("OU=",start))>=0 && temp<end) || + ((temp=subject.indexOf("CN=",start))>=0 && temp<end)) { + String[] sa = Split.splitTrim(':', subject, temp+3,end); + if(sa.length==1 || (sa.length>1 && env!=null && env.equals(sa[1]))) { // Check Environment + return new X509HttpTafResp(access, + new X509Principal(sa[0], certarr[0],(byte[])null), + "X509Taf validated " + sa[0] + (sa.length<2?"":" for aaf_env " + env ), RESP.IS_AUTHENTICATED); + } + } + + } + } + } + + + byte[] array = null; + byte[] certBytes = null; + X509Certificate cert=null; + String responseText=null; + String authHeader = req.getHeader("Authorization"); + + if(certarr!=null) { // If cert !=null, Cert is Tested by Mutual Protocol. + if(authHeader!=null) { // This is only intended to be a Secure Connection, not an Identity + for(String auth : Split.split(',',authHeader)) { + if(auth.startsWith("Bearer ")) { // Bearer = OAuth... Don't use as Authenication + return new X509HttpTafResp(access, null, "Certificate verified, but Bearer Token is presented", RESP.TRY_ANOTHER_TAF); + } + } + } + cert = certarr[0]; + responseText = ", validated by Mutual SSL Protocol"; + } else { // If cert == null, Get Declared Cert (in header), but validate by having them sign something + if(authHeader != null) { + for(String auth : Split.splitTrim(',',authHeader)) { + if(auth.startsWith("x509 ")) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(auth.length()); + try { + array = auth.getBytes(); + ByteArrayInputStream bais = new ByteArrayInputStream(array); + Symm.base64noSplit.decode(bais, baos, 5); + certBytes = baos.toByteArray(); + cert = getCert(certBytes); + + /** + * Identity from CERT if well know CA and specific encoded information + */ + // If found Identity doesn't work, try SignedStuff Protocol + // cert.checkValidity(); + // cert.--- GET FINGERPRINT? + String stuff = req.getHeader("Signature"); + if(stuff==null) + return new X509HttpTafResp(access, null, "Header entry 'Signature' required to validate One way X509 Certificate", RESP.TRY_ANOTHER_TAF); + String data = req.getHeader("Data"); + // if(data==null) + // return new X509HttpTafResp(access, null, "No signed Data to validate with X509 Certificate", RESP.TRY_ANOTHER_TAF); + + // Note: Data Pos shows is "<signatureType> <data>" + // int dataPos = (stuff.indexOf(' ')); // determine what is Algorithm + // Get Signature + bais = new ByteArrayInputStream(stuff.getBytes()); + baos = new ByteArrayOutputStream(stuff.length()); + Symm.base64noSplit.decode(bais, baos); + array = baos.toByteArray(); + // Signature sig = Signature.getInstance(stuff.substring(0, dataPos)); // get Algorithm from first part of Signature + + Signature sig = Signature.getInstance(cert.getSigAlgName()); + sig.initVerify(cert.getPublicKey()); + sig.update(data.getBytes()); + if(!sig.verify(array)) { + access.log(Level.ERROR, "Signature doesn't Match"); + return new X509HttpTafResp(access, null, CERTIFICATE_NOT_VALID_FOR_AUTHENTICATION, RESP.TRY_ANOTHER_TAF); + } + responseText = ", validated by Signed Data"; + } catch (Exception e) { + access.log(e, "Exception while validating Cert"); + return new X509HttpTafResp(access, null, CERTIFICATE_NOT_VALID_FOR_AUTHENTICATION, RESP.TRY_ANOTHER_TAF); + } + } + } + } + if(cert==null) { + return new X509HttpTafResp(access, null, "No Certificate Info on Transaction", RESP.TRY_ANOTHER_TAF); + } + + // A cert has been found, match Identify + TaggedPrincipal prin=null; + + for(int i=0;prin==null && i<certIdents.length;++i) { + if((prin=certIdents[i].identity(req, cert, certBytes))!=null) { + responseText = prin.getName() + " matches Certificate " + cert.getSubjectX500Principal().getName() + responseText; + } + } + + // if Principal is found, check for "AS_USER" and whether this entity is trusted to declare + if(prin!=null) { + return new X509HttpTafResp( + access, + prin, + responseText, + RESP.IS_AUTHENTICATED); + } + } + } catch(Exception e) { + return new X509HttpTafResp(access, null, e.getMessage(), RESP.TRY_ANOTHER_TAF); + } + + return new X509HttpTafResp(access, null, "Certificate cannot be used for authentication", RESP.TRY_ANOTHER_TAF); + } + + @Override + public Resp revalidate(CachedPrincipal prin, Object state) { + return null; + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/taf/dos/DenialOfServiceTaf.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/dos/DenialOfServiceTaf.java new file mode 100644 index 00000000..44a3a4a3 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/dos/DenialOfServiceTaf.java @@ -0,0 +1,375 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.taf.dos; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.CachedPrincipal; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.CachedPrincipal.Resp; +import org.onap.aaf.cadi.Taf.LifeForm; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.taf.HttpTaf; +import org.onap.aaf.cadi.taf.PuntTafResp; +import org.onap.aaf.cadi.taf.TafResp; +import org.onap.aaf.cadi.taf.TafResp.RESP; + +public class DenialOfServiceTaf implements HttpTaf { + private static Map<String, Counter> deniedIP=null, deniedID=null; + private Access access; + private final TafResp puntNotDenied; + private static File dosIP, dosID; + + /** + * + * @param hostname + * @param prod + * @throws CadiException + */ + public DenialOfServiceTaf(Access access) throws CadiException { + puntNotDenied = new PuntTafResp("DenialOfServiceTaf", "This Transaction is not denied"); + this.access = access; + if(dosIP==null || dosID == null) { + String dirStr; + if((dirStr = access.getProperty(Config.AAF_DATA_DIR, null))!=null) { + dosIP = new File(dirStr+"/dosIP"); + readIP(); + dosID = new File(dirStr+"/dosID"); + readID(); + } + } + } + + @Override + public TafResp validate(LifeForm reading, HttpServletRequest req, final HttpServletResponse resp) { + // Performance, when not needed + if(deniedIP != null) { + String ip; + Counter c = deniedIP.get(ip=req.getRemoteAddr()); + if(c!=null) { + c.inc(); + return respDenyIP(access,ip); + } + } + + // Note: Can't process Principal, because this is the first TAF, and no Principal is created. + // Other TAFs use "isDenied()" on this Object to validate. + return puntNotDenied; + } + + @Override + public Resp revalidate(CachedPrincipal prin, Object state) { + // We always return NOT MINE, because DOS Taf does not ever validate + return Resp.NOT_MINE; + } + + /* + * for use in Other TAFs, before they attempt backend validation of + */ + public static Counter isDeniedID(String identity) { + if(deniedID!=null) { + return deniedID.get(identity); + } + return null; + } + + /** + * + */ + public static Counter isDeniedIP(String ipvX) { + if(deniedIP!=null) { + return deniedIP.get(ipvX); + } + return null; + } + + /** + * Return of "True" means IP has been added. + * Return of "False" means IP already added. + * + * @param ip + * @return + */ + public static synchronized boolean denyIP(String ip) { + boolean rv = false; + if(deniedIP==null) { + deniedIP = new HashMap<String,Counter>(); + deniedIP.put(ip, new Counter(ip)); // Noted duplicated for minimum time spent + rv= true; + } else if(deniedIP.get(ip)==null) { + deniedIP.put(ip, new Counter(ip)); + rv = true; + } + if(rv) { + writeIP(); + } + return rv; + } + + private static void writeIP() { + if(dosIP!=null && deniedIP!=null) { + if(deniedIP.isEmpty()) { + if(dosIP.exists()) { + dosIP.delete(); + } + } else { + PrintStream fos; + try { + fos = new PrintStream(new FileOutputStream(dosIP,false)); + try { + for(String ip: deniedIP.keySet()) { + fos.println(ip); + } + } finally { + fos.close(); + } + } catch (IOException e) { + e.printStackTrace(System.err); + } + } + } + } + + private static void readIP() { + if(dosIP!=null && dosIP.exists()) { + BufferedReader br; + try { + br = new BufferedReader(new FileReader(dosIP)); + try { + if(deniedIP==null) { + deniedIP=new HashMap<String,Counter>(); + } + + String line; + while((line=br.readLine())!=null) { + deniedIP.put(line, new Counter(line)); + } + } finally { + br.close(); + } + } catch (IOException e) { + e.printStackTrace(System.err); + } + } + } + + + /** + * Return of "True" means IP has was removed. + * Return of "False" means IP wasn't being denied. + * + * @param ip + * @return + */ + public static synchronized boolean removeDenyIP(String ip) { + if(deniedIP!=null && deniedIP.remove(ip)!=null) { + writeIP(); + if(deniedIP.isEmpty()) { + deniedIP=null; + } + return true; + } + return false; + } + + /** + * Return of "True" means ID has been added. + * Return of "False" means ID already added. + * + * @param ip + * @return + */ + public static synchronized boolean denyID(String id) { + boolean rv = false; + if(deniedID==null) { + deniedID = new HashMap<String,Counter>(); + deniedID.put(id, new Counter(id)); // Noted duplicated for minimum time spent + rv = true; + } else if(deniedID.get(id)==null) { + deniedID.put(id, new Counter(id)); + rv = true; + } + if(rv) { + writeID(); + } + return rv; + + } + + private static void writeID() { + if(dosID!=null && deniedID!=null) { + if(deniedID.isEmpty()) { + if(dosID.exists()) { + dosID.delete(); + } + } else { + PrintStream fos; + try { + fos = new PrintStream(new FileOutputStream(dosID,false)); + try { + for(String ip: deniedID.keySet()) { + fos.println(ip); + } + } finally { + fos.close(); + } + } catch (IOException e) { + e.printStackTrace(System.err); + } + } + } + } + + private static void readID() { + if(dosID!=null && dosID.exists()) { + BufferedReader br; + try { + br = new BufferedReader(new FileReader(dosID)); + try { + if(deniedID==null) { + deniedID=new HashMap<String,Counter>(); + } + + String line; + while((line=br.readLine())!=null) { + deniedID.put(line, new Counter(line)); + } + } finally { + br.close(); + } + } catch (IOException e) { + e.printStackTrace(System.err); + } + } + } + + /** + * Return of "True" means ID has was removed. + * Return of "False" means ID wasn't being denied. + * + * @param ip + * @return + */ + public static synchronized boolean removeDenyID(String id) { + if(deniedID!=null && deniedID.remove(id)!=null) { + writeID(); + if(deniedID.isEmpty()) { + deniedID=null; + } + + return true; + } + return false; + } + + public List<String> report() { + int initSize = 0; + if(deniedIP!=null)initSize+=deniedIP.size(); + if(deniedID!=null)initSize+=deniedID.size(); + ArrayList<String> al = new ArrayList<String>(initSize); + if(deniedID!=null) { + for(Counter c : deniedID.values()) { + al.add(c.toString()); + } + } + if(deniedIP!=null) { + for(Counter c : deniedIP.values()) { + al.add(c.toString()); + } + } + return al; + } + + public static class Counter { + private final String name; + private int count = 0; + private Date first; + private long last; // note, we use "last" as long, to avoid popping useless dates on Heap. + + public Counter(String name) { + this.name = name; + first = null; + last = 0L; + count = 0; + } + + public String getName() { + return name; + } + + public int getCount() { + return count; + } + + public long getLast() { + return last; + } + + /* + * Only allow Denial of ServiceTaf to increment + */ + private synchronized void inc() { + ++count; + last = System.currentTimeMillis(); + if(first==null) { + first = new Date(last); + } + } + + public String toString() { + if(count==0) + return name + " is on the denied list, but has not attempted Access"; + else + return + name + + " has been denied " + + count + + " times since " + + first + + ". Last denial was " + + new Date(last); + } + } + + public static TafResp respDenyID(Access access, String identity) { + return new DenialOfServiceTafResp(access, RESP.NO_FURTHER_PROCESSING, identity + " is on the Identity Denial list"); + } + + public static TafResp respDenyIP(Access access, String ip) { + return new DenialOfServiceTafResp(access, RESP.NO_FURTHER_PROCESSING, ip + " is on the IP Denial list"); + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/taf/dos/DenialOfServiceTafResp.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/dos/DenialOfServiceTafResp.java new file mode 100644 index 00000000..b156392d --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/dos/DenialOfServiceTafResp.java @@ -0,0 +1,47 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.taf.dos; + +import java.io.IOException; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.taf.AbsTafResp; + +public class DenialOfServiceTafResp extends AbsTafResp { + private RESP ect; // Homage to Arethra Franklin + + public DenialOfServiceTafResp(Access access, RESP resp, String description ) { + super(access, null, description); + ect = resp; + } + + // Override base behavior of checking Principal and trying another TAF + @Override + public RESP isAuthenticated() { + return ect; + } + + + public RESP authenticate() throws IOException { + return ect; + } +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/util/Chmod.java b/cadi/core/src/main/java/org/onap/aaf/cadi/util/Chmod.java new file mode 100644 index 00000000..74bf805d --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/util/Chmod.java @@ -0,0 +1,62 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.util; + +import java.io.File; +import java.io.IOException; + +public interface Chmod { + public void chmod(File f) throws IOException; + + public static final Chmod to755 = new Chmod() { + public void chmod(File f) throws IOException { + f.setExecutable(true, false); + f.setExecutable(true, true); + f.setReadable(true, false); + f.setReadable(true, true); + f.setWritable(false, false); + f.setWritable(true, true); + } + }; + + public static final Chmod to644 = new Chmod() { + public void chmod(File f) throws IOException { + f.setExecutable(false, false); + f.setExecutable(false, true); + f.setReadable(true, false); + f.setReadable(true, true); + f.setWritable(false, false); + f.setWritable(true, true); + } + }; + + public static final Chmod to400 = new Chmod() { + public void chmod(File f) throws IOException { + f.setExecutable(false, false); + f.setExecutable(false, true); + f.setReadable(false, false); + f.setReadable(true, true); + f.setWritable(false, false); + f.setWritable(false, true); + } + }; +}
\ No newline at end of file diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/util/FQI.java b/cadi/core/src/main/java/org/onap/aaf/cadi/util/FQI.java new file mode 100644 index 00000000..4ea50a10 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/util/FQI.java @@ -0,0 +1,51 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.util; + +public class FQI { + /** + * Take a Fully Qualified User, and get a Namespace from it. + * @param fqi + * @return + */ + public final static String reverseDomain(final String fqi) { + StringBuilder sb = null; + String[] split = Split.split('.',fqi); + int at; + for(int i=split.length-1;i>=0;--i) { + if(sb == null) { + sb = new StringBuilder(); + } else { + sb.append('.'); + } + + if((at = split[i].indexOf('@'))>0) { + sb.append(split[i].subSequence(at+1, split[i].length())); + } else { + sb.append(split[i]); + } + } + + return sb==null?"":sb.toString(); + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/util/JsonOutputStream.java b/cadi/core/src/main/java/org/onap/aaf/cadi/util/JsonOutputStream.java new file mode 100644 index 00000000..7b04942f --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/util/JsonOutputStream.java @@ -0,0 +1,89 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.util; + +import java.io.IOException; +import java.io.OutputStream; + +public class JsonOutputStream extends OutputStream { + private static final byte[] TWO_SPACE = " ".getBytes(); + private OutputStream os; + private boolean closeable; + private int indent = 0; + private int prev,ret=0; + + public JsonOutputStream(OutputStream os) { + // Don't close these, or dire consequences. + closeable = !os.equals(System.out) && !os.equals(System.err); + this.os = os; + } + + @Override + public void write(int b) throws IOException { + if(ret=='\n') { + ret = 0; + if(prev!=',' || (b!='{' && b!='[')) { + os.write('\n'); + for(int i=0;i<indent;++i) { + os.write(TWO_SPACE); + } + } + } + switch(b) { + case '{': + case '[': + ret = '\n'; + ++indent; + break; + case '}': + case ']': + --indent; + os.write('\n'); + for(int i=0;i<indent;++i) { + os.write(TWO_SPACE); + } + break; + case ',': + ret = '\n'; + break; + + } + os.write(b); + prev = b; + } + public void resetIndent() { + indent = 1; + } + + @Override + public void flush() throws IOException { + os.flush(); + } + + @Override + public void close() throws IOException { + if(closeable) { + os.close(); + } + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/util/MaskFormatException.java b/cadi/core/src/main/java/org/onap/aaf/cadi/util/MaskFormatException.java new file mode 100644 index 00000000..7dd51c0a --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/util/MaskFormatException.java @@ -0,0 +1,31 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.util; + +@SuppressWarnings("serial") +public class MaskFormatException extends Exception { + + public MaskFormatException(String string) { + super(string); + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/util/MyConsole.java b/cadi/core/src/main/java/org/onap/aaf/cadi/util/MyConsole.java new file mode 100644 index 00000000..2312d00b --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/util/MyConsole.java @@ -0,0 +1,28 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.util; + +public interface MyConsole { + public String readLine(String fmt, Object ... args); + public char[] readPassword(String fmt, Object ... args); + public void printf(String fmt, Object ...args); +}
\ No newline at end of file diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/util/NetMask.java b/cadi/core/src/main/java/org/onap/aaf/cadi/util/NetMask.java new file mode 100644 index 00000000..fccb04fc --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/util/NetMask.java @@ -0,0 +1,99 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.util; + +/* + * NetMask - a class to quickly validate whether a given IP is part of a mask, as defined by bytes or standard String format. + * + * Needs the IPV6 Mask Builder. + */ +public class NetMask { + private long mask; + + public NetMask(byte[] inBytes) { + mask = derive(inBytes); + } + + public NetMask(String string) throws MaskFormatException { + mask = derive(string,true); + } + + public boolean isInNet(byte[] inBytes) { + long addr = derive(inBytes); + return (mask & addr) == addr; + } + + public boolean isInNet(String str) { + long addr; + try { + addr = derive(str,false); + return (mask & addr) == addr; + } catch (MaskFormatException e) { + // will not hit this code; + return false; + } + } + + public static long derive(byte[] inBytes) { + long addr = 0L; + int offset = inBytes.length*8; + for(int i=0;i<inBytes.length;++i) { + addr&=(inBytes[i]<<offset); + offset-=8; + } + return addr; + } + + public static long derive(String str, boolean check) throws MaskFormatException { + long rv=0L; + int idx=str.indexOf(':'); + int slash = str.indexOf('/'); + + if(idx<0) { // Not IPV6, so it's IPV4... Is there a mask of 123/254? + idx=str.indexOf('.'); + int offset = 24; + int end = slash>=0?slash:str.length(); + int bits = slash>=0?Integer.parseInt(str.substring(slash+1)):32; + if(check && bits>32) { + throw new MaskFormatException("Invalid Mask Offset in IPV4 Address"); + } + int prev = 0; + long lbyte; + while(prev<end) { + if(idx<0) { + idx = end; + } + lbyte = Long.parseLong(str.substring(prev, idx)); + if(check && (lbyte>255 || lbyte<0)) { + throw new MaskFormatException("Invalid Byte in IPV4 Address"); + } + rv|=lbyte<<offset; + prev = ++idx; + idx=str.indexOf('.',prev); + offset-=8; + } + rv|=0x00000000FFFFFFFFL>>bits; + } + return rv; + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/util/Pool.java b/cadi/core/src/main/java/org/onap/aaf/cadi/util/Pool.java new file mode 100644 index 00000000..4312c3ca --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/util/Pool.java @@ -0,0 +1,382 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +/* + * Pool + * + * Author: Jonathan + * 5/27/2011 + */ +package org.onap.aaf.cadi.util; + +import java.util.Iterator; +import java.util.LinkedList; + +import org.onap.aaf.cadi.CadiException; + +/** + * This Class pools on an As-Needed-Basis any particular kind of class, which is + * quite suitable for expensive operations. + * + * The user calls "get" on a Pool, and if a waiting resource (T) is available, + * it will be returned. Otherwise, one will be created with the "Creator" class + * (must be defined for (T)). + * + * You can Prime the instances to avoid huge startup costs + * + * The returned "Pooled" object simply has to call "done()" and the object is + * returned to the pool. If the developer does not return the object, a memory + * leak does not occur. There are no references to the object once "get" is + * called. However, the developer who does not return the object when done + * obviates the point of the pool, as new Objects are created in place of the + * Object not returned when another call to "get" is made. + * + * There is a cushion of extra objects, currently defaulted to MAX_RANGE. If the + * items returned become higher than the MAX_RANGE, the object is allowed to go + * out of scope, and be cleaned up. the default can be changed on a per-pool + * basis. + * + * Class revamped for CadiExceptions and Access logging 10/4/2017 + * + * @author Jonathan + * + * @param <T> + */ +public class Pool<T> { + /** + * This is a constant which specified the default maximum number of unused + * objects to be held at any given time. + */ + private static final int MAX_RANGE = 6; // safety + + /** + * only Simple List needed. + * + * NOTE TO MAINTAINERS: THIS OBJECT DOES IT'S OWN SYNCHRONIZATION. All + * changes that touch list must account for correctly synchronizing list. + */ + private LinkedList<Pooled<T>> list; + + /** + * keep track of how many elements exist, to avoid asking list. + */ + private int count; + + /** + * Spares are those Object that are primed and ready to go. + */ + private int spares; + + /** + * Actual MAX number of spares allowed to hang around. Can be set to + * something besides the default MAX_RANGE. + */ + private int max_range = MAX_RANGE; + + /** + * The Creator for this particular pool. It must work for type T. + */ + private Creator<T> creator; + + private Log logger; + + /** + * Create a new Pool, given the implementation of Creator<T>, which must be + * able to create/destroy T objects at will. + * + * @param creator + */ + public Pool(Creator<T> creator) { + count = spares = 0; + this.creator = creator; + list = new LinkedList<Pooled<T>>(); + logger = Log.NULL; + } + + /** + * Attach Pool Logging activities to any other Logging Mechanism. + * @param logger + */ + public void setLogger(Log logger) { + this.logger = logger; + } + + public void log(Object ...objects) { + logger.log(objects); + } + + /** + * Preallocate a certain number of T Objects. Useful for services so that + * the first transactions don't get hit with all the Object creation costs + * + * @param lt + * @param prime + * @throws CadiException + */ + public void prime(int prime) throws CadiException { + for (int i = 0; i < prime; ++i) { + Pooled<T> pt = new Pooled<T>(creator.create(), this); + synchronized (list) { + list.addFirst(pt); + ++count; + } + } + + } + + /** + * Destroy and remove all remaining objects. This is valuable for closing + * down all Allocated objects cleanly for exiting. It is also a good method + * for removing objects when, for instance, all Objects are invalid because + * of broken connections, etc. + */ + public void drain() { + synchronized (list) { + for (int i = 0; i < list.size(); ++i) { + Pooled<T> pt = list.remove(); + creator.destroy(pt.content); + logger.log("Pool drained ", creator.toString()); + } + count = spares = 0; + } + + } + + /** + * This is the essential function for Pool. Get an Object "T" inside a + * "Pooled<T>" object. If there is a spare Object, then use it. If not, then + * create and pass back. + * + * This one uses a Null LogTarget + * + * IMPORTANT: When the use of this object is done (and the object is still + * in a valid state), then "done()" should be called immediately to allow + * the object to be reused. That is the point of the Pool... + * + * If the Object is in an invalid state, then "toss()" should be used so the + * Pool doesn't pass on invalid objects to others. + * + * @param lt + * @return + * @throws CadiException + */ + public Pooled<T> get() throws CadiException { + Pooled<T> pt; + synchronized (list) { + if (list.isEmpty()) { + pt = null; + } else { + pt = list.removeLast(); + --count; + creator.reuse(pt.content); + } + } + if (pt == null) { + if (spares < max_range) + ++spares; + pt = new Pooled<T>(creator.create(), this); + } else { + if (spares > 1) + --spares; + } + return pt; + } + + /** + * This function will validate whether the Objects are still in a usable + * state. If not, they are tossed from the Pool. This is valuable to have + * when Remote Connections go down, and there is a question on whether the + * Pooled Objects are still functional. + * + * @return + */ + public boolean validate() { + boolean rv = true; + synchronized (list) { + for (Iterator<Pooled<T>> iter = list.iterator(); iter.hasNext();) { + Pooled<T> t = iter.next(); + if (!creator.isValid(t.content)) { + rv = false; + t.toss(); + iter.remove(); + } + } + } + return rv; + } + + /** + * This is an internal method, used only by the Internal Pooled<T> class. + * + * The Pooled<T> class "offers" it's Object back after use. It is an + * "offer", because Pool will simply destroy and remove the object if it has + * more than enough spares. + * + * @param lt + * @param used + * @return + */ + // Used only by Pooled<T> + private boolean offer(Pooled<T> used) { + if (count < spares) { + synchronized (list) { + list.addFirst(used); + ++count; + } + logger.log("Pool recovered ", creator); + } else { + logger.log("Pool destroyed ", creator); + creator.destroy(used.content); + } + return false; + } + + /** + * The Creator Interface give the Pool the ability to Create, Destroy and + * Validate the Objects it is maintaining. Thus, it is a specially written + * Implementation for each type. + * + * @author Jonathan + * + * @param <T> + */ + public interface Creator<T> { + public T create() throws CadiException; + + public void destroy(T t); + + public boolean isValid(T t); + + public void reuse(T t); + } + + public interface Log { + public void log(Object ... o); + + public final static Log NULL = new Log() { + @Override + public void log(Object ... o) { + } + }; + } + /** + * The "Pooled<T>" class is the transient class that wraps the actual Object + * T for API use/ It gives the ability to return ("done()", or "toss()") the + * Object to the Pool when processing is finished. + * + * For Safety, i.e. to avoid memory leaks and invalid Object States, there + * is a "finalize" method. It is strictly for when coder forgets to return + * the object, or perhaps hasn't covered the case during Exceptions or + * Runtime Exceptions with finally (preferred). This should not be + * considered normal procedure, as finalize() is called at an undetermined + * time during garbage collection, and is thus rather useless for a Pool. + * However, we don't want Coding Mistakes to put the whole program in an + * invalid state, so if something happened such that "done()" or "toss()" + * were not called, the resource is still cleaned up as well as possible. + * + * @author Jonathan + * + * @param <T> + */ + public static class Pooled<T> { + public final T content; + private Pool<T> pool; + + /** + * Create the Wrapping Object Pooled<T>. + * + * @param t + * @param pool + * @param logTarget + */ + public Pooled(T t, Pool<T> pool) { + content = t; + this.pool = pool; + + } + + /** + * This is the key API for the Pool, as calling "done()" offers this + * object back to the Pool for reuse. + * + * Do not use the Pooled<T> object again after calling "done()". + */ + public void done() { + if (pool != null) { + pool.offer(this); + } + } + + /** + * The user of the Object may discover that the Object t is no longer in + * a valid state. Don't put Garbage back in the Refrigerator... Toss it, + * if it's no longer valid. + * + * toss() is also used for draining the Pool, etc. + * + * toss() will attempt to destroy the Object by using the Creator + * Interface. + * + */ + public void toss() { + if (pool != null) { + pool.creator.destroy(content); + } + // Don't allow finalize to put it back in. + pool = null; + } + + /** + * Just in case someone neglected to offer back object... Do not rely on + * this, as there is no specific time when finalize is called, which + * rather defeats the purpose of a Pool. + */ + @Override + protected void finalize() throws Throwable { + if (pool != null) { + done(); + pool = null; + } + } + } + + /** + * Get the maximum number of spare objects allowed at any moment + * + * @return + */ + public int getMaxRange() { + return max_range; + } + + /** + * Set a Max Range for numbers of spare objects waiting to be used. + * + * No negative numbers are allowed + * + * @return + */ + public void setMaxRange(int max_range) { + // Do not allow negative numbers + this.max_range = Math.max(0, max_range); + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/util/Split.java b/cadi/core/src/main/java/org/onap/aaf/cadi/util/Split.java new file mode 100644 index 00000000..3fa9a3f1 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/util/Split.java @@ -0,0 +1,114 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.util; + +/** + * Split by Char, optional Trim + * + * Note: Copied from Inno to avoid linking issues. + * Note: I read the String split and Pattern split code, and we can do this more efficiently for a single Character + * + * 8/20/2015 + */ + +public class Split { + public static String[] split(char c, String value) { + return split(c,value,0,value.length()); + } + + public static String[] split(char c, String value, int start, int end) { + if(value==null) { + return new String[0]; + } + + // Count items to preallocate Array (memory alloc is more expensive than counting twice) + int count,idx; + for(count=1,idx=value.indexOf(c,start);idx>=0 && idx<end;idx=value.indexOf(c,++idx),++count); + String[] rv = new String[count]; + if(count==1) { + rv[0]=value.substring(start,end); + } else { + int last=0; + count=-1; + for(idx=value.indexOf(c,start);idx>=0 && idx<end;idx=value.indexOf(c,idx)) { + rv[++count]=value.substring(last,idx); + last = ++idx; + } + rv[++count]=value.substring(last,end); + } + return rv; + } + + public static String[] splitTrim(char c, String value, int start, int end) { + if(value==null) { + return new String[0]; + } + + // Count items to preallocate Array (memory alloc is more expensive than counting twice) + int count,idx; + for(count=1,idx=value.indexOf(c,start);idx>=0 && idx<end;idx=value.indexOf(c,++idx),++count); + String[] rv = new String[count]; + if(count==1) { + rv[0]=value.substring(start,end).trim(); + } else { + int last=0; + count=-1; + for(idx=value.indexOf(c,start);idx>=0 && idx<end;idx=value.indexOf(c,idx)) { + rv[++count]=value.substring(last,idx).trim(); + last = ++idx; + } + rv[++count]=value.substring(last,end).trim(); + } + return rv; + } + + public static String[] splitTrim(char c, String value) { + return splitTrim(c,value,0,value.length()); + } + + public static String[] splitTrim(char c, String value, int size) { + if(value==null) { + return new String[0]; + } + + int idx; + String[] rv = new String[size]; + if(size==1) { + rv[0]=value.trim(); + } else { + int last=0; + int count=-1; + size-=2; + for(idx=value.indexOf(c);idx>=0 && count<size;idx=value.indexOf(c,idx)) { + rv[++count]=value.substring(last,idx).trim(); + last = ++idx; + } + if(idx>0) { + rv[++count]=value.substring(last,idx).trim(); + } else { + rv[++count]=value.substring(last).trim(); + } + } + return rv; + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/util/SubStandardConsole.java b/cadi/core/src/main/java/org/onap/aaf/cadi/util/SubStandardConsole.java new file mode 100644 index 00000000..8d528119 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/util/SubStandardConsole.java @@ -0,0 +1,62 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.util; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +// Substandard, because System.in doesn't do Passwords.. +public class SubStandardConsole implements MyConsole { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + @Override + public String readLine(String fmt, Object... args) { + String rv; + try { + System.out.printf(fmt,args); + rv = br.readLine(); + if(args.length==1 && rv.length()==0) { + rv = args[0].toString(); + } + } catch (IOException e) { + System.err.println("uh oh..."); + rv = ""; + } + return rv; + } + + @Override + public char[] readPassword(String fmt, Object... args) { + try { + System.out.printf(fmt,args); + return br.readLine().toCharArray(); + } catch (IOException e) { + System.err.println("uh oh..."); + return new char[0]; + } + } + + @Override + public void printf(String fmt, Object... args) { + System.out.printf(fmt, args); + } +}
\ No newline at end of file diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/util/TheConsole.java b/cadi/core/src/main/java/org/onap/aaf/cadi/util/TheConsole.java new file mode 100644 index 00000000..4c5d35b7 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/util/TheConsole.java @@ -0,0 +1,47 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.util; + +public class TheConsole implements MyConsole { + @Override + public String readLine(String fmt, Object... args) { + String rv = System.console().readLine(fmt, args); + if(args.length>0 && args[0]!=null && rv.length()==0) { + rv = args[0].toString(); + } + return rv; + } + + @Override + public char[] readPassword(String fmt, Object... args) { + return System.console().readPassword(fmt, args); + } + + public static boolean implemented() { + return System.console()!=null; + } + + @Override + public void printf(String fmt, Object... args) { + System.console().printf(fmt, args); + } +}
\ No newline at end of file diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/util/UserChainManip.java b/cadi/core/src/main/java/org/onap/aaf/cadi/util/UserChainManip.java new file mode 100644 index 00000000..a8c0690f --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/util/UserChainManip.java @@ -0,0 +1,77 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.util; + +import org.onap.aaf.cadi.UserChain; + +public class UserChainManip { + /** + Build an element in the correct format for UserChain. + Format:<APP>:<ID>:<protocol>[:AS][,<APP>:<ID>:<protocol>]* + @see UserChain + */ + public static StringBuilder build(StringBuilder sb, String app, String id, UserChain.Protocol proto, boolean as) { + boolean mayAs; + if(!(mayAs=sb.length()==0)) { + sb.append(','); + } + sb.append(app); + sb.append(':'); + sb.append(id); + sb.append(':'); + sb.append(proto.name()); + if(as && mayAs) { + sb.append(":AS"); + } + return sb; + } + + public static String idToNS(String id) { + if(id==null) { + return ""; + } else { + StringBuilder sb = new StringBuilder(); + char c; + int end; + boolean first = true; + for(int idx = end = id.length()-1;idx>=0;--idx) { + if((c = id.charAt(idx))=='@' || c=='.') { + if(idx<end) { + if(first) { + first = false; + } else { + sb.append('.'); + } + for(int i=idx+1;i<=end;++i) { + sb.append(id.charAt(i)); + } + } + end=idx-1; + if(c=='@') { + break; + } + } + } + return sb.toString(); + } + } +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/util/Vars.java b/cadi/core/src/main/java/org/onap/aaf/cadi/util/Vars.java new file mode 100644 index 00000000..55470f99 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/util/Vars.java @@ -0,0 +1,120 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.util; + +import java.util.List; + +public class Vars { + /** + * Simplified Conversion based on typical use of getting AT&T style RESTful Error Messages + * @param text + * @param vars + * @return + */ + public static String convert(final String text, final List<String> vars) { + String[] array = new String[vars.size()]; + StringBuilder sb = new StringBuilder(); + convert(sb,text,vars.toArray(array)); + return sb.toString(); + } + /** + * Convert a format string with "%s" into AT&T RESTful Error %1 %2 (number) format + * If "holder" is passed in, it is built with full Message extracted (typically for Logging) + * @param holder + * @param text + * @param vars + * @return + */ + public static String convert(final StringBuilder holder, final String text, final String ... vars) { + StringBuilder sb = null; + int idx,index=0,prev = 0; + + if(text.contains("%s")) { + sb = new StringBuilder(); + } + + StringBuilder[] sbs = new StringBuilder[] {sb,holder}; + boolean replace, clearIndex = false; + int c; + while((idx=text.indexOf('%',prev))>=0) { + replace = false; + if(clearIndex) { + index=0; + } + if(sb!=null) { + sb.append(text,prev,idx); + } + if(holder!=null) { + holder.append(text,prev,idx); + } + + boolean go = true; + while(go) { + if(text.length()>++idx) { + switch(c=text.charAt(idx)) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + index *=10; + index +=(c-'0'); + clearIndex=replace=true; + continue; + case 's': + ++index; + replace = true; + continue; + default: + break; + } + } + prev = idx; + go=false; + if(replace) { + if(sb!=null) { + sb.append('%'); + sb.append(index); + } + if(index<=vars.length) { + if(holder!=null) { + holder.append(vars[index-1]); + } + } + } else { + for(StringBuilder s : sbs) { + if(s!=null) { + s.append("%"); + } + } + } + } + } + + if(sb!=null) { + sb.append(text,prev,text.length()); + } + if(holder!=null) { + holder.append(text,prev,text.length()); + } + + return sb==null?text:sb.toString(); + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/wsse/Action.java b/cadi/core/src/main/java/org/onap/aaf/cadi/wsse/Action.java new file mode 100644 index 00000000..dff18acd --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/wsse/Action.java @@ -0,0 +1,37 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.wsse; + +/** + * Interface to specify an action deep within a parsing tree on a local object + * + * We use a Generic so as to be flexible on create what that object actually is. This is passed in at the + * root "parse" call of Match. Similar to a "Visitor" Pattern, this object is passed upon reaching the right + * point in a parse tree. + * + * @author Jonathan + * + * @param <OUTPUT> + */ +interface Action<OUTPUT> { + public boolean content(OUTPUT output, String text); +}
\ No newline at end of file diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/wsse/Match.java b/cadi/core/src/main/java/org/onap/aaf/cadi/wsse/Match.java new file mode 100644 index 00000000..2582bc17 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/wsse/Match.java @@ -0,0 +1,130 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.wsse; + +import javax.xml.namespace.QName; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.XMLEvent; + +/** + * Match Class allows you to build an automatic Tree of StAX (or StAX like) + * Objects for frequent use. + * + * OBJECT is a type which you which to do some end Actions on, similar to a Visitor pattern, see Action + * + * Note: We have implemented with XReader and XEvent, rather than StAX for performance reasons. + * + * @see Action + * @see Match + * @see XEvent + * @see XReader + * + * @author Jonathan + * + * @param <OUTPUT> + */ +//@SuppressWarnings("restriction") +public class Match<OUTPUT> { + private QName qname; + private Match<OUTPUT>[] next; + private Match<OUTPUT> prev; + private Action<OUTPUT> action = null; + private boolean stopAfter; + private boolean exclusive; + + + @SafeVarargs + public Match(String ns, String name, Match<OUTPUT> ... next) { + this.qname = new QName(ns,name); + this.next = next; + stopAfter = exclusive = false; + for(Match<OUTPUT> m : next) { // add the possible tags to look for + if(!m.stopAfter)m.prev = this; + } + } + + public Match<OUTPUT> onMatch(OUTPUT output, XReader reader) throws XMLStreamException { + while(reader.hasNext()) { + XEvent event = reader.nextEvent(); + switch(event.getEventType()) { + case XMLEvent.START_ELEMENT: + QName e_qname = event.asStartElement().getName(); + //System.out.println("Start - " + e_qname); + boolean match = false; + for(Match<OUTPUT> m : next) { + if(e_qname.equals(m.qname)) { + match=true; + if(m.onMatch(output, reader)==null) { + return null; // short circuit Parsing + } + break; + } + } + if(exclusive && !match) // When Tag MUST be present, i.e. the Root Tag, versus info we're not interested in + return null; + break; + case XMLEvent.CHARACTERS: + //System.out.println("Data - " +event.asCharacters().getData()); + if(action!=null) { + if(!action.content(output,event.asCharacters().getData())) { + return null; + } + } + break; + case XMLEvent.END_ELEMENT: + //System.out.println("End - " + event.asEndElement().getName()); + if(event.asEndElement().getName().equals(qname)) { + return prev; + } + break; + case XMLEvent.END_DOCUMENT: + return null; // Exit Chain + } + } + return this; + } + + /** + * When this Matched Tag has completed, Stop parsing and end + * @return + */ + public Match<OUTPUT> stopAfter() { + stopAfter = true; + return this; + } + + /** + * Mark that this Object MUST be matched at this level or stop parsing and end + * + * @param action + * @return + */ + public Match<OUTPUT> exclusive() { + exclusive = true; + return this; + } + + public Match<OUTPUT> set(Action<OUTPUT> action) { + this.action = action; + return this; + } +}
\ No newline at end of file diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/wsse/WSSEParser.java b/cadi/core/src/main/java/org/onap/aaf/cadi/wsse/WSSEParser.java new file mode 100644 index 00000000..017337b1 --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/wsse/WSSEParser.java @@ -0,0 +1,83 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.wsse; + +import java.io.InputStream; + +import javax.xml.stream.XMLStreamException; + +import org.onap.aaf.cadi.BasicCred; + + +/** + * WSSE Parser + * + * Read the User and Password from WSSE Formatted SOAP Messages + * + * This class uses StAX so that processing is stopped as soon as the Security User/Password are read into BasicCred, or the Header Ends + * + * This class is intended to be created once (or very few times) and reused as much as possible. + * + * It is as thread safe as StAX parsing is. + * + * @author Jonathan + */ +public class WSSEParser { + private static final String SOAP_NS = "http://schemas.xmlsoap.org/soap/envelope/"; + private static final String WSSE_NS = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"; + private Match<BasicCred> parseTree; + + public WSSEParser() { + // soap:Envelope/soap:Header/wsse:Security/wsse:UsernameToken/[wsse:Password&wsse:Username] + parseTree = new Match<BasicCred>(SOAP_NS,"root", // need a root level to start from... Doesn't matter what the tag is + new Match<BasicCred>(SOAP_NS,"Envelope", + new Match<BasicCred>(SOAP_NS,"Header", + new Match<BasicCred>(WSSE_NS,"Security", + new Match<BasicCred>(WSSE_NS,"UsernameToken", + new Match<BasicCred>(WSSE_NS,"Password").set(new Action<BasicCred>() { + public boolean content(BasicCred bc,String text) { + bc.setCred(text.getBytes()); + return true; + } + }), + new Match<BasicCred>(WSSE_NS,"Username").set(new Action<BasicCred>() { + public boolean content(BasicCred bc,String text) { + bc.setUser(text); + return true; + } + }) + ).stopAfter() // if found, end when UsernameToken ends (no further processing needed) + ) + ).stopAfter() // Stop Processing when Header Ends + ).exclusive()// Envelope must match Header, and no other. FYI, Body comes after Header short circuits (see above), so it's ok + ).exclusive(); // root must be Envelope + } + + public XMLStreamException parse(BasicCred bc, InputStream is) { + try { + parseTree.onMatch(bc, new XReader(is)); + return null; + } catch (XMLStreamException e) { + return e; + } + } +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/wsse/XEvent.java b/cadi/core/src/main/java/org/onap/aaf/cadi/wsse/XEvent.java new file mode 100644 index 00000000..12de366e --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/wsse/XEvent.java @@ -0,0 +1,135 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.wsse; + +import javax.xml.namespace.QName; +import javax.xml.stream.events.XMLEvent; + +/** + * XEvent + * + * This mechanism mimics a minimal portion of StAX "XMLEvent", enough to work with minimal XReader. + * + * We implement the same interface, as much as minimally necessary, as XMLEvent for these small usages so as to + * be interchangeable in the future, if so desired + * + * @author Jonathan + * + */ +// @SuppressWarnings("restriction") +public abstract class XEvent { + + public abstract int getEventType(); + + public StartElement asStartElement() { + return (StartElement)this; + } + + public Characters asCharacters() { + return (Characters)this; + } + + public EndElement asEndElement() { + return (EndElement)this; + } + + public static abstract class NamedXEvent extends XEvent { + private QName qname; + + public NamedXEvent(QName qname) { + this.qname = qname; + } + + public QName getName() { + return qname; + } + } + public static class StartElement extends NamedXEvent { + + public StartElement(String ns, String tag) { + super(new QName(ns,tag)); + } + + @Override + public int getEventType() { + return XMLEvent.START_ELEMENT; + } + } + + public static class EndElement extends NamedXEvent { + public EndElement(String ns, String tag) { + super(new QName(ns,tag)); + } + + @Override + public int getEventType() { + return XMLEvent.END_ELEMENT; + } + } + + public static class Characters extends XEvent { + private String data; + + public Characters(String data) { + this.data = data; + } + @Override + public int getEventType() { + return XMLEvent.CHARACTERS; + } + + public String getData() { + return data; + } + } + + public static class StartDocument extends XEvent { + + @Override + public int getEventType() { + return XMLEvent.START_DOCUMENT; + } + + } + + public static class EndDocument extends XEvent { + + @Override + public int getEventType() { + return XMLEvent.END_DOCUMENT; + } + + } + public static class Comment extends XEvent { + public final String value; + public Comment(String value) { + this.value = value; + } + + @Override + public int getEventType() { + return XMLEvent.COMMENT; + } + + } + +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/wsse/XReader.java b/cadi/core/src/main/java/org/onap/aaf/cadi/wsse/XReader.java new file mode 100644 index 00000000..7af475ad --- /dev/null +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/wsse/XReader.java @@ -0,0 +1,419 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.wsse; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Stack; + +import javax.xml.stream.XMLStreamException; + +/** + * XReader + * This class works similarly as StAX, except StAX has more behavior than is needed. That would be ok, but + * StAX also was Buffering in their code in such as way as to read most if not all the incoming stream into memory, + * defeating the purpose of pre-reading only the Header + * + * This Reader does no back-tracking, but is able to create events based on syntax and given state only, leaving the + * Read-ahead mode of the InputStream up to the other classes. + * + * At this time, we only implement the important events, though if this is good enough, it could be expanded, perhaps to + * replace the original XMLReader from StAX. + * + * @author Jonathan + * + */ +// @SuppressWarnings("restriction") +public class XReader { + private XEvent curr,another; + private InputStream is; + private ByteArrayOutputStream baos; + private int state, count, last; + + private Stack<Map<String,String>> nsses; + + public XReader(InputStream is) { + this.is = is; + curr = another = null; + baos = new ByteArrayOutputStream(); + state = BEGIN_DOC; + count = 0; + nsses = new Stack<Map<String,String>>(); + } + + public boolean hasNext() throws XMLStreamException { + if(curr==null) { + curr = parse(); + } + return curr!=null; + } + + public XEvent nextEvent() { + XEvent xe = curr; + curr = null; + return xe; + } + + // + // State Flags + // + // Note: The State of parsing XML can be complicated. There are too many to cleanly keep in "booleans". Additionally, + // there are certain checks that can be better made with Bitwise operations within switches + // Keeping track of state this way also helps us to accomplish logic without storing any back characters except one + private final static int BEGIN_DOC= 0x000001; + private final static int DOC_TYPE= 0x000002; + private final static int QUESTION_F= 0x000004; + private final static int QUESTION = 0x000008; + private final static int START_TAG = 0x000010; + private final static int END_TAG = 0x000020; + private final static int VALUE= 0x000040; + private final static int COMMENT = 0x001000; + private final static int COMMENT_E = 0x002000; + private final static int COMMENT_D1 =0x010000; + private final static int COMMENT_D2 =0x020000; + private final static int COMMENT_D3 =0x040000; + private final static int COMMENT_D4 =0x080000; + // useful combined Comment states + private final static int IN_COMMENT=COMMENT|COMMENT_E|COMMENT_D1|COMMENT_D2; + private final static int COMPLETE_COMMENT = COMMENT|COMMENT_E|COMMENT_D1|COMMENT_D2|COMMENT_D3|COMMENT_D4; + + + private XEvent parse() throws XMLStreamException { + Map<String,String> nss = nsses.isEmpty()?null:nsses.peek(); + + XEvent rv; + if((rv=another)!=null) { // "another" is a tag that may have needed to be created, but not + // immediately returned. Save for next parse. If necessary, this could be turned into + // a FIFO storage, but a single reference is enough for now. + another = null; // "rv" is now set for the Event, and will be returned. Set to Null. + } else { + boolean go = true; + int c=0; + + try { + while(go && (c=is.read())>=0) { + ++count; + switch(c) { + case '<': // Tag is opening + state|=~BEGIN_DOC; // remove BEGIN_DOC flag, this is possibly an XML Doc + XEvent cxe = null; + if(baos.size()>0) { // If there are any characters between tags, we send as Character Event + String chars = baos.toString().trim(); // Trim out WhiteSpace before and after + if(chars.length()>0) { // don't send if Characters were only whitespace + cxe = new XEvent.Characters(chars); + baos.reset(); + go = false; + } + } + last = c; // make sure "last" character is set for use in "ParseTag" + Tag t = parseTag(); // call subroutine to process the tag as a unit + String ns; + switch(t.state&(START_TAG|END_TAG)) { + case START_TAG: + nss = getNss(nss,t); // Only Start Tags might have NS Attributes + // Get any NameSpace elements from tag. If there are, nss will become + // a new Map with all the previous NSs plus the new. This provides + // scoping behavior when used with the Stack + // drop through on purpose + case END_TAG: + ns = t.prefix==null||nss==null?"":nss.get(t.prefix); // Get the namespace from prefix (if exists) + break; + default: + ns = ""; + } + if(ns==null) + throw new XMLStreamException("Invalid Namespace Prefix at " + count); + go = false; + switch(t.state) { // based on + case DOC_TYPE: + rv = new XEvent.StartDocument(); + break; + case COMMENT: + rv = new XEvent.Comment(t.value); + break; + case START_TAG: + rv = new XEvent.StartElement(ns,t.name); + nsses.push(nss); // Change potential scope for Namespace + break; + case END_TAG: + rv = new XEvent.EndElement(ns,t.name); + nss = nsses.pop(); // End potential scope for Namespace + break; + case START_TAG|END_TAG: // This tag is both start/end aka <myTag/> + rv = new XEvent.StartElement(ns,t.name); + if(last=='/')another = new XEvent.EndElement(ns,t.name); + } + if(cxe!=null) { // if there is a Character Event, it actually should go first. ow. + another = rv; // Make current Event the "another" or next event, and + rv = cxe; // send Character Event now + } + break; + case ' ': + case '\t': + case '\n': + if((state&BEGIN_DOC)==BEGIN_DOC) { // if Whitespace before doc, just ignore + break; + } + // fallthrough on purpose + default: + if((state&BEGIN_DOC)==BEGIN_DOC) { // if there is any data at the start other than XML Tag, it's not XML + throw new XMLStreamException("Parse Error: This is not an XML Doc"); + } + baos.write(c); // save off Characters + } + last = c; // Some processing needs to know what the last character was, aka Escaped characters... ex \" + } + } catch (IOException e) { + throw new XMLStreamException(e); // all errors parsing will be treated as XMLStreamErrors (like StAX) + } + if(c==-1 && (state&BEGIN_DOC)==BEGIN_DOC) { // Normally, end of stream is ok, however, we need to know if the + throw new XMLStreamException("Premature End of File"); // document isn't an XML document, so we throw exception if it + } // hasn't yet been determined to be an XML Doc + } + return rv; + } + + /** + * parseTag + * + * Parsing a Tag is somewhat complicated, so it's helpful to separate this process from the + * higher level Parsing effort + * @return + * @throws IOException + * @throws XMLStreamException + */ + private Tag parseTag() throws IOException, XMLStreamException { + Tag tag = null; + boolean go = true; + state = 0; + int c, quote=0; // If "quote" is 0, then we're not in a quote. We set ' (in pretag) or " in attribs accordingly to denote quoted + String prefix=null,name=null,value=null; + baos.reset(); + + while(go && (c=is.read())>=0) { + ++count; + if(quote!=0) { // If we're in a quote, we only end if we hit another quote of the same time, not preceded by \ + if(c==quote && last!='\\') { + quote=0; + } else { + baos.write(c); + } + } else if((state&COMMENT)==COMMENT) { // similar to Quote is being in a comment + switch(c) { + case '-': + switch(state) { // XML has a complicated Quote set... <!-- --> ... we keep track if each has been met with flags. + case COMMENT|COMMENT_E: + state|=COMMENT_D1; + break; + case COMMENT|COMMENT_E|COMMENT_D1: + state|=COMMENT_D2; + baos.reset(); // clear out "!--", it's a Comment + break; + case COMMENT|COMMENT_E|COMMENT_D1|COMMENT_D2: + state|=COMMENT_D3; + baos.write(c); + break; + case COMMENT|COMMENT_E|COMMENT_D1|COMMENT_D2|COMMENT_D3: + state|=COMMENT_D4; + baos.write(c); + break; + } + break; + case '>': // Tag indicator has been found, do we have all the comment characters in line? + if((state&COMPLETE_COMMENT)==COMPLETE_COMMENT) { + byte ba[] = baos.toByteArray(); + tag = new Tag(null,null, new String(ba,0,ba.length-2)); + baos.reset(); + go = false; + break; + } + // fall through on purpose + default: + state&=~(COMMENT_D3|COMMENT_D4); + if((state&IN_COMMENT)!=IN_COMMENT) state&=~IN_COMMENT; // false alarm, it's not actually a comment + baos.write(c); + } + } else { // Normal Tag Processing loop + switch(c) { + case '?': + switch(state & (QUESTION_F|QUESTION)) { // Validate the state of Doc tag... <?xml ... ?> + case QUESTION_F: + state |= DOC_TYPE; + state &= ~QUESTION_F; + break; + case 0: + state |=QUESTION_F; + break; + default: + throw new IOException("Bad character [?] at " + count); + } + break; + case '!': + if(last=='<') { + state|=COMMENT|COMMENT_E; // likely a comment, continue processing in Comment Loop + } + baos.write(c); + break; + case '/': + state|=(last=='<'?END_TAG:(END_TAG|START_TAG)); // end tag indicator </xxx>, ,or both <xxx/> + break; + case ':': + prefix=baos.toString(); // prefix indicator + baos.reset(); + break; + case '=': // used in Attributes + name=baos.toString(); + baos.reset(); + state|=VALUE; + break; + case '>': // end the tag, which causes end of this subprocess as well as formulation of the found data + go = false; + // passthrough on purpose + case ' ': + case '\t': + case '\n': // white space indicates change in internal tag state, ex between name and between attributes + if((state&VALUE)==VALUE) { + value = baos.toString(); // we're in VALUE state, add characters to Value + } else if(name==null) { + name = baos.toString(); // we're in Name state (default) add characters to Name + } + baos.reset(); // we've assigned chars, reset buffer + if(name!=null) { // Name is not null, there's a tag in the offing here... + Tag t = new Tag(prefix,name,value); + if(tag==null) { // Set as the tag to return, if not exists + tag = t; + } else { // if we already have a Tag, then we'll treat this one as an attribute + tag.add(t); + } + } + prefix=name=value=null; // reset these values in case we loop for attributes. + break; + case '\'': // is the character one of two kinds of quote? + case '"': + if(last!='\\') { + quote=c; + break; + } + // Fallthrough ok + default: + baos.write(c); // write any unprocessed bytes into buffer + + } + } + last = c; + } + int type = state&(DOC_TYPE|COMMENT|END_TAG|START_TAG); // get just the Tag states and turn into Type for Tag + if(type==0) { + type=START_TAG; + } + if(tag!=null) { + tag.state|=type; // add the appropriate Tag States + } + return tag; + } + + /** + * getNSS + * + * If the tag contains some Namespace attributes, create a new nss from the passed in one, copy all into it, then add + * This provides Scoping behavior + * + * if Nss is null in the first place, create an new nss, so we don't have to deal with null Maps. + * + * @param nss + * @param t + * @return + */ + private Map<String, String> getNss(Map<String, String> nss, Tag t) { + Map<String,String> newnss = null; + if(t.attribs!=null) { + for(Tag tag : t.attribs) { + if("xmlns".equals(tag.prefix)) { + if(newnss==null) { + newnss = new HashMap<String,String>(); + if(nss!=null)newnss.putAll(nss); + } + newnss.put(tag.name, tag.value); + } + } + } + return newnss==null?(nss==null?new HashMap<String,String>():nss):newnss; + } + + /** + * The result of the parseTag method + * + * Data is split up into prefix, name and value portions. "Tags" with Values that are inside a Tag are known in XLM + * as Attributes. + * + * @author Jonathan + * + */ + public class Tag { + public int state; + public String prefix,name,value; + public List<Tag> attribs; + + public Tag(String prefix, String name, String value) { + this.prefix = prefix; + this.name = name; + this.value = value; + attribs = null; + } + + /** + * add an attribute + * Not all tags need attributes... lazy instantiate to save time and memory + * @param tag + */ + public void add(Tag attrib) { + if(attribs == null) { + attribs = new ArrayList<Tag>(); + } + attribs.add(attrib); + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + if(prefix!=null) { + sb.append(prefix); + sb.append(':'); + } + sb.append(name==null?"!!ERROR!!":name); + + char quote = ((state&DOC_TYPE)==DOC_TYPE)?'\'':'"'; + if(value!=null) { + sb.append('='); + sb.append(quote); + sb.append(value); + sb.append(quote); + } + return sb.toString(); + } + } + +} diff --git a/cadi/core/src/test/java/.gitignore b/cadi/core/src/test/java/.gitignore new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/cadi/core/src/test/java/.gitignore diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/config/test/JU_Get.java b/cadi/core/src/test/java/org/onap/aaf/cadi/config/test/JU_Get.java new file mode 100644 index 00000000..586c50c7 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/config/test/JU_Get.java @@ -0,0 +1,116 @@ +/******************************************************************************* + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ + +package org.onap.aaf.cadi.config.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import org.junit.*; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.config.Get; + +public class JU_Get { + + private String defaultVal = "some default value"; + + private ByteArrayOutputStream outStream; + + private TestBean tb; + + @Before + public void setup() { + outStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outStream)); + } + + @After + public void tearDown() { + System.setOut(System.out); + } + + @Test + public void beanTest() { + tb = new TestBean(); + tb.setProperty1("prop1"); + + Get.Bean testBean = new Get.Bean(tb); + assertThat(testBean.get("property1", defaultVal, true), is("prop1")); + assertThat(testBean.get("property2", defaultVal, true), is(defaultVal)); + assertThat(testBean.get("thrower", defaultVal, true), is(defaultVal)); + } + + @Test + public void nullTest() { + assertThat(Get.NULL.get("name", defaultVal, true), is(defaultVal)); + } + + @Test + public void accessTest() { + String output; + + PropAccess access = new PropAccess(); + access.setProperty("tag", "value"); + Get.AccessGet accessGet = new Get.AccessGet(access); + + assertThat(accessGet.get("tag", defaultVal, true), is("value")); + output = outStream.toString().split(" ", 2)[1]; + assertThat(output, is("INIT [cadi] tag is set to value\n")); + + outStream.reset(); + + assertThat(accessGet.get("not a real tag", defaultVal, true), is(defaultVal)); + output = outStream.toString().split(" ", 2)[1]; + assertThat(output, is("INIT [cadi] not a real tag is set to " + defaultVal + "\n")); + + outStream.reset(); + + assertThat(accessGet.get("not a real tag", null, true), is(nullValue())); + output = outStream.toString().split(" ", 2)[1]; + assertThat(output, is("INIT [cadi] not a real tag is not set\n")); + + outStream.reset(); + + assertThat(accessGet.get("tag", defaultVal, false), is("value")); + assertThat(outStream.toString(), is("")); + } + + public class TestBean implements java.io.Serializable { + + private static final long serialVersionUID = 1L; + private String property1 = null; + private String property2 = null; + @SuppressWarnings("unused") + private String thrower = null; + + public TestBean() { } + public String getProperty1() { return property1; } + public void setProperty1(final String value) { this.property1 = value; } + public String getProperty2() { return property2; } + public void setProperty2(final String value) { this.property2 = value; } + public String getThrower() throws Exception { throw new Exception(); } + public void setThrower(final String value) { this.thrower = value; } + + } +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/config/test/JU_GetAccess.java b/cadi/core/src/test/java/org/onap/aaf/cadi/config/test/JU_GetAccess.java new file mode 100644 index 00000000..36da3073 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/config/test/JU_GetAccess.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ + +package org.onap.aaf.cadi.config.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import org.junit.*; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.PrintStream; + +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.config.Get; +import org.onap.aaf.cadi.config.GetAccess; + +public class JU_GetAccess { + + private String defaultVal = "some default value"; + + private ByteArrayOutputStream outStream; + + private PropAccess access; + private Get.AccessGet accessGet; + private File file; + private String filePath; + + @Before + public void setup() throws IOException { + outStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outStream)); + + file = File.createTempFile("GetAccess_test", ""); + filePath = file.getAbsolutePath(); + + access = new PropAccess(); + access.setProperty("cadi_prop_files", filePath); + accessGet = new Get.AccessGet(access); + + } + + @After + public void tearDown() { + System.setOut(System.out); + + file.delete(); + } + + @Test + public void constructorTest() { + String output; + + @SuppressWarnings("unused") + GetAccess getAccess = new GetAccess(accessGet); + String[] lines = outStream.toString().split("\n"); + assertThat(lines.length, is(2)); + output = lines[0].split(" ", 2)[1]; + assertThat(output, is("INIT [cadi] cadi_prop_files is set to " + filePath)); + output = lines[1].split(" ", 2)[1]; + assertThat(output, is("INIT [cadi] Loading CADI Properties from " + filePath)); + } + + @Test + public void getPropertyTest1() { + GetAccess getAccess = new GetAccess(accessGet); + + getAccess.setProperty("tag", "value"); + assertThat(getAccess.getProperty("tag", defaultVal), is("value")); + assertThat(getAccess.getProperty("not_a_tag", defaultVal), is(defaultVal)); + } + + @Test + public void getPropertyTest2() { + GetAccess getAccess = new GetAccess(accessGet); + + getAccess.setProperty("tag", "value"); + assertThat(getAccess.getProperty("tag"), is("value")); + assertThat(getAccess.getProperty("not_a_tag"), is(nullValue())); + } + + @Test + public void getTest() { + GetAccess getAccess = new GetAccess(accessGet); + assertThat((Get.AccessGet)getAccess.get(), is(accessGet)); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/config/test/JU_MultiGet.java b/cadi/core/src/test/java/org/onap/aaf/cadi/config/test/JU_MultiGet.java new file mode 100644 index 00000000..6510bdcd --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/config/test/JU_MultiGet.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ + +package org.onap.aaf.cadi.config.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import org.junit.*; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; + +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.config.Get; +import org.onap.aaf.cadi.config.MultiGet; + +public class JU_MultiGet { + + private String defaultVal = "some default value"; + + private ByteArrayOutputStream outStream; + + private MultiGet multiGet; + private Get.AccessGet accessGet; + private PropAccess access; + + @Before + public void setup() throws IOException { + outStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outStream)); + + access = new PropAccess(); + access.setProperty("tag", "value"); + accessGet = new Get.AccessGet(access); + multiGet = new MultiGet(accessGet, Get.NULL); + } + + @After + public void tearDown() { + System.setOut(System.out); + } + + @Test + public void getTest() { + assertThat(multiGet.get("tag", defaultVal, false), is("value")); + assertThat(multiGet.get("not_a_tag", defaultVal, false), is(defaultVal)); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/config/test/JU_SecurityInfo.java b/cadi/core/src/test/java/org/onap/aaf/cadi/config/test/JU_SecurityInfo.java new file mode 100644 index 00000000..842a7098 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/config/test/JU_SecurityInfo.java @@ -0,0 +1,125 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.config.test; + + +import static org.junit.Assert.assertNotNull; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; + +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.config.SecurityInfo; + +public class JU_SecurityInfo { + + private static PropAccess access; + + private static final String keyStoreFileName = "src/test/resources/keystore.p12"; + private static final String keyStorePassword = "Password for the keystore"; + private static final String keyPassword = "Password for the key"; + + private static final String trustStoreFileName = "src/test/resources/truststore.jks"; + private static final String trustStorePasswd = "Password for the truststore"; + + @BeforeClass + public static void setupOnce() throws NoSuchAlgorithmException, CertificateException, IOException, KeyStoreException { + KeyStore keyStore = KeyStore.getInstance("PKCS12"); + keyStore.load(null, null); + keyStore.store(new FileOutputStream(keyStoreFileName), keyStorePassword.toCharArray()); + + KeyStore trustStore = KeyStore.getInstance("JKS"); + trustStore.load(null, null); + trustStore.store(new FileOutputStream(trustStoreFileName), trustStorePasswd.toCharArray()); + } + + @Before + public void setup() throws IOException { + access = new PropAccess(new PrintStream(new ByteArrayOutputStream()), new String[0]); + + access.setProperty(Config.CADI_KEYSTORE, keyStoreFileName); + access.setProperty(Config.CADI_KEYSTORE_PASSWORD, access.encrypt(keyStorePassword)); + access.setProperty(Config.CADI_KEY_PASSWORD, access.encrypt(keyPassword)); + + access.setProperty(Config.CADI_TRUSTSTORE, trustStoreFileName); + access.setProperty(Config.CADI_TRUSTSTORE_PASSWORD, access.encrypt(trustStorePasswd)); + } + + @AfterClass + public static void tearDownOnce() { + File keyStoreFile = new File(keyStoreFileName); + if (keyStoreFile.exists()) { + keyStoreFile.delete(); + } + File trustStoreFile = new File(trustStoreFileName); + if (trustStoreFile.exists()) { + trustStoreFile.delete(); + } + } + + @Test + public void test() throws CadiException { + SecurityInfo si = new SecurityInfo(access); + + assertNotNull(si.getSSLSocketFactory()); + assertNotNull(si.getSSLContext()); + assertNotNull(si.getKeyManagers()); + } + + @Test(expected = CadiException.class) + public void nullkeyStoreTest() throws CadiException { + access.setProperty(Config.CADI_KEYSTORE, "passwords.txt"); + @SuppressWarnings("unused") + SecurityInfo si = new SecurityInfo(access); + } + + @Test(expected = CadiException.class) + public void nullTrustStoreTest() throws CadiException { + access.setProperty(Config.CADI_TRUSTSTORE, "passwords.txt"); + @SuppressWarnings("unused") + SecurityInfo si = new SecurityInfo(access); + } + + @Test + public void coverageTest() throws CadiException { + PropAccess badAccess = new PropAccess(new PrintStream(new ByteArrayOutputStream()), new String[0]); + @SuppressWarnings("unused") + SecurityInfo si = new SecurityInfo(badAccess); + badAccess.setProperty(Config.CADI_KEYSTORE, keyStoreFileName); + si = new SecurityInfo(badAccess); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/config/test/JU_SecurityInfoC.java b/cadi/core/src/test/java/org/onap/aaf/cadi/config/test/JU_SecurityInfoC.java new file mode 100644 index 00000000..27014b9a --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/config/test/JU_SecurityInfoC.java @@ -0,0 +1,109 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.config.test; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; + +import org.junit.*; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.SecuritySetter; +import org.onap.aaf.cadi.config.SecurityInfoC; + +public class JU_SecurityInfoC { + + ByteArrayOutputStream outStream; + ByteArrayOutputStream errStream; + + @Before + public void setup() { + outStream = new ByteArrayOutputStream(); + errStream = new ByteArrayOutputStream(); + + System.setOut(new PrintStream(outStream)); + System.setErr(new PrintStream(errStream)); + } + + @After + public void tearDown() { + System.setOut(System.out); + System.setErr(System.err); + } + + @Test + public void instanceTest() throws CadiException, MalformedURLException { + SecurityInfoC<HttpURLConnection> si = SecurityInfoC.instance(new PropAccess(), HttpURLConnection.class); + assertThat(si.defSS.getID(), is(SecurityInfoC.DEF_ID)); + try { + si.defSS.setSecurity(new HttpURLConnectionStub()); + fail("Should have thrown an exception"); + } catch (CadiException e) { + assertTrue(e instanceof CadiException); + assertThat(e.getMessage(), is("No Client Credentials set.")); + } + assertThat(si.defSS.setLastResponse(0), is(0)); + + // Try it again for coverage + SecurityInfoC<HttpURLConnection> siClone = SecurityInfoC.instance(new PropAccess(), HttpURLConnection.class); + assertThat(siClone, is(si)); + } + + @Test + public void setTest() throws MalformedURLException, CadiException { + SecurityInfoC<HttpURLConnectionStub> si = SecurityInfoC.instance(new PropAccess(), HttpURLConnectionStub.class); + SecuritySetter<HttpURLConnectionStub> ss = new SecuritySetterStub<HttpURLConnectionStub>(); + assertThat(si.set(ss), is(si)); + assertThat(si.defSS.getID(), is("Example ID")); + try { + si.defSS.setSecurity(new HttpURLConnectionStub()); + fail("Should have thrown an exception"); + } catch (CadiException e) { + assertTrue(e instanceof CadiException); + assertThat(e.getMessage(), is("Example exception")); + } + assertThat(si.defSS.setLastResponse(0), is(0)); + assertThat(si.defSS.setLastResponse(1), is(1)); + assertThat(si.defSS.setLastResponse(-1), is(-1)); + } + + private class HttpURLConnectionStub extends HttpURLConnection { + public HttpURLConnectionStub() throws MalformedURLException { super(new URL("http://www.example.com")); } + @Override public void disconnect() { } + @Override public boolean usingProxy() { return false; } + @Override public void connect() throws IOException { } + } + + private class SecuritySetterStub<CT> implements SecuritySetter<CT> { + public String getID() { return "Example ID"; } + public void setSecurity(CT client) throws CadiException { throw new CadiException("Example exception"); } + public int setLastResponse(int respCode) { return respCode; } + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/config/test/JU_UsersDump.java b/cadi/core/src/test/java/org/onap/aaf/cadi/config/test/JU_UsersDump.java new file mode 100644 index 00000000..7d7ca77c --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/config/test/JU_UsersDump.java @@ -0,0 +1,145 @@ +/******************************************************************************* + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ + +package org.onap.aaf.cadi.config.test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.PrintStream; + +import org.onap.aaf.cadi.AbsUserCache; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.config.UsersDump; +import org.onap.aaf.cadi.lur.LocalLur; +import org.onap.aaf.cadi.lur.LocalPermission; +import org.onap.aaf.cadi.util.Split; + +public class JU_UsersDump { + + private ByteArrayOutputStream outStream; + private ByteArrayOutputStream stdoutSuppressor; + + private static final String expected = "<?xml version='1.0' encoding='utf-8'?>\n" + + "<!--\n" + + " Code Generated Tomcat Users and Roles from AT&T LUR on ...\n" + + "-->\n" + + "<tomcat-users>\n" + + " <role rolename=\"suser\"/>\n" + + " <role rolename=\"admin\"/>\n" + + " <role rolename=\"groupB\"/>\n" + + " <role rolename=\"groupA\"/>\n" + + " \n" + + " <user username=\"yourname@none\" roles=\"admin\"/>\n" + + " <user username=\"m1234@none\" roles=\"suser\"/>\n" + + " <user username=\"hisname@none\" roles=\"suser\"/>\n" + + " <user username=\"hername@none\" roles=\"suser\"/>\n" + + " <user username=\"myname\" roles=\"groupB,groupA\"/>\n" + + " <user username=\"myname@none\" roles=\"admin\"/>\n" + + "</tomcat-users>\n"; + + private final static String groups = "myname:groupA,groupB"; + private final static String names = "admin:myname,yourname;suser:hisname,hername,m1234"; + + private AbsUserCache<LocalPermission> lur; + + @Before + public void setup() throws IOException { + outStream = new ByteArrayOutputStream(); + stdoutSuppressor = new ByteArrayOutputStream(); + + System.setOut(new PrintStream(stdoutSuppressor)); + + lur = new LocalLur(new PropAccess(), groups, names); + } + + @After + public void tearDown() { + System.setOut(System.out); + } + + @Test + public void writeTest() throws IOException { + UsersDump.write(outStream, lur); + String[] actualLines = Split.splitTrim('\n', outStream.toString()); + String[] expectedLines = Split.splitTrim('\n', expected); + for (String s : actualLines) { + System.out.println(s); + } + + assertThat(actualLines.length, is(expectedLines.length)); + + // Check that the output starts with an XML tag + assertThat(actualLines[0], is(expectedLines[0])); + // Check that lines 2-4 are a comment + assertThat(actualLines[1], is(expectedLines[1])); + assertThat(actualLines[3], is(expectedLines[3])); + + // Check that the rest of the output matches the expected output + for (int i = 4; i < actualLines.length; i++) { + assertThat(actualLines[i], is(expectedLines[i])); + } + + // Run the test again with outStream as a PrintStream (for coverage) + outStream.reset(); + UsersDump.write(new PrintStream(outStream), lur); + actualLines = Split.splitTrim('\n', outStream.toString()); + + assertThat(actualLines.length, is(expectedLines.length)); + + // Check that the output starts with an XML tag + assertThat(actualLines[0], is(expectedLines[0])); + // Check that lines 2-4 are a comment + assertThat(actualLines[1], is(expectedLines[1])); + assertThat(actualLines[3], is(expectedLines[3])); + + // Check that the rest of the output matches the expected output + for (int i = 4; i < actualLines.length; i++) { + assertThat(actualLines[i], is(expectedLines[i])); + } + } + + @Test + public void updateUsersTest() { + String output; + File outputFile = new File("src/test/resources/userdump.xml"); + assertThat(outputFile.exists(), is(false)); + + output = UsersDump.updateUsers("src/test/resources/userdump.xml", (LocalLur) lur); + assertThat(output, is(nullValue())); + assertThat(outputFile.exists(), is(true)); + + output = UsersDump.updateUsers("src/test/resources/userdump.xml", (LocalLur) lur); + assertThat(output, is(nullValue())); + assertThat(outputFile.exists(), is(true)); + + outputFile.delete(); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/filter/test/JU_AUTHZServlet.java b/cadi/core/src/test/java/org/onap/aaf/cadi/filter/test/JU_AUTHZServlet.java new file mode 100644 index 00000000..6daa2720 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/filter/test/JU_AUTHZServlet.java @@ -0,0 +1,107 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.filter.test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.lang.reflect.Field; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.onap.aaf.cadi.filter.AUTHZServlet; + +import javax.servlet.Servlet; +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequestWrapper; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class JU_AUTHZServlet { + + @Mock private Servlet servletMock; + @Mock private ServletConfig servletConfigMock; + @Mock private HttpServletRequest reqMock; + @Mock private HttpServletResponse respMock; + @Mock private ServletRequestWrapper servletWrapperMock; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void test() throws ServletException, IOException { + AUTHZServletStub servlet = new AUTHZServletStub(Servlet.class); + + try { + servlet.init(servletConfigMock); + fail("Should've thrown an exception"); + } catch (ServletException e) { + assertThat(e.getMessage(), is("Invalid Servlet Delegate")); + } + + setPrivateField(AUTHZServlet.class, "delegate", servlet, servletMock); + servlet.init(servletConfigMock); + servlet.getServletConfig(); + servlet.getServletInfo(); + + servlet.service(reqMock, respMock); + + String[] roles = new String[] {"role1", "role2"}; + setPrivateField(AUTHZServlet.class, "roles", servlet, roles); + servlet.service(reqMock, respMock); + + when(reqMock.isUserInRole("role1")).thenReturn(true); + servlet.service(reqMock, respMock); + + try { + servlet.service(servletWrapperMock, respMock); + fail("Should've thrown an exception"); + } catch (ServletException e) { + assertThat(e.getMessage(), is("JASPIServlet only supports HTTPServletRequest/HttpServletResponse")); + } + servlet.destroy(); + } + + private class AUTHZServletStub extends AUTHZServlet<Servlet> { + public AUTHZServletStub(Class<Servlet> cls) { super(cls); } + } + + private void setPrivateField(Class<?> clazz, String fieldName, Object target, Object value) { + try { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + field.set(target, value); + field.setAccessible(false); + } catch(Exception e) { + System.err.println("Could not set field [" + fieldName + "] to " + value); + } + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/filter/test/JU_AccessGetter.java b/cadi/core/src/test/java/org/onap/aaf/cadi/filter/test/JU_AccessGetter.java new file mode 100644 index 00000000..b53a9ea9 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/filter/test/JU_AccessGetter.java @@ -0,0 +1,54 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.filter.test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import org.junit.Before; +import org.junit.Test; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.filter.AccessGetter; + +public class JU_AccessGetter { + + private static final String tag = "tag"; + private static final String value = "value"; + + private PropAccess access; + + @Before + public void setup() { + access = new PropAccess(new PrintStream(new ByteArrayOutputStream()), new String[0]); + access.setProperty(tag, value); + } + + @Test + public void test() { + AccessGetter getter = new AccessGetter(access); + assertThat(getter.get(tag, null, false), is(value)); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/filter/test/JU_MapPermConverter.java b/cadi/core/src/test/java/org/onap/aaf/cadi/filter/test/JU_MapPermConverter.java new file mode 100644 index 00000000..9fb951a2 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/filter/test/JU_MapPermConverter.java @@ -0,0 +1,45 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.filter.test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import org.junit.Test; +import org.onap.aaf.cadi.filter.MapPermConverter; + +public class JU_MapPermConverter { + + private static final String tag = "tag"; + private static final String value = "value"; + private static final String nontag = "nontag"; + + @Test + public void test() { + MapPermConverter converter = new MapPermConverter(); + assertThat(converter.map().isEmpty(), is(true)); + converter.map().put(tag, value); + assertThat(converter.convert(tag), is(value)); + assertThat(converter.convert(nontag), is(nontag)); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/filter/test/JU_NullPermConverter.java b/cadi/core/src/test/java/org/onap/aaf/cadi/filter/test/JU_NullPermConverter.java new file mode 100644 index 00000000..0a6dc2d5 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/filter/test/JU_NullPermConverter.java @@ -0,0 +1,38 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.filter.test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import org.junit.Test; +import org.onap.aaf.cadi.filter.NullPermConverter; + +public class JU_NullPermConverter { + + @Test + public void test() { + NullPermConverter converter = NullPermConverter.singleton(); + assertThat(converter.convert("test"), is("test")); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/filter/test/JU_PathFilter.java b/cadi/core/src/test/java/org/onap/aaf/cadi/filter/test/JU_PathFilter.java new file mode 100644 index 00000000..a36dd462 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/filter/test/JU_PathFilter.java @@ -0,0 +1,105 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.filter.test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.when; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.security.Principal; + +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.filter.PathFilter; + +public class JU_PathFilter { + + private PropAccess access; + + @Mock private FilterConfig filterConfigMock; + @Mock private ServletContext contextMock; + @Mock private HttpServletRequest reqMock; + @Mock private HttpServletResponse respMock; + @Mock private FilterChain chainMock; + @Mock private Principal princMock; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + when(filterConfigMock.getServletContext()).thenReturn(contextMock); + when(reqMock.getUserPrincipal()).thenReturn(princMock); + when(princMock.getName()).thenReturn("name"); + + access = new PropAccess(new PrintStream(new ByteArrayOutputStream()), new String[0]); + } + + @Test + public void test() throws ServletException, IOException { + PathFilter pathFilter = new PathFilter(access); + try { + pathFilter.init(filterConfigMock); + fail("Should've thrown an exception"); + } catch (ServletException e) { + assertThat(e.getMessage(), is("PathFilter - pathfilter_ns is not set")); + } + + when(contextMock.getAttribute(Config.PATHFILTER_NS)).thenReturn(5); + when(contextMock.getAttribute(Config.PATHFILTER_STACK)).thenReturn(5); + when(contextMock.getAttribute(Config.PATHFILTER_URLPATTERN)).thenReturn(5); + when(contextMock.getAttribute(Config.PATHFILTER_NOT_AUTHORIZED_MSG)).thenReturn(5); + pathFilter.init(filterConfigMock); + + pathFilter.doFilter(reqMock, respMock, chainMock); + + when(reqMock.isUserInRole(anyString())).thenReturn(true); + pathFilter.doFilter(reqMock, respMock, chainMock); + + pathFilter.destroy(); + + pathFilter = new PathFilter(); + pathFilter.init(filterConfigMock); + + pathFilter.doFilter(reqMock, respMock, chainMock); + + when(reqMock.isUserInRole(anyString())).thenReturn(false); + pathFilter.doFilter(reqMock, respMock, chainMock); + + pathFilter.destroy(); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/lur/test/JU_ConfigPrincipal.java b/cadi/core/src/test/java/org/onap/aaf/cadi/lur/test/JU_ConfigPrincipal.java new file mode 100644 index 00000000..9853f88e --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/lur/test/JU_ConfigPrincipal.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.lur.test; + +import org.junit.*; +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import java.lang.reflect.Field; + +import java.io.IOException; + +import org.onap.aaf.cadi.lur.ConfigPrincipal; + +public class JU_ConfigPrincipal { + + private final String name = "User"; + private final String pass = "pass"; + + // Expected output of base64("User:pass") + private final String b64encoded = "VXNlcjpwYXNz"; + + private Field content_field; + + @Before + public void setup() throws NoSuchFieldException { + content_field = ConfigPrincipal.class.getDeclaredField("content"); + content_field.setAccessible(true); + } + + @Test + public void testConfigPrincipalStringString() throws IOException, IllegalArgumentException, IllegalAccessException { + ConfigPrincipal p = new ConfigPrincipal(name, pass); + + assertThat(p.getName(), is(name)); + assertThat(p.toString(), is(name)); + assertThat(p.getCred(), is(pass.getBytes())); + assertThat(p.getAsBasicAuthHeader(), is("Basic " + b64encoded)); + content_field.set(p, "pass"); + assertThat(p.getAsBasicAuthHeader(), is("Basic " + b64encoded)); + + // One more time for coverage purposes + assertThat(p.getAsBasicAuthHeader(), is("Basic " + b64encoded)); + } + + @Test + public void testConfigPrincipalStringByteArray() throws IOException, IllegalArgumentException, IllegalAccessException { + ConfigPrincipal p = new ConfigPrincipal(name, pass.getBytes()); + + assertThat(p.getName(), is(name)); + assertThat(p.toString(), is(name)); + assertThat(p.getCred(), is(pass.getBytes())); + assertThat(p.getAsBasicAuthHeader(), is("Basic " + b64encoded)); + content_field.set(p, "pass"); + assertThat(p.getAsBasicAuthHeader(), is("Basic " + b64encoded)); + + // One more time for coverage purposes + assertThat(p.getAsBasicAuthHeader(), is("Basic " + b64encoded)); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/lur/test/JU_EpiLur.java b/cadi/core/src/test/java/org/onap/aaf/cadi/lur/test/JU_EpiLur.java new file mode 100644 index 00000000..f7c3a0a2 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/lur/test/JU_EpiLur.java @@ -0,0 +1,128 @@ +/** + * + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.lur.test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.when; + +import java.security.Principal; +import java.util.ArrayList; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.onap.aaf.cadi.CachingLur; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.CredVal; +import org.onap.aaf.cadi.Lur; +import org.onap.aaf.cadi.Permission; +import org.onap.aaf.cadi.lur.EpiLur; + +public class JU_EpiLur { + + private ArrayList<Permission> perms; + private CredValStub lurMock3; + + @Mock private Lur lurMock1; + @Mock private CachingLur<?> lurMock2; + @Mock private Principal princMock; + @Mock private Permission permMock; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + + perms = new ArrayList<>(); + perms.add(permMock); + + lurMock3 = new CredValStub(); + } + + @Test + public void test() throws CadiException { + EpiLur lur; + try { + lur = new EpiLur(); + } catch (CadiException e) { + assertThat(e.getMessage(), is("Need at least one Lur implementation in constructor")); + } + lur = new EpiLur(lurMock1, lurMock2, lurMock3); + assertThat(lur.fish(null, null), is(false)); + + assertThat(lur.fish(princMock, permMock), is(false)); + + when(lurMock2.handlesExclusively(permMock)).thenReturn(true); + assertThat(lur.fish(princMock, permMock), is(false)); + + when(lurMock2.fish(princMock, permMock)).thenReturn(true); + assertThat(lur.fish(princMock, permMock), is(true)); + + lur.fishAll(princMock, perms); + + assertThat(lur.handlesExclusively(permMock), is(false)); + + assertThat(lur.get(-1), is(nullValue())); + assertThat(lur.get(0), is(lurMock1)); + assertThat(lur.get(1), is((Lur)lurMock2)); + assertThat(lur.get(2), is((Lur)lurMock3)); + assertThat(lur.get(3), is(nullValue())); + + assertThat(lur.handles(princMock), is(false)); + when(lurMock2.handles(princMock)).thenReturn(true); + assertThat(lur.handles(princMock), is(true)); + + lur.remove("id"); + + lur.clear(princMock, null); + + assertThat(lur.createPerm("perm"), is(not(nullValue()))); + + lur.getUserPassImpl(); + assertThat(lur.getUserPassImpl(), is((CredVal)lurMock3)); + + lur.toString(); + lur.destroy(); + + lur = new EpiLur(lurMock1, lurMock2); + assertThat(lur.getUserPassImpl(), is(nullValue())); + + assertThat(lur.subLur(Lur.class), is(nullValue())); + } + + private class CredValStub implements Lur, CredVal { + @Override public boolean validate(String user, Type type, byte[] cred, Object state) { return false; } + @Override public Permission createPerm(String p) { return null; } + @Override public boolean fish(Principal bait, Permission pond) { return false; } + @Override public void fishAll(Principal bait, List<Permission> permissions) { } + @Override public void destroy() { } + @Override public boolean handlesExclusively(Permission pond) { return false; } + @Override public boolean handles(Principal principal) { return false; } + @Override public void clear(Principal p, StringBuilder report) { } + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/lur/test/JU_LocalLur.java b/cadi/core/src/test/java/org/onap/aaf/cadi/lur/test/JU_LocalLur.java new file mode 100644 index 00000000..d86a0754 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/lur/test/JU_LocalLur.java @@ -0,0 +1,174 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ + +package org.onap.aaf.cadi.lur.test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.when; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.security.Principal; +import java.util.ArrayList; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.onap.aaf.cadi.Permission; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.AbsUserCache; +import org.onap.aaf.cadi.CredVal.Type; +import org.onap.aaf.cadi.lur.ConfigPrincipal; +import org.onap.aaf.cadi.lur.LocalLur; +import org.onap.aaf.cadi.lur.LocalPermission; + +public class JU_LocalLur { + + private static final String password = "<pass>"; + private String encrypted; + + private PropAccess access; + private ByteArrayOutputStream outStream; + + @Mock Permission permMock; + + @Before + public void setup() throws IOException { + MockitoAnnotations.initMocks(this); + + encrypted = rot13(password); + + outStream = new ByteArrayOutputStream(); + access = new PropAccess(new PrintStream(outStream), new String[0]) { + @Override public String decrypt(String encrypted, boolean anytext) throws IOException { + return rot13(encrypted); + } + @Override public String encrypt(String unencrypted) throws IOException { + return rot13(unencrypted); + } + }; + + } + + @Test + public void test() throws IOException { + LocalLur lur; + List<AbsUserCache<LocalPermission>.DumpInfo> info; + + lur = new LocalLur(access, null, null); + assertThat(lur.dumpInfo().size(), is(0)); + + lur = new LocalLur(access, "user1", null); + info = lur.dumpInfo(); + assertThat(info.size(), is(1)); + assertThat(info.get(0).user, is("user1")); + + lur.clearAll(); + assertThat(lur.dumpInfo().size(), is(0)); + + lur = new LocalLur(access, "user1%" + encrypted, null); + info = lur.dumpInfo(); + assertThat(info.size(), is(1)); + assertThat(info.get(0).user, is("user1@none")); + + lur.clearAll(); + assertThat(lur.dumpInfo().size(), is(0)); + + lur = new LocalLur(access, "user1@domain%" + encrypted, null); + info = lur.dumpInfo(); + assertThat(info.size(), is(1)); + assertThat(info.get(0).user, is("user1@domain")); + + lur = new LocalLur(access, "user1@domain%" + encrypted + ":groupA", null); + info = lur.dumpInfo(); + assertThat(info.size(), is(1)); + assertThat(info.get(0).user, is("user1@domain")); + + when(permMock.getKey()).thenReturn("groupA"); + assertThat(lur.handlesExclusively(permMock), is(true)); + when(permMock.getKey()).thenReturn("groupB"); + assertThat(lur.handlesExclusively(permMock), is(false)); + + assertThat(lur.fish(null, null), is(false)); + + Principal princ = new ConfigPrincipal("user1@localized", encrypted); + + lur = new LocalLur(access, "user1@localized%" + password + ":groupA", null); + assertThat(lur.fish(princ, lur.createPerm("groupA")), is(true)); + assertThat(lur.fish(princ, lur.createPerm("groupB")), is(false)); + assertThat(lur.fish(princ, permMock), is(false)); + + princ = new ConfigPrincipal("user1@domain", encrypted); + assertThat(lur.fish(princ, lur.createPerm("groupB")), is(false)); + + princ = new ConfigPrincipal("user1@localized", "badpass"); + assertThat(lur.fish(princ, lur.createPerm("groupB")), is(false)); + + assertThat(lur.handles(null), is(false)); + + lur.fishAll(null, null); + + List<Permission> perms = new ArrayList<>(); + perms.add(lur.createPerm("groupB")); + perms.add(lur.createPerm("groupA")); + princ = new ConfigPrincipal("user1@localized", encrypted); + lur.fishAll(princ, perms); + princ = new ConfigPrincipal("user1@localized", "badpass"); + lur.fishAll(princ, perms); + + assertThat(lur.validate(null, null, null, null), is(false)); + assertThat(lur.validate("user", null, "badpass".getBytes(), null), is(false)); + assertThat(lur.validate("user1@localized", null, encrypted.getBytes(), null), is(false)); + + lur = new LocalLur(access, "user1@localized%" + password + ":groupA", null); + assertThat(lur.validate("user1@localized", Type.PASSWORD, encrypted.getBytes(), null), is(true)); + + lur = new LocalLur(access, null, "admin"); + lur = new LocalLur(access, null, "admin:user1"); + lur = new LocalLur(access, null, "admin:user1@localized"); + lur = new LocalLur(access, null, "admin:user1@localized,user2@localized%" + password + ";user:user1@localized"); + } + + public static String rot13(String input) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < input.length(); i++) { + char c = input.charAt(i); + if (c >= 'a' && c <= 'm') { + c += 13; + } else if (c >= 'A' && c <= 'M') { + c += 13; + } else if (c >= 'n' && c <= 'z') { + c -= 13; + } else if (c >= 'N' && c <= 'Z') { + c -= 13; + } + sb.append(c); + } + return sb.toString(); + } + +} + diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/lur/test/JU_LocalPermission.java b/cadi/core/src/test/java/org/onap/aaf/cadi/lur/test/JU_LocalPermission.java new file mode 100644 index 00000000..f4b051a8 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/lur/test/JU_LocalPermission.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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,Z + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.lur.test; + +import static org.junit.Assert.*; + +import static org.hamcrest.CoreMatchers.*; +import org.junit.*; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import static org.mockito.Mockito.*; + +import org.onap.aaf.cadi.lur.LocalPermission; +import org.onap.aaf.cadi.Permission; + +public class JU_LocalPermission { + + @Mock + Permission perm; + + private LocalPermission localPerm; + private String role = "Fake Role"; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + when(perm.getKey()).thenReturn(role); + + localPerm = new LocalPermission(role); + } + + @Test + public void getKeyTest() { + assertThat(localPerm.getKey(), is(role)); + } + + @Test + public void toStringTest() { + assertThat(localPerm.toString(), is(role)); + } + + @Test + public void matchTest() { + assertTrue(localPerm.match(perm)); + } + + @Test + public void permTypeTest() { + assertThat(localPerm.permType(), is("LOCAL")); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/lur/test/JU_NullLur.java b/cadi/core/src/test/java/org/onap/aaf/cadi/lur/test/JU_NullLur.java new file mode 100644 index 00000000..1a7293d3 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/lur/test/JU_NullLur.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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,Z + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.lur.test; + +import java.security.Principal; +import java.util.List; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; +import org.junit.*; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import java.lang.reflect.*; + +import org.onap.aaf.cadi.Permission; +import org.onap.aaf.cadi.lur.NullLur; + +public class JU_NullLur { + + @Mock + Principal p; + + @Mock + Permission perm; + + @Mock + List<Permission> perms; + + private NullLur nullLur; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + + nullLur = new NullLur(); + } + + @Test + public void coverageTests() throws Exception { + + Field nullClass = NullLur.class.getDeclaredField("NULL"); + nullClass.setAccessible(true); + assertThat(((Permission) nullClass.get(NullLur.class)).permType(), is("")); + assertThat(((Permission) nullClass.get(NullLur.class)).getKey(), is("")); + assertFalse(((Permission) nullClass.get(NullLur.class)).match(perm)); + + nullLur.fishAll(p, perms); + nullLur.destroy(); + + assertFalse(nullLur.fish(p, perm)); + assertFalse(nullLur.handlesExclusively(perm)); + assertFalse(nullLur.handles(p)); + assertThat(nullLur.createPerm(""), is(nullClass.get(NullLur.class))); + + StringBuilder sb = new StringBuilder(); + nullLur.clear(p, sb); + assertThat(sb.toString(), is("NullLur\n")); + assertThat(nullLur.toString(), is("NullLur\n")); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/principal/test/JU_BasicPrincipal.java b/cadi/core/src/test/java/org/onap/aaf/cadi/principal/test/JU_BasicPrincipal.java new file mode 100644 index 00000000..32d6cd0a --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/principal/test/JU_BasicPrincipal.java @@ -0,0 +1,124 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ + +package org.onap.aaf.cadi.principal.test; + +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.mock; +import org.junit.*; + +import java.io.IOException; +import java.util.Date; + +import org.onap.aaf.cadi.BasicCred; +import org.onap.aaf.cadi.Symm; +import org.onap.aaf.cadi.principal.BasicPrincipal; + +public class JU_BasicPrincipal { + + @Test + public void Constructor1Test() throws Exception { + // Test that everything works when the content doesn't contain "Basic" + BasicPrincipal bp = new BasicPrincipal("content", "domain"); + assertThat(bp.getName(), is("content")); + assertThat(bp.getCred(), is(nullValue())); + + // Test sending a user without an implicit domain + String name = "User"; + String password = "password"; + String content = name + ":" + password; + String domain = "exampledomain.com"; + String encrypted = new String(Symm.base64.encode(content.getBytes())); + bp = new BasicPrincipal("Basic " + encrypted, domain); + assertThat(bp.getShortName(), is(name)); + assertThat(bp.getName(), is(name + "@" + domain)); + assertThat(bp.getCred(), is(password.getBytes())); + + // Test sending a user with an implicit domain + String longName = name + "@" + domain + ":" + password; + encrypted = new String(Symm.base64.encode(longName.getBytes())); + bp = new BasicPrincipal("Basic " + encrypted, domain); + assertThat(bp.getShortName(), is(name)); + assertThat(bp.getName(), is(name + "@" + domain)); + assertThat(bp.getCred(), is(password.getBytes())); + + // Check that an exception is throw if no name is given in the content + try { + bp = new BasicPrincipal("Basic " + new String(Symm.base64.encode("no name".getBytes())), ""); + fail("Should have thrown an exception"); + } catch (IOException e) { + assertThat(e.getMessage(), is("Invalid Coding")); + } + } + + @Test + public void Constructor2Test() { + String name = "User"; + String password = "password"; + BasicCred bc = mock(BasicCred.class); + when(bc.getUser()).thenReturn(name); + when(bc.getCred()).thenReturn(password.getBytes()); + + BasicPrincipal bp = new BasicPrincipal(bc, "domain"); + assertThat(bp.getName(), is(name)); + assertThat(bp.getCred(), is(password.getBytes())); + } + + @Test + public void accessorsTest() throws IOException { + String name = "User"; + String password = "password"; + String content = name + ":" + password; + String domain = "exampledomain.com"; + String encrypted = new String(Symm.base64.encode(content.getBytes())); + String bearer = "bearer"; + long created = System.currentTimeMillis(); + BasicPrincipal bp = new BasicPrincipal("Basic " + encrypted, domain); + bp.setBearer(bearer); + + String expected = "Basic Authorization for " + name + "@" + domain + " evaluated on " + new Date(bp.created()).toString(); + assertTrue(Math.abs(bp.created() - created) < 10); + assertThat(bp.toString(), is(expected)); + assertThat(bp.tag(), is("BAth")); + assertThat(bp.personalName(), is(bp.getName())); + + // This test hits the abstract class BearerPrincipal + assertThat(bp.getBearer(), is(bearer)); + } + + + @Test + public void coverageTest() throws IOException { + String name = "User"; + String password = "password:with:colons"; + String content = name + ":" + password; + String encrypted = new String(Symm.base64.encode(content.getBytes())); + @SuppressWarnings("unused") + BasicPrincipal bp = new BasicPrincipal("Basic " + encrypted, "domain"); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/principal/test/JU_CachedBasicPrincipal.java b/cadi/core/src/test/java/org/onap/aaf/cadi/principal/test/JU_CachedBasicPrincipal.java new file mode 100644 index 00000000..20e1d4d9 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/principal/test/JU_CachedBasicPrincipal.java @@ -0,0 +1,124 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ + +package org.onap.aaf.cadi.principal.test; + +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.hamcrest.CoreMatchers.is; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.mock; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.io.IOException; +import java.lang.reflect.Field; + +import org.onap.aaf.cadi.BasicCred; +import org.onap.aaf.cadi.CachedPrincipal; +import org.onap.aaf.cadi.principal.CachedBasicPrincipal; +import org.onap.aaf.cadi.taf.HttpTaf; + +public class JU_CachedBasicPrincipal { + private Field creatorField; + private Field timeToLiveField; + + @Mock + private HttpTaf creator; + + private CachedPrincipal.Resp resp; + + @Before + public void setup() throws NoSuchFieldException, SecurityException { + MockitoAnnotations.initMocks(this); + + creatorField = CachedBasicPrincipal.class.getDeclaredField("creator"); + timeToLiveField = CachedBasicPrincipal.class.getDeclaredField("timeToLive"); + + creatorField.setAccessible(true); + timeToLiveField.setAccessible(true); + } + + @Test + public void Constructor1Test() throws IllegalArgumentException, IllegalAccessException { + String name = "User"; + String password = "password"; + BasicCred bc = mock(BasicCred.class); + when(bc.getUser()).thenReturn(name); + when(bc.getCred()).thenReturn(password.getBytes()); + + long timeToLive = 10000L; + long expires = System.currentTimeMillis() + timeToLive; + CachedBasicPrincipal cbp = new CachedBasicPrincipal(creator, bc, "domain", timeToLive); + + assertThat((HttpTaf)creatorField.get(cbp), is(creator)); + assertThat((Long)timeToLiveField.get(cbp), is(timeToLive)); + assertTrue(Math.abs(cbp.expires() - expires) < 10); + } + + @Test + public void Constructor2Test() throws Exception { + String name = "User"; + String password = "password"; + String content = name + ":" + password; + long timeToLive = 10000L; + long expires = System.currentTimeMillis() + timeToLive; + CachedBasicPrincipal cbp = new CachedBasicPrincipal(creator, content, "domain", timeToLive); + + assertThat((HttpTaf)creatorField.get(cbp), is(creator)); + assertThat((Long)timeToLiveField.get(cbp), is(timeToLive)); + assertTrue(Math.abs(cbp.expires() - expires) < 10); + } + + @Test + public void revalidateTest() throws IOException, IllegalArgumentException, IllegalAccessException, InterruptedException { + resp = CachedPrincipal.Resp.REVALIDATED; + when(creator.revalidate((CachedPrincipal)any(), any())).thenReturn(resp); + + String name = "User"; + String password = "password"; + String content = name + ":" + password; + long timeToLive = 10000L; + long expires = System.currentTimeMillis() + timeToLive; + CachedBasicPrincipal cbp = new CachedBasicPrincipal(creator, content, "domain", timeToLive); + + assertTrue(Math.abs(cbp.expires() - expires) < 10); + + Thread.sleep(1); + expires = System.currentTimeMillis() + timeToLive; + assertThat(cbp.revalidate(new Object()), is(resp)); + assertTrue(Math.abs(cbp.expires() - expires) < 10); + + resp = CachedPrincipal.Resp.UNVALIDATED; + when(creator.revalidate((CachedPrincipal)any(), any())).thenReturn(resp); + expires = System.currentTimeMillis() + timeToLive; + cbp = new CachedBasicPrincipal(creator, content, "domain", timeToLive); + + assertThat(cbp.revalidate(new Object()), is(resp)); + assertTrue(Math.abs(cbp.expires() - expires) < 10); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/principal/test/JU_Kind.java b/cadi/core/src/test/java/org/onap/aaf/cadi/principal/test/JU_Kind.java new file mode 100644 index 00000000..e9bd799c --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/principal/test/JU_Kind.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ + +package org.onap.aaf.cadi.principal.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import org.junit.*; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import org.onap.aaf.cadi.principal.BasicPrincipal; +import org.onap.aaf.cadi.principal.Kind; +import org.onap.aaf.cadi.principal.OAuth2FormPrincipal; +import org.onap.aaf.cadi.principal.TrustPrincipal; +import org.onap.aaf.cadi.principal.X509Principal; + +public class JU_Kind { + + @Mock + private TrustPrincipal trust; + + @Mock + private X509Principal x509; + + @Mock + private OAuth2FormPrincipal oauth; + + @Mock + private BasicPrincipal basic; + + @Before + public void setup() throws SecurityException { + MockitoAnnotations.initMocks(this); + } + + @Test + public void getKind() { + assertThat(Kind.getKind(trust), is('U')); + assertThat(Kind.getKind(x509), is('X')); + assertThat(Kind.getKind(oauth), is('O')); + assertThat(Kind.getKind(basic), is('B')); + } + + @Test + public void coverageTest() { + @SuppressWarnings("unused") + Kind kind = new Kind(); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/principal/test/JU_OAuth2FormPrincipal.java b/cadi/core/src/test/java/org/onap/aaf/cadi/principal/test/JU_OAuth2FormPrincipal.java new file mode 100644 index 00000000..c0b1c2af --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/principal/test/JU_OAuth2FormPrincipal.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ + +package org.onap.aaf.cadi.principal.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import org.junit.*; + +import org.onap.aaf.cadi.principal.OAuth2FormPrincipal; + +public class JU_OAuth2FormPrincipal { + + private String username = "user"; + private String id = "id"; + + @Test + public void accessorsTest() { + OAuth2FormPrincipal oauth = new OAuth2FormPrincipal(id, username); + assertThat(oauth.getName(), is(username)); + assertThat(oauth.client_id(), is(id)); + assertThat(oauth.tag(), is("OAuth")); + } + + @Test + public void personalNameTest() { + OAuth2FormPrincipal oauth = new OAuth2FormPrincipal(id, username); + assertThat(oauth.personalName(), is(username + "|" + id)); + + oauth = new OAuth2FormPrincipal(id, null); + assertThat(oauth.personalName(), is(id)); + + oauth = new OAuth2FormPrincipal(id, id); + assertThat(oauth.personalName(), is(id)); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/principal/test/JU_StringTagLookup.java b/cadi/core/src/test/java/org/onap/aaf/cadi/principal/test/JU_StringTagLookup.java new file mode 100644 index 00000000..cce86255 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/principal/test/JU_StringTagLookup.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ + +package org.onap.aaf.cadi.principal.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import org.junit.*; + +import org.onap.aaf.cadi.principal.StringTagLookup; + +public class JU_StringTagLookup { + + @Test + public void accessorsTest() throws Exception { + String tag = "tag"; + StringTagLookup stl = new StringTagLookup(tag); + assertThat(stl.lookup(), is(tag)); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/principal/test/JU_TaggedPrincipal.java b/cadi/core/src/test/java/org/onap/aaf/cadi/principal/test/JU_TaggedPrincipal.java new file mode 100644 index 00000000..2bbfee23 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/principal/test/JU_TaggedPrincipal.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ + +package org.onap.aaf.cadi.principal.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import org.junit.*; + +import org.onap.aaf.cadi.principal.TaggedPrincipal; +import org.onap.aaf.cadi.principal.TaggedPrincipal.TagLookup; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.principal.StringTagLookup; + +public class JU_TaggedPrincipal { + + private final String name = "stubbedName"; + private final String tag = "tag"; + + private class TaggedPrincipalStub extends TaggedPrincipal { + public TaggedPrincipalStub() { super(); } + public TaggedPrincipalStub(final TagLookup tl) { super(tl); } + @Override public String getName() { return name; } + @Override public String tag() { return null; } + } + + private class WhinyTagLookup implements TagLookup { + public WhinyTagLookup(final String tag) { } + @Override + public String lookup() throws CadiException { + throw new CadiException(); + } + } + + @Test + public void personalNameTest() { + TaggedPrincipal tp = new TaggedPrincipalStub(); + assertThat(tp.personalName(), is(name)); + + StringTagLookup stl = new StringTagLookup(tag); + tp = new TaggedPrincipalStub(stl); + assertThat(tp.personalName(), is(tag)); + + WhinyTagLookup wtl = new WhinyTagLookup(tag); + tp.setTagLookup(wtl); + assertThat(tp.personalName(), is(name)); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/principal/test/JU_TrustPrincipal.java b/cadi/core/src/test/java/org/onap/aaf/cadi/principal/test/JU_TrustPrincipal.java new file mode 100644 index 00000000..12b4bc9f --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/principal/test/JU_TrustPrincipal.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ + +package org.onap.aaf.cadi.principal.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import org.junit.*; + +import java.security.Principal; + +import org.onap.aaf.cadi.UserChain; +import org.onap.aaf.cadi.principal.TaggedPrincipal; +import org.onap.aaf.cadi.principal.TrustPrincipal; + +public class JU_TrustPrincipal { + + private final String ucName = "UserChain"; + private final String uc = "This is a UserChain"; + private final String taggedName = "TaggedPrincipal"; + private final String tag = "tag"; + private final String pName = "Principal"; + + private class UserChainPrincipalStub implements Principal, UserChain { + @Override public String userChain() { return uc; } + @Override public String getName() { return ucName; } + } + + private class TaggedPrincipalStub extends TaggedPrincipal { + public TaggedPrincipalStub() { super(); } + @Override public String getName() { return taggedName; } + @Override public String tag() { return tag; } + } + + private class PrincipalStub implements Principal { + @Override public String getName() { return pName; } + } + + @Test + public void userChainConstructorTest() { + UserChainPrincipalStub ucps = new UserChainPrincipalStub(); + TrustPrincipal tp = new TrustPrincipal(ucps, taggedName); + assertThat(tp.getName(), is(taggedName)); + assertThat(tp.userChain(), is(uc)); + assertSame(tp.original(), ucps); + assertThat(tp.tag(), is(uc)); + assertThat(tp.personalName(), is(ucName + '[' + uc + ']')); + } + + @Test + public void taggedPrincipalConstructorTest() { + TaggedPrincipal tagged = new TaggedPrincipalStub(); + TrustPrincipal tp = new TrustPrincipal(tagged, taggedName); + assertThat(tp.getName(), is(taggedName)); + assertThat(tp.userChain(), is(tag)); + assertSame(tp.original(), tagged); + assertThat(tp.tag(), is(tag)); + assertThat(tp.personalName(), is(taggedName + '[' + tag + ']')); + } + + @Test + public void principalConstructorTest() { + Principal principal = new PrincipalStub(); + TrustPrincipal tp = new TrustPrincipal(principal, pName); + assertThat(tp.getName(), is(pName)); + assertThat(tp.userChain(), is(principal.getClass().getSimpleName())); + assertSame(tp.original(), principal); + assertThat(tp.tag(), is(principal.getClass().getSimpleName())); + assertThat(tp.personalName(), is(pName + '[' + principal.getClass().getSimpleName() + ']')); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/principal/test/JU_UnAuthPrincipal.java b/cadi/core/src/test/java/org/onap/aaf/cadi/principal/test/JU_UnAuthPrincipal.java new file mode 100644 index 00000000..c0095131 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/principal/test/JU_UnAuthPrincipal.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ + +package org.onap.aaf.cadi.principal.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import org.junit.*; + +import org.onap.aaf.cadi.principal.UnAuthPrincipal; + +public class JU_UnAuthPrincipal { + + private final String name = "name"; + + @Test + public void accessorsTest() { + UnAuthPrincipal up = new UnAuthPrincipal(name); + assertThat(up.getName(), is(name)); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/principal/test/JU_X509Principal.java b/cadi/core/src/test/java/org/onap/aaf/cadi/principal/test/JU_X509Principal.java new file mode 100644 index 00000000..e62dda4f --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/principal/test/JU_X509Principal.java @@ -0,0 +1,140 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ + +package org.onap.aaf.cadi.principal.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import static org.mockito.Mockito.*; +import org.junit.*; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.io.IOException; +import java.security.Principal; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; + +import org.onap.aaf.cadi.principal.X509Principal; + +public class JU_X509Principal { + + private final String name = "x509 name"; + private final byte[] cred = "super duper secret password".getBytes(); + + @Mock + X509Certificate cert; + + @Mock + Principal subject; + + @Before + public void setup() throws CertificateEncodingException { + MockitoAnnotations.initMocks(this); + when(cert.getEncoded()).thenReturn(cred); + } + + @Test + public void constructor1Test() throws IOException { + X509Principal x509 = new X509Principal(name, cert); + // Call twice to hit both branches + assertThat(x509.getAsHeader(), is("X509 " + cred)); + assertThat(x509.getAsHeader(), is("X509 " + cred)); + assertThat(x509.toString(), is("X509 Authentication for " + name)); + assertTrue(x509.getCred().equals(cred)); + assertThat(x509.getName(), is(name)); + assertThat(x509.tag(), is("x509")); + } + + @Test + public void constructor2Test() throws IOException { + X509Principal x509 = new X509Principal(name, cert, cred); + // Call twice to hit both branches + assertThat(x509.getAsHeader(), is("X509 " + cred)); + assertThat(x509.toString(), is("X509 Authentication for " + name)); + assertTrue(x509.getCred().equals(cred)); + assertThat(x509.getName(), is(name)); + assertThat(x509.tag(), is("x509")); + } + + @Test + public void constructor3Test() throws IOException { + final String longName = "name@domain"; + when(subject.getName()).thenReturn("OU=" + longName + ",extra"); + when(cert.getSubjectDN()).thenReturn(subject); + X509Principal x509 = new X509Principal(cert, cred); + // Call twice to hit both branches + assertThat(x509.getAsHeader(), is("X509 " + cred)); + assertThat(x509.toString(), is("X509 Authentication for " + longName)); + assertTrue(x509.getCred().equals(cred)); + assertThat(x509.getName(), is(longName)); + + when(subject.getName()).thenReturn(longName + ",extra"); + when(cert.getSubjectDN()).thenReturn(subject); + try { + x509 = new X509Principal(cert, cred); + fail("Should have thrown an Exception"); + } catch(IOException e) { + assertThat(e.getMessage(), is("X509 does not have Identity as CN")); + } + + when(subject.getName()).thenReturn("OU=" + longName); + when(cert.getSubjectDN()).thenReturn(subject); + try { + x509 = new X509Principal(cert, cred); + fail("Should have thrown an Exception"); + } catch(IOException e) { + assertThat(e.getMessage(), is("X509 does not have Identity as CN")); + } + + when(subject.getName()).thenReturn("OU=" + name + ",exta"); + when(cert.getSubjectDN()).thenReturn(subject); + try { + x509 = new X509Principal(cert, cred); + fail("Should have thrown an Exception"); + } catch(IOException e) { + assertThat(e.getMessage(), is("X509 does not have Identity as CN")); + } + + } + + @Test + public void throwsTest() throws CertificateEncodingException { + when(cert.getEncoded()).thenThrow(new CertificateEncodingException()); + X509Principal x509 = new X509Principal(name, cert); + assertThat(x509.getCred(), is(nullValue())); + try { + x509.getAsHeader(); + fail("Should have thrown an Exception"); + } catch (IOException e) { + } + } + + @Test + public void getCredTest() { + X509Principal x509 = new X509Principal(name, cert); + // Call twice to hit both branches + assertTrue(x509.getCred().equals(cred)); + assertTrue(x509.getCred().equals(cred)); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/taf/basic/test/JU_BasicHttpTaf.java b/cadi/core/src/test/java/org/onap/aaf/cadi/taf/basic/test/JU_BasicHttpTaf.java new file mode 100644 index 00000000..137eab3b --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/taf/basic/test/JU_BasicHttpTaf.java @@ -0,0 +1,187 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.taf.basic.test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.when; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.io.UnsupportedEncodingException; +import java.security.Principal; +import java.util.Collection; +import java.util.Enumeration; +import java.util.Locale; +import java.util.Map; + +import javax.servlet.AsyncContext; +import javax.servlet.DispatcherType; +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletInputStream; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import javax.servlet.http.Part; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.onap.aaf.cadi.BasicCred; +import org.onap.aaf.cadi.CachedPrincipal; +import org.onap.aaf.cadi.CachedPrincipal.Resp; +import org.onap.aaf.cadi.CredVal; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.Symm; +import org.onap.aaf.cadi.Taf.LifeForm; +import org.onap.aaf.cadi.taf.basic.BasicHttpTaf; + +public class JU_BasicHttpTaf { + + private final static String realm = "realm"; + private final static String id = "id"; + private final static String addr = "addr"; + + private final static String name = "User"; + private final static String password = "password"; + private final static String content = name + ":" + password; + private static String encrypted; + + private final static long timeToLive = 10000L; + + private PropAccess access; + + @Mock private HttpServletResponse respMock; + @Mock private HttpServletRequest reqMock; + @Mock private CredVal rbacMock; + @Mock private CachedPrincipal princMock; + + @Before + public void setup() throws IOException { + MockitoAnnotations.initMocks(this); + access = new PropAccess(new PrintStream(new ByteArrayOutputStream()), new String[0]); + encrypted = new String(Symm.base64.encode(content.getBytes())); + } + + @Test + public void test() { + BasicHttpTaf taf = new BasicHttpTaf(access, rbacMock, realm, timeToLive, true); + BasicCredStub bcstub = new BasicCredStub(); + assertThat(taf.validate(LifeForm.SBLF, bcstub, respMock), is(not(nullValue()))); + + assertThat(taf.validate(LifeForm.SBLF, reqMock, respMock), is(not(nullValue()))); + + when(reqMock.getHeader("Authorization")).thenReturn("test"); + assertThat(taf.validate(LifeForm.SBLF, reqMock, respMock), is(not(nullValue()))); + + when(reqMock.getHeader("Authorization")).thenReturn("Basic " + encrypted); + assertThat(taf.validate(LifeForm.SBLF, reqMock, respMock), is(not(nullValue()))); + + assertThat(taf.revalidate(princMock, "state"), is(Resp.NOT_MINE)); + + assertThat(taf.toString(), is("Basic Auth enabled on realm: " + realm)); + } + + private class BasicCredStub implements HttpServletRequest, BasicCred { + @Override public String getUser() { return id; } + @Override public String getRemoteAddr() { return addr; } + + @Override public AsyncContext getAsyncContext() { return null; } + @Override public Object getAttribute(String arg0) { return null; } + @Override public Enumeration<String> getAttributeNames() { return null; } + @Override public String getCharacterEncoding() { return null; } + @Override public int getContentLength() { return 0; } + @Override public String getContentType() { return null; } + @Override public DispatcherType getDispatcherType() { return null; } + @Override public ServletInputStream getInputStream() throws IOException { return null; } + @Override public String getLocalAddr() { return null; } + @Override public String getLocalName() { return null; } + @Override public int getLocalPort() { return 0; } + @Override public Locale getLocale() { return null; } + @Override public Enumeration<Locale> getLocales() { return null; } + @Override public String getParameter(String arg0) { return null; } + @Override public Map<String, String[]> getParameterMap() { return null; } + @Override public Enumeration<String> getParameterNames() { return null; } + @Override public String[] getParameterValues(String arg0) { return null; } + @Override public String getProtocol() { return null; } + @Override public BufferedReader getReader() throws IOException { return null; } + @Override public String getRealPath(String arg0) { return null; } + @Override public String getRemoteHost() { return null; } + @Override public int getRemotePort() { return 0; } + @Override public RequestDispatcher getRequestDispatcher(String arg0) { return null; } + @Override public String getScheme() { return null; } + @Override public String getServerName() { return null; } + @Override public int getServerPort() { return 0; } + @Override public ServletContext getServletContext() { return null; } + @Override public boolean isAsyncStarted() { return false; } + @Override public boolean isAsyncSupported() { return false; } + @Override public boolean isSecure() { return false; } + @Override public void removeAttribute(String arg0) { } + @Override public void setAttribute(String arg0, Object arg1) { } + @Override public void setCharacterEncoding(String arg0) throws UnsupportedEncodingException { } + @Override public AsyncContext startAsync() throws IllegalStateException { return null; } + @Override public AsyncContext startAsync(ServletRequest arg0, ServletResponse arg1) throws IllegalStateException { return null; } + @Override public byte[] getCred() { return null; } + @Override public void setUser(String user) { } + @Override public void setCred(byte[] passwd) { } + @Override public boolean authenticate(HttpServletResponse arg0) throws IOException, ServletException { return false; } + @Override public String getAuthType() { return null; } + @Override public String getContextPath() { return null; } + @Override public Cookie[] getCookies() { return null; } + @Override public long getDateHeader(String arg0) { return 0; } + @Override public String getHeader(String arg0) { return null; } + @Override public Enumeration<String> getHeaderNames() { return null; } + @Override public Enumeration<String> getHeaders(String arg0) { return null; } + @Override public int getIntHeader(String arg0) { return 0; } + @Override public String getMethod() { return null; } + @Override public Part getPart(String arg0) throws IOException, ServletException { return null; } + @Override public Collection<Part> getParts() throws IOException, ServletException { return null; } + @Override public String getPathInfo() { return null; } + @Override public String getPathTranslated() { return null; } + @Override public String getQueryString() { return null; } + @Override public String getRemoteUser() { return null; } + @Override public String getRequestURI() { return null; } + @Override public StringBuffer getRequestURL() { return null; } + @Override public String getRequestedSessionId() { return null; } + @Override public String getServletPath() { return null; } + @Override public HttpSession getSession() { return null; } + @Override public HttpSession getSession(boolean arg0) { return null; } + @Override public Principal getUserPrincipal() { return null; } + @Override public boolean isRequestedSessionIdFromCookie() { return false; } + @Override public boolean isRequestedSessionIdFromURL() { return false; } + @Override public boolean isRequestedSessionIdFromUrl() { return false; } + @Override public boolean isRequestedSessionIdValid() { return false; } + @Override public boolean isUserInRole(String arg0) { return false; } + @Override public void login(String arg0, String arg1) throws ServletException { } + @Override public void logout() throws ServletException { } + } +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/taf/basic/test/JU_BasicHttpTafResp.java b/cadi/core/src/test/java/org/onap/aaf/cadi/taf/basic/test/JU_BasicHttpTafResp.java new file mode 100644 index 00000000..8eba1faf --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/taf/basic/test/JU_BasicHttpTafResp.java @@ -0,0 +1,67 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.taf.basic.test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; + +import javax.servlet.http.HttpServletResponse; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.principal.TaggedPrincipal; +import org.onap.aaf.cadi.taf.TafResp.RESP; +import org.onap.aaf.cadi.taf.basic.BasicHttpTafResp; + +public class JU_BasicHttpTafResp { + + private final static String realm = "realm"; + private final static String description = "description"; + + private PropAccess access; + + @Mock private HttpServletResponse respMock; + @Mock private TaggedPrincipal princMock; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + access = new PropAccess(new PrintStream(new ByteArrayOutputStream()), new String[0]); + } + + @Test + public void test() throws IOException { + BasicHttpTafResp tafResp = new BasicHttpTafResp(access, princMock, description, RESP.IS_AUTHENTICATED, respMock, realm, false); + + assertThat(tafResp.authenticate(), is(RESP.HTTP_REDIRECT_INVOKED)); + assertThat(tafResp.isAuthenticated(), is (RESP.IS_AUTHENTICATED)); + assertThat(tafResp.isFailedAttempt(), is(false)); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/taf/cert/test/JU_X509HttpTafResp.java b/cadi/core/src/test/java/org/onap/aaf/cadi/taf/cert/test/JU_X509HttpTafResp.java new file mode 100644 index 00000000..36f17ef1 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/taf/cert/test/JU_X509HttpTafResp.java @@ -0,0 +1,63 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.taf.cert.test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.principal.TaggedPrincipal; +import org.onap.aaf.cadi.taf.TafResp.RESP; +import org.onap.aaf.cadi.taf.cert.X509HttpTafResp; + +public class JU_X509HttpTafResp { + + private final static String description = "description"; + private final static RESP status = RESP.IS_AUTHENTICATED; + + private PropAccess access; + + @Mock private TaggedPrincipal princMock; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + access = new PropAccess(new PrintStream(new ByteArrayOutputStream()), new String[0]); + } + + @Test + public void test() throws IOException { + X509HttpTafResp resp = new X509HttpTafResp(access, princMock, description, status); + assertThat(resp.authenticate(), is(RESP.TRY_ANOTHER_TAF)); + assertThat(resp.isAuthenticated(), is(status)); + assertThat(resp.toString(), is(status.name())); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/taf/dos/test/JU_DenialOfServiceTaf.java b/cadi/core/src/test/java/org/onap/aaf/cadi/taf/dos/test/JU_DenialOfServiceTaf.java new file mode 100644 index 00000000..ce49654b --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/taf/dos/test/JU_DenialOfServiceTaf.java @@ -0,0 +1,369 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.taf.dos.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import static org.mockito.Mockito.*; +import org.junit.*; +import org.mockito.*; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.CachedPrincipal.Resp; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.taf.TafResp; +import org.onap.aaf.cadi.Taf.LifeForm; +import org.onap.aaf.cadi.taf.dos.DenialOfServiceTaf; +import org.onap.aaf.cadi.taf.dos.DenialOfServiceTaf.Counter; + +public class JU_DenialOfServiceTaf { + + @Mock + HttpServletResponse respMock; + + @Mock + HttpServletRequest reqMock1; + + @Mock + HttpServletRequest reqMock2; + + @Mock + HttpServletRequest reqMock3; + + @Mock + Access accessMock; + + private File dosIPFile; + private File dosIDFile; + private File dosDir; + private final String dosDirName = "test"; + + private final String id1 = "id1"; + private final String id2 = "id2"; + + private final String ip1 = "111.111.111.111"; + private final String ip2 = "222.222.222.222"; + + @Before + public void setup() throws IOException { + MockitoAnnotations.initMocks(this); + + dosDir = new File(dosDirName); + dosDir.mkdirs(); + dosIPFile = new File(dosDirName, "/dosIP"); + dosIDFile = new File(dosDirName, "/dosID"); + dosIPFile.delete(); + dosIDFile.delete(); + + when(accessMock.getProperty(Config.AAF_DATA_DIR, null)).thenReturn(dosDirName); + when(reqMock1.getRemoteAddr()).thenReturn(ip1); + when(reqMock2.getRemoteAddr()).thenReturn(ip2); + + setPrivateField(DenialOfServiceTaf.class, "deniedIP", null); + setPrivateField(DenialOfServiceTaf.class, "deniedID", null); + setPrivateField(DenialOfServiceTaf.class, "dosIP", null); + setPrivateField(DenialOfServiceTaf.class, "dosID", null); + } + + @After + public void tearDown() { + dosIPFile = new File(dosDirName, "/dosIP"); + dosIDFile = new File(dosDirName, "/dosID"); + dosIPFile.delete(); + dosIDFile.delete(); + } + + @Test + public void constructorTest() throws CadiException { + @SuppressWarnings("unused") + DenialOfServiceTaf dost; + + // coverage... + when(accessMock.getProperty(Config.AAF_DATA_DIR, null)).thenReturn(null); + dost = new DenialOfServiceTaf(accessMock); + + when(accessMock.getProperty(Config.AAF_DATA_DIR, null)).thenReturn(dosDirName); + dost = new DenialOfServiceTaf(accessMock); + + // more coverage... + dost = new DenialOfServiceTaf(accessMock); + + // more coverage... + setPrivateField(DenialOfServiceTaf.class, "dosID", null); + dost = new DenialOfServiceTaf(accessMock); + } + + @Test + public void validateTest() throws CadiException { + DenialOfServiceTaf dost; + TafResp tafResp; + + dost = new DenialOfServiceTaf(accessMock); + tafResp = dost.validate(LifeForm.SBLF, reqMock1, respMock); + + assertThat(tafResp.desc(), is("DenialOfServiceTaf is not processing this transaction: This Transaction is not denied")); + + assertThat(DenialOfServiceTaf.denyIP(ip1), is(true)); + + tafResp = dost.validate(LifeForm.SBLF, reqMock1, respMock); + assertThat(tafResp.desc(), is(ip1 + " is on the IP Denial list")); + + tafResp = dost.validate(LifeForm.SBLF, reqMock2, respMock); + assertThat(tafResp.desc(), is("DenialOfServiceTaf is not processing this transaction: This Transaction is not denied")); + } + + @Test + public void revalidateTest() throws CadiException { + DenialOfServiceTaf dost = new DenialOfServiceTaf(accessMock); + Resp resp = dost.revalidate(null, null); + assertThat(resp, is(Resp.NOT_MINE)); + } + + @Test + public void denyIPTest() throws CadiException { + assertThat(DenialOfServiceTaf.isDeniedIP(ip1), is(nullValue())); + assertThat(DenialOfServiceTaf.denyIP(ip1), is(true)); // true because it's been added + assertThat(DenialOfServiceTaf.denyIP(ip2), is(true)); // true because it's been added + assertThat(DenialOfServiceTaf.denyIP(ip1), is(false)); // false because it's already been added + assertThat(DenialOfServiceTaf.denyIP(ip2), is(false)); // false because it's already been added + + Counter counter; + counter = DenialOfServiceTaf.isDeniedIP(ip1); + assertThat(counter.getName(), is(ip1)); + assertThat(counter.getCount(), is(0)); + assertThat(counter.getLast(), is(0L)); + assertThat(counter.toString(), is(ip1 + " is on the denied list, but has not attempted Access" )); + + DenialOfServiceTaf dost = new DenialOfServiceTaf(accessMock); + dost.validate(LifeForm.SBLF, reqMock1, respMock); + long approxTime = System.currentTimeMillis(); + + counter = DenialOfServiceTaf.isDeniedIP(ip1); + assertThat(counter.getName(), is(ip1)); + assertThat(counter.getCount(), is(1)); + assertThat((Math.abs(approxTime - counter.getLast()) < 10), is(true)); + assertThat(counter.toString().contains(ip1), is(true)); + assertThat(counter.toString().contains(" has been denied 1 times since "), is(true)); + assertThat(counter.toString().contains(". Last denial was "), is(true)); + + // coverage... + dost.validate(LifeForm.SBLF, reqMock1, respMock); + + assertThat(DenialOfServiceTaf.removeDenyIP(ip1), is(true)); + assertThat(DenialOfServiceTaf.removeDenyIP(ip1), is(false)); + assertThat(DenialOfServiceTaf.removeDenyIP(ip2), is(true)); + assertThat(DenialOfServiceTaf.removeDenyIP(ip2), is(false)); + } + + @Test + public void denyIDTest() throws CadiException { + assertThat(DenialOfServiceTaf.isDeniedID(id1), is(nullValue())); + assertThat(DenialOfServiceTaf.denyID(id1), is(true)); // true because it's been added + assertThat(DenialOfServiceTaf.denyID(id2), is(true)); // true because it's been added + assertThat(DenialOfServiceTaf.denyID(id1), is(false)); // false because it's already been added + assertThat(DenialOfServiceTaf.denyID(id2), is(false)); // false because it's already been added + + Counter counter; + counter = DenialOfServiceTaf.isDeniedID(id1); + assertThat(counter.getName(), is(id1)); + assertThat(counter.getCount(), is(0)); + assertThat(counter.getLast(), is(0L)); + + assertThat(DenialOfServiceTaf.removeDenyID(id1), is(true)); + assertThat(DenialOfServiceTaf.removeDenyID(id1), is(false)); + assertThat(DenialOfServiceTaf.removeDenyID(id2), is(true)); + assertThat(DenialOfServiceTaf.removeDenyID(id2), is(false)); + } + + @Test + public void reportTest() throws CadiException { + DenialOfServiceTaf dost = new DenialOfServiceTaf(accessMock); + List<String> denials = dost.report(); + assertThat(denials.size(), is(0)); + + DenialOfServiceTaf.denyID(id1); + DenialOfServiceTaf.denyID(id2); + + DenialOfServiceTaf.denyIP(ip1); + DenialOfServiceTaf.denyIP(ip2); + + denials = dost.report(); + assertThat(denials.size(), is(4)); + for (String denied : denials) { + switch (denied.split(" ", 2)[0]) { + case ip1: + case ip2: + case id1: + case id2: + break; + default: + fail("The line: [" + denied + "] shouldn't be in the report"); + } + } + } + + @Test + public void respDenyIDTest() { + TafResp tafResp = DenialOfServiceTaf.respDenyID(accessMock, id1); + assertThat(tafResp.desc(), is(id1 + " is on the Identity Denial list")); + } + + @Test + public void ipFileIOTest() throws CadiException, IOException { + @SuppressWarnings("unused") + DenialOfServiceTaf dost; + + dosIPFile.createNewFile(); + + // coverage... + DenialOfServiceTaf.denyIP(ip1); + DenialOfServiceTaf.removeDenyIP(ip1); + + dost = new DenialOfServiceTaf(accessMock); + DenialOfServiceTaf.denyIP(ip1); + DenialOfServiceTaf.denyIP(ip2); + // coverage... + DenialOfServiceTaf.denyIP(ip2); + + String contents = readContentsFromFile(dosIPFile); + assertThat(contents.contains(ip1), is(true)); + assertThat(contents.contains(ip2), is(true)); + + // Removing all ips should delete the file + assertThat(dosIPFile.exists(), is(true)); + DenialOfServiceTaf.removeDenyIP(ip1); + DenialOfServiceTaf.removeDenyIP(ip2); + assertThat(dosIPFile.exists(), is(false)); + + dosIPFile.createNewFile(); + + DenialOfServiceTaf.denyIP(ip1); + DenialOfServiceTaf.denyIP(ip2); + + setPrivateField(DenialOfServiceTaf.class, "dosIP", null); + dost = new DenialOfServiceTaf(accessMock); + + contents = readContentsFromFile(dosIPFile); + assertThat(contents.contains(ip1), is(true)); + assertThat(contents.contains(ip2), is(true)); + + dosIPFile.delete(); + + // coverage... + setPrivateField(DenialOfServiceTaf.class, "deniedIP", null); + DenialOfServiceTaf.denyIP(ip1); + dosIPFile.delete(); + DenialOfServiceTaf.removeDenyIP(ip1); + + // coverage... + dosIPFile.delete(); + setPrivateField(DenialOfServiceTaf.class, "dosIP", null); + dost = new DenialOfServiceTaf(accessMock); + } + + @Test + public void idFileIOTest() throws CadiException, IOException { + @SuppressWarnings("unused") + DenialOfServiceTaf dost; + + dosIDFile.createNewFile(); + + // coverage... + DenialOfServiceTaf.denyID(id1); + DenialOfServiceTaf.removeDenyID(id1); + + dost = new DenialOfServiceTaf(accessMock); + DenialOfServiceTaf.denyID(id1); + DenialOfServiceTaf.denyID(id2); + // coverage... + DenialOfServiceTaf.denyID(id2); + + String contents = readContentsFromFile(dosIDFile); + assertThat(contents.contains(id1), is(true)); + assertThat(contents.contains(id2), is(true)); + + // Removing all ids should delete the file + assertThat(dosIDFile.exists(), is(true)); + DenialOfServiceTaf.removeDenyID(id1); + DenialOfServiceTaf.removeDenyID(id2); + assertThat(dosIDFile.exists(), is(false)); + + dosIDFile.createNewFile(); + + DenialOfServiceTaf.denyID(id1); + DenialOfServiceTaf.denyID(id2); + + setPrivateField(DenialOfServiceTaf.class, "dosID", null); + dost = new DenialOfServiceTaf(accessMock); + + contents = readContentsFromFile(dosIDFile); + assertThat(contents.contains(id1), is(true)); + assertThat(contents.contains(id2), is(true)); + + dosIDFile.delete(); + + // coverage... + setPrivateField(DenialOfServiceTaf.class, "deniedID", null); + DenialOfServiceTaf.denyID(id1); + dosIDFile.delete(); + DenialOfServiceTaf.removeDenyID(id1); + + // coverage... + dosIDFile.delete(); + setPrivateField(DenialOfServiceTaf.class, "dosID", null); + dost = new DenialOfServiceTaf(accessMock); + } + + private void setPrivateField(Class<?> clazz, String fieldName, Object value) { + try { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + field.set(null, value); + field.setAccessible(false); + } catch(Exception e) { + System.err.println("Could not set field [" + fieldName + "] to " + value); + } + } + + private String readContentsFromFile(File file) throws IOException { + BufferedReader br = new BufferedReader(new FileReader(file)); + StringBuilder sb = new StringBuilder(); + String line; + while ((line = br.readLine()) != null) { + sb.append(line); + } + br.close(); + return sb.toString(); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/taf/dos/test/JU_DenialOfServiceTafResp.java b/cadi/core/src/test/java/org/onap/aaf/cadi/taf/dos/test/JU_DenialOfServiceTafResp.java new file mode 100644 index 00000000..34b2a513 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/taf/dos/test/JU_DenialOfServiceTafResp.java @@ -0,0 +1,57 @@ +/** + * + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.taf.dos.test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; + +import org.junit.Before; +import org.junit.Test; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.taf.TafResp.RESP; +import org.onap.aaf.cadi.taf.dos.DenialOfServiceTafResp; + +public class JU_DenialOfServiceTafResp { + + private final static String description = "description"; + private final static RESP status = RESP.IS_AUTHENTICATED; + + private PropAccess access; + + @Before + public void setup() { + access = new PropAccess(new PrintStream(new ByteArrayOutputStream()), new String[0]); + } + + @Test + public void test() throws IOException { + DenialOfServiceTafResp resp = new DenialOfServiceTafResp(access, status, description); + assertThat(resp.isAuthenticated(), is(status)); + assertThat(resp.authenticate(), is(status)); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/taf/test/JU_AbsTafResp.java b/cadi/core/src/test/java/org/onap/aaf/cadi/taf/test/JU_AbsTafResp.java new file mode 100644 index 00000000..6d0c04b7 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/taf/test/JU_AbsTafResp.java @@ -0,0 +1,87 @@ +/******************************************************************************* +* ============LICENSE_START==================================================== +* * org.onap.aaf +* * =========================================================================== +* * Copyright © 2017 AT&T Intellectual Property. All rights reserved. +* * =========================================================================== +* * 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. +* * ============LICENSE_END==================================================== +* * +* * +******************************************************************************/ + +package org.onap.aaf.cadi.taf.test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; + +import org.junit.Before; +import org.junit.Test; +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.principal.TaggedPrincipal; +import org.onap.aaf.cadi.taf.AbsTafResp; +import org.onap.aaf.cadi.taf.TafResp.RESP; + +public class JU_AbsTafResp { + + private static final String name = "name"; + private static final String tag = "tag"; + private static final String description = "description"; + + private Access access; + private TaggedPrincipal taggedPrinc; + + @Before + public void setup() { + access = new PropAccess(new PrintStream(new ByteArrayOutputStream()), new String[0]); + taggedPrinc = new TaggedPrincipal() { + @Override public String getName() { return name; } + @Override public String tag() { return tag; } + }; + } + + @Test + public void test() { + AbsTafResp tafResp = new AbsTafResp(access, taggedPrinc, description) { + @Override public RESP authenticate() throws IOException { + return null; + } + }; + + assertThat(tafResp.isValid(), is(true)); + assertThat(tafResp.desc(), is(description)); + assertThat(tafResp.isAuthenticated(), is(RESP.IS_AUTHENTICATED)); + assertThat(tafResp.getPrincipal(), is(taggedPrinc)); + assertThat(tafResp.getAccess(), is(access)); + assertThat(tafResp.isFailedAttempt(), is(false)); + + tafResp = new AbsTafResp(null, null, null) { + @Override public RESP authenticate() throws IOException { + return null; + } + }; + + assertThat(tafResp.isValid(), is(false)); + assertThat(tafResp.isAuthenticated(), is(RESP.TRY_ANOTHER_TAF)); + assertThat(tafResp.getPrincipal(), is(nullValue())); + assertThat(tafResp.getAccess(), is(nullValue())); + assertThat(tafResp.isFailedAttempt(), is(false)); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/taf/test/JU_EpiTaf.java b/cadi/core/src/test/java/org/onap/aaf/cadi/taf/test/JU_EpiTaf.java new file mode 100644 index 00000000..a1190590 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/taf/test/JU_EpiTaf.java @@ -0,0 +1,104 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.taf.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import org.junit.*; + +import java.io.IOException; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.Taf; +import org.onap.aaf.cadi.taf.TafResp; +import org.onap.aaf.cadi.taf.TafResp.RESP; + +import org.onap.aaf.cadi.taf.EpiTaf; +import org.onap.aaf.cadi.taf.NullTaf; +import org.onap.aaf.cadi.Taf.LifeForm; +import org.onap.aaf.cadi.principal.TaggedPrincipal; + +public class JU_EpiTaf { + + @Test(expected = CadiException.class) + @SuppressWarnings("unused") + public void constructorTest() throws CadiException { + EpiTaf et = new EpiTaf(); + } + + @Test + public void validateTryAnotherTest() throws CadiException { + EpiTaf et = new EpiTaf(new TryAnotherTaf()); + TafResp output = et.validate(LifeForm.CBLF); + assertThat(output.isAuthenticated(), is(RESP.NO_FURTHER_PROCESSING)); + } + + @Test + public void validateTryAuthenticatingTest() throws CadiException { + EpiTaf et = new EpiTaf(new TryAuthenticatingTaf(), new TryAuthenticatingTaf()); + TafResp output = et.validate(LifeForm.CBLF); + assertThat(output.isAuthenticated(), is(RESP.TRY_AUTHENTICATING)); + output = et.validate(LifeForm.CBLF); + assertThat(output.isAuthenticated(), is(RESP.TRY_AUTHENTICATING)); + } + + @Test + public void validateDefaultCaseTest() throws CadiException { + EpiTaf et = new EpiTaf(new NullTaf()); + TafResp output = et.validate(LifeForm.CBLF); + assertThat(output.isAuthenticated(), is(RESP.NO_FURTHER_PROCESSING)); + } + + class TryAnotherTafResp implements TafResp { + @Override public boolean isValid() { return false; } + @Override public String desc() { return null; } + @Override public RESP isAuthenticated() { return RESP.TRY_ANOTHER_TAF; } + @Override public RESP authenticate() throws IOException { return null; } + @Override public TaggedPrincipal getPrincipal() { return null; } + @Override public Access getAccess() { return null; } + @Override public boolean isFailedAttempt() { return false; } + } + + class TryAnotherTaf implements Taf { + @Override public TafResp validate(LifeForm reading, String ... info) { return new TryAnotherTafResp(); } + } + + class TryAuthenticatingResp implements TafResp { + @Override public boolean isValid() { return false; } + @Override public String desc() { return null; } + @Override public RESP isAuthenticated() { return RESP.TRY_AUTHENTICATING; } + @Override public RESP authenticate() throws IOException { return null; } + @Override public TaggedPrincipal getPrincipal() { return null; } + @Override public Access getAccess() { return null; } + @Override public boolean isFailedAttempt() { return false; } + } + + class TryAuthenticatingTaf implements Taf { + @Override public TafResp validate(LifeForm reading, String ... info) { return new TryAuthenticatingResp(); } + } + + class EpiTafStub extends EpiTaf { + public EpiTafStub() throws CadiException { } + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/taf/test/JU_HttpEpiTaf.java b/cadi/core/src/test/java/org/onap/aaf/cadi/taf/test/JU_HttpEpiTaf.java new file mode 100644 index 00000000..93a20474 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/taf/test/JU_HttpEpiTaf.java @@ -0,0 +1,145 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.taf.test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.when; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.net.URI; +import java.net.URISyntaxException; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.CachedPrincipal.Resp; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.Locator; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.Taf.LifeForm; +import org.onap.aaf.cadi.TrustChecker; +import org.onap.aaf.cadi.taf.HttpEpiTaf; +import org.onap.aaf.cadi.taf.HttpTaf; +import org.onap.aaf.cadi.taf.NullTaf; +import org.onap.aaf.cadi.taf.Redirectable; +import org.onap.aaf.cadi.taf.TafResp; +import org.onap.aaf.cadi.taf.TafResp.RESP; + +public class JU_HttpEpiTaf { + + private PropAccess access; + + @Mock private Locator<URI> locMock; + @Mock private TrustChecker trustCheckerMock; + @Mock private HttpServletRequest reqMock; + @Mock private HttpServletResponse respMock; + @Mock private HttpTaf tafMock; + @Mock private TafResp trespMock; + @Mock private Redirectable redirMock; + + @Before + public void setup() throws URISyntaxException { + MockitoAnnotations.initMocks(this); + + access = new PropAccess(new PrintStream(new ByteArrayOutputStream()), new String[0]); + } + + @Test + public void test() throws Exception { + HttpEpiTaf taf; + try { + taf = new HttpEpiTaf(access, locMock, trustCheckerMock); + fail("Should've thrown an exception"); + } catch (CadiException e) { + assertThat(e.getMessage(), is("Need at least one HttpTaf implementation in constructor")); + } + + taf = new HttpEpiTaf(access, locMock, trustCheckerMock, new NullTaf()); + taf.validate(LifeForm.CBLF, reqMock, respMock); + + // Coverage of tricorderScan + taf.validate(LifeForm.LFN, reqMock, respMock); + when(reqMock.getHeader("User-Agent")).thenReturn("Non-mozilla-header"); + taf.validate(LifeForm.LFN, reqMock, respMock); + when(reqMock.getHeader("User-Agent")).thenReturn("Mozilla-header"); + taf.validate(LifeForm.LFN, reqMock, respMock); + + access.setLogLevel(Level.DEBUG); + taf.validate(LifeForm.CBLF, reqMock, respMock); + + when(tafMock.validate(LifeForm.CBLF, reqMock, respMock)).thenReturn(trespMock); + when(trespMock.isAuthenticated()).thenReturn(RESP.TRY_ANOTHER_TAF); + taf = new HttpEpiTaf(access, locMock, trustCheckerMock, tafMock); + taf.validate(LifeForm.CBLF, reqMock, respMock); + + when(trespMock.isAuthenticated()).thenReturn(RESP.IS_AUTHENTICATED); + taf.validate(LifeForm.CBLF, reqMock, respMock); + + when(trespMock.isAuthenticated()).thenReturn(RESP.TRY_AUTHENTICATING); + taf.validate(LifeForm.CBLF, reqMock, respMock); + + taf = new HttpEpiTaf(access, locMock, trustCheckerMock, tafMock, tafMock); + taf.validate(LifeForm.CBLF, reqMock, respMock); + + when(tafMock.validate(LifeForm.CBLF, reqMock, respMock)).thenReturn(redirMock); + when(redirMock.isAuthenticated()).thenReturn(RESP.TRY_AUTHENTICATING); + taf.validate(LifeForm.CBLF, reqMock, respMock); + + taf = new HttpEpiTaf(access, locMock, trustCheckerMock, tafMock, tafMock); + taf.validate(LifeForm.CBLF, reqMock, respMock); + + taf = new HttpEpiTaf(access, locMock, trustCheckerMock, tafMock); + taf.validate(LifeForm.CBLF, reqMock, respMock); + + taf = new HttpEpiTaf(access, locMock, null, tafMock); + when(redirMock.isAuthenticated()).thenReturn(RESP.IS_AUTHENTICATED); + try { + taf.validate(LifeForm.CBLF, reqMock, respMock); + fail("Should've thrown an exception"); + } catch (Exception e) { + } + + assertThat(taf.revalidate(null), is(false)); + assertThat(taf.revalidate(null), is(false)); + + when(tafMock.revalidate(null, null)).thenReturn(Resp.NOT_MINE); + assertThat(taf.revalidate(null, null), is(Resp.NOT_MINE)); + when(tafMock.revalidate(null, null)).thenReturn(Resp.REVALIDATED); + assertThat(taf.revalidate(null, null), is(Resp.REVALIDATED)); + + when(tafMock.revalidate(null, null)).thenReturn(Resp.NOT_MINE).thenReturn(Resp.NOT_MINE).thenReturn(Resp.REVALIDATED); + taf = new HttpEpiTaf(access, locMock, trustCheckerMock, tafMock, tafMock, tafMock); + assertThat(taf.revalidate(null, null), is(Resp.REVALIDATED)); + + taf.toString(); + + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/taf/test/JU_LoginPageTafResp.java b/cadi/core/src/test/java/org/onap/aaf/cadi/taf/test/JU_LoginPageTafResp.java new file mode 100644 index 00000000..3124bbd4 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/taf/test/JU_LoginPageTafResp.java @@ -0,0 +1,101 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.cadi.taf.test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.when; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.http.HttpServletResponse; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.Locator; +import org.onap.aaf.cadi.Locator.Item; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.taf.LoginPageTafResp; +import org.onap.aaf.cadi.taf.Redirectable; +import org.onap.aaf.cadi.taf.TafResp; +import org.onap.aaf.cadi.taf.TafResp.RESP; + +public class JU_LoginPageTafResp { + + private static final String uriString = "example.com"; + + private URI uri; + private Access access; + private List<Redirectable> redirectables; + + @Mock private HttpServletResponse respMock; + @Mock private Locator<URI> locatorMock; + @Mock private Redirectable redirMock; + + @Before + public void setup() throws URISyntaxException { + MockitoAnnotations.initMocks(this); + + access = new PropAccess(new PrintStream(new ByteArrayOutputStream()), new String[0]); + + redirectables = new ArrayList<>(); + uri = new URI(uriString); + } + + @Test + public void test() throws LocatorException, IOException { + TafResp resp; + resp = LoginPageTafResp.create(access, null, respMock, redirectables); + assertThat(resp.desc(), is("All Authentication denied")); + + redirectables.add(redirMock); + redirectables.add(redirMock); + resp = LoginPageTafResp.create(access, null, respMock, redirectables); + assertThat((Redirectable)resp, is(redirMock)); + + resp = LoginPageTafResp.create(access, locatorMock, respMock, redirectables); + assertThat(resp.desc(), is("All Authentication denied")); + + when(locatorMock.get((Item)any())).thenReturn(uri); + resp = LoginPageTafResp.create(access, locatorMock, respMock, redirectables); + assertThat(resp.desc(), is("Multiple Possible HTTP Logins available. Redirecting to Login Choice Page")); + assertThat(resp.authenticate(), is(RESP.HTTP_REDIRECT_INVOKED)); + assertThat(resp.isAuthenticated(), is(RESP.TRY_AUTHENTICATING)); + + redirectables = new ArrayList<>(); + resp = LoginPageTafResp.create(access, locatorMock, respMock, redirectables); + assertThat(resp.desc(), is("All Authentication denied")); + + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/taf/test/JU_NullTaf.java b/cadi/core/src/test/java/org/onap/aaf/cadi/taf/test/JU_NullTaf.java new file mode 100644 index 00000000..f42184df --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/taf/test/JU_NullTaf.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.taf.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import org.junit.*; + +import java.io.IOException; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.CachedPrincipal.Resp; +import org.onap.aaf.cadi.taf.TafResp; +import org.onap.aaf.cadi.taf.TafResp.RESP; + +import org.onap.aaf.cadi.taf.NullTaf; + +public class JU_NullTaf { + + @Test + public void test() throws IOException { + NullTaf nt = new NullTaf(); + TafResp singleton1 = nt.validate(null); + TafResp singleton2 = nt.validate(null, null, null); + Resp singleton3 = nt.revalidate(null, null); + + assertThat(singleton1, is(singleton2)); + + assertFalse(singleton1.isValid()); + + assertThat(singleton1.isAuthenticated(), is(RESP.NO_FURTHER_PROCESSING)); + + assertThat(singleton1.desc(), is("All Authentication denied")); + + assertThat(singleton1.authenticate(), is(RESP.NO_FURTHER_PROCESSING)); + + assertThat(singleton1.getPrincipal(), is(nullValue())); + + assertThat(singleton1.getAccess(), is(Access.NULL)); + + assertTrue(singleton1.isFailedAttempt()); + + assertThat(singleton3, is(Resp.NOT_MINE)); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/taf/test/JU_PuntTafResp.java b/cadi/core/src/test/java/org/onap/aaf/cadi/taf/test/JU_PuntTafResp.java new file mode 100644 index 00000000..516f4044 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/taf/test/JU_PuntTafResp.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.taf.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import org.junit.*; + +import java.io.IOException; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.taf.TafResp.RESP; + +import org.onap.aaf.cadi.taf.PuntTafResp; + + +public class JU_PuntTafResp { + + @Test + public void test() throws IOException { + String name = "name"; + String explanation = "example explanation"; + + PuntTafResp punt = new PuntTafResp(name, explanation); + + assertFalse(punt.isValid()); + assertThat(punt.isAuthenticated(), is(RESP.TRY_ANOTHER_TAF)); + assertThat(punt.desc(), is(name + " is not processing this transaction: " + explanation)); + assertThat(punt.authenticate(), is(RESP.TRY_ANOTHER_TAF)); + assertThat(punt.getPrincipal(), is(nullValue())); + assertThat(punt.getAccess(), is(Access.NULL)); + assertFalse(punt.isFailedAttempt()); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/taf/test/JU_TrustNotTafResp.java b/cadi/core/src/test/java/org/onap/aaf/cadi/taf/test/JU_TrustNotTafResp.java new file mode 100644 index 00000000..b032c020 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/taf/test/JU_TrustNotTafResp.java @@ -0,0 +1,72 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.taf.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import static org.mockito.Mockito.*; +import org.junit.*; +import org.mockito.*; + +import java.io.IOException; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.taf.TafResp; +import org.onap.aaf.cadi.taf.TafResp.RESP; +import org.onap.aaf.cadi.taf.TrustNotTafResp; +import org.onap.aaf.cadi.principal.TaggedPrincipal; + +public class JU_TrustNotTafResp { + + @Mock + TafResp delegateMock; + + @Mock + TaggedPrincipal principalMock; + + @Mock + Access accessMock; + + private final String description = "Example Description"; + + @Before + public void setup() throws IOException { + MockitoAnnotations.initMocks(this); + + when(delegateMock.getPrincipal()).thenReturn(principalMock); + when(delegateMock.getAccess()).thenReturn(accessMock); + } + + @Test + public void test() throws IOException { + TrustNotTafResp ttr = new TrustNotTafResp(delegateMock, description); + assertThat(ttr.isValid(), is(false)); + assertThat(ttr.desc(), is(description)); + assertThat(ttr.authenticate(), is(RESP.NO_FURTHER_PROCESSING)); + assertThat(ttr.isAuthenticated(), is(RESP.NO_FURTHER_PROCESSING)); + assertThat(ttr.getPrincipal(), is(principalMock)); + assertThat(ttr.getAccess(), is(accessMock)); + assertThat(ttr.isFailedAttempt(), is(true)); + assertThat(ttr.toString(), is(description)); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/taf/test/JU_TrustTafResp.java b/cadi/core/src/test/java/org/onap/aaf/cadi/taf/test/JU_TrustTafResp.java new file mode 100644 index 00000000..10b5f146 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/taf/test/JU_TrustTafResp.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.taf.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import static org.mockito.Mockito.*; +import org.junit.*; +import org.mockito.*; + +import java.io.IOException; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.taf.TafResp; +import org.onap.aaf.cadi.taf.TafResp.RESP; +import org.onap.aaf.cadi.taf.TrustTafResp; +import org.onap.aaf.cadi.principal.TaggedPrincipal; + +public class JU_TrustTafResp { + + @Mock + TafResp delegateMock; + + @Mock + TaggedPrincipal principalMock; + + @Mock + Access accessMock; + + private final String description = "Example Description"; + private final String anotherDescription = "Another Description"; + private final String name = "name"; + + private final RESP resp = RESP.IS_AUTHENTICATED; + + @Before + public void setup() throws IOException { + MockitoAnnotations.initMocks(this); + + when(delegateMock.desc()).thenReturn(anotherDescription); + when(delegateMock.isValid()).thenReturn(true); + when(delegateMock.isAuthenticated()).thenReturn(resp); + when(delegateMock.authenticate()).thenReturn(resp); + when(delegateMock.getAccess()).thenReturn(accessMock); + when(delegateMock.isFailedAttempt()).thenReturn(true); + + when(principalMock.getName()).thenReturn(name); + } + + @Test + public void test() throws IOException { + TrustTafResp ttr = new TrustTafResp(delegateMock, principalMock, description); + assertThat(ttr.isValid(), is(true)); + assertThat(ttr.desc(), is(description + ' ' + anotherDescription)); + assertThat(ttr.authenticate(), is(resp)); + assertThat(ttr.isAuthenticated(), is(resp)); + assertThat(ttr.getPrincipal(), is(principalMock)); + assertThat(ttr.getAccess(), is(accessMock)); + assertThat(ttr.isFailedAttempt(), is(true)); + assertThat(ttr.toString(), is(name + " by trust of " + description + ' ' + anotherDescription)); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_AES.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_AES.java new file mode 100644 index 00000000..d78706dc --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_AES.java @@ -0,0 +1,194 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.test; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; +import org.junit.*; + + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.lang.reflect.Field; +import java.nio.file.Files; +import java.nio.file.Paths; + +import javax.crypto.CipherInputStream; +import javax.crypto.CipherOutputStream; +import javax.crypto.SecretKey; + +import org.onap.aaf.cadi.AES; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.Symm; + +public class JU_AES { + private AES aes; + private ByteArrayInputStream baisEncrypt; + private ByteArrayInputStream baisDecrypt; + private ByteArrayOutputStream baosEncrypt; + private ByteArrayOutputStream baosDecrypt; + + private ByteArrayOutputStream errStream; + + @Before + public void setup() throws Exception { + byte[] keyBytes = new byte[AES.AES_KEY_SIZE/8]; + char[] codeset = Symm.base64.codeset; + int offset = (Math.abs(codeset[0]) + 47) % (codeset.length - keyBytes.length); + for(int i = 0; i < keyBytes.length; ++i) { + keyBytes[i] = (byte)codeset[i+offset]; + } + aes = new AES(keyBytes, 0, keyBytes.length); + + errStream = new ByteArrayOutputStream(); + System.setErr(new PrintStream(errStream)); + } + + @After + public void tearDown() { + System.setErr(System.err); + } + + @Test + public void newKeyTest() throws Exception { + SecretKey secretKey = AES.newKey(); + assertThat(secretKey.getAlgorithm(), is(AES.class.getSimpleName())); + } + + @Test + public void encryptDecrpytFromBytes() throws Exception { + String orig = "I'm a password, really"; + byte[] encrypted = aes.encrypt(orig.getBytes()); + byte[] decrypted = aes.decrypt(encrypted); + assertThat(new String(decrypted), is(orig)); + + Field aeskeySpec_field = AES.class.getDeclaredField("aeskeySpec"); + aeskeySpec_field.setAccessible(true); + aeskeySpec_field.set(aes, null); + + try { + aes.encrypt(orig.getBytes()); + fail("Should have thrown an exception"); + } catch (CadiException e) { + } + try { + aes.decrypt(encrypted); + fail("Should have thrown an exception"); + } catch (CadiException e) { + } + } + + @Test + public void saveToFileTest() throws Exception { + String filePath = "src/test/resources/output_key"; + File keyfile = new File(filePath); + aes.save(keyfile); + assertTrue(Files.isReadable(Paths.get(filePath))); + assertFalse(Files.isWritable(Paths.get(filePath))); + assertFalse(Files.isExecutable(Paths.get(filePath))); + keyfile.delete(); + } + + @Test + public void encryptDecryptFromInputStream() throws Exception { + String orig = "I'm a password, really"; + byte[] b64encrypted; + String output; + + CipherInputStream cisEncrypt; + CipherInputStream cisDecrypt; + + // Test CipherInputStream + baisEncrypt = new ByteArrayInputStream(orig.getBytes()); + cisEncrypt = aes.inputStream(baisEncrypt, true); + baosEncrypt = new ByteArrayOutputStream(); + transferFromInputStreamToOutputStream(cisEncrypt, baosEncrypt); + cisEncrypt.close(); + + b64encrypted = baosEncrypt.toByteArray(); + + baisDecrypt = new ByteArrayInputStream(b64encrypted); + cisDecrypt = aes.inputStream(baisDecrypt, false); + baosDecrypt = new ByteArrayOutputStream(); + transferFromInputStreamToOutputStream(cisDecrypt, baosDecrypt); + cisDecrypt.close(); + + output = new String(baosDecrypt.toByteArray()); + assertThat(output, is(orig)); + + Field aeskeySpec_field = AES.class.getDeclaredField("aeskeySpec"); + aeskeySpec_field.setAccessible(true); + aeskeySpec_field.set(aes, null); + + assertNull(aes.inputStream(baisEncrypt, true)); + assertThat(errStream.toString(), is("Error creating Aes CipherInputStream\n")); + } + + @Test + public void encryptDecryptFromOutputStream() throws Exception { + String orig = "I'm a password, really"; + byte[] b64encrypted; + String output; + + CipherOutputStream cosEncrypt; + CipherOutputStream cosDecrypt; + + // Test CipherOutputStream + baisEncrypt = new ByteArrayInputStream(orig.getBytes()); + baosEncrypt = new ByteArrayOutputStream(); + cosEncrypt = aes.outputStream(baosEncrypt, true); + transferFromInputStreamToOutputStream(baisEncrypt, cosEncrypt); + cosEncrypt.close(); + + b64encrypted = baosEncrypt.toByteArray(); + + baosDecrypt = new ByteArrayOutputStream(); + cosDecrypt = aes.outputStream(baosDecrypt, false); + baisDecrypt = new ByteArrayInputStream(b64encrypted); + transferFromInputStreamToOutputStream(baisDecrypt, cosDecrypt); + cosDecrypt.close(); + + output = new String(baosDecrypt.toByteArray()); + assertThat(output, is(orig)); + + Field aeskeySpec_field = AES.class.getDeclaredField("aeskeySpec"); + aeskeySpec_field.setAccessible(true); + aeskeySpec_field.set(aes, null); + + assertNull(aes.outputStream(baosEncrypt, true)); + assertThat(errStream.toString(), is("Error creating Aes CipherOutputStream\n")); + } + + public void transferFromInputStreamToOutputStream(InputStream is, OutputStream os) throws IOException { + byte[] buffer = new byte[200]; + int len; + while ((len = is.read(buffer)) != -1) { + os.write(buffer, 0, len); + } + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_AbsUserCache.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_AbsUserCache.java new file mode 100644 index 00000000..b2739b9d --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_AbsUserCache.java @@ -0,0 +1,365 @@ +/******************************************************************************* + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ + +package org.onap.aaf.cadi.test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.lang.reflect.Field; +import java.security.Principal; +import java.util.ArrayList; +import java.util.List; + +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.onap.aaf.cadi.AbsUserCache; +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.CachedPrincipal.Resp; +import org.onap.aaf.cadi.CachingLur; +import org.onap.aaf.cadi.GetCred; +import org.onap.aaf.cadi.Permission; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.User; +import org.onap.aaf.cadi.lur.LocalPermission; +import org.onap.aaf.cadi.principal.CachedBasicPrincipal; + +public class JU_AbsUserCache { + + @Mock private CachingLur<Permission> cl; + @Mock private Principal principal; + @Mock private CachedBasicPrincipal cbp; + @Mock private LocalPermission permission1; + @Mock private LocalPermission permission2; + + private Access access; + + private ByteArrayOutputStream outStream; + + private String name1 = "name1"; + private String name2 = "name2"; + private byte[] password = "password".getBytes(); + + private static Field timerField; + + @BeforeClass + public static void setupOnce() throws Exception { + timerField = AbsUserCache.class.getDeclaredField("timer"); + timerField.setAccessible(true); + } + + @Before + public void setup() throws Exception { + MockitoAnnotations.initMocks(this); + + outStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outStream)); + + // This must happen after changing System.out + access = new PropAccess(); + + when(permission1.getKey()).thenReturn("NewKey1"); + when(permission2.getKey()).thenReturn("NewKey2"); + + timerField.set(null, null); + } + + @After + public void tearDown() throws Exception { + System.setOut(System.out); + timerField.set(null, null); + } + + @SuppressWarnings("unused") + @Test + public void constructorTest() { + int cleanInterval = 65000; + int maxInterval = 70000; + + AbsUserCacheStub<Permission> aucs1 = new AbsUserCacheStub<Permission>(access, cleanInterval, maxInterval, Integer.MAX_VALUE); + String output = outStream.toString().split(" ", 2)[1]; + StringBuilder expected = new StringBuilder(); + expected.append("INIT [cadi] Cleaning Thread initialized with interval of "); + expected.append(String.valueOf(cleanInterval)); + expected.append(" ms and max objects of "); + expected.append(String.valueOf(maxInterval)); + expected.append("\n"); + assertThat(output, is(expected.toString())); + + outStream.reset(); + AbsUserCacheStub<Permission> aucs2 = new AbsUserCacheStub<Permission>(access, cleanInterval, maxInterval, Integer.MAX_VALUE); + output = outStream.toString().split(" ", 2)[1]; + expected = new StringBuilder(); + expected.append("INIT [cadi] Cleaning Thread initialized with interval of "); + expected.append(String.valueOf(cleanInterval)); + expected.append(" ms and max objects of "); + expected.append(String.valueOf(maxInterval)); + expected.append("\n"); + assertThat(output, is(expected.toString())); + + AbsUserCacheStub<Permission> aucs3 = new AbsUserCacheStub<Permission>(access, 0, 0, Integer.MAX_VALUE); + AbsUserCacheStub<Permission> aucs4 = new AbsUserCacheStub<Permission>(aucs1); + + // For coverage + AbsUserCacheCLStub<Permission> auccls1 = new AbsUserCacheCLStub<Permission>(aucs1); + aucs1.setLur(cl); + auccls1 = new AbsUserCacheCLStub<Permission>(aucs1); + AbsUserCacheCLStub<Permission> auccls2 = new AbsUserCacheCLStub<Permission>(aucs3); + } + + @Test + public void setLurTest() { + AbsUserCacheStub<Permission> aucs1 = new AbsUserCacheStub<Permission>(access, 65000, 70000, Integer.MAX_VALUE); + AbsUserCacheStub<Permission> aucs2 = new AbsUserCacheStub<Permission>(access, 0, 0, Integer.MAX_VALUE); + aucs1.setLur(cl); + aucs2.setLur(cl); + } + + @Test + public void addUserGetUserTest() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + AbsUserCacheStub<Permission> aucs = new AbsUserCacheStub<Permission>(access, 0, 0, Integer.MAX_VALUE); + User<Permission> user; + + // Test adding a user with a principal (non-GetCred). user does not have a cred + // Then test getting that user + when(principal.getName()).thenReturn(name1); + user = new User<Permission>(principal, 0); + aucs.addUser(user); + assertThat(aucs.getUser(principal), is(user)); + + // Test adding a user with a principal (GetCred). user does not have a cred + // Then test getting that user + GetCredStub gc = new GetCredStub(); + user = new User<Permission>(gc, 0); + aucs.addUser(user); + assertThat(aucs.getUser(gc), is(user)); + + // Test adding a user with no principal + // Then test getting that user via his name and cred + user = new User<Permission>(name2, password); + aucs.addUser(user); + assertThat(aucs.getUser(name2, password), is(user)); + + // Test getting a user by a CachedBasicPrincipal + when(cbp.getName()).thenReturn(name2); + when(cbp.getCred()).thenReturn(password); + assertThat(aucs.getUser(cbp), is(user)); + + // Force the user to expire, then test that he is no longer in the cache + Field permExpiresField = User.class.getDeclaredField("permExpires"); + permExpiresField.setAccessible(true); + permExpiresField.set(user, 0); + assertThat(aucs.getUser(name2, password), is(nullValue())); + + // Test adding a user with a custom key + // Then test gettin that user + user = new User<Permission>(principal, 0); + String key = principal.getName() + "NoCred"; + aucs.addUser(key, user); + assertThat(aucs.getUser(principal), is(user)); + + // Test that getUser returns null for principals that don't match any users + when(principal.getName()).thenReturn("not in the cache"); + assertThat(aucs.getUser(principal), is(nullValue())); + + // That that getUser returns null for name/creds that are not in the cache + assertThat(aucs.getUser("not a real user", "not in the cache".getBytes()), is(nullValue())); + } + + @Test + public void removeTest() { + AbsUserCacheStub<Permission> aucs = new AbsUserCacheStub<Permission>(access, 0, 0, Integer.MAX_VALUE); + User<Permission> user; + + when(principal.getName()).thenReturn(name1); + user = new User<Permission>(principal); + // Add a user with a principal + aucs.addUser(user); + // Check that the user is in the cache + assertThat(aucs.getUser(principal), is(user)); + // Remove the user + when(principal.getName()).thenReturn(name1 + "NoCred"); + aucs.remove(user); + // Check that the user is no longer in the cache + when(principal.getName()).thenReturn(name1); + assertThat(aucs.getUser(principal), is(nullValue())); + + // Add the user again + aucs.addUser(user); + // Check that the user is in the cache + assertThat(aucs.getUser(principal), is(user)); + // Remove the user by name + aucs.remove(name1 + "NoCred"); + // Check that the user is no longer in the cache + assertThat(aucs.getUser(principal), is(nullValue())); + + // Coverage test - attempt to remove a user that is not in the cache + aucs.remove(name1 + "NoCred"); + assertThat(aucs.getUser(principal), is(nullValue())); + } + + @Test + public void clearAllTest() { + AbsUserCacheStub<Permission> aucs = new AbsUserCacheStub<Permission>(access, 0, 0, Integer.MAX_VALUE); + User<Permission> user1; + User<Permission> user2; + + // Add some users to the cache + when(principal.getName()).thenReturn(name1); + user1 = new User<Permission>(principal); + when(principal.getName()).thenReturn(name2); + user2 = new User<Permission>(principal); + aucs.addUser(user1); + aucs.addUser(user2); + + // Check that the users are in the cache + when(principal.getName()).thenReturn(name1); + assertThat(aucs.getUser(principal), is(user1)); + when(principal.getName()).thenReturn(name2); + assertThat(aucs.getUser(principal), is(user2)); + + // Clear the cache + aucs.clearAll(); + + // Check that the users are no longer in the cache + when(principal.getName()).thenReturn(name1); + assertThat(aucs.getUser(principal), is(nullValue())); + when(principal.getName()).thenReturn(name2); + assertThat(aucs.getUser(principal), is(nullValue())); + } + + @Test + public void dumpInfoTest() { + AbsUserCacheStub<Permission> aucs = new AbsUserCacheStub<Permission>(access, 0, 0, Integer.MAX_VALUE); + User<Permission> user1; + User<Permission> user2; + + Principal principal1 = mock(Principal.class); + Principal principal2 = mock(Principal.class); + when(principal1.getName()).thenReturn(name1); + when(principal2.getName()).thenReturn(name2); + + // Add some users with permissions to the cache + user1 = new User<Permission>(principal1); + user1.add(permission1); + user1.add(permission2); + user2 = new User<Permission>(principal2); + user2.add(permission1); + user2.add(permission2); + aucs.addUser(user1); + aucs.addUser(user2); + + // Dump the info + List<AbsUserCache<Permission>.DumpInfo> dumpInfo = aucs.dumpInfo(); + assertThat(dumpInfo.size(), is(2)); + + // Utility lists + List<String> names = new ArrayList<String>(); + names.add(name1); + names.add(name2); + List<String> permissions = new ArrayList<String>(); + permissions.add("NewKey1"); + permissions.add("NewKey2"); + + // We need to use "contains" because the dumpInfo was created from a list, so we don't know it's order + for (AbsUserCache<Permission>.DumpInfo di : dumpInfo) { + assertTrue(names.contains(di.user)); + for (String perm : di.perms) { + assertTrue(permissions.contains(perm)); + } + } + } + + @Test + public void handlesExclusivelyTest() { + AbsUserCacheStub<Permission> aucs = new AbsUserCacheStub<Permission>(access, 0, 0, Integer.MAX_VALUE); + assertFalse(aucs.handlesExclusively(permission1)); + assertFalse(aucs.handlesExclusively(permission2)); + } + + @Test + public void destroyTest() { + AbsUserCacheStub<Permission> aucs = new AbsUserCacheStub<Permission>(access, 0, 0, Integer.MAX_VALUE); + aucs.destroy(); + aucs = new AbsUserCacheStub<Permission>(access, 1, 1, Integer.MAX_VALUE); + aucs.destroy(); + } + + @Test + public void missTest() throws IOException { + AbsUserCacheStub<Permission> aucs = new AbsUserCacheStub<Permission>(access, 0, 0, Integer.MAX_VALUE); + // Add the Miss to the missmap + assertTrue(aucs.addMiss("key", password)); // This one actually adds it + assertTrue(aucs.addMiss("key", password)); // this one doesn't really do anything + assertTrue(aucs.addMiss("key", password)); // neither does this one + assertFalse(aucs.addMiss("key", password)); // By this time, the missMap is tired of this nonsense, and retaliates + assertFalse(aucs.addMiss("key", password)); // Oh yea. He's angry + + // Can't really test this due to visibility + aucs.missed("key", password); + + // Coverage + AbsUserCacheStub<Permission> aucs1 = new AbsUserCacheStub<Permission>(access, 1, 1, Integer.MAX_VALUE); + aucs1.addMiss("key", password); + } + + class AbsUserCacheStub<PERM extends Permission> extends AbsUserCache<PERM> { + public AbsUserCacheStub(Access access, long cleanInterval, int highCount, int usageCount) { super(access, cleanInterval, highCount, usageCount); } + public AbsUserCacheStub(AbsUserCache<PERM> cache) { super(cache); } + @Override public void setLur(CachingLur<PERM> lur) { super.setLur(lur); } + @Override public void addUser(User<PERM> user) { super.addUser(user); } + @Override public void addUser(String key, User<PERM> user) { super.addUser(key, user); } + @Override public User<PERM> getUser(Principal p) { return super.getUser(p); } + @Override public User<PERM> getUser(CachedBasicPrincipal p) { return super.getUser(p); } + @Override public User<PERM> getUser(String user, byte[] cred) { return super.getUser(user, cred); } + @Override public void remove(User<PERM> user) { super.remove(user); } + @Override public boolean addMiss(String key, byte[] bs) { return super.addMiss(key, bs); } + @Override public Miss missed(String key, byte[] bs) throws IOException { return super.missed(key, bs); } + } + + class AbsUserCacheCLStub<PERM extends Permission> extends AbsUserCache<PERM> implements CachingLur<PERM> { + public AbsUserCacheCLStub(AbsUserCache<PERM> cache) { super(cache); } + @Override public Permission createPerm(String p) { return null; } + @Override public boolean fish(Principal bait, Permission pond) { return false; } + @Override public void fishAll(Principal bait, List<Permission> permissions) { } + @Override public boolean handles(Principal principal) { return false; } + @Override public Resp reload(User<PERM> user) { return null; } + @Override public void setDebug(String commaDelimIDsOrNull) { } + } + + class GetCredStub implements Principal, GetCred { + @Override public byte[] getCred() { return password; } + @Override public String getName() { return name1; } + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_Access.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_Access.java new file mode 100644 index 00000000..98903567 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_Access.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import org.junit.*; + +import java.io.IOException; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.Access.Level; + +public class JU_Access { + + @Test + public void levelTests() { + assertTrue(Level.DEBUG.inMask(0x1)); + for (int i = 2; i > 0; i <<= 1) { + assertFalse(Level.DEBUG.inMask(i)); + } + assertFalse(Level.DEBUG.inMask(0x80000000)); + + assertThat(Level.DEBUG.addToMask(0x2), is(0x3)); + assertThat(Level.DEBUG.delFromMask(0x1), is(0x0)); + assertThat(Level.DEBUG.toggle(0x2), is(0x3)); + assertThat(Level.DEBUG.toggle(0x1), is(0x0)); + assertThat(Level.DEBUG.maskOf(), is(123153)); + assertThat(Level.NONE.maskOf(), is(0)); + } + + @Test + public void nullTests() throws IOException { + // These are entirely for coverage + Access.NULL.log(Level.DEBUG); + Access.NULL.printf(Level.DEBUG, ""); + Access.NULL.log(new Exception()); + Access.NULL.classLoader(); + assertThat(Access.NULL.getProperty("", ""), is(nullValue())); + Access.NULL.load(System.in); + Access.NULL.setLogLevel(Level.DEBUG); + assertThat(Access.NULL.decrypt("test", true), is("test")); + assertFalse(Access.NULL.willLog(Level.DEBUG)); + assertThat(Access.NULL.getProperties(), is(not(nullValue()))); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_Base64.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_Base64.java new file mode 100644 index 00000000..801259d4 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_Base64.java @@ -0,0 +1,92 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.security.SecureRandom; + +import org.junit.Test; +import org.onap.aaf.cadi.Symm; +import org.onap.aaf.cadi.config.Config; + +public class JU_Base64 { + private static final String encoding = "Man is distinguished, not only by his reason, but by this singular " + + "passion from other animals, which is a lust of the mind, that by a " + + "perseverance of delight in the continued and indefatigable generation of " + + "knowledge, exceeds the short vehemence of any carnal pleasure."; + + private static final String expected = + "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz\n" + + "IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg\n" + + "dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu\n" + + "dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo\n" + + "ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4="; + + @Test + public void test() throws Exception { + // Test with different Padding + assertEncoded("leas", "bGVhcw=="); + assertEncoded("leasu", "bGVhc3U="); + assertEncoded("leasur", "bGVhc3Vy"); + assertEncoded("leasure", "bGVhc3VyZQ=="); + assertEncoded("leasure.", "bGVhc3VyZS4="); + + // Test with line ends + assertEncoded(encoding, expected); + } + + @Test + public void symmetric() throws IOException { + String symmetric = new String(Symm.keygen()); + Symm bsym = Symm.obtain(symmetric); + String result = bsym.encode(encoding); + assertThat(bsym.decode(result), is(encoding)); + + char[] manipulate = symmetric.toCharArray(); + int spot = new SecureRandom().nextInt(manipulate.length); + manipulate[spot]|=0xFF; + String newsymmetric = new String(manipulate); + assertThat(symmetric, is(not(newsymmetric))); + try { + bsym = Symm.obtain(newsymmetric); + result = bsym.decode(result); + assertThat(result, is(encoding)); + } catch (IOException e) { + // this is what we want to see if key wrong + } + } + + private void assertEncoded(String toEncode, String expected) throws IOException { + String result = Symm.base64.encode(toEncode); + assertThat(result, is(expected)); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + Symm.base64.decode(new ByteArrayInputStream(result.getBytes()), baos); + result = baos.toString(Config.UTF_8); + assertThat(result, is(toEncode)); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_BufferedCadiWrap.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_BufferedCadiWrap.java new file mode 100644 index 00000000..172270da --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_BufferedCadiWrap.java @@ -0,0 +1,45 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ +package org.onap.aaf.cadi.test; + +import javax.servlet.http.HttpServletRequest; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +public class JU_BufferedCadiWrap { + @Mock + private HttpServletRequest request; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + } + + @Test + public void constructorTest() { + // TODO: Ian - This will always fail beacuse the constructor is invalid + // BufferedCadiWrap bcw = new BufferedCadiWrap(request); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_BufferedServletInputStream.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_BufferedServletInputStream.java new file mode 100644 index 00000000..66ac3610 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_BufferedServletInputStream.java @@ -0,0 +1,320 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.test; + +import java.io.ByteArrayInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.lang.reflect.*; +import junit.framework.Assert; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onap.aaf.cadi.BufferedServletInputStream; + +import static junit.framework.Assert.assertEquals; + +public class JU_BufferedServletInputStream { + private BufferedServletInputStream bsis; + private String expected; + + @Before + public void setup() throws FileNotFoundException { + expected = new String("This is the expected output"); + bsis = new BufferedServletInputStream(new ByteArrayInputStream(expected.getBytes())); + } + + @After + public void tearDown() throws IOException { + bsis.close(); + } + + @Test + public void ByteReadNoMarkTest() throws Exception { + int c; + int i = 0; + byte output[] = new byte[100]; + while ((c = bsis.read()) != -1) { + output[i++] = (byte)c; + } + Assert.assertEquals(new String(output, 0, i), expected); + } + + @Test + public void ByteReadMarkTest() throws Exception { + bsis.mark(0); + int c; + int i = 0; + byte output[] = new byte[100]; + while ((c = bsis.read()) != -1) { + output[i++] = (byte)c; + } + Assert.assertEquals(new String(output, 0, i), expected); + } + + @Test + public void ByteReadStateIsStoreTest() throws Exception { + Field state_field = BufferedServletInputStream.class.getDeclaredField("state"); + state_field.setAccessible(true); + bsis.mark(0); + int c; + int i = 0; + byte output[] = new byte[100]; + while ((c = bsis.read()) != -1) { + output[i++] = (byte)c; + } + bsis.reset(); + Assert.assertEquals(state_field.get(bsis), 2); // state == READ + } + + @Test + public void ByteReadStateIsReadTest() throws Exception { + bsis.mark(0); // Initialize the capacitor + boolean isReset = false; + int c; + int i = 0; + byte output[] = new byte[100]; + while ((c = bsis.read()) != -1) { + output[i++] = (byte)c; + if ((i > 5) && !isReset) { + // Close the capacitor and start over. This is done for coverage purposes + i = 0; + isReset = true; + bsis.reset(); // Sets state to READ + } + } + Assert.assertEquals(new String(output, 0, i), expected); + } + + @Test + public void ByteReadStateIsNoneTest() throws Exception { + Field state_field = BufferedServletInputStream.class.getDeclaredField("state"); + state_field.setAccessible(true); + bsis.mark(0); // Initialize the capacitor + int c; + c = bsis.read(); + // Close the capacitor. This is done for coverage purposes + bsis.reset(); // Sets state to READ + state_field.setInt(bsis, 0); // state == NONE + c = bsis.read(); + Assert.assertEquals(c, -1); + } + + @Test + public void ByteArrayReadNoMarkTest() throws Exception { + byte output[] = new byte[100]; + int count = bsis.read(output, 0, expected.length()); + Assert.assertEquals(new String(output, 0, count), expected); + Assert.assertEquals(count, expected.length()); + } + + @Test + public void ByteArrayReadTest() throws Exception { + byte[] output = new byte[100]; + bsis.mark(0); + bsis.read(output); + Assert.assertEquals(new String(output, 0, expected.length()), expected); + } + + @Test + public void ByteArrayReadStateIsStoreTest() throws Exception { + byte output[] = new byte[100]; + bsis.mark(0); + int count = bsis.read(output, 0, expected.length()); + Assert.assertEquals(new String(output, 0, count), expected); + Assert.assertEquals(count, expected.length()); + + count = bsis.read(output, 0, 0); + Assert.assertEquals(count, -1); + } + + @Test + public void ByteArrayReadStateIsReadTest() throws Exception { + byte output[] = new byte[200]; + for(int i = 0; i < 2; ++i) { + bsis.mark(0); + bsis.read(output, 0, 100); + Assert.assertEquals(new String(output, 0, expected.length()), expected); + + bsis.reset(); + bsis.read(output, 0, output.length); + Assert.assertEquals(new String(output, 0, expected.length()), expected); + bsis = new BufferedServletInputStream(new ByteArrayInputStream(output)); + if(i == 0) { + output = new byte[200]; + } + } + + Assert.assertEquals(new String(output, 0, expected.length()), expected); + } + + @Test + public void ByteArrayReadStateIsNoneTest() throws Exception { + byte output[] = new byte[100]; + bsis.mark(0); + + Field state_field = BufferedServletInputStream.class.getDeclaredField("state"); + state_field.setAccessible(true); + state_field.setInt(bsis, 0); // state == NONE + + int count = bsis.read(output, 0, 100); + Assert.assertEquals(count, -1); + } + + @Test + public void skipTest() throws Exception { + byte output[] = new byte[100]; + bsis.mark(0); + bsis.read(output, 0, 10); + long count = bsis.skip(200); + // skip returns the number left _before_ skipping. that number starts at 256 + Assert.assertEquals(count, 246); + + count = bsis.skip(200); + Assert.assertEquals(count, 17); + } + + @Test + public void availableTest() throws Exception { + int count = bsis.available(); + Assert.assertEquals(count, 27); + bsis.mark(0); + count = bsis.available(); + Assert.assertEquals(count, 27); + } + + @Test + public void bufferedTest() throws Exception { + bsis.mark(0); + Assert.assertEquals(bsis.buffered(), 0); + } + + @Test + public void closeTest() throws Exception { + Field capacitor_field = BufferedServletInputStream.class.getDeclaredField("capacitor"); + capacitor_field.setAccessible(true); + bsis.mark(0); + Assert.assertNotNull(capacitor_field.get(bsis)); + bsis.close(); + Assert.assertNull(capacitor_field.get(bsis)); + } + + @Test + public void markTest() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + Field state_field = BufferedServletInputStream.class.getDeclaredField("state"); + Field capacitor_field = BufferedServletInputStream.class.getDeclaredField("capacitor"); + capacitor_field.setAccessible(true); + state_field.setAccessible(true); + + // capacitor is null initially + Assert.assertNull(capacitor_field.get(bsis)); + + state_field.setInt(bsis, 0); // state == NONE + bsis.mark(0); // the value passed into mark is ignored + Assert.assertNotNull(capacitor_field.get(bsis)); + Assert.assertEquals(state_field.get(bsis), 1); // state == STORE + + state_field.setInt(bsis, 1); // state == STORE + bsis.mark(0); // the value passed into mark is ignored + Assert.assertEquals(state_field.get(bsis), 1); // state == STORE + + state_field.setInt(bsis, 2); // state == READ + bsis.mark(0); // the value passed into mark is ignored + Assert.assertEquals(state_field.get(bsis), 1); // state == STORE + } + + @Test + public void resetTest() throws Exception { + Field state_field = BufferedServletInputStream.class.getDeclaredField("state"); + state_field.setAccessible(true); + + bsis.mark(0); + Assert.assertEquals(state_field.get(bsis), 1); // state == STORE + bsis.reset(); + Assert.assertEquals(state_field.get(bsis), 2); // state == READ + bsis.reset(); + Assert.assertEquals(state_field.get(bsis), 2); // state == READ + + state_field.setInt(bsis, -1); // state is invalid + bsis.reset(); // This call does nothing. It is for coverage alone + Assert.assertEquals(state_field.get(bsis), -1); // state doesn't change + + try { + state_field.setInt(bsis, 0); // state == NONE + bsis.reset(); + } catch (IOException e) { + Assert.assertEquals(e.getMessage(), "InputStream has not been marked"); + } + } + + @Test + public void markSupportedTest() { + Assert.assertTrue(bsis.markSupported()); + } + + // "Bug" 4/22/2013 + // Some XML code expects Buffered InputStream can never return 0... This isn't actually true, but we'll accommodate as far + // as we can. + // Here, we make sure we set and read the Buffered data, making sure the buffer is empty on the last test... + @Test + public void issue04_22_2013() throws IOException { + String testString = "We want to read in and get out with a Buffered Stream seamlessly."; + ByteArrayInputStream bais = new ByteArrayInputStream(testString.getBytes()); + BufferedServletInputStream bsis = new BufferedServletInputStream(bais); + try { + bsis.mark(0); + byte aa[] = new byte[testString.length()]; // 65 count... important for our test (divisible by 5); + + int read; + for(int i=0;i<aa.length;i+=5) { + read = bsis.read(aa, i, 5); + assertEquals(5,read); + } + // System.out.println(new String(aa)); + + bsis.reset(); + + byte bb[] = new byte[aa.length]; + read = 0; + for(int i=0;read>=0;i+=read) { + read = bsis.read(bb,i,5); + switch(i) { + case 65: + assertEquals(read,-1); + break; + default: + assertEquals(read,5); + } + } + // System.out.println(new String(bb)); + assertEquals(testString,new String(aa)); + assertEquals(testString,new String(bb)); + + } finally { + bsis.close(); + bais.close(); + } + + } + + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_CadiException.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_CadiException.java new file mode 100644 index 00000000..bfcaeeab --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_CadiException.java @@ -0,0 +1,121 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.test; + +import static org.junit.Assert.*; + +import org.junit.Test; +import org.onap.aaf.cadi.CadiException; + +import static org.hamcrest.CoreMatchers.is; + +public class JU_CadiException { + @Test + public void testCadiException() { + CadiException exception = new CadiException(); + + assertNotNull(exception); + } + + @Test + public void testCadiExceptionString() { + CadiException exception = new CadiException("New Exception"); + assertNotNull(exception); + assertThat(exception.getMessage(), is("New Exception")); + } + + @Test + public void testCadiExceptionThrowable() { + CadiException exception = new CadiException(new Throwable("New Exception")); + assertNotNull(exception); + assertThat(exception.getMessage(), is("java.lang.Throwable: New Exception")); + } + + @Test + public void testCadiExceptionStringThrowable() { + CadiException exception = new CadiException("New Exception",new Throwable("New Exception")); + assertNotNull(exception); + assertThat(exception.getMessage(), is("New Exception")); + + } + + @Test + public void testCadiException1() { + CadiException exception = new CadiException(); + + assertNotNull(exception); + } + + @Test + public void testCadiExceptionString1() { + CadiException exception = new CadiException("New Exception"); + assertNotNull(exception); + assertThat(exception.getMessage(), is("New Exception")); + } + + @Test + public void testCadiExceptionThrowable1() { + CadiException exception = new CadiException(new Throwable("New Exception")); + assertNotNull(exception); + assertThat(exception.getMessage(), is("java.lang.Throwable: New Exception")); + } + + @Test + public void testCadiExceptionStringThrowable1() { + CadiException exception = new CadiException("New Exception",new Throwable("New Exception")); + assertNotNull(exception); + assertThat(exception.getMessage(), is("New Exception")); + + } + + @Test + public void testCadiException2() { + CadiException exception = new CadiException(); + + assertNotNull(exception); + } + + @Test + public void testCadiExceptionString2() { + CadiException exception = new CadiException("New Exception"); + assertNotNull(exception); + assertThat(exception.getMessage(), is("New Exception")); + } + + @Test + public void testCadiExceptionThrowable2() { + CadiException exception = new CadiException(new Throwable("New Exception")); + assertNotNull(exception); + assertThat(exception.getMessage(), is("java.lang.Throwable: New Exception")); + } + + @Test + public void testCadiExceptionStringThrowable2() { + CadiException exception = new CadiException("New Exception",new Throwable("New Exception")); + assertNotNull(exception); + assertThat(exception.getMessage(), is("New Exception")); + + } + + + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_CadiWrap.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_CadiWrap.java new file mode 100644 index 00000000..d9a4437c --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_CadiWrap.java @@ -0,0 +1,161 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.test; + +import org.junit.*; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.security.Principal; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; + +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.CachingLur; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.CadiWrap; +import org.onap.aaf.cadi.Lur; +import org.onap.aaf.cadi.Permission; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.User; +import org.onap.aaf.cadi.CachedPrincipal.Resp; +import org.onap.aaf.cadi.filter.MapPermConverter; +import org.onap.aaf.cadi.lur.EpiLur; +import org.onap.aaf.cadi.principal.TaggedPrincipal; +import org.onap.aaf.cadi.taf.TafResp; + +public class JU_CadiWrap { + + @Mock + private HttpServletRequest request; + + @Mock + private TafResp tafResp; + + @Mock + private TaggedPrincipal principle; + + @Mock + private Lur lur; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + System.setOut(new PrintStream(new ByteArrayOutputStream())); + } + + @After + public void tearDown() { + System.setOut(System.out); + } + + @SuppressWarnings("unchecked") + @Test + public void testInstantiate() throws CadiException { + Access a = new PropAccess(); + when(tafResp.getAccess()).thenReturn(a); + + lur.fishAll(isA(Principal.class), (List<Permission>)isA(List.class)); + + EpiLur lur1 = new EpiLur(lur); + + CadiWrap wrap = new CadiWrap(request, tafResp, lur1); + + assertNull(wrap.getUserPrincipal()); + assertNull(wrap.getRemoteUser()); + assertNull(wrap.getUser()); + assertEquals(wrap.getPermissions(principle).size(), 0); + assertTrue(wrap.access() instanceof PropAccess); + + byte[] arr = {'1','2'}; + wrap.setCred(arr); + + assertEquals(arr, wrap.getCred()); + + wrap.setUser("User1"); + assertEquals("User1", wrap.getUser()); + + wrap.invalidate("1"); + + assertFalse(wrap.isUserInRole(null)); + + wrap.set(tafResp, lur); + + wrap.invalidate("2"); + + assertFalse(wrap.isUserInRole("User1")); + } + + @Test + public void testInstantiateWithPermConverter() throws CadiException { + Access a = new PropAccess(); + when(tafResp.getAccess()).thenReturn(a); + when(tafResp.getPrincipal()).thenReturn(principle); + + // Anonymous object for testing purposes + CachingLur<Permission> lur1 = new CachingLur<Permission>() { + @Override public Permission createPerm(String p) { return null; } + @Override public boolean fish(Principal bait, Permission pond) { return true; } + @Override public void fishAll(Principal bait, List<Permission> permissions) { } + @Override public void destroy() { } + @Override public boolean handlesExclusively(Permission pond) { return false; } + @Override public boolean handles(Principal principal) { return false; } + @Override public void remove(String user) { } + @Override public Resp reload(User<Permission> user) { return null; } + @Override public void setDebug(String commaDelimIDsOrNull) { } + @Override public void clear(Principal p, StringBuilder sb) { } + }; + + MapPermConverter pc = new MapPermConverter(); + + CadiWrap wrap = new CadiWrap(request, tafResp, lur1, pc); + + assertNotNull(wrap.getUserPrincipal()); + assertNull(wrap.getRemoteUser()); + assertNull(wrap.getUser()); + + byte[] arr = {'1','2'}; + wrap.setCred(arr); + + assertEquals(arr, wrap.getCred()); + + wrap.setUser("User1"); + assertEquals("User1", wrap.getUser()); + + wrap.invalidate("1"); + wrap.setPermConverter(new MapPermConverter()); + + assertTrue(wrap.getLur() instanceof CachingLur); + assertTrue(wrap.isUserInRole("User1")); + + wrap.set(tafResp, lur); + assertFalse(wrap.isUserInRole("Perm1")); + } +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_Capacitor.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_Capacitor.java new file mode 100644 index 00000000..e9bceccd --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_Capacitor.java @@ -0,0 +1,155 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.test; + +import static junit.framework.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import org.junit.Before; +import org.junit.Test; +import org.onap.aaf.cadi.Capacitor; + +import java.lang.reflect.*; + +public class JU_Capacitor { + private Capacitor cap; + public final static String TEST_DATA = + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + + "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" + + "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" + + "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" + + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; + + @Before + public void setup() { + cap = new Capacitor(); + } + + @Test + public void singleByteTest() throws Exception { + assertEquals(cap.read(), -1); + cap.setForRead(); + Field curr_field = Capacitor.class.getDeclaredField("curr"); + curr_field.setAccessible(true); + Field idx_field = Capacitor.class.getDeclaredField("idx"); + idx_field.setAccessible(true); + assertNull(curr_field.get(cap)); + assertEquals(idx_field.get(cap), 0); + + for(int iter = 0; iter < 20; ++iter) { + for(int i = 0; i < 20; ++i) { + cap.put((byte)('a' + i)); + } + cap.setForRead(); + byte[] array = new byte[20]; + for(int i = 0; i < 20; ++i) { + array[i]=(byte)cap.read(); + } + assertEquals("abcdefghijklmnopqrst", new String(array)); + assertEquals(-1, cap.read()); + + cap.done(); + } + + for(int i = 0; i < 500; i++) { + cap.put((byte)'a'); + } + cap.setForRead(); + byte[] array = new byte[500]; + for(int i = 0; i < 500; ++i) { + array[i]=(byte)cap.read(); + } + assertEquals((new String(array)).length(), 500); + assertEquals(-1, cap.read()); + } + + @Test + public void availableTest() { + assertEquals(cap.available(), 0); + for(int i = 0; i < 100; ++i) { + cap.put((byte)'a'); + } + // The Capacitor can hold 256 bytes. After reading 100 bytes, + // it should have 156 available + assertEquals(cap.available(), 156); + } + + @Test + public void byteArrayTest() { + byte[] arrayA = TEST_DATA.getBytes(); + assertEquals(cap.read(arrayA, 0, arrayA.length), -1); + + cap.put(arrayA, 0, arrayA.length); + + byte[] arrayB = new byte[arrayA.length]; + cap.setForRead(); + assertEquals(arrayA.length, cap.read(arrayB, 0, arrayB.length)); + assertEquals(TEST_DATA, new String(arrayB)); + assertEquals(-1, cap.read()); + cap.done(); + + String b = "This is some content that we want to read"; + byte[] a = b.getBytes(); + byte[] c = new byte[b.length()]; // we want to use this to test reading offsets, etc + + for(int i = 0; i < a.length; i += 11) { + cap.put(a, i, Math.min(11, a.length-i)); + } + cap.reset(); + int read; + for(int i = 0; i < c.length; i += read) { + read = cap.read(c, i, Math.min(3, c.length-i)); + } + assertEquals(b, new String(c)); + } + + @Test + public void resetTest() throws Exception { + cap.reset(); + Field curr_field = Capacitor.class.getDeclaredField("curr"); + curr_field.setAccessible(true); + Field idx_field = Capacitor.class.getDeclaredField("idx"); + idx_field.setAccessible(true); + assertNull(curr_field.get(cap)); + assertEquals(idx_field.get(cap), 0); + + cap.put((byte)'a'); + cap.reset(); + assertNotNull(curr_field.get(cap)); + assertEquals(idx_field.get(cap), 1); + } + + @Test + public void skipTest() throws Exception { + // capacitor can't skip if nothing has been put into it + assertEquals(cap.skip(10), 0); + cap.put((byte)'a'); + // The Capacitor can hold 256 bytes. If we try to skip 100 bytes, + // it should only skip 1 byte, leaving 255 remaining + assertEquals(cap.skip(100), 255); + + // Skipping 200 bytes leaves 0 remaining + assertEquals(cap.skip(200), 0); + } +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_CmdLine.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_CmdLine.java new file mode 100644 index 00000000..efcc1b29 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_CmdLine.java @@ -0,0 +1,273 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Properties; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.onap.aaf.cadi.CmdLine; +import org.onap.aaf.cadi.Symm; + +public class JU_CmdLine { + + @Mock + private OutputStream thrower; + + private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); + + private String password; + private String keyfile; + private String quickBrownFoxPlain = "The quick brown fox jumps over the lazy dog"; + private String quickBrownFoxMD5 = "0x9e107d9d372bb6826bd81d3542a419d6"; + private String quickBrownFoxSHA256 = "0xd7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592"; + private Symm symm; + + @Before + public void setup() throws Exception { + MockitoAnnotations.initMocks(this); + + System.setOut(new PrintStream(outContent)); + + Properties p = new Properties(); + p.setProperty("force_exit", "false"); + + CmdLine.setSystemExit(false); + keyfile = "src/test/resources/keyfile"; + password = "password"; + + File keyF = new File("src/test/resources", "keyfile"); + FileInputStream fis = new FileInputStream(keyF); + try { + symm = Symm.obtain(fis); + } finally { + fis.close(); + } + } + + @After + public void restoreStreams() throws IOException { + System.setOut(System.out); + System.setIn(System.in); + } + + @Test + public void digestTest() throws Exception { + CmdLine.main(new String[]{"digest", password, keyfile}); + String decrypted = symm.depass(outContent.toString()); + assertThat(decrypted, is(password)); + + System.setIn(new ByteArrayInputStream(password.getBytes())); + CmdLine.main(new String[]{"digest", "-i", keyfile}); + decrypted = symm.depass(outContent.toString()); + assertThat(decrypted, is(password)); + } + + @Test + public void encode64Test() throws Exception { + CmdLine.main(new String[]{"encode64", password}); + String decrypted = Symm.base64.decode(outContent.toString()); + assertThat(decrypted, is(password)); + } + + @Test + public void decode64Test() throws Exception { + String encrypted = Symm.base64.encode(password); + CmdLine.main(new String[]{"decode64", encrypted}); + assertThat(outContent.toString(), is(password + "\n")); + } + + @Test + public void encode64urlTest() throws Exception { + CmdLine.main(new String[]{"encode64url", password}); + String decrypted = Symm.base64url.decode(outContent.toString()); + assertThat(decrypted, is(password)); + } + + @Test + public void decode64urlTest() throws Exception { + String encrypted = Symm.base64url.encode(password); + CmdLine.main(new String[]{"decode64url", encrypted}); + assertThat(outContent.toString(), is(password + "\n")); + } + + @Test + public void md5Test() throws Exception { + CmdLine.main(new String[]{"md5", quickBrownFoxPlain}); + assertThat(outContent.toString(), is(quickBrownFoxMD5 + "\n")); + } + + @Test + public void sha256Test() throws Exception { + CmdLine.main(new String[]{"sha256", quickBrownFoxPlain}); + assertThat(outContent.toString(), is(quickBrownFoxSHA256 + "\n")); + + outContent.reset(); + CmdLine.main(new String[]{"sha256", quickBrownFoxPlain, "10"}); + String hash1 = outContent.toString(); + + outContent.reset(); + CmdLine.main(new String[]{"sha256", quickBrownFoxPlain, "10"}); + String hash2 = outContent.toString(); + + outContent.reset(); + CmdLine.main(new String[]{"sha256", quickBrownFoxPlain, "11"}); + String hash3 = outContent.toString(); + + assertThat(hash1, is(hash2)); + assertThat(hash1, is(not(hash3))); + } + + @Test + public void keygenTest() throws Exception { + CmdLine.main(new String[]{"keygen"}); + assertThat(outContent.toString().length(), is(2074)); + + String filePath = "test/output_key"; + File testDir = new File("test"); + if(!testDir.exists()) { + testDir.mkdirs(); + } + CmdLine.main(new String[]{"keygen", filePath}); + File keyfile = new File(filePath); + assertTrue(Files.isReadable(Paths.get(filePath))); + assertFalse(Files.isWritable(Paths.get(filePath))); + assertFalse(Files.isExecutable(Paths.get(filePath))); + keyfile.delete(); + } + + @Test + public void passgenTest() throws Exception { + CmdLine.main(new String[]{"passgen"}); + String output = outContent.toString().trim(); + assertThat(output.length(), is(24)); + assertTrue(containsAny(output, "+!@#$%^&*(){}[]?:;,.")); + assertTrue(containsAny(output, "ABCDEFGHIJKLMNOPQRSTUVWXYZ")); + assertTrue(containsAny(output, "abcdefghijklmnopqrstuvwxyz")); + assertTrue(containsAny(output, "0123456789")); + + int length = 10; + outContent.reset(); + CmdLine.main(new String[]{"passgen", String.valueOf(length)}); + output = outContent.toString().trim(); + assertThat(output.length(), is(length)); + + length = 5; + outContent.reset(); + CmdLine.main(new String[]{"passgen", String.valueOf(length)}); + output = outContent.toString().trim(); + assertThat(output.length(), is(8)); + + // Check that the custom hasRepeats method works + assertTrue(hasRepeats("aa")); + assertTrue(hasRepeats("baa")); + assertTrue(hasRepeats("aab")); + assertTrue(hasRepeats("baab")); + assertFalse(hasRepeats("abc")); + assertFalse(hasRepeats("aba")); + + // Run this a bunch of times for coverage + for (int i = 0; i < 1000; i++) { + outContent.reset(); + CmdLine.main(new String[]{"passgen"}); + output = outContent.toString().trim(); + assertFalse(hasRepeats(output)); + } + } + + @Test + public void urlgenTest() throws Exception { + CmdLine.main(new String[]{"urlgen"}); + String output = outContent.toString().trim(); + assertThat(output.length(), is(24)); + + int length = 5; + outContent.reset(); + CmdLine.main(new String[]{"urlgen", String.valueOf(length)}); + output = outContent.toString().trim(); + assertThat(output.length(), is(5)); + } + + @Test + public void showHelpTest() { + String expected = + "Usage: java -jar <this jar> ...\n" + + " keygen [<keyfile>] (Generates Key on file, or Std Out)\n" + + " digest [<passwd>|-i|] <keyfile> (Encrypts Password with \"keyfile\"\n" + + " if passwd = -i, will read StdIin\n" + + " if passwd is blank, will ask securely)\n" + + " passgen <digits> (Generate Password of given size)\n" + + " urlgen <digits> (Generate URL field of given size)\n" + + " csptest (Tests for CSP compatibility)\n" + + " encode64 <your text> (Encodes to Base64)\n" + + " decode64 <base64 encoded text> (Decodes from Base64)\n" + + " encode64url <your text> (Encodes to Base64 URL charset)\n" + + " decode64url <base64url encoded text> (Decodes from Base64 URL charset)\n" + + " sha256 <text> <salts(s)> (Digest String into SHA256 Hash)\n" + + " md5 <text> (Digest String into MD5 Hash)\n"; + + CmdLine.main(new String[]{}); + + assertThat(outContent.toString(), is(expected)); + } + + private boolean containsAny(String str, String searchChars) { + for (char c : searchChars.toCharArray()) { + if (str.indexOf(c) >= 0) { + return true; + } + } + return false; + } + + private boolean hasRepeats(String str) { + int c = -1; + int last; + for (int i = 0; i < str.length(); i++) { + last = c; + c = str.charAt(i); + if (c == last) { + return true; + } + } + return false; + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_Hash.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_Hash.java new file mode 100644 index 00000000..f5c4d872 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_Hash.java @@ -0,0 +1,210 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.test; + +import org.junit.Test; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.Hash; + +import static org.junit.Assert.*; + +import org.junit.BeforeClass; + +import static org.hamcrest.CoreMatchers.*; + +public class JU_Hash { + // Some common test vectors + private String quickBrownFoxVector = "The quick brown fox jumps over the lazy dog"; + private String quickBrownFoxMD5 = "0x9e107d9d372bb6826bd81d3542a419d6"; + private String quickBrownFoxSHA256 = "0xd7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592"; + + private String emptyVector = ""; + private String emptyMD5 = "0xd41d8cd98f00b204e9800998ecf8427e"; + private String emptySHA256 = "0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; + + + private byte[] same1 = "this is a twin".getBytes(); + private byte[] same2 = "this is a twin".getBytes(); + private byte[] different1 = "guvf vf n gjva".getBytes(); + private byte[] different2 = "this is an only child".getBytes(); + + + private String uppersDec = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + private String uppersHex1 = "0x4142434445464748494A4B4C4D4E4F505152535455565758595A"; + private String uppersHex2 = "0x4142434445464748494a4b4c4d4e4f505152535455565758595a"; + private String uppersHexNo0x1 = "4142434445464748494a4b4c4d4e4f505152535455565758595a"; + private String uppersHexNo0x2 = "4142434445464748494A4B4C4D4E4F505152535455565758595A"; + + private String lowersDec = "abcdefghijklmnopqrstuvwxyz"; + private String lowersHex = "0x6162636465666768696a6b6c6d6e6f707172737475767778797a"; + private String lowersHexNo0x1 = "6162636465666768696a6b6c6d6e6f707172737475767778797a"; + private String lowersHexNo0x2 = "6162636465666768696A6B6C6D6E6F707172737475767778797A"; + + private String numbersDec = "1234567890"; + private String numbersHex = "0x31323334353637383930"; + private String numbersHexNo0x = "31323334353637383930"; + + @SuppressWarnings("unused") + @BeforeClass + public static void getCoverage() { + // All of this class's methods are static, so we never need to instantiate an object. + // That said, we can't get 100% coverage unless we instantiate one + Hash hash = new Hash(); + } + + @Test + public void hashMD5Test() throws Exception { + byte[] output = Hash.hashMD5(quickBrownFoxVector.getBytes()); + assertEquals(quickBrownFoxMD5, new String(Hash.toHex(output))); + + output = Hash.hashMD5(emptyVector.getBytes()); + assertEquals(emptyMD5, new String(Hash.toHex(output))); + } + + @Test + public void hashMD5WithOffsetTest() throws Exception { + byte[] output = Hash.hashMD5(quickBrownFoxVector.getBytes(), 0, quickBrownFoxVector.length()); + assertEquals(quickBrownFoxMD5, new String(Hash.toHex(output))); + + output = Hash.hashMD5(emptyVector.getBytes(), 0, emptyVector.length()); + assertEquals(emptyMD5, new String(Hash.toHex(output))); + } + + @Test + public void hashMD5AsStringHexTest() throws Exception { + String output = Hash.hashMD5asStringHex(quickBrownFoxVector); + assertEquals(quickBrownFoxMD5, output); + + output = Hash.hashMD5asStringHex(emptyVector); + assertEquals(emptyMD5, output); + } + + @Test + public void hashSHA256Test() throws Exception { + byte[] output = Hash.hashSHA256(quickBrownFoxVector.getBytes()); + assertEquals(quickBrownFoxSHA256, new String(Hash.toHex(output))); + + output = Hash.hashSHA256(emptyVector.getBytes()); + assertEquals(emptySHA256, new String(Hash.toHex(output))); + } + + @Test + public void hashSHA256WithOffsetTest() throws Exception { + byte[] output = Hash.hashSHA256(quickBrownFoxVector.getBytes(), 0, quickBrownFoxVector.length()); + assertEquals(quickBrownFoxSHA256, new String(Hash.toHex(output))); + + output = Hash.hashSHA256(emptyVector.getBytes(), 0, emptyVector.length()); + assertEquals(emptySHA256, new String(Hash.toHex(output))); + } + + @Test + public void hashSHA256AsStringHexTest() throws Exception { + String output = Hash.hashSHA256asStringHex(quickBrownFoxVector); + assertEquals(quickBrownFoxSHA256, output); + + output = Hash.hashSHA256asStringHex(emptyVector); + assertEquals(emptySHA256, output); + } + + @Test + public void hashSaltySHA256AsStringHexTest() throws Exception { + String input = "password"; + String hash1 = Hash.hashSHA256asStringHex(input, 10); + String hash2 = Hash.hashSHA256asStringHex(input, 10); + String hash3 = Hash.hashSHA256asStringHex(input, 11); + + assertEquals(hash1, hash2); + assertThat(hash1, not(equalTo(hash3))); + } + + @Test + public void isEqualTest() throws Exception { + assertTrue(Hash.isEqual(same1, same2)); + assertFalse(Hash.isEqual(same1, different1)); + assertFalse(Hash.isEqual(same1, different2)); + } + + @Test + public void compareToTest() throws Exception { + assertEquals(0, Hash.compareTo(same1, same2)); + // different1 is rot13(same1), so the difference should be 13 + assertEquals(13, Hash.compareTo(same1, different1)); + assertEquals(-78, Hash.compareTo(same1, different2)); + } + + @Test + public void toHexNo0xTest() throws Exception { + assertEquals(uppersHexNo0x1, Hash.toHexNo0x(uppersDec.getBytes())); + assertEquals(lowersHexNo0x1, Hash.toHexNo0x(lowersDec.getBytes())); + assertEquals(numbersHexNo0x, Hash.toHexNo0x(numbersDec.getBytes())); + } + + @Test + public void toHexTest() throws Exception { + assertEquals(uppersHex2, Hash.toHex(uppersDec.getBytes())); + assertEquals(lowersHex, Hash.toHex(lowersDec.getBytes())); + assertEquals(numbersHex, Hash.toHex(numbersDec.getBytes())); + } + + @Test + public void toHexWithOffset() throws Exception { + assertEquals(uppersHex2, Hash.toHex(uppersDec.getBytes(), 0, uppersDec.length())); + assertEquals(lowersHex, Hash.toHex(lowersDec.getBytes(), 0, lowersDec.length())); + assertEquals(numbersHex, Hash.toHex(numbersDec.getBytes(), 0, numbersDec.length())); + } + + @Test + public void fromHexTest() throws Exception { + assertEquals(uppersDec, new String(Hash.fromHex(uppersHex1))); + assertEquals(lowersDec, new String(Hash.fromHex(lowersHex))); + assertEquals(numbersDec, new String(Hash.fromHex(numbersHex))); + + try { + // This string doesn't begin with "0x" + Hash.fromHex("0X65"); + fail("Should have thrown CadiException"); + } catch (CadiException e) { + assertEquals("HexString must start with \"0x\"", e.getMessage()); + } + + try { + // This string has invalid hex characters + Hash.fromHex("0xQ"); + fail("Should have thrown CadiException"); + } catch (CadiException e) { + // 81 is dec(Q) + assertEquals("Invalid char '81' in HexString", e.getMessage()); + } + } + + @Test + public void fromHexNo0xTest() throws Exception { + assertEquals(uppersDec, new String(Hash.fromHexNo0x(uppersHexNo0x1))); + assertEquals(lowersDec, new String(Hash.fromHexNo0x(lowersHexNo0x1))); + assertEquals(uppersDec, new String(Hash.fromHexNo0x(uppersHexNo0x2))); + assertEquals(lowersDec, new String(Hash.fromHexNo0x(lowersHexNo0x2))); + assertEquals(numbersDec, new String(Hash.fromHexNo0x(numbersHexNo0x))); + byte[] output = Hash.fromHexNo0x("ABC"); + assertEquals(new String(new byte[] {(byte)0x0A, (byte)0xB0}), new String(output)); + assertNull(Hash.fromHexNo0x("~~")); + } +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_LocatorException.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_LocatorException.java new file mode 100644 index 00000000..96cf8e51 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_LocatorException.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.test; + +import static org.junit.Assert.*; + +import org.junit.Test; +import org.onap.aaf.cadi.LocatorException; + +import static org.hamcrest.CoreMatchers.is; + +public class JU_LocatorException { + @Test + public void stringTest() { + LocatorException exception = new LocatorException("New Exception"); + assertNotNull(exception); + assertThat(exception.getMessage(), is("New Exception")); + } + + @Test + public void throwableTest() { + LocatorException exception = new LocatorException(new Throwable("New Exception")); + assertNotNull(exception); + assertThat(exception.getMessage(), is("java.lang.Throwable: New Exception")); + } + + @Test + public void stringThrowableTest() { + LocatorException exception = new LocatorException("New Exception",new Throwable("New Exception")); + assertNotNull(exception); + assertThat(exception.getMessage(), is("New Exception")); + } + + @Test + public void characterSequenceTest() { + CharSequence testCS = new String("New Exception"); + LocatorException exception = new LocatorException(testCS); + assertNotNull(exception); + assertThat(exception.getMessage(), is("New Exception")); + } +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_PropAccess.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_PropAccess.java new file mode 100644 index 00000000..7b5da6c1 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_PropAccess.java @@ -0,0 +1,151 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.test; + +import static org.junit.Assert.*; +import org.junit.Test; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.PropAccess.LogIt; + +import static org.mockito.Mockito.*; +import static org.hamcrest.CoreMatchers.*; + +import java.lang.reflect.Field; + +import java.io.ByteArrayInputStream; +import java.io.PrintStream; +import java.util.Properties; + +@SuppressWarnings("unused") +public class JU_PropAccess { + // Note: We can't actually get coverage of the protected constructor - + // that will be done later, when testing the child class "ServletContextAccess" + + + @Test + public void ConstructorTest() throws Exception { + PropAccess prop = new PropAccess(); + assertThat(prop.getProperties(), is(not(nullValue()))); + } + + @Test + public void noPrintStreamConstructionTest() throws Exception { + // Test for coverage + PropAccess prop = new PropAccess((PrintStream)null, new String[]{"Invalid argument"}); + } + + @Test + public void noLogItConstructionTest() throws Exception { + // Test for coverage + PropAccess prop = new PropAccess((LogIt)null, new String[]{"Invalid argument"}); + } + + @Test + public void propertiesConstructionTest() throws Exception { + // Coverage tests + PropAccess prop = new PropAccess(System.getProperties()); + prop = new PropAccess((PrintStream)null, System.getProperties()); + } + + @Test + public void stringConstructionTest() throws Exception { + Properties testSystemProps = new Properties(System.getProperties()); + testSystemProps.setProperty("cadi_name", "user"); + System.setProperties(testSystemProps); + PropAccess prop = new PropAccess("cadi_keyfile=src/test/resources/keyfile", "cadi_loglevel=DEBUG", "cadi_prop_files=test/cadi.properties:not_a_file"); + } + + @Test + public void loadTest() throws Exception { + // Coverage tests + Properties props = mock(Properties.class); + when(props.getProperty("cadi_prop_files")).thenReturn("test/cadi.properties").thenReturn(null); + PropAccess pa = new PropAccess(); + Field props_field = PropAccess.class.getDeclaredField("props"); + props_field.setAccessible(true); + props_field.set(pa, props); + ByteArrayInputStream bais = new ByteArrayInputStream(new byte[0]); + pa.load(bais); + } + + @Test + public void specialConversionsTest() throws Exception { + // Coverage tests + Properties testSystemProps = new Properties(System.getProperties()); + testSystemProps.setProperty("java.specification.version", "1.7"); + System.setProperties(testSystemProps); + PropAccess pa = new PropAccess("AFT_LATITUDE=1", "AFT_LONGITUDE=1", "cadi_protocols=TLSv1.2"); + } + + @Test + public void logTest() throws Exception { + // Coverage tests + PropAccess pa = new PropAccess(); + + pa.log(Level.DEBUG); + pa.printf(Level.DEBUG, "not a real format string"); + + pa.setLogLevel(Level.DEBUG); + pa.log(Level.DEBUG); + pa.log(Level.DEBUG, 1, " ", null, ""); + pa.log(Level.DEBUG, "This is a string", "This is another"); + pa.set(new LogIt() { + @Override public void push(Level level, Object ... elements) {} + }); + try { + pa.log(new Exception("This exception was thrown intentionally, please ignore it")); + } catch(Exception e) { + fail("Should have thrown an exception"); + } + } + + @Test + public void classLoaderTest() { + PropAccess pa = new PropAccess(); + assertThat(pa.classLoader(), instanceOf(ClassLoader.class)); + } + + @Test + public void encryptionTest() throws Exception { + PropAccess pa = new PropAccess(); + String plainText = "This is a secret message"; + String secret_message = pa.encrypt(plainText); + String modified = secret_message.substring(4); + // Plenty of assertions to hit all branches + assertThat(pa.decrypt(secret_message, false), is(plainText)); + assertThat(pa.decrypt(null, false), is(nullValue())); + assertThat(pa.decrypt(modified, true), is(plainText)); + assertThat(pa.decrypt(modified, false), is(modified)); + } + + @Test + public void setPropertyTest() { + PropAccess pa = new PropAccess(); + pa.setProperty("test", null); + String prop = "New Property"; + String val ="And it's faithful value"; + pa.setProperty(prop, val); + + assertThat(pa.getProperty(prop), is(val)); + } +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_ServletContextAccess.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_ServletContextAccess.java new file mode 100644 index 00000000..8531e1d1 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_ServletContextAccess.java @@ -0,0 +1,104 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.test; + +import static org.junit.Assert.*; + +import org.junit.Before; +import org.junit.Test; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.ServletContextAccess; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.PropAccess.LogIt; + +import static org.mockito.Mockito.*; +import static org.hamcrest.CoreMatchers.*; + +import java.lang.reflect.Field; + +import java.io.ByteArrayInputStream; +import java.io.PrintStream; +import java.util.Enumeration; +import java.util.Properties; +import java.util.StringTokenizer; + +import javax.servlet.FilterConfig; +import javax.servlet.ServletContext; + +@SuppressWarnings("unused") +public class JU_ServletContextAccess { + + private FilterConfig filter_mock; + Enumeration<String> enumeration; + + private class CustomEnumeration implements Enumeration<String> { + private int idx = 0; + private final String[] elements = {"This", "is", "a", "test"}; + @Override + public String nextElement() { + return idx >= elements.length ? null : elements[idx++]; + } + @Override + public boolean hasMoreElements() { + return idx < elements.length; + } + } + + @Before + public void setup() { + enumeration = new CustomEnumeration(); + filter_mock = mock(FilterConfig.class); + when(filter_mock.getInitParameterNames()).thenReturn(enumeration); + } + + @Test + public void ConstructorTest() throws Exception { + ServletContextAccess sca = new ServletContextAccess(filter_mock); + } + + @Test + public void logTest() throws Exception { + ServletContext sc_mock = mock(ServletContext.class); + when(filter_mock.getServletContext()).thenReturn(sc_mock); + ServletContextAccess sca = new ServletContextAccess(filter_mock); + + sca.log(Level.DEBUG); + + sca.setLogLevel(Level.DEBUG); + sca.log(Level.DEBUG); + + try { + sca.log(new Exception("This exception was thrown intentionally, please ignore it")); + } catch(Exception e) { + fail("Should have thrown an exception"); + } + } + + @Test + public void contextTest() { + ServletContext sc_mock = mock(ServletContext.class); + when(filter_mock.getServletContext()).thenReturn(sc_mock); + ServletContextAccess sca = new ServletContextAccess(filter_mock); + assertThat(sca.context(), instanceOf(ServletContext.class)); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_Symm.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_Symm.java new file mode 100644 index 00000000..753451ed --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_Symm.java @@ -0,0 +1,212 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.test; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; +import java.lang.reflect.*; +import org.junit.*; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.Arrays; + +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.Symm; + +public class JU_Symm { + private Symm defaultSymm; + + private ByteArrayOutputStream outStream; + + @Before + public void setup() throws Exception { + defaultSymm = new Symm( + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray() + ,76, "Use default!" ,true); + outStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outStream)); + } + + @After + public void tearDown() { + System.setOut(System.out); + } + + @Test + public void constructorTest() throws Exception { + Symm myCustomSymm = new Symm( + "ACEGIKMOQSUWYacegikmoqsuwy02468+/".toCharArray(), 76, "Default", true); + Field convert_field = Symm.class.getDeclaredField("convert"); + convert_field.setAccessible(true); + + Class<?> Unordered_class = Class.forName("org.onap.aaf.cadi.Symm$Unordered"); + assertThat(convert_field.get(myCustomSymm), instanceOf(Unordered_class)); + } + + @SuppressWarnings("unused") + @Test + public void copyTest() throws Exception { + Symm copy = Symm.base64.copy(76); + } + + @SuppressWarnings("deprecation") + @Test + public void deprecatedTest() { + assertEquals(Symm.base64(), Symm.base64); + assertEquals(Symm.base64noSplit(), Symm.base64noSplit); + assertEquals(Symm.base64url(), Symm.base64url); + assertEquals(Symm.baseCrypt(), Symm.encrypt); + } + + @Test + public void encodeDecodeStringTest() throws Exception { + String orig = "hello"; + String b64encrypted = Symm.base64.encode(orig); + assertEquals(Symm.base64.decode(b64encrypted), orig); + + String defaultEnrypted = defaultSymm.encode(orig); + assertEquals(defaultSymm.decode(defaultEnrypted), orig); + } + + @Test + public void encodeDecodeByteArrayTest() throws Exception { + String orig = "hello"; + byte[] b64encrypted = Symm.base64.encode(orig.getBytes()); + assertEquals(new String(Symm.base64.decode(b64encrypted)), orig); + + byte[] empty = null; + assertTrue(Arrays.equals(Symm.base64.encode(empty), new byte[0])); + } + + @Test + public void encodeDecodeStringToStreamTest() throws Exception { + String orig = "I'm a password, really"; + String b64encrypted; + String output; + + ByteArrayOutputStream baosEncrypt = new ByteArrayOutputStream(); + Symm.base64.encode(orig, baosEncrypt); + b64encrypted = new String(baosEncrypt.toByteArray()); + + ByteArrayOutputStream baosDecrypt = new ByteArrayOutputStream(); + Symm.base64.decode(b64encrypted, baosDecrypt); + output = new String(baosDecrypt.toByteArray()); + + assertEquals(orig, output); + } + + @Test + public void encryptDecryptStreamWithPrefixTest() throws Exception { + String orig = "I'm a password, really"; + byte[] b64encrypted; + String output; + + byte[] prefix = "enc:".getBytes(); + + ByteArrayInputStream baisEncrypt = new ByteArrayInputStream(orig.getBytes()); + ByteArrayOutputStream baosEncrypt = new ByteArrayOutputStream(); + Symm.base64.encode(baisEncrypt, baosEncrypt, prefix); + + b64encrypted = baosEncrypt.toByteArray(); + + ByteArrayInputStream baisDecrypt = new ByteArrayInputStream(b64encrypted); + ByteArrayOutputStream baosDecrypt = new ByteArrayOutputStream(); + Symm.base64.decode(baisDecrypt, baosDecrypt, prefix.length); + + output = new String(baosDecrypt.toByteArray()); + assertEquals(orig, output); + } + + @Test + public void randomGenTest() { + // Ian - There really isn't a great way to test for randomness... + String prev = null; + for (int i = 0; i < 10; i++) { + String current = Symm.randomGen(100); + if (current.equals(prev)) { + fail("I don't know how, but you generated the exact same random string twice in a row"); + } + prev = current; + } + assertTrue(true); + } + + @Test + public void obtainTest() throws Exception { + Symm symm = Symm.base64.obtain(); + + String orig ="Another Password, please"; + String encrypted = symm.enpass(orig); + String decrypted = symm.depass(encrypted); + assertEquals(orig, decrypted); + } + + @Test + public void InputStreamObtainTest() throws Exception { + byte[] keygen = Symm.keygen(); + + Symm symm = Symm.obtain(new ByteArrayInputStream(keygen)); + + String orig ="Another Password, please"; + String encrypted = symm.enpass(orig); + String decrypted = symm.depass(encrypted); + assertEquals(orig, decrypted); + } + + @Test + public void StringObtainTest() throws Exception { + byte[] keygen = Symm.keygen(); + + Symm symm = Symm.obtain(new String(keygen)); + + String orig ="Another Password, please"; + String encrypted = symm.enpass(orig); + String decrypted = symm.depass(encrypted); + assertEquals(orig, decrypted); + } + + @Test + public void AccessObtainTest() throws Exception { + PropAccess pa = new PropAccess("cadi_keyfile=src/test/resources/keyfile"); + Symm symm = Symm.obtain(pa); + String orig ="Another Password, please"; + String encrypted = symm.enpass(orig); + String decrypted = symm.depass(encrypted); + assertEquals(orig, decrypted); + + try { + PropAccess badPa = mock(PropAccess.class); + when(badPa.getProperty("cadi_keyfile", null)).thenReturn("not_a_real_file.txt"); + symm = Symm.obtain(badPa); + fail("Should have thrown an exception"); + } catch (CadiException e) { + assertTrue(e.getMessage().contains("ERROR: ")); + assertTrue(e.getMessage().contains("not_a_real_file.txt")); + assertTrue(e.getMessage().contains(" does not exist!")); + } + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_TrustChecker.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_TrustChecker.java new file mode 100644 index 00000000..511c6ee3 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_TrustChecker.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import org.junit.*; + +import org.onap.aaf.cadi.TrustChecker; + +public class JU_TrustChecker { + + @Test + public void noTrustTests() { + assertThat(TrustChecker.NOTRUST.mayTrust(null, null), is(nullValue())); + TrustChecker.NOTRUST.setLur(null); + } +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_User.java b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_User.java new file mode 100644 index 00000000..25683249 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/test/JU_User.java @@ -0,0 +1,185 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.test; + +import static org.junit.Assert.*; + +import org.junit.Test; + + +import static org.hamcrest.CoreMatchers.*; +import static org.mockito.Mockito.when; + +import java.lang.reflect.Field; +import java.security.Principal; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.Before; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.onap.aaf.cadi.Permission; +import org.onap.aaf.cadi.User; +import org.onap.aaf.cadi.lur.LocalPermission; + +public class JU_User { + + private final Long SECOND = 1000L; + private final String name = "Fakey McFake"; + private final String cred = "Fake credentials"; + + private Field perms_field; + private Field count_field; + + @Mock + private Principal principal; + + @Mock + private LocalPermission permission; + @Mock + private LocalPermission permission2; + + @Before + public void setup() throws NoSuchFieldException, SecurityException { + MockitoAnnotations.initMocks(this); + + when(principal.getName()).thenReturn("Principal"); + + when(permission.getKey()).thenReturn("NewKey"); + when(permission.match(permission)).thenReturn(true); + + when(permission2.getKey()).thenReturn("NewKey2"); + when(permission2.match(permission)).thenReturn(false); + + perms_field = User.class.getDeclaredField("perms"); + perms_field.setAccessible(true); + + count_field = User.class.getDeclaredField("count"); + count_field.setAccessible(true); + } + + @Test + public void constructorPrincipalTest() throws IllegalArgumentException, IllegalAccessException { + User<Permission> user = new User<Permission>(principal); + assertThat(user.name, is(principal.getName())); + assertThat(user.principal, is(principal)); + assertThat(user.permExpires(), is(Long.MAX_VALUE)); + assertThat((int)count_field.get(user), is(0)); + } + + @Test + public void constructorNameCredTest() throws IllegalArgumentException, IllegalAccessException { + User<Permission> user = new User<Permission>(name, cred.getBytes()); + assertThat(user.name, is(name)); + assertThat(user.principal, is(nullValue())); + assertThat(user.permExpires(), is(Long.MAX_VALUE)); + assertThat((int)count_field.get(user), is(0)); + assertThat(user.getCred(), is(cred.getBytes())); + } + + @Test + public void constructorPrincipalIntervalTest() throws IllegalArgumentException, IllegalAccessException { + User<Permission> user = new User<Permission>(principal, 61 * SECOND); + Long approxExpiration = System.currentTimeMillis() + 61 * SECOND; + assertThat(user.name, is(principal.getName())); + assertThat(user.principal, is(principal)); + assertTrue(Math.abs(user.permExpires() - approxExpiration) < 10L); + assertThat((int)count_field.get(user), is(0)); + } + + @Test + public void constructorNameCredIntervalTest() throws IllegalArgumentException, IllegalAccessException { + String name = "Fakey McFake"; + User<Permission> user = new User<Permission>(name, cred.getBytes(), 61 * SECOND); + Long approxExpiration = System.currentTimeMillis() + 61 * SECOND; + assertThat(user.name, is(name)); + assertThat(user.principal, is(nullValue())); + assertTrue(Math.abs(user.permExpires() - approxExpiration) < 10L); + assertThat((int)count_field.get(user), is(0)); + assertThat(user.getCred(), is(cred.getBytes())); + } + + @Test + public void countCheckTest() throws IllegalArgumentException, IllegalAccessException { + User<Permission> user = new User<Permission>(principal); + user.resetCount(); + assertThat((int)count_field.get(user), is(0)); + user.incCount(); + assertThat((int)count_field.get(user), is(1)); + user.incCount(); + assertThat((int)count_field.get(user), is(2)); + user.resetCount(); + assertThat((int)count_field.get(user), is(0)); + } + + @Test + public void permTest() throws InterruptedException, IllegalArgumentException, IllegalAccessException { + User<Permission> user = new User<Permission>(principal); + assertThat(user.permExpires(), is(Long.MAX_VALUE)); + user.renewPerm(); + Thread.sleep(1); // Let it expire + assertThat(user.permExpired(), is(true)); + + user = new User<Permission>(principal,100); + assertTrue(user.noPerms()); + user.add(permission); + assertFalse(user.permsUnloaded()); + assertFalse(user.noPerms()); + user.setNoPerms(); + assertThat(user.permExpired(), is(false)); + assertTrue(user.permsUnloaded()); + assertTrue(user.noPerms()); + perms_field.set(user, null); + assertTrue(user.permsUnloaded()); + assertTrue(user.noPerms()); + } + + @Test + public void addValuesToNewMapTest() { + User<Permission> user = new User<Permission>(principal); + Map<String, Permission> newMap = new HashMap<String,Permission>(); + + assertFalse(user.contains(permission)); + + user.add(newMap, permission); + user.setMap(newMap); + + assertTrue(user.contains(permission)); + + List<Permission> sink = new ArrayList<Permission>(); + user.copyPermsTo(sink); + + assertThat(sink.size(), is(1)); + assertTrue(sink.contains(permission)); + + assertThat(user.toString(), is("Principal|:NewKey")); + + user.add(newMap, permission2); + user.setMap(newMap); + assertFalse(user.contains(permission2)); + + assertThat(user.toString(), is("Principal|:NewKey2,NewKey")); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_Chmod.java b/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_Chmod.java new file mode 100644 index 00000000..62114189 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_Chmod.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.util.test; + +import static org.junit.Assert.*; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.PosixFilePermissions; +import java.util.Set; + +import static org.hamcrest.CoreMatchers.*; +import org.junit.*; + +import org.onap.aaf.cadi.util.Chmod; + +public class JU_Chmod { + + private File file; + private String filePath; + + @Before + public void setup() throws IOException { + file = File.createTempFile("chmod_test", ""); + filePath = file.getAbsolutePath(); + } + + @After + public void tearDown() { + file.delete(); + } + + @Test + public void to755Test() throws IOException { + Chmod.to755.chmod(file); + Set<PosixFilePermission> set = Files.getPosixFilePermissions(Paths.get(filePath)); + assertThat(PosixFilePermissions.toString(set), is("rwxr-xr-x")); + } + + @Test + public void to644Test() throws IOException { + Chmod.to644.chmod(file); + Set<PosixFilePermission> set = Files.getPosixFilePermissions(Paths.get(filePath)); + assertThat(PosixFilePermissions.toString(set), is("rw-r--r--")); + } + + @Test + public void to400Test() throws IOException { + Chmod.to400.chmod(file); + Set<PosixFilePermission> set = Files.getPosixFilePermissions(Paths.get(filePath)); + assertThat(PosixFilePermissions.toString(set), is("r--------")); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_FQI.java b/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_FQI.java new file mode 100644 index 00000000..bcd2f776 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_FQI.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ + +package org.onap.aaf.cadi.util.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import org.junit.*; + +import org.onap.aaf.cadi.util.FQI; + +public class JU_FQI { + + @Test + public void reverseDomainTest() { + assertThat(FQI.reverseDomain("user@att.com"), is("com.att")); + } + + @Test + public void coverageTest() { + @SuppressWarnings("unused") + FQI fqi = new FQI(); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_JsonOutputStream.java b/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_JsonOutputStream.java new file mode 100644 index 00000000..da3557cb --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_JsonOutputStream.java @@ -0,0 +1,93 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ + +package org.onap.aaf.cadi.util.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; + +import java.io.ByteArrayOutputStream; + +import org.junit.*; + +import java.io.IOException; +import java.lang.reflect.Field; + +import org.onap.aaf.cadi.util.JsonOutputStream; + +public class JU_JsonOutputStream { + + private JsonOutputStream jos; + + @Before + public void setup() { + jos = new JsonOutputStream(new ByteArrayOutputStream()); + } + + @Test + public void constructorTest() { + jos = new JsonOutputStream(System.out); + jos = new JsonOutputStream(System.err); + } + + @Test + public void writeTest() throws IOException { + byte[] json = ("{" + + "name: user," + + "password: pass," + + "contact: {" + + "email: user@att.com," + + "phone: 555-5555" + + "}," + + "list: [" + + "item1," + + "item2" + + "],[],{}," + + "list:" + + "[" + + "item1," + + "item2" + + "]" + + "}").getBytes(); + jos.write(json); + } + + @Test + public void resetIndentTest() throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException { + Field indentField = JsonOutputStream.class.getDeclaredField("indent"); + indentField.setAccessible(true); + + assertThat((int)indentField.get(jos), is(0)); + jos.resetIndent(); + assertThat((int)indentField.get(jos), is(1)); + } + + @Test + public void coverageTest() throws IOException { + jos.flush(); + jos.close(); + + jos = new JsonOutputStream(System.out); + jos.close(); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_MaskFormatException.java b/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_MaskFormatException.java new file mode 100644 index 00000000..ea743430 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_MaskFormatException.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ + +package org.onap.aaf.cadi.util.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import org.junit.*; + +import org.onap.aaf.cadi.util.MaskFormatException; + +public class JU_MaskFormatException { + + @Test + public void throwsTest() { + String errorMessage = "This is a MaskFormatException"; + try { + throw new MaskFormatException(errorMessage); + } catch (Exception e) { + assertThat(e.getMessage(), is(errorMessage)); + assertTrue(e instanceof MaskFormatException); + } + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_NetMask.java b/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_NetMask.java new file mode 100644 index 00000000..403f1f1e --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_NetMask.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.util.test; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.onap.aaf.cadi.util.NetMask; + +public class JU_NetMask { + + @Test + public void deriveTest() { + String test = "test"; + assertEquals(NetMask.derive(test.getBytes()), 0); + } + + @Test + public void deriveTest2() { + String test = "1.2.3.4"; + assertEquals(NetMask.derive(test.getBytes()), 0); + } + + @Test + public void deriveTest3() { + String test = "1.2.4"; + assertEquals(NetMask.derive(test.getBytes()), 0); + } + + @Test + public void deriveTest4() { + String test = "1.3.4"; + assertEquals(NetMask.derive(test.getBytes()), 0); + } + + @Test + public void deriveTest5() { + String test = "2.3.4"; + assertEquals(NetMask.derive(test.getBytes()), 0); + } + + @Test + public void deriveTest6() { + String test = "3.4"; + assertEquals(NetMask.derive(test.getBytes()), 0); + } +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_Pool.java b/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_Pool.java new file mode 100644 index 00000000..79209322 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_Pool.java @@ -0,0 +1,159 @@ +/******************************************************************************* + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.util.test; + +import static org.junit.Assert.*; + +import java.util.ArrayList; +import java.util.List; + +import static org.hamcrest.CoreMatchers.*; +import org.junit.*; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.util.Pool; +import org.onap.aaf.cadi.util.Pool.*; + +public class JU_Pool { + + private StringBuilder sb = new StringBuilder(); + + private class IntegerCreator implements Creator<Integer> { + private int current = 0; + + @Override + public Integer create() { + return current++; + } + + @Override + public void destroy(Integer t) { + t = 0; + } + + @Override + public boolean isValid(Integer t) { + return (t & 0x1) == 0; + } + + @Override + public void reuse(Integer t) { + } + } + + private class CustomLogger implements Log { + @Override + public void log(Object... o) { + for (Object item : o) { + sb.append(item.toString()); + } + } + } + + @Test + public void getTest() throws CadiException { + Pool<Integer> intPool = new Pool<Integer>(new IntegerCreator()); + + List<Pooled<Integer>> gotten = new ArrayList<Pooled<Integer>>(); + for (int i = 0; i < 10; i++) { + gotten.add(intPool.get()); + assertThat(gotten.get(i).content, is(i)); + } + + gotten.get(9).done(); + gotten.set(9, intPool.get()); + assertThat(gotten.get(9).content, is(9)); + + for (int i = 0; i < 10; i++) { + gotten.get(i).done(); + } + + for (int i = 0; i < 10; i++) { + gotten.set(i, intPool.get()); + if (i < 5) { + assertThat(gotten.get(i).content, is(i)); + } else { + assertThat(gotten.get(i).content, is(i + 5)); + } + } + + for (int i = 0; i < 10; i++) { + gotten.get(i).toss(); + // Coverage calls + gotten.get(i).toss(); + gotten.get(i).done(); + + // only set some objects to null -> this is for the finalize coverage test + if (i < 5) { + gotten.set(i, null); + } + } + + // Coverage of finalize() + System.gc(); + } + + @Test + public void bulkTest() throws CadiException { + Pool<Integer> intPool = new Pool<Integer>(new IntegerCreator()); + + intPool.prime(10); + // Remove all of the invalid items (in this case, odd numbers) + assertFalse(intPool.validate()); + + // Make sure we got them all + assertTrue(intPool.validate()); + + // Get an item from the pool + Pooled<Integer> gotten = intPool.get(); + assertThat(gotten.content, is(0)); + + // finalize that item, then check the next one to make sure we actually purged + // the odd numbers + gotten = intPool.get(); + assertThat(gotten.content, is(2)); + + intPool.drain(); + + } + + @Test + public void setMaxTest() { + Pool<Integer> intPool = new Pool<Integer>(new IntegerCreator()); + intPool.setMaxRange(10); + assertThat(intPool.getMaxRange(), is(10)); + intPool.setMaxRange(-10); + assertThat(intPool.getMaxRange(), is(0)); + } + + @Test + public void loggingTest() { + Pool<Integer> intPool = new Pool<Integer>(new IntegerCreator()); + + // Log to Log.NULL for coverage + intPool.log("Test log output"); + + intPool.setLogger(new CustomLogger()); + intPool.log("Test log output"); + + assertThat(sb.toString(), is("Test log output")); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_Split.java b/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_Split.java new file mode 100644 index 00000000..12be4e15 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_Split.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.util.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import org.junit.*; + +import org.onap.aaf.cadi.util.Split; + +public class JU_Split { + + @Test + public void splitTest() { + String[] output = Split.split('c', "ctestctc", 0, "ctestctc".length()); + assertThat(output.length, is(4)); + assertThat(output[0], is("")); + assertThat(output[1], is("test")); + assertThat(output[2], is("t")); + assertThat(output[3], is("")); + + output = Split.split('c', "ctestctc", 0, 4); + assertThat(output.length, is(2)); + assertThat(output[0], is("")); + assertThat(output[1], is("tes")); + + output = Split.split('c', "test", 0, "test".length()); + assertThat(output.length, is(1)); + assertThat(output[0], is("test")); + + assertThat(Split.split('c', null, 0, 0).length, is(0)); + + // Test with fewer arguments + output = Split.split('c', "ctestctc"); + assertThat(output.length, is(4)); + assertThat(output[0], is("")); + assertThat(output[1], is("test")); + assertThat(output[2], is("t")); + assertThat(output[3], is("")); + } + + @Test + public void splitTrimTest() { + String[] output = Split.splitTrim('c', " cte stc ctc ", 0, " cte stc ctc ".length()); + assertThat(output.length, is(5)); + assertThat(output[0], is("")); + assertThat(output[1], is("te st")); + assertThat(output[2], is("")); + assertThat(output[3], is("t")); + assertThat(output[4], is("")); + + output = Split.splitTrim('c', " cte stc ctc ", 0, 5); + assertThat(output.length, is(2)); + assertThat(output[0], is("")); + assertThat(output[1], is("te")); + + assertThat(Split.splitTrim('c', " te st ", 0, " te st ".length())[0], is("te st")); + + assertThat(Split.splitTrim('c', null, 0, 0).length, is(0)); + + // Test with 2 arguments + output = Split.splitTrim('c', " cte stc ctc "); + assertThat(output.length, is(5)); + assertThat(output[0], is("")); + assertThat(output[1], is("te st")); + assertThat(output[2], is("")); + assertThat(output[3], is("t")); + assertThat(output[4], is("")); + + // Tests with 1 argument + output = Split.splitTrim('c', " cte stc ctc ", 1); + assertThat(output.length, is(1)); + assertThat(output[0], is("cte stc ctc")); + + output = Split.splitTrim('c', "testctest2", 2); + assertThat(output.length, is(2)); + assertThat(output[0], is("test")); + assertThat(output[1], is("test2")); + + output = Split.splitTrim('c', " cte stc ctc ", 4); + assertThat(output.length, is(4)); + assertThat(output[0], is("")); + assertThat(output[1], is("te st")); + assertThat(output[2], is("")); + + assertThat(Split.splitTrim('c', null, 0).length, is(0)); + } + + @Test + public void coverageTest() { + @SuppressWarnings("unused") + Split split = new Split(); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_SubStandardConsole.java b/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_SubStandardConsole.java new file mode 100644 index 00000000..4d8e8f84 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_SubStandardConsole.java @@ -0,0 +1,124 @@ +/******************************************************************************* + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.util.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import static org.mockito.Mockito.*; +import org.junit.*; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.lang.reflect.Field; + +import org.onap.aaf.cadi.util.SubStandardConsole; + +public class JU_SubStandardConsole { + + private String inputString = "An input string"; + private ByteArrayOutputStream outStream; + private ByteArrayOutputStream errStream; + + @Before + public void setup() { + outStream = new ByteArrayOutputStream(); + errStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outStream)); + System.setErr(new PrintStream(errStream)); + } + + @After + public void tearDown() { + System.setOut(System.out); + System.setErr(System.err); + } + + @Test + public void readLineTest() { + byte[] input = inputString.getBytes(); + System.setIn(new ByteArrayInputStream(input)); + SubStandardConsole ssc = new SubStandardConsole(); + String output = ssc.readLine("%s\n", ">>> "); + assertThat(output, is(inputString)); + assertThat(outStream.toString(), is(">>> \n")); + } + + @Test + public void readLineTest2() { + byte[] input = inputString.getBytes(); + System.setIn(new ByteArrayInputStream(input)); + SubStandardConsole ssc = new SubStandardConsole(); + String output = ssc.readLine("%s %s\n", ">>> ", "Another argument for coverage"); + assertThat(output, is(inputString)); + } + + @Test + public void readLineTest3() { + byte[] input = "\n".getBytes(); + System.setIn(new ByteArrayInputStream(input)); + SubStandardConsole ssc = new SubStandardConsole(); + String output = ssc.readLine("%s\n", ">>> "); + assertThat(output, is(">>> ")); + assertThat(outStream.toString(), is(">>> \n")); + } + + @Test + public void readPasswordTest() { + byte[] input = inputString.getBytes(); + System.setIn(new ByteArrayInputStream(input)); + SubStandardConsole ssc = new SubStandardConsole(); + char[] output = ssc.readPassword("%s\n", ">>> "); + System.out.println(output); + assertThat(output, is(inputString.toCharArray())); + assertThat(outStream.toString(), is(">>> \nAn input string\n")); + } + + @Test + public void printfTest() { + byte[] input = inputString.getBytes(); + System.setIn(new ByteArrayInputStream(input)); + SubStandardConsole ssc = new SubStandardConsole(); + ssc.printf("%s", "A format specifier"); + assertThat(outStream.toString(), is("A format specifier")); + } + + @Test + public void throwsTest() throws IOException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + BufferedReader brMock = mock(BufferedReader.class); + when(brMock.readLine()).thenThrow(new IOException()); + + SubStandardConsole ssc = new SubStandardConsole(); + + Field brField = SubStandardConsole.class.getDeclaredField("br"); + brField.setAccessible(true); + brField.set(ssc, brMock); + + assertThat(ssc.readLine(""), is("")); + assertThat(errStream.toString(), is("uh oh...\n")); + errStream.reset(); + assertThat(ssc.readPassword("").length, is(0)); + assertThat(errStream.toString(), is("uh oh...\n")); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_TheConsole.java b/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_TheConsole.java new file mode 100644 index 00000000..40f88a3a --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_TheConsole.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.util.test; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.onap.aaf.cadi.util.TheConsole; + +public class JU_TheConsole { + + @Test + public void implemented(){ + assertEquals(TheConsole.implemented(),false); + } +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_UserChainManip.java b/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_UserChainManip.java new file mode 100644 index 00000000..21f8c21b --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_UserChainManip.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.util.test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import org.junit.*; + +import org.onap.aaf.cadi.UserChain; +import org.onap.aaf.cadi.util.UserChainManip; + +public class JU_UserChainManip { + + @Test + public void build(){ + UserChain.Protocol baseAuth=UserChain.Protocol.BasicAuth; + StringBuilder sb = UserChainManip.build(new StringBuilder(""), "app", "id", baseAuth, true); + assertThat(sb.toString(), is("app:id:BasicAuth:AS")); + + // for coverage + sb = UserChainManip.build(sb, "app", "id", baseAuth, true); + assertThat(sb.toString(), is("app:id:BasicAuth:AS,app:id:BasicAuth")); + + sb = UserChainManip.build(new StringBuilder(""), "app", "id", baseAuth, false); + assertThat(sb.toString(), is("app:id:BasicAuth")); + } + + @Test + public void idToNSTEST() { + assertThat(UserChainManip.idToNS(null), is("")); + assertThat(UserChainManip.idToNS(""), is("")); + assertThat(UserChainManip.idToNS("something"), is("")); + assertThat(UserChainManip.idToNS("something@@"), is("")); + assertThat(UserChainManip.idToNS("something@@."), is("")); + assertThat(UserChainManip.idToNS("something@com"), is("com")); + assertThat(UserChainManip.idToNS("something@random.com"), is("com.random")); + assertThat(UserChainManip.idToNS("@random.com"), is("com.random")); + assertThat(UserChainManip.idToNS("something@random.com."), is("com.random")); + assertThat(UserChainManip.idToNS("something@..random...com..."), is("com.random")); + assertThat(UserChainManip.idToNS("something@this.random.com"), is("com.random.this")); + } + + @Test + public void coverageTest() { + @SuppressWarnings("unused") + UserChainManip ucm = new UserChainManip(); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_Vars.java b/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_Vars.java new file mode 100644 index 00000000..b2600aa5 --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_Vars.java @@ -0,0 +1,148 @@ +/******************************************************************************* + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.util.test; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; +import org.onap.aaf.cadi.util.Vars; + +public class JU_Vars { + + @Test + public void coverage() { + @SuppressWarnings("unused") + Vars my_nonstatic_object_for_coverage = new Vars(); + } + + @Test + public void convert() { + String test = "test"; + List<String> list = new ArrayList<String>(); + list.add("method"); + assertEquals(Vars.convert(test, list), test); + } + + @Test + public void convertTest1() { + List<String> list = new ArrayList<String>(); + list.add("method"); + assertEquals(Vars.convert("test", list), "test"); + } + + @Test + public void convertTest2() { + List<String> list = new ArrayList<String>(); + list.add("method"); + assertEquals(Vars.convert("test", list), "test"); + } + + @Test + public void test() { + StringBuilder holder = new StringBuilder(); + String str,bstr; + assertEquals(str = "set %1 to %2",Vars.convert(holder,str, "a","b")); + assertEquals("set a to b",holder.toString()); + assertEquals(str,Vars.convert(null,str, "a","b")); + holder.setLength(0); + assertEquals(str,Vars.convert(holder,bstr="set %s to %s", "a","b")); + assertEquals("set a to b",holder.toString()); + assertEquals(str,Vars.convert(null,bstr, "a","b")); + + holder.setLength(0); + assertEquals(str = "%1=%2",Vars.convert(holder,str, "a","b")); + assertEquals("a=b",holder.toString()); + assertEquals(str,Vars.convert(null,str, "a","b")); + holder.setLength(0); + assertEquals(str,Vars.convert(holder,bstr="%s=%s", "a","b")); + assertEquals("a=b",holder.toString()); + assertEquals(str,Vars.convert(null,bstr, "a","b")); + + holder.setLength(0); + assertEquals(str = "%1%2",Vars.convert(holder,str, "a","b")); + assertEquals("ab",holder.toString()); + assertEquals(str ,Vars.convert(null,str, "a","b")); + holder.setLength(0); + assertEquals(str,Vars.convert(holder,bstr="%s%s", "a","b")); + assertEquals("ab",holder.toString()); + assertEquals(str ,Vars.convert(null,bstr, "a","b")); + + + holder.setLength(0); + assertEquals(str = " %1=%2 ",Vars.convert(holder,str, "a","b")); + assertEquals(" a=b ",holder.toString()); + assertEquals(str ,Vars.convert(null,str, "a","b")); + holder.setLength(0); + assertEquals(str,Vars.convert(holder,bstr = " %s=%s ", "a","b")); + assertEquals(" a=b ",holder.toString()); + assertEquals(str ,Vars.convert(null,bstr, "a","b")); + + holder.setLength(0); + assertEquals(str = " %1%2%10 ",Vars.convert(holder,str, "a","b","c","d","e","f","g","h","i","j")); + assertEquals(" abj ",holder.toString()); + assertEquals(str,Vars.convert(null,str, "a","b","c","d","e","f","g","h","i","j")); + holder.setLength(0); + assertEquals(str=" %1%2%3 ",Vars.convert(holder,bstr = " %s%s%s ", "a","b","c","d","e","f","g","h","i","j")); + assertEquals(" abc ",holder.toString()); + assertEquals(str,Vars.convert(null,bstr, "a","b","c","d","e","f","g","h","i","j")); + + + holder.setLength(0); + assertEquals(str = "set %1 to %2",Vars.convert(holder,str, "Something much","larger")); + assertEquals("set Something much to larger",holder.toString()); + assertEquals(str,Vars.convert(null,str,"Something much","larger")); + holder.setLength(0); + assertEquals(str,Vars.convert(holder,bstr="set %s to %s", "Something much","larger")); + assertEquals("set Something much to larger",holder.toString()); + assertEquals(str,Vars.convert(null,bstr, "Something much","larger")); + + holder.setLength(0); + assertEquals(str = "Text without Vars",Vars.convert(holder,str)); + assertEquals(str,holder.toString()); + assertEquals(str = "Text without Vars",Vars.convert(null,str)); + + + holder.setLength(0); + assertEquals(str = "Not %1 Enough %2 Vars %3",Vars.convert(holder,str, "a","b")); + assertEquals("Not a Enough b Vars ",holder.toString()); + assertEquals(str ,Vars.convert(null,str, "a","b")); + holder.setLength(0); + assertEquals(str,Vars.convert(holder,bstr="Not %s Enough %s Vars %s", "a","b")); + assertEquals("Not a Enough b Vars ",holder.toString()); + assertEquals(str ,Vars.convert(null,bstr, "a","b")); + + holder.setLength(0); + assertEquals(str = "!@#$%^*()-+?/,:;.",Vars.convert(holder,str, "a","b")); + assertEquals(str,holder.toString()); + assertEquals(str ,Vars.convert(null,str, "a","b")); + + holder.setLength(0); + bstr = "%s !@#$%^*()-+?/,:;."; + str = "%1 !@#$%^*()-+?/,:;."; + assertEquals(str,Vars.convert(holder,bstr, "Not Acceptable")); + assertEquals("Not Acceptable !@#$%^*()-+?/,:;.",holder.toString()); + assertEquals(str ,Vars.convert(null,bstr, "Not Acceptable")); + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/wsse/test/JU_WSSEParser.java b/cadi/core/src/test/java/org/onap/aaf/cadi/wsse/test/JU_WSSEParser.java new file mode 100644 index 00000000..0d7bdc2c --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/wsse/test/JU_WSSEParser.java @@ -0,0 +1,163 @@ +/******************************************************************************* +* ============LICENSE_START==================================================== +* * org.onap.aaf +* * =========================================================================== +* * Copyright © 2017 AT&T Intellectual Property. All rights reserved. +* * =========================================================================== +* * 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. +* * ============LICENSE_END==================================================== +* * +* * +******************************************************************************/ +package org.onap.aaf.cadi.wsse.test; + +import org.junit.Test; +import org.onap.aaf.cadi.wsse.WSSEParser; + +public class JU_WSSEParser { + + @Test + public void test() { + @SuppressWarnings("unused") + WSSEParser wp = new WSSEParser(); + + // TODO: test the rest of this class +// final BasicCred bc = new BasicCred() { +// private String user; +// private byte[] password; +// +// public void setUser(String user) { this.user = user; } +// public void setCred(byte[] passwd) { this.password = passwd; } +// public String getUser() { return user; } +// public byte[] getCred() { return password; } +// }; + +// FileInputStream fis; +// fis = new FileInputStream("test/example.xml"); +// BufferedServletInputStream is = new BufferedServletInputStream(fis); +// try { +// is.mark(1536); +// try { +// assertNull(wp.parse(bc, is)); +// } finally { +// is.reset(); +// assertEquals(814,is.buffered()); +// } +// String password = new String(bc.getCred()); +// System.out.println("CadiWrap credentials are: " + bc.getUser() + ", " + password); +// assertEquals("some_user", bc.getUser()); +// assertEquals("some_password", password); +// +// } finally { +// fis.close(); +// } +// +// // CBUS (larger) +// fis = new FileInputStream("test/CBUSevent.xml"); +// is = new BufferedServletInputStream(fis); +// try { +// is.mark(1536); +// try { +// assertNull(wp.parse(bc, is)); +// } finally { +// is.reset(); +// assertEquals(667,is.buffered()); +// } +// String password = new String(bc.getCred()); +// System.out.println("CadiWrap credentials are: " + bc.getUser() + ", " + password); +// assertEquals("none", bc.getUser()); +// assertEquals("none", password); +// +// } finally { +// fis.close(); +// } +// +// // Closed Stream +// fis = new FileInputStream("test/example.xml"); +// fis.close(); +// bc.setCred(null); +// bc.setUser(null); +// XMLStreamException ex = wp.parse(bc, fis); +// assertNotNull(ex); +// assertNull(bc.getUser()); +// assertNull(bc.getCred()); +// +// +// fis = new FileInputStream("test/exampleNoSecurity.xml"); +// try { +// bc.setCred(null); +// bc.setUser(null); +// assertNull(wp.parse(bc, fis)); +// assertNull(bc.getUser()); +// assertNull(bc.getCred()); +// } finally { +// fis.close(); +// } +// +// fis = new FileInputStream("test/exampleBad1.xml"); +// try { +// bc.setCred(null); +// bc.setUser(null); +// assertNull(wp.parse(bc, fis)); +// assertNull(bc.getUser()); +// assertNull(bc.getCred()); +// } finally { +// fis.close(); +// } +// +// XMLStreamException e = wp.parse(bc, new ByteArrayInputStream("Not XML".getBytes())); // empty +// assertNotNull(e); +// +// e = wp.parse(bc, new ByteArrayInputStream("".getBytes())); // empty +// assertNotNull(e); +// +// +// long start, count = 0L; +// int iter = 30000; +// File f = new File("test/CBUSevent.xml"); +// fis = new FileInputStream(f); +// is = new BufferedServletInputStream(fis); +// is.mark(0); +// try { +// while(is.read()>=0); +// } finally { +// fis.close(); +// } +// +// for(int i=0;i<iter;++i) { +// start = System.nanoTime(); +// is.reset(); +// try { +// assertNull(wp.parse(bc, is)); +// } finally { +// count += System.nanoTime()-start; +// } +// } +// float ms = count/1000000f; +// System.out.println("Executed " + iter + " WSSE reads from Memory Stream in " + ms + "ms. " + ms/iter + "ms per trans"); +// +// // SPECIFIC ISSUES +// +// fis = new FileInputStream("test/error2013_04_23.xml"); +// try { +// bc.setCred(null); +// bc.setUser(null); +// assertNull(wp.parse(bc, fis)); +// assertNull(bc.getUser()); +// assertNull(bc.getCred()); +// } finally { +// fis.close(); +// } + } + +} diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/wsse/test/JU_XReader.java b/cadi/core/src/test/java/org/onap/aaf/cadi/wsse/test/JU_XReader.java new file mode 100644 index 00000000..e75cea4e --- /dev/null +++ b/cadi/core/src/test/java/org/onap/aaf/cadi/wsse/test/JU_XReader.java @@ -0,0 +1,152 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aaf + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * =========================================================================== + * * 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. + * * ============LICENSE_END==================================================== + * * + * * + ******************************************************************************/ +package org.onap.aaf.cadi.wsse.test; + +import static org.junit.Assert.assertThat; +import static org.hamcrest.CoreMatchers.is; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.PrintWriter; + +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.XMLEvent; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.onap.aaf.cadi.wsse.XEvent; +import org.onap.aaf.cadi.wsse.XReader; + +public class JU_XReader { + + private final static String TEST_DIR_NAME = "src/test/resources"; + private final static String TEST_XML_NAME = "test.xml"; + private static File testXML; + + private final static String COMMENT = "a comment"; + private final static String OUTER_TAG = "outerTag"; + private final static String INNER_TAG = "innerTag"; + private final static String DATA_TAG = "dataTag"; + private final static String DATA = "some text that represents data"; + private final static String SELF_CLOSING_TAG = "selfClosingTag"; + private final static String PREFIX = "prefix"; + private final static String SUFFIX = "suffix"; + + @BeforeClass + public static void setupOnce() throws IOException { + testXML = setupXMLFile(); + } + + @AfterClass + public static void tearDownOnce() { + testXML.delete(); + } + + @Test + public void test() throws XMLStreamException, IOException { + FileInputStream fis = new FileInputStream(TEST_DIR_NAME + '/' + TEST_XML_NAME); + try { + XReader xr = new XReader(fis); + assertThat(xr.hasNext(), is(true)); + XEvent xe; + + xe = getNextEvent(xr); + assertThat(xe.getEventType(), is(XMLEvent.START_DOCUMENT)); + + xe = getNextEvent(xr); + assertThat(xe.getEventType(), is(XMLEvent.START_ELEMENT)); + + xe = getNextEvent(xr); + assertThat(xe.getEventType(), is(XMLEvent.COMMENT)); + assertThat(((XEvent.Comment)xe).value, is(COMMENT)); + + xe = getNextEvent(xr); + assertThat(xe.getEventType(), is(XMLEvent.START_ELEMENT)); + assertThat(xe.asStartElement().getName().toString(), is(OUTER_TAG)); + + xe = getNextEvent(xr); + assertThat(xe.getEventType(), is(XMLEvent.START_ELEMENT)); + assertThat(xe.asStartElement().getName().toString(), is(INNER_TAG)); + + xe = getNextEvent(xr); + assertThat(xe.getEventType(), is(XMLEvent.START_ELEMENT)); + assertThat(xe.asStartElement().getName().toString(), is(DATA_TAG)); + + xe = getNextEvent(xr); + assertThat(xe.getEventType(), is(XMLEvent.CHARACTERS)); + assertThat(xe.asCharacters().getData().toString(), is(DATA)); + + xe = getNextEvent(xr); + assertThat(xe.getEventType(), is(XMLEvent.END_ELEMENT)); + assertThat(xe.asEndElement().getName().toString(), is(DATA_TAG)); + + xe = getNextEvent(xr); + assertThat(xe.getEventType(), is(XMLEvent.START_ELEMENT)); + assertThat(xe.asStartElement().getName().toString(), is(SELF_CLOSING_TAG)); + + xe = getNextEvent(xr); + assertThat(xe.getEventType(), is(XMLEvent.START_ELEMENT)); + assertThat(xe.asStartElement().getName().toString(), is(SUFFIX)); + + xe = getNextEvent(xr); + assertThat(xe.getEventType(), is(XMLEvent.END_ELEMENT)); + assertThat(xe.asEndElement().getName().toString(), is(INNER_TAG)); + + xe = getNextEvent(xr); + assertThat(xe.getEventType(), is(XMLEvent.END_ELEMENT)); + assertThat(xe.asEndElement().getName().toString(), is(OUTER_TAG)); + + assertThat(xr.hasNext(), is(false)); + + } finally { + fis.close(); + } + } + + private static XEvent getNextEvent(XReader xr) throws XMLStreamException { + if (xr.hasNext()) { + return xr.nextEvent(); + } + return null; + } + + private static File setupXMLFile() throws IOException { + File xmlFile = new File(TEST_DIR_NAME, TEST_XML_NAME); + PrintWriter writer = new PrintWriter(xmlFile); + writer.println(" "); // Whitespace before the document - this is for coverage + writer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); + writer.println("<!DOCTYPE xml>"); + writer.println("<!--" + COMMENT + "-->"); + writer.println("<" + OUTER_TAG + ">"); + writer.println(" <" + INNER_TAG + ">"); + writer.println(" <" + DATA_TAG + ">" + DATA + "</" + DATA_TAG + ">"); + writer.println(" <" + SELF_CLOSING_TAG + " withAnAttribute=\"That has nested \\\" marks\" />"); + writer.println(" <" + PREFIX + ":" + SUFFIX + "/>"); + writer.println(" </" + INNER_TAG + ">"); + writer.println("</" + OUTER_TAG + ">"); + writer.flush(); + writer.close(); + return xmlFile; + } +} diff --git a/cadi/core/src/test/resources/AESKeyFile b/cadi/core/src/test/resources/AESKeyFile new file mode 100644 index 00000000..35795c34 --- /dev/null +++ b/cadi/core/src/test/resources/AESKeyFile @@ -0,0 +1,27 @@ +xteeS5pA6m4SW2fMANEeF__VDm3F2wlUqyeUKDKxlSFiS_ICs0Eg7Xeqj3WqbgRqOisc1hLIbyk3 +2bal9qYwT59VxcZy-vrS3ytf0uu5gWwxGfo2-ut3CQTBfwVOj88RMdiyM13-dxJGOdQxT9_Czc9A +it4edvcVQOTeazJ9JJ0KtO5tvsdihsaYYOVbMbMWPTzyDKY2KE7iMmPaqeGPLvxZSVvjQzjU8qMp +OwzllAhRXZd0DWOullSotpt8P2VKcbnoKVA2SQvLTt5Zd9TziaaCMP88-fJQUhXvWhUPG_ZdH2R1 +MVyS0WrnBN6rY2h_aTiUswYZ6GGTDa_7O4AQixNR02NAbn7718Mw3bbe12d6nJZ2uYqMb9Hl1bzO +-mZbJ_TUVAIUBgOb7XjScIS12JLlUuf-kIlQjfT2kfAzSuwcYHUZmB_jAfdZBjyhqVj4x7N47wb1 +7GbBBbECLAPMk9633_3HzadqZu6J3TmfmW2IYR9kqEF1NwfaXgJAL4I43YDSo2XyD-i9MUb3diYd +LVElQP8gwMh2gbfRe_7BU49_HdbCk4n6BNgT0Z0EgtnMAA0ZZWmBTJTz5BlC0lXL-7NAWyOw1vRs +ovjqc46zpQq8LYtJ2Vg5WwfpqBpyXqCdp9QYTNtN0GVB4iPBvaWRsQoZKzEESHavxKbGX2_Z7h2Q +k03Okhl4Ud3MduR6pyxfxVqAdFu3xr2tEIcv_FjyD-5XiTfKcWPw-Srwy-_YiTy_io4nu2swC8Xm +TsNcWtebM0W80L1nw0MwHFFIoAMBrHUjHxIrZL5JWZyaGxUdtnbKKlkVR1kDC8_pHrevwIijAEyi +NnwDYaMw7tZo6f06J3yPVCVzVLLXFCCTkvdJAFBhaZI600mf7UZP2BMqomYVROoQNZDAO_GzP1sX +_B4oebfYPkLk3fBkHasHPDZNy-oNHDEw8ytMXlMhyKX7UHUy28E8zpZWoRMmGPnzOSwp3P-Q08DP +ja05l6vgvGtzuWNUKcFjSTdqx73JJJ1-QrZZlTd0N1gYqhRyh8YssGDYXEHh5zKuF6vNTinJwGLY +P5NnOSBCbm9rcPcZGtZYer-uNUY9Z_rscfxiVork1SfnG25FwxCRkc_Nf47THAVM9T7m9Ou_g2N8 +eethvrQxDxEi_DVRBTJYe_9iUOec4KQY7VGhTiFbfvDPRB8yF7Znu5UPJXIeOjvcf9gi8lSwTTRx +sqRpB3D5SJUSnBGzOCUvYRBbGP0olaVYyVXLcDknRTKbwkIf0tKAEFRDkvkXdlJnQ4lldFHuuHO9 +G7_iqEjCCNncdtLZMwe6LPe1usfJmnl3x95wkpVQdAKl7QoP5fMR2XoXwQbSO2qwIdgBwq9Zm6FW +wRPStR0pS2ICjHusgmLPsdf2pVZ8q0fqjjzF4Ch7MfOWjhRsK9fCvVDXlrEOACTt7o0roXuswxKT +EEbibkLsEAQOfOCYa66G37yQKRNnR8PWeRLAaZGF8ewfqF0c2KBAQuLYFlE8OthP_vFDKfVT2zMX +BfneXOJNY2kZTEJA4MQOC4_Y1JJ7NJc1zqJRuPD8Ifo4oE1Qo2FE-mjm2G_Zb4XsmBEdWsiSAYum +2DiGm5Io7OXQXv2zOKsBvcoG-24A4M3kTxhEH16sueTKY_DqOjjxkcVIUX_PM7TGkeRU9cnJ2sDH +Y749blu8BWrRKSRiksxwwNAGW_IdElVVGd89gyGGRzZ2I4h-FXf9EBS1sqo-F_hOq_O3KOoMDFWK +gdc4XIqeqmjwVTSpkKyxSCFYQW-aPBuTSdJYmPZRQQlCXwkm7SHbLRBKM4h62koA8A3hpzda3qnZ +w_Wyb8u42yZpqNuUjUUOb4JApmOVCXIe4P9yfhiTbYRvGX50XjPIHBKjAzXhKLGaBaugBYhaGpXf +kFjvsEF-4PrtQWORKudvlk8D7DnhxqgJdG0GoZAETBTCq_m1trg2TJ2WyAMidFUOWrgGPpshFq1F +Nu7buFG6nsOsg4sfLmSm2oYhVb0TmEbBGRr_Apkg6nVJzX7DE_Rvt2slZDoIrXeKSbIJ_i5Y
\ No newline at end of file diff --git a/cadi/core/src/test/resources/CBUSevent.xml b/cadi/core/src/test/resources/CBUSevent.xml new file mode 100644 index 00000000..15fc5f22 --- /dev/null +++ b/cadi/core/src/test/resources/CBUSevent.xml @@ -0,0 +1,44 @@ +<!-- + ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * +--> +<assembly> + <id>app</id> + <formats> + <format>jar</format> + </formats> + <includeBaseDirectory>false</includeBaseDirectory> + <dependencySets> + <dependencySet> + <outputDirectory></outputDirectory> + <outputFileNameMapping></outputFileNameMapping> + <unpack>true</unpack> + <scope>runtime</scope> + <!-- includes> + <include>web</include> + </includes --> + </dependencySet> + </dependencySets> + <fileSets> + <fileSet> + <directory>target/classes</directory> + <outputDirectory></outputDirectory> + </fileSet> + </fileSets> +</assembly> diff --git a/cadi/core/src/test/resources/cadi.properties b/cadi/core/src/test/resources/cadi.properties new file mode 100644 index 00000000..b84509b2 --- /dev/null +++ b/cadi/core/src/test/resources/cadi.properties @@ -0,0 +1,60 @@ +#------------------------------------------------------------------------------- +# ============LICENSE_START==================================================== +# * org.onap.aaf +# * =========================================================================== +# * Copyright © 2017 AT&T Intellectual Property. All rights reserved. +# * =========================================================================== +# * 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. +# * ============LICENSE_END==================================================== +# * +#------------------------------------------------------------------------------- +############################################################################### +# Copyright (c) 2016 AT&T Intellectual Property. All rights reserved. +############################################################################### +# This is a normal Java Properties File +# Comments are with Pound Signs at beginning of lines, +# and multi-line expression of properties can be obtained by backslash at end of line + +# Certain machines have several possible machine names, and +# the right one may not be reported. This is especially +# important for CSP Authorization, which will only +# function on official AT&T domains. +hostname=veeger.mo.sbc.com + +port=2533 + +# CSP has Production mode (active users) or DEVL mode (for +# Testing purposes... Bogus users) +#csp_domain=DEVL +csp_domain=PROD + +# Report all AUTHN and AUTHZ activity +loglevel=AUDIT + +# +# BasicAuth and other User/Password support +# +# The realm reported on BasicAuth callbacks +basic_realm=spiderman.agile.att.com +users=ks%xiVUs_25_1jqGdJ24hqy43Gi; +groups=aaf:Jd8bb3jslg88b@spiderman.agile.att.com%7sZCPBZ_8iWbslqdjWFIDLgTZlm9ung0ym-G,\ + jg1555,lg2384,rd8227,tp007s,pe3617; + + +# Keyfile (with relative path) for encryption. This file +# should be marked as ReadOnly by Only the running process +# for security's sake +keyfile=conf/keyfile + +# This is here to force property chaining in tests +cadi_prop_files=test/cadi.properties.duplicate diff --git a/cadi/core/src/test/resources/cadi.properties.duplicate b/cadi/core/src/test/resources/cadi.properties.duplicate new file mode 100644 index 00000000..03c04d02 --- /dev/null +++ b/cadi/core/src/test/resources/cadi.properties.duplicate @@ -0,0 +1,58 @@ +#------------------------------------------------------------------------------- +# ============LICENSE_START==================================================== +# * org.onap.aaf +# * =========================================================================== +# * Copyright � 2017 AT&T Intellectual Property. All rights reserved. +# * =========================================================================== +# * 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. +# * ============LICENSE_END==================================================== +# * +# * +#------------------------------------------------------------------------------- +############################################################################### +# Copyright (c) 2016 AT&T Intellectual Property. All rights reserved. +############################################################################### +# This is a normal Java Properties File +# Comments are with Pound Signs at beginning of lines, +# and multi-line expression of properties can be obtained by backslash at end of line + +# Certain machines have several possible machine names, and +# the right one may not be reported. This is especially +# important for CSP Authorization, which will only +# function on official AT&T domains. +hostname=veeger.mo.sbc.com + +port=2533 + +# CSP has Production mode (active users) or DEVL mode (for +# Testing purposes... Bogus users) +#csp_domain=DEVL +csp_domain=PROD + +# Report all AUTHN and AUTHZ activity +loglevel=AUDIT + +# +# BasicAuth and other User/Password support +# +# The realm reported on BasicAuth callbacks +basic_realm=spiderman.agile.att.com +users=ks%xiVUs_25_1jqGdJ24hqy43Gi; +groups=aaf:Jd8bb3jslg88b@spiderman.agile.att.com%7sZCPBZ_8iWbslqdjWFIDLgTZlm9ung0ym-G,\ + jg1555,lg2384,rd8227,tp007s,pe3617; + + +# Keyfile (with relative path) for encryption. This file +# should be marked as ReadOnly by Only the running process +# for security's sake +keyfile=conf/keyfile diff --git a/cadi/core/src/test/resources/keyfile b/cadi/core/src/test/resources/keyfile new file mode 100644 index 00000000..e84bd616 --- /dev/null +++ b/cadi/core/src/test/resources/keyfile @@ -0,0 +1,27 @@ +9zgJxUXT1CrzC_A2Z0PdKi3n9l6zmErB26ZlSXCCyloxi3bGqD3lNHC3aFHfgC8-ZwNMuLBM93WY +JV4sEacNodHGjgmAqSVyMHiPTEP4XRrydfjXAvaBIERcU1Yvu4pa4Mq25RXLHt8tIAnToFVbq82n +bjkfdcv2-shgwkEvRiNIdK5TITO8JTvTRWND5MqXc9gnCKkR6Rl5dU5QGIB2SxWOPCvKBBWeUGRO +bSinrjkI-iXabuLOYUaGo6FI_XAU5S9WxvfrDVpBijUAGJW8QZe1oBIo5QmQlx6ONB4ohjEu89ZZ +gTee22MvSNUvaT8IGbj_Zt_TyuCqcdmkVahWp5ffeK2J3bmHActAC2IxXD4yV-sFLB7PW7I8KMA7 +tML3Lcy9ozmYa2E8N8B9uQ0zMHz_TVpPvj5xkVF4_FEKOTD1mkf-JYC1CyzwJS2YWWxO6fqsxIjD +1qB4OJudv4RK6hSxdVrNxc_wchVAGXVD6ulm8UPBGP_wpfItP8BGYwCHlOjUrZofewKB2Aa9Uk9m +oyk309WmPVBeRzZ0vRlXUp8jhKlAPISvv8CBbG-6SuXAszY2qedgd3huYKNreVN-xMZM2hnYbEUW +0sdcqpFqIV039Awfwjn5sZPFW4iT3yWhxib1PwFzwfaXnrwgwbLAda68mRDAWCrsDRu11IiQJqb7 +cjNLYBOGDVhX7jeUyBJUzW-xhl__DsoCZSqP39vFoPtglXHlQNtVqQ8d96mu_QMY5bcuhevI4RQ_ +SD7WcRyAiUztiC4Eb6BYwld0RITdB1-Y43jkZlfA8Ej5Zw8sX_-2J2hKdDPT4KrTYWA5T6wiIJK9 +lxIc39wGHpxQ4kz8gx0VeqRU2hgHVKovuaEvBnwv8JW3qeuowaUmiPi7UuIRwi4pFX5iQv62yrfO +5Z6EXBDVI8Ikq4UTu70vX_bCuXHtvqm97PFh2KXjBHS--iNVQ5GhnDKKv_Fd4naQjCSwTTgtxD4X +ASgLSSETGJ8wAjWHOWUuVT4jUDFIQwunNaH6y2NaDWA0tkO74oYaQIL_-kd9ChGLzGL389v8BV2X +oaw70W9L3-OOtzAz-hACbOtbbMkx2bVMmS8QhjYg-_2bpwSb8NR322pQ9AodFTU4x5HrLoERk2Rw +hRExZP7K-_idMJUGLF9gJFFS01UyBLijyWGyN0teQleXgn6IzZk7dH9roddoe9IacjiV7XfE4i1U +rVNTRKiDdHSX02KGOihs_j-Tf0PYsz0wEeACINA5MafGzc9x2b8yMzBxwPHxRszjL4dymCoLXRI5 +srLsWk2Jwtp9meW8jhkoAi5xUKzLiYIhEohIX3eEEA0O0wuK0fzcMB7IbyTYYazawUKmUXZ94OLu +Fmb-UaAEvU-9U4O3DNfbDN2ELxUHmWaqNqpGl1IV0ZxGrKNZi9Rga9-_vfVGcoVMD7vZOhiZddc9 +WRlom3tQZRx2Sm42baNH8wS34J0KuUYPcjQ-1_GEJxcH0hv6hzSm4is7mUdnyB95g1UohKdQOfaY +tOdHlXbu2zG6SyPaYyQFfQbMPwBn-hx_7bYj9Px-EhYeMpBIP8X98jkd3BlWY4sdWqxsQfAb5pml +cnDRynHag2XxLqttAWSwru_owfeXzmYsPD-PINRu-Csjzlbdhq73amTFN-U8mYA09dlCck2fW8qo +mAXLkVlboVaPuem6WvfSd93ZinsB5Wi5RX6RQxeHeo88cWrJ11Au14J8xFlurcZwdSjO4dsnZj_D +ry0uKWsyNoLogBuDansiNGGO8-1qsyRxVp3zbxOMQmPouN6l0ZfxQdACqX8_4HTD7NMNMnLYjPjC +4YfOUx4pQMdjzno05vuF5zY-UQ3SN7HkmXsF6tVJdt15cmtLFetD5LTbvdRr1eeHWuwD4-aJQx4T +SdOLQ3zHeMnNFsxR_xKsu4AGjcC2-TpGixmA1kJtYBm1WIGoxQ6N4rneEo-82yvKwYst9-DJcV6x +xy1dpJqtx3I7M6DqPVURomeh2czO6UMRPVIQ1ltj4E27_FWFsWC38ZyR4nFimovFLJNCzy2k
\ No newline at end of file diff --git a/cadi/oauth-enduser/.gitignore b/cadi/oauth-enduser/.gitignore new file mode 100644 index 00000000..6028f0a5 --- /dev/null +++ b/cadi/oauth-enduser/.gitignore @@ -0,0 +1,4 @@ +/.classpath +/.settings/ +/target/ +/.project diff --git a/cadi/oauth-enduser/cadi.properties b/cadi/oauth-enduser/cadi.properties new file mode 100644 index 00000000..ecd5a3d5 --- /dev/null +++ b/cadi/oauth-enduser/cadi.properties @@ -0,0 +1,57 @@ +############################################################ +# Properties for OAuth Example +# Jonathan Gathman +# on 2018-01-30 +# These properties are the BARE essentials for OAuth calling +############################################################ +# aaf_locate is the replacement whenever a URL is set to "AAF_LOCATE_URL" +# at this time, only AAF has this ability. +# +# This is, effectively, the Environment you will use for AAF Location +# TEST ENV +aaf_locate_url=https://aaftest.test.att.com + +# IST ENV +# aaf_locate_url=https://aafist.test.att.com + +# PROD ENV +# aaf_locate_url=https://aaf.it.att.com + +cadi_latitude=<YOUR Latitude (try bing.com/maps) +cadi_longitude=<YOUR Longitude> + +aaf_url=https://AAF_LOCATE_URL/locate/com.att.aaf.service:2.0 +cadi_keyfile=<YOUR Keyfile. Create with java -jar cadi-core<Version>.jar keygen keyfile. chmod 400 keyfile> + +aaf_id=<YOUR Fully Qualified AAF MechID> +aaf_password=enc:<YOUR encrypted passwrod. Create with java -jar cadi-core<Version>.jar digest keyfile> +# aaf_alias=<YOUR AAF Certman Generated alias FOR the right AAF Env> + +# aaf_conn_timeout=6000 +# aaf_timeout=10000 + +# A Sample AAF OAuth Enabled Service +#aaf_oauth2_hello_url=https://AAF_LOCATE_URL/locate/com.att.aaf.hello:2.0/hello +aaf_oauth2_hello_url=http://135.46.170.156:32245/restservices/echo/v1/testCXF/testGet + +# OAuth2 +# AAF OAuth2 Service. +aaf_oauth2_token_url=https://AAF_LOCATE_URL/locate/com.att.aaf.token:2.0/token +aaf_oauth2_introspect_url=https://AAF_LOCATE_URL/locate/com.att.aaf.introspect:2.0/introspect + +#ISAM +aaf_alt_oauth2_domain=isam.att.com +#aaf_alt_oauth2_client_id=<get from ISAM> +#aaf_alt_oauth2_domain=csp.att.com + +#ISAM TEST +aaf_alt_oauth2_token_url=https://oauth.stage.elogin.att.com/mga/sps/oauth/oauth20/token +aaf_alt_oauth2_introspect_url=https://oauthapp.stage.att.com/mga/sps/oauth/oauth20/introspect +aaf_alt_oauth2_client_secret=enc:<encrypt with cadi tool> + +#ISAM PROD +#aaf_alt_oauth2_token_url=https://oauth.idp.elogin.att.com/mga/sps/oauth/oauth20/token +#aaf_alt_oauth2_introspect_url=https://oa-app.e-access.att.com/mga/sps/oauth/oauth20/introspect +#aaf_alt_oauth2_client_secret=enc:<encrypt with cadi tool> + + diff --git a/cadi/oauth-enduser/pom.xml b/cadi/oauth-enduser/pom.xml new file mode 100644 index 00000000..83ea803a --- /dev/null +++ b/cadi/oauth-enduser/pom.xml @@ -0,0 +1,232 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * +--> +<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.onap.aaf.authz</groupId> + <artifactId>cadiparent</artifactId> + <version>2.1.0-SNAPSHOT</version> + <relativePath>..</relativePath> + </parent> + + <name>AAF CADI Sample OAuth EndUser</name> + <groupId>org.onap.aaf.authz</groupId> + <version>2.1.0-SNAPSHOT</version> + <artifactId>aaf-cadi-oauth-enduser</artifactId> + <packaging>jar</packaging> + + <properties> + <!-- SONAR --> + <sonar.skip>true</sonar.skip> + <jacoco.version>0.7.7.201606060606</jacoco.version> + <sonar-jacoco-listeners.version>3.2</sonar-jacoco-listeners.version> + <sonar.core.codeCoveragePlugin>jacoco</sonar.core.codeCoveragePlugin> + <!-- Default Sonar configuration --> + <sonar.jacoco.reportPaths>target/code-coverage/jacoco-ut.exec</sonar.jacoco.reportPaths> + <sonar.jacoco.itReportPaths>target/code-coverage/jacoco-it.exec</sonar.jacoco.itReportPaths> + <!-- Note: This list should match jacoco-maven-plugin's exclusion list below --> + <sonar.exclusions>**/gen/**,**/generated-sources/**,**/yang-gen**,**/pax/**</sonar.exclusions> + <nexusproxy>https://nexus.onap.org</nexusproxy> + <snapshotNexusPath>/content/repositories/snapshots/</snapshotNexusPath> + <releaseNexusPath>/content/repositories/releases/</releaseNexusPath> + <stagingNexusPath>/content/repositories/staging/</stagingNexusPath> + <sitePath>/content/sites/site/org/onap/aaf/authz/${project.artifactId}/${project.version}</sitePath> + </properties> + + <developers> + <developer> + <name>Jonathan Gathman</name> + <email>jonathan.gathman@att.com</email> + <organization>ATT</organization> + <roles> + <role>Architect</role> + <role>Lead Developer</role> + </roles> + </developer> + <developer> + <name>Gabe Maurer</name> + <email>gabe.maurer@att.com</email> + <organization>ATT</organization> + <roles> + <role>Developer</role> + </roles> + </developer> + <developer> + <name>Ian Howell</name> + <email>ian.howell@att.com</email> + <organization>ATT</organization> + <roles> + <role>Developer</role> + </roles> + </developer> + </developers> + + <dependencies> + <dependency> + <groupId>org.onap.aaf.authz</groupId> + <artifactId>aaf-cadi-core</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.onap.aaf.authz</groupId> + <artifactId>aaf-cadi-aaf</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + + <build> + <pluginManagement> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>2.3.2</version> + <configuration> + <source>1.8</source> + <target>1.8</target> + </configuration> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <version>2.4</version> + <artifactId>maven-jar-plugin</artifactId> + <configuration> + <outputDirectory>target</outputDirectory> + </configuration> + </plugin> + <plugin> + <artifactId>maven-assembly-plugin</artifactId> + <version>2.4</version> + <configuration> + <archive> + <manifest> + <mainClass>org.onap.aaf.cadi.enduser.OAuthExample</mainClass> + </manifest> + </archive> + <descriptors> + <descriptor>src/main/assemble/cadi-oauth-enduser-assemble.xml</descriptor> + </descriptors> + </configuration> + </plugin> + <plugin> + <groupId>org.sonatype.plugins</groupId> + <artifactId>nexus-staging-maven-plugin</artifactId> + <version>1.6.7</version> + <extensions>true</extensions> + <configuration> + <nexusUrl>${nexusproxy}</nexusUrl> + <stagingProfileId>176c31dfe190a</stagingProfileId> + <serverId>ecomp-staging</serverId> + </configuration> + </plugin> + <plugin> + <groupId>org.jacoco</groupId> + <artifactId>jacoco-maven-plugin</artifactId> + <version>${jacoco.version}</version> + <configuration> + <excludes> + <exclude>**/gen/**</exclude> + <exclude>**/generated-sources/**</exclude> + <exclude>**/yang-gen/**</exclude> + <exclude>**/pax/**</exclude> + </excludes> + </configuration> + <executions> + <execution> + <id>pre-unit-test</id> + <goals> + <goal>prepare-agent</goal> + </goals> + <configuration> + <destFile>${project.build.directory}/code-coverage/jacoco-ut.exec</destFile> + <propertyName>surefireArgLine</propertyName> + </configuration> + </execution> + <execution> + <id>post-unit-test</id> + <phase>test</phase> + <goals> + <goal>report</goal> + </goals> + <configuration> + <dataFile>${project.build.directory}/code-coverage/jacoco-ut.exec</dataFile> + <outputDirectory>${project.reporting.outputDirectory}/jacoco-ut</outputDirectory> + </configuration> + </execution> + <execution> + <id>pre-integration-test</id> + <phase>pre-integration-test</phase> + <goals> + <goal>prepare-agent</goal> + </goals> + <configuration> + <destFile>${project.build.directory}/code-coverage/jacoco-it.exec</destFile> + + <propertyName>failsafeArgLine</propertyName> + </configuration> + </execution> + <execution> + <id>post-integration-test</id> + <phase>post-integration-test</phase> + <goals> + <goal>report</goal> + </goals> + <configuration> + <dataFile>${project.build.directory}/code-coverage/jacoco-it.exec</dataFile> + <outputDirectory>${project.reporting.outputDirectory}/jacoco-it</outputDirectory> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>2.8.1</version> + <configuration> + <skip>false</skip> + </configuration> + + </plugin> + </plugins> + </pluginManagement> + </build> + + + <distributionManagement> + <repository> + <id>ecomp-releases</id> + <name>AAF Release Repository</name> + <url>${nexusproxy}${releaseNexusPath}</url> + </repository> + <snapshotRepository> + <id>ecomp-snapshots</id> + <name>AAF Snapshot Repository</name> + <url>${nexusproxy}${snapshotNexusPath}</url> + </snapshotRepository> + <site> + <id>ecomp-site</id> + <url>dav:${nexusproxy}${sitePath}</url> + </site> + </distributionManagement> +</project> diff --git a/cadi/oauth-enduser/src/.gitignore b/cadi/oauth-enduser/src/.gitignore new file mode 100644 index 00000000..9bb88d37 --- /dev/null +++ b/cadi/oauth-enduser/src/.gitignore @@ -0,0 +1 @@ +/.DS_Store diff --git a/cadi/oauth-enduser/src/test/java/com/att/cadi/enduser/OAuthExample.java b/cadi/oauth-enduser/src/test/java/com/att/cadi/enduser/OAuthExample.java new file mode 100644 index 00000000..9cb4b4af --- /dev/null +++ b/cadi/oauth-enduser/src/test/java/com/att/cadi/enduser/OAuthExample.java @@ -0,0 +1,233 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ + +package com.att.cadi.enduser; + +import java.io.IOException; +import java.net.ConnectException; +import java.security.GeneralSecurityException; +import java.util.Date; +import java.util.GregorianCalendar; + +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.client.Future; +import org.onap.aaf.cadi.client.Rcli; +import org.onap.aaf.cadi.client.Result; +import org.onap.aaf.cadi.client.Retryable; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.oauth.TimedToken; +import org.onap.aaf.cadi.oauth.TokenClient; +import org.onap.aaf.cadi.oauth.TokenClientFactory; +import org.onap.aaf.cadi.oauth.TzClient; +import org.onap.aaf.cadi.util.FQI; +import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.misc.env.util.Chrono; + +import aafoauth.v2_0.Introspect; +import aafoauth.v2_0.Token; + + +public class OAuthExample { + private static TokenClientFactory tcf; + private static PropAccess access; + + public final static void main(final String args[]) { + // These Objects are expected to be Long-Lived... Construct once + + // Property Access + // This method will allow you to set "cadi_prop_files" (or any other property) on Command line + access = new PropAccess(args); + + // access = PropAccess(); + // Note: This style will load "cadi_prop_files" from VM Args + + // Token aware Client Factory + try { + tcf = TokenClientFactory.instance(access); + } catch (APIException | GeneralSecurityException | IOException | CadiException e1) { + access.log(e1, "Unable to setup OAuth Client Factory, Fail Fast"); + System.exit(1); + } + + + // Obtain Endpoints for OAuth2 from Properties. Expected is "cadi.properties" file, pointed to by "cadi_prop_files" + String tokenServiceURL = access.getProperty(Config.AAF_OAUTH2_TOKEN_URL); + String tokenIntrospectURL = access.getProperty(Config.AAF_OAUTH2_INTROSPECT_URL); + + + // Get Properties + final String endServicesURL = access.getProperty(Config.AAF_OAUTH2_HELLO_URL); + + final int CALL_TIMEOUT = Integer.parseInt(access.getProperty(Config.AAF_CALL_TIMEOUT,Config.AAF_CALL_TIMEOUT_DEF)); + + try { + ////////////////////////////////////////////////////////////////////// + // Scenario 1: + // Get and use an OAuth Client, which understands Token Management + ////////////////////////////////////////////////////////////////////// + // Create a Token Client, that gets its tokens from expected OAuth Server + // In this example, it is AAF, but it can be the Alternate OAuth + + TokenClient tc = tcf.newClient(tokenServiceURL); // can set your own timeout here (url, timeoutMilliseconds) + // Set your Application (MicroService, whatever) Credentials here + // These are how your Application is known, particularly to the OAuth Server. + // If AAF Token server, then its just the same as your other AAF MechID creds + // If it is the Alternate OAUTH, you'll need THOSE credentials. See that tool's Onboarding procedures. + String client_id = access.getProperty(Config.AAF_APPID); + String client_secret = access.getProperty(Config.AAF_APPPASS); + tc.client_creds(client_id, client_secret); + + // If you are working with Credentials the End User, set username/password as appropriate to the OAuth Server + // tc.password(end_user_id, end_user_password); + // IMPORTANT: + // if you are setting client Credentials, you MAY NOT reuse this Client mid-transaction. You CAN reuse after setting + // tc.clearEndUser(); + // You may want to see "Pooled Client" example, using special CADI utility + + // With AAF, the Scopes you put in are the AAF Namespaces you want access to. Your Token will contain the + // AAF Permissions of the Namespaces (you can put in more than one), the user name (or client_id if no user_name), + // is allowed to see. + + // Here's a trick to get the namespace out of a Fully Qualified AAF Identity (your MechID) + String ns = FQI.reverseDomain(client_id); + System.out.printf("\nNote: The AAF Namespace of FQI (Fully Qualified Identity) %s is %s\n\n",client_id, ns); + + // Now, we can get a Token. Note: for "scope", use AAF Namespaces to get AAF Permissions embedded in + // Note: getToken checks if Token is expired, if so, then refreshes before handing back. + Result<TimedToken> rtt = tc.getToken(ns,"org.onap.test"); + + // Note: you can clear a Token's Disk/Memory presence by + // 1) removing the Token from the "token/outgoing" directory on the O/S + // 2) programmatically by calling "clearToken" with exact params as "getToken", when it has the same credentials set + // tc.clearToken("org.onap.aaf","org.onap.test"); + + // Result Object can be queried for success + if(rtt.isOK()) { + TimedToken token = rtt.value; + print(token); // Take a look at what's in a Token + + // Use this Token in your client calls with "Tokenized Client" (TzClient) + // These should NOT be used cross thread. + TzClient helloClient = tcf.newTzClient(endServicesURL); + helloClient.setToken(client_id, token); + + // This client call style, "best" call with "Retryable" inner class covers finding an available Service + // (when Multi-services exist) for the best service, based (currently) on distance. + // + // the "Generic" in Type gives a Return Value for the Code, which you can set on the "best" method + // Note that variables used in the inner class from this part of the code must be "final", see "CALL_TIMEOUT" + String rv = helloClient.best(new Retryable<String>() { + @Override + public String code(Rcli<?> client) throws CadiException, ConnectException, APIException { + Future<String> future = client.read(null,"text/plain"); + // The "future" calling method allows you to do other processing, such as call more than one backend + // client before picking up the result + // If "get" matches the HTTP Code for the method (i.e. read HTTP Return value is 200), then + if(future.get(CALL_TIMEOUT)) { + // Client Returned expected value + return future.value; + } else { + throw new APIException(future.code() + future.body()); + } + } + }); + + // You want to do something with returned value. Here, we say "hello" + System.out.printf("\nPositive Response from Hello: %s\n",rv); + + + ////////////////////////////////////////////////////////////////////// + // Scenario 2: + // As a Service, read Introspection information as proof of Authenticated Authorization + ////////////////////////////////////////////////////////////////////// + // CADI Framework (i.e. CadiFilter) works with the Introspection to drive the J2EE interfaces ( + // i.e. if(isUserInRole("ns.perm|instance|action")) {... + // + // Here, however, is a way to introspect via Java + // + // now, call Introspect (making sure right URLs are set in properties) + // We need a Different Introspect TokenClient, because different Endpoint (and usually different Services) + TokenClient tci = tcf.newClient(tokenIntrospectURL); + tci.client_creds(client_id, client_secret); + Result<Introspect> is = tci.introspect(token.getAccessToken()); + if(is.isOK()) { + // Note that AAF will add JSON set of Permissions as part of "Content:", legitimate extension of OAuth Structure + print(is.value); // do something with Introspect Object + } else { + access.printf(Level.ERROR, "Unable to introspect OAuth Token %s: %d %s\n", + token.getAccessToken(),rtt.code,rtt.error); + } + } else { + access.printf(Level.ERROR, "Unable to obtain OAuth Token: %d %s\n",rtt.code,rtt.error); + } + + } catch (CadiException | LocatorException | APIException | IOException e) { + e.printStackTrace(); + } + } + + ///////////////////////////////////////////////////////////// + // Examples of Object Access + ///////////////////////////////////////////////////////////// + private static void print(Token t) { + GregorianCalendar exp_date = new GregorianCalendar(); + exp_date.add(GregorianCalendar.SECOND, t.getExpiresIn()); + System.out.printf("Access Token\n\tToken:\t\t%s\n\tToken Type:\t%s\n\tExpires In:\t%d (%s)\n\tScope:\t\t%s\n\tRefresh Token:\t%s\n", + t.getAccessToken(), + t.getTokenType(), + t.getExpiresIn(), + Chrono.timeStamp(new Date(System.currentTimeMillis()+(t.getExpiresIn()*1000))), + t.getScope(), + t.getRefreshToken()); + } + + private static void print(Introspect ti) { + if(ti==null || ti.getClientId()==null) { + System.out.println("Empty Introspect"); + return; + } + Date exp = new Date(ti.getExp()*1000); // seconds + System.out.printf("Introspect\n" + + "\tAccessToken:\t%s\n" + + "\tClient-id:\t%s\n" + + "\tClient Type:\t%s\n" + + "\tActive: \t%s\n" + + "\tUserName:\t%s\n" + + "\tExpires: \t%d (%s)\n" + + "\tScope:\t\t%s\n" + + "\tContent:\t\t%s\n", + ti.getAccessToken(), + ti.getClientId(), + ti.getClientType(), + ti.isActive()?Boolean.TRUE.toString():Boolean.FALSE.toString(), + ti.getUsername(), + ti.getExp(), + Chrono.timeStamp(exp), + ti.getScope(), + ti.getContent()==null?"":ti.getContent()); + + System.out.println(); + } + +} diff --git a/cadi/pom.xml b/cadi/pom.xml new file mode 100644 index 00000000..5dfdf9ef --- /dev/null +++ b/cadi/pom.xml @@ -0,0 +1,482 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * +--> +<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.onap.aaf.authz</groupId> + <artifactId>parent</artifactId> + <version>2.1.0-SNAPSHOT</version> + </parent> + <groupId>org.onap.aaf.authz</groupId> + <artifactId>cadiparent</artifactId> + <name>AAF CADI Parent (Code, Access, Data, Identity)</name> + <version>2.1.0-SNAPSHOT</version> + <inceptionYear>2015-07-20</inceptionYear> + <organization> + <name>ONAP</name> + </organization> + <packaging>pom</packaging> + + + <properties> + <!-- <sonar.skip>true</sonar.skip> --> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <project.jettyVersion>9.3.9.v20160517</project.jettyVersion> + <powermock.version>1.5.1</powermock.version> + <!-- SONAR --> + <jacoco.version>0.7.7.201606060606</jacoco.version> + <sonar-jacoco-listeners.version>3.2</sonar-jacoco-listeners.version> + <sonar.core.codeCoveragePlugin>jacoco</sonar.core.codeCoveragePlugin> + <!-- Default Sonar configuration --> + <sonar.jacoco.reportPaths>target/code-coverage/jacoco-ut.exec</sonar.jacoco.reportPaths> + <sonar.jacoco.itReportPaths>target/code-coverage/jacoco-it.exec</sonar.jacoco.itReportPaths> + <!-- Note: This list should match jacoco-maven-plugin's exclusion list below --> + <sonar.exclusions>**/gen/**,**/generated-sources/**,**/yang-gen**,**/pax/**</sonar.exclusions> + <nexusproxy>https://nexus.onap.org</nexusproxy> + <snapshotNexusPath>/content/repositories/snapshots/</snapshotNexusPath> + <releaseNexusPath>/content/repositories/releases/</releaseNexusPath> + <stagingNexusPath>/content/repositories/staging/</stagingNexusPath> + <sitePath>/content/sites/site/org/onap/aaf/authz/${project.artifactId}/${project.version}</sitePath> + </properties> + + <!-- ============================================================== --> + <!-- Define the major contributors and developers of CADI --> + <!-- ============================================================== --> + <developers> + <developer> + <name>Jonathan Gathman</name> + <email>jonathan.gathman@att.com</email> + <organization>ATT</organization> + <roles> + <role>Architect</role> + <role>Lead Developer</role> + </roles> + </developer> + <developer> + <name>Gabe Maurer</name> + <email>gabe.maurer@att.com</email> + <organization>ATT</organization> + <roles> + <role>Developer</role> + </roles> + </developer> + <developer> + <name>Ian Howell</name> + <email>ian.howell@att.com</email> + <organization>ATT</organization> + <roles> + <role>Developer</role> + </roles> + </developer> + <developer> + <name>Sai Gandham</name> + <email>sai.gandham@att.com</email> + <organization>ATT</organization> + <roles> + <role>Developer</role> + </roles> + </developer> + </developers> + + + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-all</artifactId> + <version>1.9.5</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.powermock</groupId> + <artifactId>powermock-module-junit4</artifactId> + <version>${powermock.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.powermock</groupId> + <artifactId>powermock-api-mockito</artifactId> + <version>${powermock.version}</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.10</version> + <scope>test</scope> + </dependency> + </dependencies> + + <!-- ============================================================== --> + <!-- Define sub-projects (modules) --> + <!-- ============================================================== --> + <modules> + <module>core</module> + <module>client</module> + <module>aaf</module> + <module>oauth-enduser</module> + <module>shiro</module> + <module>shiro-osgi-bundle</module> + </modules> + + <!-- ============================================================== --> + <!-- Define project-wide dependencies --> + <!-- ============================================================== --> + <dependencyManagement> + <dependencies> + <dependency> + <groupId>org.onap.aaf.authz</groupId> + <artifactId>aaf-auth-client</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.onap.aaf.authz</groupId> + <artifactId>aaf-cadi-core</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.onap.aaf.authz</groupId> + <artifactId>aaf-cadi-oauth</artifactId> + <version>${project.version}</version> + </dependency> + + + <!-- Prevent Cycles in Testing --> + <dependency> + <groupId>org.onap.aaf.authz</groupId> + <artifactId>aaf-cadi-core</artifactId> + <version>${project.version}</version> + <classifier>tests</classifier> + </dependency> + + <dependency> + <groupId>org.onap.aaf.authz</groupId> + <artifactId>aaf-cadi-jetty</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.onap.aaf.authz</groupId> + <artifactId>aaf-cadi-cass</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.onap.aaf.authz</groupId> + <artifactId>aaf-cadi-aaf</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.onap.aaf.authz</groupId> + <artifactId>aaf-cadi-aaf</artifactId> + <version>${project.version}</version> + <classifier>full</classifier> + </dependency> + + <dependency> + <groupId>org.onap.aaf.authz</groupId> + <artifactId>aaf-cadi-client</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.onap.aaf.authz</groupId> + <artifactId>aaf-misc-env</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.onap.aaf.authz</groupId> + <artifactId>aaf-misc-rosetta</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.onap.aaf.authz</groupId> + <artifactId>aaf-misc-log4j</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-servlet</artifactId> + <version>${project.jettyVersion}</version> + </dependency> + + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-io</artifactId> + <version>${project.jettyVersion}</version> + </dependency> + + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-security</artifactId> + <version>${project.jettyVersion}</version> + </dependency> + + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-http</artifactId> + <version>${project.jettyVersion}</version> + </dependency> + + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-util</artifactId> + <version>${project.jettyVersion}</version> + </dependency> + + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-server</artifactId> + <version>${project.jettyVersion}</version> + </dependency> + + <dependency> + <groupId>javax.servlet</groupId> + <artifactId>javax.servlet-api</artifactId> + <version>3.0.1</version> + </dependency> + + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + <version>1.7.5</version> + </dependency> + </dependencies> + </dependencyManagement> + + <!-- ============================================================== --> + <!-- Define common plugins and make them available for all modules --> + <!-- ============================================================== --> + <build> + <testSourceDirectory>src/test/java</testSourceDirectory> + <plugins> + </plugins> + <pluginManagement> + <plugins> + <plugin> + <inherited>true</inherited> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>2.3.2</version> + <configuration> + <source>1.7</source> + <target>1.7</target> + </configuration> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <version>2.4</version> + <artifactId>maven-jar-plugin</artifactId> + <configuration> + <outputDirectory>target</outputDirectory> + <archive> + <manifestEntries> + <Sealed>true</Sealed> + </manifestEntries> + </archive> + </configuration> + </plugin> + + <!-- Define the javadoc plugin --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-javadoc-plugin</artifactId> + <version>2.10</version> + <configuration> + <excludePackageNames>org.opendaylight.*</excludePackageNames> + </configuration> + </plugin> + + <plugin> + <artifactId>maven-release-plugin</artifactId> + <version>2.5.2</version> + <configuration> + <goals>-s ${mvn.settings} deploy</goals> + </configuration> + </plugin> + + <plugin> + <artifactId>maven-assembly-plugin</artifactId> + <version>2.5.5</version> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>2.8.1</version> + <configuration> + <skip>false</skip> + </configuration> + + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-dependency-plugin</artifactId> + <version>2.10</version> + </plugin> + + <!-- Maven surefire plugin for testing --> + <plugin> + <artifactId>maven-surefire-plugin</artifactId> + <version>2.17</version> + <configuration> + <skipTests>false</skipTests> + <includes> + <include>**/JU*.java</include> + </includes> + <excludes> + </excludes> + </configuration> + </plugin> + + <!--This plugin's configuration is used to store Eclipse m2e settings + only. It has no influence on the Maven build itself. --> + <plugin> + <groupId>org.eclipse.m2e</groupId> + <artifactId>lifecycle-mapping</artifactId> + <version>1.0.0</version> + <configuration> + <lifecycleMappingMetadata> + <pluginExecutions> + <pluginExecution> + <pluginExecutionFilter> + <groupId> + org.codehaus.mojo + </groupId> + <artifactId> + jaxb2-maven-plugin + </artifactId> + <versionRange> + [1.3,) + </versionRange> + <goals> + <goal>xjc</goal> + </goals> + </pluginExecutionFilter> + <action> + <ignore /> + </action> + </pluginExecution> + </pluginExecutions> + </lifecycleMappingMetadata> + </configuration> + </plugin> + <plugin> + <groupId>org.sonatype.plugins</groupId> + <artifactId>nexus-staging-maven-plugin</artifactId> + <version>1.6.7</version> + <extensions>true</extensions> + <configuration> + <nexusUrl>${nexusproxy}</nexusUrl> + <stagingProfileId>176c31dfe190a</stagingProfileId> + <serverId>ecomp-staging</serverId> + </configuration> + </plugin> + <plugin> + <groupId>org.jacoco</groupId> + <artifactId>jacoco-maven-plugin</artifactId> + <version>${jacoco.version}</version> + <configuration> + <excludes> + <exclude>**/gen/**</exclude> + <exclude>**/generated-sources/**</exclude> + <exclude>**/yang-gen/**</exclude> + <exclude>**/pax/**</exclude> + </excludes> + </configuration> + <executions> + <execution> + <id>pre-unit-test</id> + <goals> + <goal>prepare-agent</goal> + </goals> + <configuration> + <destFile>${project.build.directory}/code-coverage/jacoco-ut.exec</destFile> + <propertyName>surefireArgLine</propertyName> + </configuration> + </execution> + <execution> + <id>post-unit-test</id> + <phase>test</phase> + <goals> + <goal>report</goal> + </goals> + <configuration> + <dataFile>${project.build.directory}/code-coverage/jacoco-ut.exec</dataFile> + <outputDirectory>${project.reporting.outputDirectory}/jacoco-ut</outputDirectory> + </configuration> + </execution> + <execution> + <id>pre-integration-test</id> + <phase>pre-integration-test</phase> + <goals> + <goal>prepare-agent</goal> + </goals> + <configuration> + <destFile>${project.build.directory}/code-coverage/jacoco-it.exec</destFile> + <propertyName>failsafeArgLine</propertyName> + </configuration> + </execution> + <execution> + <id>post-integration-test</id> + <phase>post-integration-test</phase> + <goals> + <goal>report</goal> + </goals> + <configuration> + <dataFile>${project.build.directory}/code-coverage/jacoco-it.exec</dataFile> + <outputDirectory>${project.reporting.outputDirectory}/jacoco-it</outputDirectory> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </pluginManagement> + </build> + + <distributionManagement> + <repository> + <id>ecomp-releases</id> + <name>AAF Release Repository</name> + <url>${nexusproxy}${releaseNexusPath}</url> + </repository> + <snapshotRepository> + <id>ecomp-snapshots</id> + <name>AAF Snapshot Repository</name> + <url>${nexusproxy}${snapshotNexusPath}</url> + </snapshotRepository> + <site> + <id>ecomp-site</id> + <url>dav:${nexusproxy}${sitePath}</url> + </site> + </distributionManagement> + +</project> diff --git a/cadi/shiro-osgi-bundle/.gitignore b/cadi/shiro-osgi-bundle/.gitignore new file mode 100644 index 00000000..f4b8361c --- /dev/null +++ b/cadi/shiro-osgi-bundle/.gitignore @@ -0,0 +1,5 @@ +/target +/bin/ +/.classpath +/.settings +/.project diff --git a/cadi/shiro-osgi-bundle/pom.xml b/cadi/shiro-osgi-bundle/pom.xml new file mode 100644 index 00000000..578a1b66 --- /dev/null +++ b/cadi/shiro-osgi-bundle/pom.xml @@ -0,0 +1,96 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2017 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * 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.
+ * ============LICENSE_END====================================================
+ *
+-->
+<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">
+
+ <parent>
+ <groupId>org.onap.aaf.authz</groupId>
+ <artifactId>cadiparent</artifactId>
+ <version>2.1.0-SNAPSHOT</version>
+ <relativePath>..</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>aaf-shiro-aafrealm-osgi-bundle</artifactId>
+ <packaging>bundle</packaging>
+
+ <properties>
+ <sonar.skip>true</sonar.skip>
+ <cadi.shiro.version>2.1.0</cadi.shiro.version>
+ </properties>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>2.5.4</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Bundle-Version>${project.version}</Bundle-Version>
+ <Export-Package>
+ org.onap.aaf.cadi.shiro*;version=${cadi.shiro.version}
+ </Export-Package>
+ <Import-Package>
+ javax.servlet,
+ javax.servlet.http,
+ org.osgi.service.blueprint;version="[1.0.0,2.0.0)",
+ javax.net.ssl,
+ javax.crypto,
+ javax.crypto.spec,
+ javax.xml.bind.annotation,
+ javax.xml.bind,
+ javax.xml.transform,
+ javax.xml.datatype,
+ javax.management,
+ javax.security.auth,
+ javax.security.auth.login,
+ javax.security.auth.callback,
+ javax.xml.soap,
+ javax.xml.parsers,
+ javax.xml.namespace,
+ org.w3c.dom,
+ org.xml.sax,
+ javax.xml.transform.stream
+ </Import-Package>
+ <Embed-Dependency>*;scope=compile|runtime;inline=false</Embed-Dependency>
+ <!-- <Embed-Dependency>*;scope=compile|runtime;artifactId=!shiro-core;inline=false</Embed-Dependency> -->
+ <Embed-Transitive>true</Embed-Transitive>
+ <Fragment-Host>org.apache.shiro.core</Fragment-Host>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+
+
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.onap.aaf.authz</groupId>
+ <artifactId>aaf-cadi-shiro</artifactId>
+ <version>2.1.0</version>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file diff --git a/cadi/shiro/.gitignore b/cadi/shiro/.gitignore new file mode 100644 index 00000000..6028f0a5 --- /dev/null +++ b/cadi/shiro/.gitignore @@ -0,0 +1,4 @@ +/.classpath +/.settings/ +/target/ +/.project diff --git a/cadi/shiro/pom.xml b/cadi/shiro/pom.xml new file mode 100644 index 00000000..4e7790cf --- /dev/null +++ b/cadi/shiro/pom.xml @@ -0,0 +1,204 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * +--> +<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"> + <parent> + <groupId>org.onap.aaf.authz</groupId> + <artifactId>cadiparent</artifactId> + <version>2.1.0-SNAPSHOT</version> + <relativePath>..</relativePath> + </parent> + + <modelVersion>4.0.0</modelVersion> + <name>AAF CADI Shiro Plugin</name> + <packaging>jar</packaging> + <artifactId>aaf-cadi-shiro</artifactId> + + <properties> + <!-- SONAR --> + <sonar.skip>true</sonar.skip> + <jacoco.version>0.7.7.201606060606</jacoco.version> + <sonar-jacoco-listeners.version>3.2</sonar-jacoco-listeners.version> + <sonar.core.codeCoveragePlugin>jacoco</sonar.core.codeCoveragePlugin> + <!-- Default Sonar configuration --> + <sonar.jacoco.reportPaths>target/code-coverage/jacoco-ut.exec</sonar.jacoco.reportPaths> + <sonar.jacoco.itReportPaths>target/code-coverage/jacoco-it.exec</sonar.jacoco.itReportPaths> + <!-- Note: This list should match jacoco-maven-plugin's exclusion list below --> + <sonar.exclusions>**/gen/**,**/generated-sources/**,**/yang-gen**,**/pax/**</sonar.exclusions> + <nexusproxy>https://nexus.onap.org</nexusproxy> + <snapshotNexusPath>/content/repositories/snapshots/</snapshotNexusPath> + <releaseNexusPath>/content/repositories/releases/</releaseNexusPath> + <stagingNexusPath>/content/repositories/staging/</stagingNexusPath> + <sitePath>/content/sites/site/org/onap/aaf/authz/${project.artifactId}/${project.version}</sitePath> + </properties> + + <developers> + <developer> + <name>Jonathan Gathman</name> + <email>jonathan.gathman@att.com</email> + <organization>ATT</organization> + <roles> + <role>Architect</role> + <role>Lead Developer</role> + </roles> + </developer> + <developer> + <name>Gabe Maurer</name> + <email>gabe.maurer@att.com</email> + <organization>ATT</organization> + <roles> + <role>Developer</role> + </roles> + </developer> + <developer> + <name>Ian Howell</name> + <email>ian.howell@att.com</email> + <organization>ATT</organization> + <roles> + <role>Developer</role> + </roles> + </developer> + <developer> + <name>Sai Gandham</name> + <email>sai.gandham@att.com</email> + <organization>ATT</organization> + <roles> + <role>Developer</role> + </roles> + </developer> + </developers> + + <dependencies> + <dependency> + <groupId>org.onap.aaf.authz</groupId> + <artifactId>aaf-cadi-aaf</artifactId> + </dependency> + <!--<dependency> + <groupId>org.apache.shiro</groupId> + <artifactId>shiro-core</artifactId> + <version>1.4.0</version> + </dependency> --> + + <dependency> + <groupId>org.apache.shiro</groupId> + <artifactId>shiro-core</artifactId> + <version>1.3.2</version> + </dependency> + + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.sonatype.plugins</groupId> + <artifactId>nexus-staging-maven-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <nexusUrl>${nexusproxy}</nexusUrl> + <stagingProfileId>176c31dfe190a</stagingProfileId> + <serverId>ecomp-staging</serverId> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <configuration> + <skip>false</skip> + </configuration> + </plugin> + <plugin> + <groupId>org.jacoco</groupId> + <artifactId>jacoco-maven-plugin</artifactId> + <configuration> + <excludes> + <exclude>**/gen/**</exclude> + <exclude>**/generated-sources/**</exclude> + <exclude>**/yang-gen/**</exclude> + <exclude>**/pax/**</exclude> + </excludes> + </configuration> + <executions> + <execution> + <id>pre-unit-test</id> + <goals> + <goal>prepare-agent</goal> + </goals> + <configuration> + <destFile>${project.build.directory}/code-coverage/jacoco-ut.exec</destFile> + <propertyName>surefireArgLine</propertyName> + </configuration> + </execution> + <execution> + <id>post-unit-test</id> + <phase>test</phase> + <goals> + <goal>report</goal> + </goals> + <configuration> + <dataFile>${project.build.directory}/code-coverage/jacoco-ut.exec</dataFile> + <outputDirectory>${project.reporting.outputDirectory}/jacoco-ut</outputDirectory> + </configuration> + </execution> + <execution> + <id>pre-integration-test</id> + <phase>pre-integration-test</phase> + <goals> + <goal>prepare-agent</goal> + </goals> + <configuration> + <destFile>${project.build.directory}/code-coverage/jacoco-it.exec</destFile> + <propertyName>failsafeArgLine</propertyName> + </configuration> + </execution> + <execution> + <id>post-integration-test</id> + <phase>post-integration-test</phase> + <goals> + <goal>report</goal> + </goals> + <configuration> + <dataFile>${project.build.directory}/code-coverage/jacoco-it.exec</dataFile> + <outputDirectory>${project.reporting.outputDirectory}/jacoco-it</outputDirectory> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + + </build> + + <distributionManagement> + <repository> + <id>ecomp-releases</id> + <name>AAF Release Repository</name> + <url>${nexusproxy}${releaseNexusPath}</url> + </repository> + <snapshotRepository> + <id>ecomp-snapshots</id> + <name>AAF Snapshot Repository</name> + <url>${nexusproxy}${snapshotNexusPath}</url> + </snapshotRepository> + <site> + <id>ecomp-site</id> + <url>dav:${nexusproxy}${sitePath}</url> + </site> + </distributionManagement> +</project> diff --git a/cadi/shiro/src/main/java/org/onap/aaf/cadi/shiro/AAFAuthenticationInfo.java b/cadi/shiro/src/main/java/org/onap/aaf/cadi/shiro/AAFAuthenticationInfo.java new file mode 100644 index 00000000..a1d304bd --- /dev/null +++ b/cadi/shiro/src/main/java/org/onap/aaf/cadi/shiro/AAFAuthenticationInfo.java @@ -0,0 +1,90 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ +package org.onap.aaf.cadi.shiro; + +import java.nio.ByteBuffer; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +import org.apache.shiro.authc.AuthenticationInfo; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.authc.UsernamePasswordToken; +import org.apache.shiro.subject.PrincipalCollection; +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.Hash; +import org.onap.aaf.cadi.Access.Level; + +public class AAFAuthenticationInfo implements AuthenticationInfo { + private static final long serialVersionUID = -1502704556864321020L; + // We assume that Shiro is doing Memory Only, and this salt is not needed cross process + private final static int salt = new SecureRandom().nextInt(); + + private final AAFPrincipalCollection apc; + private final byte[] hash; + private Access access; + + public AAFAuthenticationInfo(Access access, String username, String password) { + this.access = access; + apc = new AAFPrincipalCollection(username); + hash = getSaltedCred(password); + } + @Override + public byte[] getCredentials() { + access.log(Level.DEBUG, "AAFAuthenticationInfo.getCredentials"); + return hash; + } + + @Override + public PrincipalCollection getPrincipals() { + access.log(Level.DEBUG, "AAFAuthenticationInfo.getPrincipals"); + return apc; + } + + public boolean matches(AuthenticationToken atoken) { + if(atoken instanceof UsernamePasswordToken) { + UsernamePasswordToken upt = (UsernamePasswordToken)atoken; + if(apc.getPrimaryPrincipal().getName().equals(upt.getPrincipal())) { + byte[] newhash = getSaltedCred(new String(upt.getPassword())); + if(newhash.length==hash.length) { + for(int i=0;i<hash.length;++i) { + if(hash[i]!=newhash[i]) { + return false; + } + } + return true; + } + } + } + return false; + } + + private byte[] getSaltedCred(String password) { + byte[] pbytes = password.getBytes(); + ByteBuffer bb = ByteBuffer.allocate(pbytes.length+Integer.SIZE/8); + bb.asIntBuffer().put(salt); + bb.put(password.getBytes()); + try { + return Hash.hashSHA256(bb.array()); + } catch (NoSuchAlgorithmException e) { + return new byte[0]; // should never get here + } + } +} diff --git a/cadi/shiro/src/main/java/org/onap/aaf/cadi/shiro/AAFAuthorizationInfo.java b/cadi/shiro/src/main/java/org/onap/aaf/cadi/shiro/AAFAuthorizationInfo.java new file mode 100644 index 00000000..bfdc6bf1 --- /dev/null +++ b/cadi/shiro/src/main/java/org/onap/aaf/cadi/shiro/AAFAuthorizationInfo.java @@ -0,0 +1,94 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ +package org.onap.aaf.cadi.shiro; + +import java.security.Principal; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.apache.shiro.authz.AuthorizationInfo; +import org.apache.shiro.authz.Permission; +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.Access.Level; + +/** + * We treat "roles" and "permissions" in a similar way for first pass. + * + * @author JonathanGathman + * + */ +public class AAFAuthorizationInfo implements AuthorizationInfo { + private static final long serialVersionUID = -4805388954462426018L; + private Access access; + private Principal bait; + private List<org.onap.aaf.cadi.Permission> pond; + private ArrayList<String> sPerms; + private ArrayList<Permission> oPerms; + + public AAFAuthorizationInfo(Access access, Principal bait, List<org.onap.aaf.cadi.Permission> pond) { + this.access = access; + this.bait = bait; + this.pond = pond; + sPerms=null; + oPerms=null; + } + + public Principal principal() { + return bait; + } + + @Override + public Collection<Permission> getObjectPermissions() { + access.log(Level.DEBUG, "AAFAuthorizationInfo.getObjectPermissions"); + synchronized(bait) { + if(oPerms == null) { + oPerms = new ArrayList<Permission>(); + for(final org.onap.aaf.cadi.Permission p : pond) { + oPerms.add(new AAFShiroPermission(p)); + } + } + } + return oPerms; + } + + @Override + public Collection<String> getRoles() { + access.log(Level.DEBUG, "AAFAuthorizationInfo.getRoles"); + // Until we decide to make Roles available, tie into String based permissions. + return getStringPermissions(); + } + + @Override + public Collection<String> getStringPermissions() { + access.log(Level.DEBUG, "AAFAuthorizationInfo.getStringPermissions"); + synchronized(bait) { + if(sPerms == null) { + sPerms = new ArrayList<String>(); + for(org.onap.aaf.cadi.Permission p : pond) { + sPerms.add(p.getKey()); + } + } + } + return sPerms; + } + +} diff --git a/cadi/shiro/src/main/java/org/onap/aaf/cadi/shiro/AAFPrincipalCollection.java b/cadi/shiro/src/main/java/org/onap/aaf/cadi/shiro/AAFPrincipalCollection.java new file mode 100644 index 00000000..145968de --- /dev/null +++ b/cadi/shiro/src/main/java/org/onap/aaf/cadi/shiro/AAFPrincipalCollection.java @@ -0,0 +1,125 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ +package org.onap.aaf.cadi.shiro; + +import java.security.Principal; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import org.apache.shiro.subject.PrincipalCollection; + +public class AAFPrincipalCollection implements PrincipalCollection { + private static final long serialVersionUID = 558246013419818831L; + private static final Set<String> realmSet; + private final Principal principal; + private List<Principal> list=null; + private Set<Principal> set=null; + + static { + realmSet = new HashSet<String>(); + realmSet.add(AAFRealm.AAF_REALM); + } + + public AAFPrincipalCollection(Principal p) { + principal = p; + } + + public AAFPrincipalCollection(final String principalName) { + principal = new Principal() { + private final String name = principalName; + @Override + public String getName() { + return name; + } + }; + } + + @Override + public Iterator<Principal> iterator() { + return null; + } + + @Override + public List<Principal> asList() { + if(list==null) { + list = new ArrayList<Principal>(); + } + list.add(principal); + return list; + } + + @Override + public Set<Principal> asSet() { + if(set==null) { + set = new HashSet<Principal>(); + } + set.add(principal); + return set; + } + + @SuppressWarnings("unchecked") + @Override + public <T> Collection<T> byType(Class<T> cls) { + Collection<T> coll = new ArrayList<T>(); + if(cls.isAssignableFrom(Principal.class)) { + coll.add((T)principal); + } + return coll; + } + + @Override + public Collection<Principal> fromRealm(String realm) { + if(AAFRealm.AAF_REALM.equals(realm)) { + return asList(); + } else { + return new ArrayList<Principal>(); + } + } + + @Override + public Principal getPrimaryPrincipal() { + return principal; + } + + @Override + public Set<String> getRealmNames() { + return realmSet; + } + + @Override + public boolean isEmpty() { + return principal==null; + } + + @SuppressWarnings("unchecked") + @Override + public <T> T oneByType(Class<T> cls) { + if(cls.isAssignableFrom(Principal.class)) { + return (T)principal; + } + return null; + } + +} diff --git a/cadi/shiro/src/main/java/org/onap/aaf/cadi/shiro/AAFRealm.java b/cadi/shiro/src/main/java/org/onap/aaf/cadi/shiro/AAFRealm.java new file mode 100644 index 00000000..006547a9 --- /dev/null +++ b/cadi/shiro/src/main/java/org/onap/aaf/cadi/shiro/AAFRealm.java @@ -0,0 +1,142 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ +package org.onap.aaf.cadi.shiro; + +import java.io.IOException; +import java.security.Principal; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authc.AuthenticationInfo; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.authc.UsernamePasswordToken; +import org.apache.shiro.realm.AuthorizingRealm; +import org.apache.shiro.subject.PrincipalCollection; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.LocatorException; +import org.onap.aaf.cadi.Permission; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.aaf.v2_0.AAFAuthn; +import org.onap.aaf.cadi.aaf.v2_0.AAFCon; +import org.onap.aaf.cadi.aaf.v2_0.AAFLurPerm; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.misc.env.APIException; + +public class AAFRealm extends AuthorizingRealm { + public static final String AAF_REALM = "AAFRealm"; + + private PropAccess access; + private AAFCon<?> acon; + private AAFAuthn<?> authn; + private HashSet<Class<? extends AuthenticationToken>> supports; + private AAFLurPerm authz; + + + /** + * + * There appears to be no configuration objects or references available for CADI to start with. + * + */ + public AAFRealm () { + access = new PropAccess(); // pick up cadi_prop_files from VM_Args + String cadi_prop_files = access.getProperty(Config.CADI_PROP_FILES); + if(cadi_prop_files==null) { + String msg = Config.CADI_PROP_FILES + " in VM Args is required to initialize AAFRealm."; + access.log(Level.INIT,msg); + throw new RuntimeException(msg); + } else { + try { + acon = AAFCon.newInstance(access); + authn = acon.newAuthn(); + authz = acon.newLur(authn); + } catch (APIException | CadiException | LocatorException e) { + String msg = "Cannot initiate AAFRealm"; + access.log(Level.INIT,msg,e.getMessage()); + throw new RuntimeException(msg,e); + } + } + supports = new HashSet<Class<? extends AuthenticationToken>>(); + supports.add(UsernamePasswordToken.class); + } + + @Override + protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { + access.log(Level.DEBUG, "AAFRealm.doGetAuthenticationInfo",token); + + final UsernamePasswordToken upt = (UsernamePasswordToken)token; + String password=new String(upt.getPassword()); + String err; + try { + err = authn.validate(upt.getUsername(),password); + } catch (IOException|CadiException e) { + err = "Credential cannot be validated"; + access.log(e, err); + } + + if(err != null) { + access.log(Level.DEBUG, err); + throw new AuthenticationException(err); + } + + return new AAFAuthenticationInfo( + access, + upt.getUsername(), + password + ); + } + + @Override + protected void assertCredentialsMatch(AuthenticationToken atoken, AuthenticationInfo ai)throws AuthenticationException { + if(ai instanceof AAFAuthenticationInfo) { + if(!((AAFAuthenticationInfo)ai).matches(atoken)) { + throw new AuthenticationException("Credentials do not match"); + } + } else { + throw new AuthenticationException("AuthenticationInfo is not an AAFAuthenticationInfo"); + } + } + + + @Override + protected AAFAuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { + access.log(Level.DEBUG, "AAFRealm.doGetAuthenthorizationInfo"); + Principal bait = (Principal)principals.getPrimaryPrincipal(); + List<Permission> pond = new ArrayList<Permission>(); + authz.fishAll(bait,pond); + + return new AAFAuthorizationInfo(access,bait,pond); + + } + + @Override + public boolean supports(AuthenticationToken token) { + return supports.contains(token.getClass()); + } + + @Override + public String getName() { + return AAF_REALM; + } + +} diff --git a/cadi/shiro/src/main/java/org/onap/aaf/cadi/shiro/AAFShiroPermission.java b/cadi/shiro/src/main/java/org/onap/aaf/cadi/shiro/AAFShiroPermission.java new file mode 100644 index 00000000..a348a045 --- /dev/null +++ b/cadi/shiro/src/main/java/org/onap/aaf/cadi/shiro/AAFShiroPermission.java @@ -0,0 +1,45 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ +package org.onap.aaf.cadi.shiro; + +import org.apache.shiro.authz.Permission; + +public class AAFShiroPermission implements Permission { + private org.onap.aaf.cadi.Permission perm; + public AAFShiroPermission(org.onap.aaf.cadi.Permission perm) { + this.perm = perm; + } + @Override + public boolean implies(Permission sp) { + if(sp instanceof AAFShiroPermission) { + if(perm.match(((AAFShiroPermission)sp).perm)){ + return true; + } + } + return false; + } + + @Override + public String toString() { + return perm.toString(); + } + +} diff --git a/cadi/shiro/src/test/java/org/onap/aaf/cadi/shiro/test/JU_AAFRealm.java b/cadi/shiro/src/test/java/org/onap/aaf/cadi/shiro/test/JU_AAFRealm.java new file mode 100644 index 00000000..add449c9 --- /dev/null +++ b/cadi/shiro/src/test/java/org/onap/aaf/cadi/shiro/test/JU_AAFRealm.java @@ -0,0 +1,93 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * 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. + * ============LICENSE_END==================================================== + * + */ +package org.onap.aaf.cadi.shiro.test; + +import java.util.ArrayList; + +import org.apache.shiro.authc.AuthenticationInfo; +import org.apache.shiro.authc.UsernamePasswordToken; +import org.apache.shiro.authz.AuthorizationInfo; +import org.apache.shiro.authz.Permission; +import org.apache.shiro.subject.PrincipalCollection; +import org.junit.Test; +import org.onap.aaf.cadi.aaf.AAFPermission; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.shiro.AAFRealm; +import org.onap.aaf.cadi.shiro.AAFShiroPermission; + +import junit.framework.Assert; + +public class JU_AAFRealm { + + // TODO: Ian - fix this test + // @Test + // public void test() { + // // NOTE This is a live test. This JUnit needs to be built with "Mock" + // try { + // System.setProperty(Config.CADI_PROP_FILES, "/opt/app/osaaf/etc/org.osaaf.common.props"); + // TestAAFRealm ar = new TestAAFRealm(); + + // UsernamePasswordToken upt = new UsernamePasswordToken("jonathan@people.osaaf.org", "new2You!"); + // AuthenticationInfo ani = ar.authn(upt); + + // AuthorizationInfo azi = ar.authz(ani.getPrincipals()); + // // Change this to something YOU have, Sai... + + // testAPerm(true,azi,"org.access","something","*"); + // testAPerm(false,azi,"org.accessX","something","*"); + // } catch (Throwable t) { + // t.printStackTrace(); + // Assert.fail(); + // } + // } + + private void testAPerm(boolean expect,AuthorizationInfo azi, String type, String instance, String action) { + + AAFShiroPermission testPerm = new AAFShiroPermission(new AAFPermission(type,instance,action,new ArrayList<String>())); + + boolean any = false; + for(Permission p : azi.getObjectPermissions()) { + if(p.implies(testPerm)) { + any = true; + } + } + if(expect) { + Assert.assertTrue(any); + } else { + Assert.assertFalse(any); + } + + + } + + /** + * Note, have to create a derived class, because "doGet"... are protected + */ + private class TestAAFRealm extends AAFRealm { + public AuthenticationInfo authn(UsernamePasswordToken upt) { + return doGetAuthenticationInfo(upt); + } + public AuthorizationInfo authz(PrincipalCollection pc) { + return doGetAuthorizationInfo(pc); + } + + } +} |