diff options
| author | Thomas Lenz <thomas.lenz@egiz.gv.at> | 2019-09-13 09:02:58 +0200 | 
|---|---|---|
| committer | Thomas Lenz <thomas.lenz@egiz.gv.at> | 2019-09-13 09:02:58 +0200 | 
| commit | 8883fd1c0efa08fe0a73a11a1c8986fa3ea72753 (patch) | |
| tree | 97ee6979a60ca1a462490e8e49a74ed8a0cb7293 /eaaf_core_utils | |
| parent | 7e85912db28d09d876fe6840e228781fb4ea5256 (diff) | |
| download | EAAF-Components-8883fd1c0efa08fe0a73a11a1c8986fa3ea72753.tar.gz EAAF-Components-8883fd1c0efa08fe0a73a11a1c8986fa3ea72753.tar.bz2 EAAF-Components-8883fd1c0efa08fe0a73a11a1c8986fa3ea72753.zip | |
refactor utils into a separate module
Diffstat (limited to 'eaaf_core_utils')
19 files changed, 2486 insertions, 0 deletions
| diff --git a/eaaf_core_utils/pom.xml b/eaaf_core_utils/pom.xml new file mode 100644 index 00000000..6168f601 --- /dev/null +++ b/eaaf_core_utils/pom.xml @@ -0,0 +1,150 @@ +<?xml version="1.0"?> +<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" +    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> +  <modelVersion>4.0.0</modelVersion> +  <parent> +    <groupId>at.gv.egiz</groupId> +    <artifactId>eaaf</artifactId> +    <version>1.0.12-SNAPSHOT</version>  +  </parent> +  <groupId>at.gv.egiz.eaaf</groupId> +  <artifactId>eaaf_core_utils</artifactId> +  <name>Utils for EAAF core components</name> +  <description>Core component Utils for identity managment implementations</description> +   +  <licenses> +    <license> +      <name>European Union Public License, version 1.2 (EUPL-1.2)</name> +      <url>https://opensource.org/licenses/EUPL-1.2</url> +      <distribution>repo</distribution> +    </license> +  </licenses> + +  <developers> +    <developer> +      <name>Thomas Lenz</name> +      <email>thomas.lenz@egiz.gv.at</email> +      <organization>eGovernment Innovation Center (EGIZ)</organization> +      <organizationUrl>https://www.egiz.gv.at</organizationUrl> +    </developer> +  </developers> +   +  <properties> +    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> +  </properties> +   + <dependencies> + 	<dependency> + 		<groupId>at.gv.egiz.eaaf</groupId> +  		<artifactId>eaaf_core_api</artifactId> + 	</dependency> + +  	<dependency> +  		<groupId>org.springframework</groupId> +		<artifactId>spring-webmvc</artifactId> +  	</dependency> +  +  	<dependency> +  		<groupId>org.slf4j</groupId> +    	<artifactId>slf4j-api</artifactId> +  	</dependency> +  	<dependency> +		<groupId>org.apache.commons</groupId> +    	<artifactId>commons-lang3</artifactId> +	</dependency> +  	<dependency> +    	<groupId>org.apache.httpcomponents</groupId> +    	<artifactId>httpclient</artifactId> +	</dependency>            					    +	<dependency> +    	<groupId>org.apache.httpcomponents</groupId> +    	<artifactId>httpcore</artifactId> +	</dependency> +  	<dependency> +    	<groupId>com.google.code.findbugs</groupId> +    	<artifactId>jsr305</artifactId> +	</dependency> +	<dependency> +	    <groupId>joda-time</groupId> +    	<artifactId>joda-time</artifactId> +	</dependency> +  	<dependency> +  		<groupId>javax.servlet</groupId> +		<artifactId>javax.servlet-api</artifactId> +		<scope>provided</scope> +  	</dependency>  +  	 	 +	<dependency> +		<groupId>junit</groupId> +      	<artifactId>junit</artifactId> +      	<scope>test</scope> +	</dependency> +	<dependency> +		<groupId>org.springframework</groupId> +		<artifactId>spring-test</artifactId> +		<scope>test</scope> +	</dependency> +	 +  </dependencies> +   +   <build>    +   	<finalName>eaaf_core_utils</finalName> +	 +	<resources> +  		<resource> +  			<directory>src/main/resources</directory> +  		</resource> +  	</resources>    +        +    <plugins> +      <plugin> +        <groupId>org.apache.maven.plugins</groupId> +        <artifactId>maven-compiler-plugin</artifactId> +        <version>3.7.0</version> +        <configuration> +          <source>1.8</source> +          <target>1.8</target> +        </configuration> +        <executions> +        	<execution> +        		<goals> +        			<goal>compile</goal> +        			<goal>testCompile</goal> +        		</goals> +        	</execution> +        </executions> +      </plugin> +      <plugin> +    	<groupId>org.apache.maven.plugins</groupId> +    	<artifactId>maven-jar-plugin</artifactId> +    	<version>3.1.0</version> +    	<executions> +        	<execution> +            	<goals> +                	<goal>test-jar</goal> +            	</goals> +        	</execution> +    	</executions> +	  </plugin> +       +      <!-- enable co-existence of testng and junit --> +			<plugin> +				<artifactId>maven-surefire-plugin</artifactId> +				<version>${surefire.version}</version> +				<configuration> +					<threadCount>1</threadCount> +				</configuration> +				<dependencies> +					<dependency> +						<groupId>org.apache.maven.surefire</groupId> +						<artifactId>surefire-junit47</artifactId> +						<version>${surefire.version}</version> +					</dependency> +				</dependencies> +			</plugin> +       +    </plugins> +  </build> +   +   +</project> diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/support/SecureRandomHolder.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/support/SecureRandomHolder.java new file mode 100644 index 00000000..a297367f --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/support/SecureRandomHolder.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright 2017 Graz University of Technology + * EAAF-Core Components has been developed in a cooperation between EGIZ,   + * A-SIT Plus, A-SIT, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/news/understanding-eupl-v12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *   + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + *******************************************************************************/ +/******************************************************************************* + *******************************************************************************/ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eaaf.core.impl.idp.process.support; + +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +/** + * Holder for a secure random instance following the initialization on demand holder design pattern. The secure random + * instance is a singleton that is initialized on first usage. + *  + * @author tknall + *  + */ +public class SecureRandomHolder { + +	private SecureRandomHolder() { +	} + +	private static final SecureRandom SRND_INSTANCE; +	static { +		try { +			SRND_INSTANCE = SecureRandom.getInstance("SHA1PRNG"); +		} catch (NoSuchAlgorithmException e) { +			throw new RuntimeException("Unable to instantiate SHA1PRNG.", e); +		} +	} + +	/** +	 * Returns a secure random generator instance. +	 * @return The secure random instance. +	 */ +	public static SecureRandom getInstance() { +		return SecureRandomHolder.SRND_INSTANCE; +	} + +}
\ No newline at end of file diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/ArrayUtils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/ArrayUtils.java new file mode 100644 index 00000000..8585bc05 --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/ArrayUtils.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright 2017 Graz University of Technology + * EAAF-Core Components has been developed in a cooperation between EGIZ,   + * A-SIT Plus, A-SIT, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/news/understanding-eupl-v12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *   + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + *******************************************************************************/ +package at.gv.egiz.eaaf.core.impl.utils; + +import java.util.List; + +public class ArrayUtils { + +	/** +	 * Check if a String 's' is part of a List 'l' in qualsIgnoreCase mode +	 *  +	 * @param s Search String +	 * @param l List of String elements +	 * @return true if 's' is in 'l', otherwise false +	 */ +	public static boolean containsCaseInsensitive(String s, List<String> l){ +		if (l == null || s == null) +			return false; +		 +        return l.stream().anyMatch(x -> x.equalsIgnoreCase(s)); +         +    } + +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/DataURLBuilder.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/DataURLBuilder.java new file mode 100644 index 00000000..a81fafbc --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/DataURLBuilder.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * Copyright 2014 Federal Chancellery Austria + * MOA-ID has been developed in a cooperation between BRZ, the Federal + * Chancellery Austria - ICT staff unit, and Graz University of Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +/* + * Copyright 2003 Federal Chancellery Austria + * MOA-ID has been developed in a cooperation between BRZ, the Federal + * Chancellery Austria - ICT staff unit, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ + + +package at.gv.egiz.eaaf.core.impl.utils; + +import org.apache.commons.lang3.StringUtils; + +import at.gv.egiz.eaaf.core.api.data.EAAFConstants; + +/** + * Builds a DataURL parameter meant for the security layer implementation + * to respond to. + *  + * @author Paul Ivancsics + * @version $Id$ + */ +public class DataURLBuilder { +	 +  /** +   * Constructor for DataURLBuilder. +   */ +  public DataURLBuilder() { +    super(); +  } +   +  /** +   * Constructs a data URL for <code>VerifyIdentityLink</code> or <code>VerifyAuthenticationBlock</code>,  +   * including the <code>MOASessionID</code> as a parameter. +   *  +   * @param authBaseURL base URL (context path) of the MOA ID Authentication component, +   * 				 including a trailing <code>'/'</code> +   * @param authServletName request part of the data URL +   * @param pendingReqId sessionID to be included in the dataURL +   * @return String +   */ +  public String buildDataURL(String authBaseURL, String authServletName, String pendingReqId) { +		String dataURL;		 +		if (!authBaseURL.endsWith("/")) +			authBaseURL += "/"; +		 +		if (authServletName.startsWith("/")) +			authServletName = authServletName.substring(1); +		 +		dataURL = authBaseURL + authServletName; + +		if (StringUtils.isNotEmpty(pendingReqId)) +			dataURL = addParameter(dataURL, EAAFConstants.PARAM_HTTP_TARGET_PENDINGREQUESTID, pendingReqId); +		 +  	return dataURL; +  } + +  /** +   * Method addParameter. +   * @param urlString represents the url  +   * @param paramname is the parameter to be added +   * @param value is the value of that parameter +   * @return String +   */ +  private String addParameter(String urlString, String paramname, String value) { +    String url = urlString; +    if (paramname != null) { +      if (url.indexOf("?") < 0) +        url += "?"; +      else +        url += "&"; +      url += paramname + "=" + value; +    } +    return url; +  } +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/FileUtils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/FileUtils.java new file mode 100644 index 00000000..fff83e53 --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/FileUtils.java @@ -0,0 +1,175 @@ +/******************************************************************************* + * Copyright 2017 Graz University of Technology + * EAAF-Core Components has been developed in a cooperation between EGIZ,   + * A-SIT Plus, A-SIT, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/news/understanding-eupl-v12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *   + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + *******************************************************************************/ +/******************************************************************************* + *******************************************************************************/ +/******************************************************************************* + *******************************************************************************/ + + +package at.gv.egiz.eaaf.core.impl.utils; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class FileUtils { +	private static final Logger log = LoggerFactory.getLogger(FileUtils.class); +	 +	 +  /** +   * Reads a file, given by URL, into a byte array. +   * @param urlString file URL +   * @return file content +   * @throws IOException on any exception thrown +   */ +  public static byte[] readURL(String urlString) throws IOException {	   +    final URL url = new URL(urlString); +    final InputStream in = new BufferedInputStream(url.openStream()); +    final byte[] content = StreamUtils.readStream(in); +    in.close(); +    return content; +  } + +  /** +   * Reads a file from a resource. +   * @param name resource name +   * @return file content as a byte array +   * @throws IOException on any exception thrown +   */ +  public static byte[] readResource(String name) throws IOException { +    final ClassLoader cl = FileUtils.class.getClassLoader(); +    final BufferedInputStream in = new BufferedInputStream(cl.getResourceAsStream(name)); +    final byte[] content = StreamUtils.readStream(in); +    in.close(); +    return content; +  } +  /** +   * Reads a file from a resource. +   * @param name filename +   * @param encoding character encoding +   * @return file content +   * @throws IOException on any exception thrown +   */ +  public static String readResource(String name, String encoding) throws IOException { +    final byte[] content = readResource(name); +    return new String(content, encoding); +  } +   +   +	/** +	 * Returns the absolute URL of a given url which is relative to the parameter root +	 * @param url +	 * @param root +	 * @return String +	 * @throws MalformedURLException  +	 */ +	public static String makeAbsoluteURL(String url, URI root) throws MalformedURLException { +		if (root != null) +			return makeAbsoluteURL(url, root.toURL().toString()); +		else +			return makeAbsoluteURL(url, StringUtils.EMPTY); + +	}   +   +	/** +	 * Returns the absolute URL of a given url which is relative to the parameter root +	 * @param url +	 * @param root +	 * @return String +	 */ +	public static String makeAbsoluteURL(String url, String root) { +		//if url is relative to rootConfigFileDirName make it absolute 					 +		 +		log.trace("Making AbsoluteURL URL: " + url + " Root-Path: " + root); +		 +		if (StringUtils.isEmpty(root)) +			root = null; +		 +		File keyFile; +		String newURL = url; +		 +		if(null == url) return  null; +     +		if (url.startsWith("http:") || url.startsWith("https:") || url.startsWith("file:") || url.startsWith("ftp:")) { +			return url; +			 +		} else { +			// check if absolute - if not make it absolute +			keyFile = new File(url); +			if (!keyFile.isAbsolute()) { +				keyFile = new File(root, url); + +				if (keyFile.toString().startsWith("file:")) +					newURL = keyFile.toString(); +         +				else +					newURL = keyFile.toURI().toString(); +         +			} +			return newURL; +		} +	}   +	 +	 +	 private static void copy( InputStream fis, OutputStream fos ) +	  { +	    try +	    { +	      final byte[] buffer = new byte[ 0xFFFF ]; +	      for ( int len; (len = fis.read(buffer)) != -1; ) +	        fos.write( buffer, 0, len ); +	    } +	    catch( final IOException e ) { +	      System.err.println( e ); +	    } +	    finally { +	      if ( fis != null ) +	        try { fis.close(); } catch ( final IOException e ) { e.printStackTrace(); } +	      if ( fos != null ) +	        try { fos.close(); } catch ( final IOException e ) { e.printStackTrace(); } +	    } +	  } +	  +	 public static void copyFile(File src, File dest) +	  { +	    try +	    { +	      copy( new FileInputStream( src ), new FileOutputStream( dest ) ); +	    } +	    catch( final IOException e ) { +	      e.printStackTrace(); +	    } +	  } +   +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HTTPUtils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HTTPUtils.java new file mode 100644 index 00000000..cf1abaa7 --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HTTPUtils.java @@ -0,0 +1,178 @@ +/******************************************************************************* + * Copyright 2014 Federal Chancellery Austria + * MOA-ID has been developed in a cooperation between BRZ, the Federal + * Chancellery Austria - ICT staff unit, and Graz University of Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +/* + * Copyright 2003 Federal Chancellery Austria + * MOA-ID has been developed in a cooperation between BRZ, the Federal + * Chancellery Austria - ICT staff unit, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ + + +package at.gv.egiz.eaaf.core.impl.utils; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.lang3.StringUtils; + + +/** + *  + * @author Rudolf Schamberger + * + */ +public class HTTPUtils { + +//  /** +//   * Utility used to obtainin correct encoded HTTP content. +//   * Reads a given Content adressed by HTTP-URL into String.  +//   * Content encoding is considered by using the Content-Type HTTP header charset value. +//   * @param URL HTTP URL to read from. +//   * @return String representation of content +//   * @throws IOException on data-reading problems +//   */ +//  public static String readHttpURL(String URL)  +//    throws IOException { +//       +//    URL url = new URL(URL); +//    HttpURLConnection conn = (HttpURLConnection)url.openConnection(); +//    conn.setRequestMethod("GET"); +//    String contentType = conn.getContentType(); +//    RE regExp = null; +//    try { +//        regExp = new RE("(;.*charset=)(\"*)(.*[^\"])"); +//    } catch (RESyntaxException e) { +//        //RESyntaxException is not possible = expr. is costant +//    } +//    boolean charsetSupplied = regExp.match(contentType); +//    String encoding = "ISO-8859-1"; 		//default HTTP encoding  +//    if (charsetSupplied) { +//      encoding = regExp.getParen(3); +//    } +//    InputStream instream = new BufferedInputStream(conn.getInputStream()); +//    InputStreamReader isr = new InputStreamReader(instream, encoding); +//    Reader in = new BufferedReader(isr); +//    int ch; +//  	StringBuffer buffer = new StringBuffer(); +//    while ((ch = in.read()) > -1) { +//  		buffer.append((char)ch); +//  	}      +//    in.close(); +//    conn.disconnect(); +//    return buffer.toString(); +//  } +   +  	/** +  	 * Helper method to retrieve server URL including context path +  	 * @param request HttpServletRequest +  	 * @return Server URL including context path (e.g. http://localhost:8443/moa-id-auth +  	 */ +  	public static String getBaseURL(HttpServletRequest request) { +		StringBuffer buffer = new StringBuffer(getServerURL(request)); +		 +	    // add context path if available +	    String contextPath = request.getContextPath(); +	    if (!StringUtils.isEmpty(contextPath)) { +	    	buffer.append(contextPath); +	    } +		 +		return buffer.toString();  +	} +	 +  	/** +  	 * Helper method to retrieve server URL +  	 * @param request HttpServletRequest +  	 * @return Server URL (e.g. http://localhost:8443) +  	 */ +	public static String getServerURL(HttpServletRequest request) { +		StringBuffer buffer = new StringBuffer(); +		 +		// get protocol +		String protocol = request.getScheme(); +		buffer.append(protocol).append("://"); +		 +		// server name +		buffer.append(request.getServerName()); +		 +		// add port if necessary +		int port = request.getServerPort(); +	    if ((protocol.equals("http") && port != 80) || (protocol.equals("https") && port != 443)) { +	    	buffer.append(':'); +	    	buffer.append(port); +	    } +	 +	    return buffer.toString();  +	} +	 +	/** +	 * Extract the IDP PublicURLPrefix from authrequest +	 *  +	 * @param req HttpServletRequest +	 * @return PublicURLPrefix <String> which ends always without / +	 */ +	public static String extractAuthURLFromRequest(HttpServletRequest req) { +	    String authURL = req.getScheme() + "://" + req.getServerName(); +	    if ((req.getScheme().equalsIgnoreCase("https") && req.getServerPort()!=443) || (req.getScheme().equalsIgnoreCase("http") && req.getServerPort()!=80)) {  +	      authURL = authURL.concat(":" + req.getServerPort()); +	    } +	    authURL = authURL.concat(req.getContextPath()); +	    return authURL; +		 +	} +	 +	/** +	 * Extract the IDP requested URL from authrequest +	 *  +	 * @param req HttpServletRequest +	 * @return RequestURL <String> which ends always without / +	 */  +	public static String extractAuthServletPathFromRequest(HttpServletRequest req) { +	    return extractAuthURLFromRequest(req).concat(req.getServletPath()); +	 	 +	} +	 +	public static String addURLParameter(String url, String paramname, +			String paramvalue) { +		String param = paramname + "=" + paramvalue; +		if (url.indexOf("?") < 0) +			return url + "?" + param; +		else +			return url + "&" + param; +	} + +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HttpClientFactory.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HttpClientFactory.java new file mode 100644 index 00000000..a141d92d --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HttpClientFactory.java @@ -0,0 +1,152 @@ +package at.gv.egiz.eaaf.core.impl.utils; + +import java.security.NoSuchAlgorithmException; + +import javax.annotation.PostConstruct; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLContext; + +import org.apache.http.HttpRequest; +import org.apache.http.HttpResponse; +import org.apache.http.ProtocolException; +import org.apache.http.client.RedirectStrategy; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.conn.socket.LayeredConnectionSocketFactory; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.DefaultRedirectStrategy; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.http.protocol.HttpContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import at.gv.egiz.eaaf.core.api.idp.IConfiguration; + +public class HttpClientFactory implements IHttpClientFactory { +	private static final Logger log = LoggerFactory.getLogger(HttpClientFactory.class);	 +	@Autowired(required=true) private IConfiguration basicConfig; +	 +	public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_USE = "client.http.connection.pool.use"; +	public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL = "client.http.connection.pool.maxtotal"; +	public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXPERROUTE = "client.http.connection.pool.maxperroute"; +	public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET = "client.http.connection.timeout.socket"; +	public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_CONNECTION = "client.http.connection.timeout.connection"; +	public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_REQUEST = "client.http.connection.timeout.request"; +	public static final String PROP_CONFIG_CLIENT_HTTP_SSL_HOSTNAMEVERIFIER_TRUSTALL = "client.http.ssl.hostnameverifier.trustall"; +	 +	// default configuration values +	public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET = "15"; +	public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_CONNECTION = "15"; +	public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_REQUEST = "30"; +	public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL = "500"; +	public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXPERROUTE = "100"; +	 +	private HttpClientBuilder httpClientBuilder = null; +	 +	/* (non-Javadoc) +	 * @see at.gv.egiz.eaaf.core.impl.utils.IHttpClientFactory#getHttpClient() +	 */ +	@Override +	public CloseableHttpClient getHttpClient() { +		return getHttpClient(true); +		 +	} +	 +	@Override +	public CloseableHttpClient getHttpClient(boolean followRedirects) { +		RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();  +		if (!followRedirects) +			redirectStrategy = new RedirectStrategy() { +				 +				@Override +				public boolean isRedirected(HttpRequest request, HttpResponse response, HttpContext context) +						throws ProtocolException { +					return false; +				} +				 +				@Override +				public HttpUriRequest getRedirect(HttpRequest request, HttpResponse response, HttpContext context) +						throws ProtocolException { +					return null; +				} +			}; +		  +		return httpClientBuilder +				.setRedirectStrategy(redirectStrategy) +				.build();			 +									 +	} +	 +	@PostConstruct +	private void initalize() { +		//initialize http client  +		log.trace("Initializing HTTP Client-builder ... "); +		httpClientBuilder = HttpClients.custom(); +		  +		//set default request configuration +		final RequestConfig requestConfig = RequestConfig.custom() +											.setConnectTimeout(Integer.valueOf(basicConfig.getBasicConfiguration( +													PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_CONNECTION,  +													DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_CONNECTION)) * 1000) +											.setConnectionRequestTimeout(Integer.valueOf(basicConfig.getBasicConfiguration( +													PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_REQUEST,  +													DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_REQUEST)) * 1000) +											.setSocketTimeout(Integer.valueOf(basicConfig.getBasicConfiguration( +													PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET,  +													DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET)) * 1000) +											.build();		 +		httpClientBuilder.setDefaultRequestConfig(requestConfig); +				 +		//set  pool connection if requested +		if (basicConfig.getBasicConfigurationBoolean( +				PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_USE,  +				true)) { +			final PoolingHttpClientConnectionManager pool = new PoolingHttpClientConnectionManager(); +			pool.setDefaultMaxPerRoute(Integer.valueOf(basicConfig.getBasicConfiguration( +					PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXPERROUTE,  +					DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXPERROUTE)));			 +			pool.setMaxTotal(Integer.valueOf(basicConfig.getBasicConfiguration( +					PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL,  +					DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL))); +			 +			 +			 +			httpClientBuilder.setConnectionManager(pool); +			log.debug("Initalize http-client pool with, maxTotal: {} maxPerRoute: {}", pool.getMaxTotal(), pool.getDefaultMaxPerRoute()); +					 +		} +		 +		try { +			log.trace("Initializing SSL Context ... "); +			final SSLContext sslContext = SSLContext.getDefault(); +			HostnameVerifier hostnameVerifier = null; +			if (basicConfig.getBasicConfigurationBoolean( +					PROP_CONFIG_CLIENT_HTTP_SSL_HOSTNAMEVERIFIER_TRUSTALL,  +					false)) { +				hostnameVerifier = new NoopHostnameVerifier(); +				log.warn("HTTP client-builder deactivates SSL Host-name verification!"); +			} +			 +			final LayeredConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext , hostnameVerifier); +			httpClientBuilder.setSSLSocketFactory(sslSocketFactory ); +			 +		} catch (final NoSuchAlgorithmException e) { +			log.warn("HTTP client-builder can NOT initialze SSL-Context", e); +			 +		} + +		 +		log.info("HTTP client-builder successfuly initialized"); +		 +	} + + + +	 +	 +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/IHttpClientFactory.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/IHttpClientFactory.java new file mode 100644 index 00000000..1975fb52 --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/IHttpClientFactory.java @@ -0,0 +1,21 @@ +package at.gv.egiz.eaaf.core.impl.utils; + +import org.apache.http.impl.client.CloseableHttpClient; + +public interface IHttpClientFactory { + +	/** +	 * Return an instance of a Apache HTTP client that follows http redirects automatically +	 *  +	 * @return +	 */ +	CloseableHttpClient getHttpClient(); + +	/** +	 * Return an instance of a Apache HTTP client +	 * @param followRedirects  +	 * @return +	 */ +	CloseableHttpClient getHttpClient(boolean followRedirects); +	 +}
\ No newline at end of file diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/KeyStoreUtils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/KeyStoreUtils.java new file mode 100644 index 00000000..e3d74066 --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/KeyStoreUtils.java @@ -0,0 +1,199 @@ +/******************************************************************************* + * Copyright 2017 Graz University of Technology + * EAAF-Core Components has been developed in a cooperation between EGIZ,   + * A-SIT Plus, A-SIT, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/news/understanding-eupl-v12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *   + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + *******************************************************************************/ +/******************************************************************************* + *******************************************************************************/ +/******************************************************************************* + *******************************************************************************/ + + +package at.gv.egiz.eaaf.core.impl.utils; + +import java.io.BufferedInputStream; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; + +/** + * Utility for creating and loading key stores. + *  + * @author Paul Ivancsics + * @version $Id$ + */ +public class KeyStoreUtils { +	 +	/** +	 * JAVA KeyStore +	 */ +	private static final String KEYSTORE_TYPE_JKS = "JKS"; +	 +	/** +	 * PKCS12 KeyStore +	 */ +	private static final String KEYSTORE_TYPE_PKCS12 = "PKCS12"; +	 +	 + +  /** +   * Loads a key store from file. +   *  +   * @param keystoreType key store type +   * @param urlString URL of key store +   * @param password password protecting the key store +   * @return key store loaded +   * @throws IOException thrown while reading the key store from file +   * @throws GeneralSecurityException thrown while creating the key store +   */ +  public static KeyStore loadKeyStore( +    String keystoreType, +    String urlString, +    String password) +    throws IOException, GeneralSecurityException { + +    URL keystoreURL = new URL(urlString); +    InputStream in = keystoreURL.openStream(); +    return loadKeyStore(keystoreType, in, password); +  } +  /** +   * Loads a key store from an <code>InputStream</code>, and +   * closes the <code>InputStream</code>. +   *  +   * @param keystoreType key store type +   * @param in input stream +   * @param password password protecting the key store +   * @return key store loaded +   * @throws IOException thrown while reading the key store from the stream +   * @throws GeneralSecurityException thrown while creating the key store +   */ +  public static KeyStore loadKeyStore( +    String keystoreType, +    InputStream in, +    String password) +    throws IOException, GeneralSecurityException { + +    char[] chPassword = null; +    if (password != null) +      chPassword = password.toCharArray(); +    KeyStore ks = KeyStore.getInstance(keystoreType); +    ks.load(in, chPassword); +    in.close(); +    return ks; +  } +  /** +   * Creates a key store from X509 certificate files, aliasing them with +   * the index in the <code>String[]</code>, starting with <code>"0"</code>. +   *  +   * @param keyStoreType key store type +   * @param certFilenames certificate filenames +   * @return key store created +   * @throws IOException thrown while reading the certificates from file +   * @throws GeneralSecurityException thrown while creating the key store +   */ +  public static KeyStore createKeyStore( +    String keyStoreType, +    String[] certFilenames) +    throws IOException, GeneralSecurityException { + +    KeyStore ks = KeyStore.getInstance(keyStoreType); +    ks.load(null, null); +    for (int i = 0; i < certFilenames.length; i++) { +      Certificate cert = loadCertificate(certFilenames[i]); +      ks.setCertificateEntry("" + i, cert); +    } +    return ks; +  } + +  /** +   * Loads an X509 certificate from file. +   * @param certFilename filename +   * @return the certificate loaded +   * @throws IOException thrown while reading the certificate from file +   * @throws GeneralSecurityException thrown while creating the certificate +   */ +  private static Certificate loadCertificate(String certFilename) +    throws IOException, GeneralSecurityException { + +    FileInputStream in = new FileInputStream(certFilename); +    CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); +    Certificate cert = certFactory.generateCertificate(in); +    in.close(); +    return cert; +  } +   +  +	/** +	 * Loads a keyStore without knowing the keyStore type +	 * @param keyStorePath URL to the keyStore +	 * @param password Password protecting the keyStore +	 * @return keyStore loaded +	 * @throws KeyStoreException thrown if keyStore cannot be loaded +	 * @throws FileNotFoundException  +	 * @throws IOException  +	 */ +  public static KeyStore loadKeyStore(String keyStorePath, String password) throws KeyStoreException, IOException{ +		 +		//InputStream is = new FileInputStream(keyStorePath); +	  	URL keystoreURL = new URL(keyStorePath); +	    InputStream in = keystoreURL.openStream(); +		InputStream isBuffered = new BufferedInputStream(in);				 +		return loadKeyStore(isBuffered, password); +		 +	} +	 +	/** +	 * Loads a keyStore without knowing the keyStore type +	 * @param in input stream +	 * @param password Password protecting the keyStore +	 * @return keyStore loaded +	 * @throws KeyStoreException thrown if keyStore cannot be loaded +	 * @throws FileNotFoundException  +	 * @throws IOException  +	 */ +public static KeyStore loadKeyStore(InputStream is, String password) throws KeyStoreException, IOException{		 +		is.mark(1024*1024); +		KeyStore ks = null; +		try { +			try {				 +				ks = loadKeyStore(KEYSTORE_TYPE_PKCS12, is, password); +			} catch (IOException e2) { +				is.reset();				 +				ks = loadKeyStore(KEYSTORE_TYPE_JKS, is, password); +			} +		} catch(Exception e) {			 +			e.printStackTrace(); + +		} +		return ks;	 +						 +	} +   +	 + + +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/KeyValueUtils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/KeyValueUtils.java new file mode 100644 index 00000000..e753f19f --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/KeyValueUtils.java @@ -0,0 +1,375 @@ +/******************************************************************************* + * Copyright 2017 Graz University of Technology + * EAAF-Core Components has been developed in a cooperation between EGIZ,   + * A-SIT Plus, A-SIT, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/news/understanding-eupl-v12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *   + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + *******************************************************************************/ +/******************************************************************************* + *******************************************************************************/ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eaaf.core.impl.utils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; +import java.util.Set; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author tlenz + * + */ +public class KeyValueUtils { +	private static final Logger log = LoggerFactory.getLogger(KeyValueUtils.class); +	 +	public static final String KEY_DELIMITER = "."; +	public static final String CSV_DELIMITER = ","; +	public static final String KEYVVALUEDELIMITER = "="; +	public static final String DEFAULT_VALUE = "default"; +	 +	/** +	 * Convert Java properties into a Map<String, String> +	 * <br><br> +	 * <b>Important:</b> The key/values from properties must be of type String!  +	 *  +	 * @param properties +	 * @return +	 */ +	public static Map<String, String> convertPropertiesToMap(Properties properties) { +		return new HashMap<String, String>((Map) properties); +				 +		//INFO Java8 solution ;) +//				return properties.entrySet().stream().collect( +//			    Collectors.toMap( +//			            e -> e.getKey().toString(), +//			            e -> e.getValue().toString() +//			       ) +//			   ); +		 +	} +	 +	/** +	 * Extract the first child of an input key after a the prefix +	 *  +	 * @param key Full input key  +	 * @param prefix Prefix  +	 * @return Child key {String} if it exists or null +	 */ +	public static String getFirstChildAfterPrefix(String key, String prefix) {		 +		final String idAfterPrefix = removePrefixFromKey(key, prefix); +		if (idAfterPrefix != null) { +			final int index = idAfterPrefix.indexOf(KEY_DELIMITER); +			if (index > 0) { +				final String adding = idAfterPrefix.substring(0, index); +				if (!(adding.isEmpty())) { +					return adding; +					 +				} +			} else if (!(idAfterPrefix.isEmpty())) { +				return idAfterPrefix; +				 +			} +			 +		}					 +		return null; +	} +	 +	/** +	 * Extract the prefix from an input key +	 *  +	 * @param key Full input key +	 * @param suffix Suffix of this key +	 * @return Prefix {String} of the key or null if input key does not ends with postfix string  +	 */ +	public static String getPrefixFromKey(String key, String suffix) {			 +		if (key != null && suffix != null && key.endsWith(suffix)) { +			final String idPreforeSuffix = key.substring(0, key.length()-suffix.length());			 +			if (idPreforeSuffix.endsWith(KEY_DELIMITER)) +				return idPreforeSuffix.substring(0, idPreforeSuffix.length()-1); +			else +				return idPreforeSuffix; +		} +		return null; +		 +	} +	 +	/** +	 * Remove a prefix string from a key +	 *  +	 * @param key Full input key +	 * @param prefix Prefix, which should be removed +	 * @return The suffix of the input key or null if the input does not starts with the prefix +	 */ +	public static String removePrefixFromKey(String key, String prefix) { +		if (prefix == null) +			prefix = new String(); +		 +		if (key!=null && key.startsWith(prefix)) { +			String afterPrefix = key.substring(prefix.length());  +			final int index = afterPrefix.indexOf(KEY_DELIMITER); + +			if (index == 0) { +				afterPrefix = afterPrefix.substring(1); +				 +			} +			return afterPrefix; +			 +		}		 +		return null; +	} +	 +	/** +	 * Remove a prefix string from all keys in {Map<String, String>} of key/value pairs +	 *  +	 * @param keys Input data of key/value pairs +	 * @param prefix Prefix which should be removed +	 * @return {Map<String, String>} of key/value pairs without prefix in key, but never null +	 */ +	public static Map<String, String> removePrefixFromKeys(Map<String, String> keys, String prefix) { +		final Map<String, String> result = new HashMap<String, String>(); +		final Iterator<Entry<String, String>> interator = keys.entrySet().iterator(); +		while(interator.hasNext()) { +			final Entry<String, String> el = interator.next(); +			final String newKey = removePrefixFromKey(el.getKey(), prefix);			 +			if (StringUtils.isNotEmpty(newKey)) { +				result.put(newKey, el.getValue()); +			}			 +		} +		 +		return result; +	} + +	/** +	 * Get a subset of key/value pairs which starts with a prefix string +	 * The Prefix is removed from the key +	 *  +	 * @param keys Input data of key/value pairs +	 * @param prefix Prefix string +	 * @return {Map<String, String>} of key/value pairs without prefix in key, but never null +	 */ +	public static Map<String, String> getSubSetWithPrefix(Map<String, String> keys, String prefix) { +		return removePrefixFromKeys(keys, prefix); +	} +	 +	 +	/** +	 * Add a prefix to key/value pairs to make the key absolute according to key namespace convention +	 *  +	 * @param input Input key/value pairs which should be updated +	 * @param prefix Key prefix, which should be added if the key is not absolute +	 * @param absolutIdentifier Key identifier, which indicates an absolute key +	 * @return {Map<String, String>} of key/value pairs in which all keys are absolute but never null +	 */ +	public static Map<String, String> makeKeysAbsolut(Map<String, String> input, String prefix, String absolutIdentifier) { +		final Map<String, String> result = new HashMap<String, String>(); +		final Iterator<Entry<String, String>> interator = input.entrySet().iterator(); +		while(interator.hasNext()) { +			final Entry<String, String> el = interator.next(); +			if (!el.getKey().startsWith(absolutIdentifier)) { +				//key is not absolute -> add prefix +				result.put(prefix  +						+ KEY_DELIMITER +						+ el.getKey(),  +						el.getValue()); +								 +			} else { +				//key is absolute +				result.put(el.getKey(), el.getValue()); +			} +		} +		return result; +	} +	 +	/** +	 * Get the parent key string from an input key +	 *  +	 * @param key input key +	 * @return parent key or the empty String if no parent exists +	 */ +	public static String getParentKey(String key) { +		if (StringUtils.isNotEmpty(key)) { +			final int index = key.lastIndexOf(KEY_DELIMITER); +			if (index > 0) { +				return key.substring(0, index); +				 +			}			 +		} +		 +		return new String(); +	} + +	/** +	 * Find the highest free list counter +	 *  +	 * @param input Array of list keys +	 * @param listPrefix {String} prefix of the list +	 * @return {int} highest free list counter +	 */ +	public static int findNextFreeListCounter(String[] input, +			String listPrefix) { +		final List<Integer> counters = new ArrayList<Integer>(); +		if (input == null || input.length == 0) +			return 0; +		 +		else { +			for (final String key : input) { +				final String listIndex = getFirstChildAfterPrefix(key, listPrefix); +				counters.add(Integer.parseInt(listIndex)); +			 +			}		 +			Collections.sort(counters);		 +			return counters.get(counters.size()-1) + 1; +		} +	} + +	/** +	 * Find the highest free list counter +	 *  +	 * @param keySet {Set<String>} of list keys +	 * @param listPrefix {String} prefix of the list +	 * @return {int} highest free list counter +	 */ +	public static int findNextFreeListCounter(Set<String> keySet, +			String listPrefix) { +		if (keySet.isEmpty()) +			return 0; +		 +		final String[] array = new String[keySet.size()]; +		keySet.toArray(array); +		return findNextFreeListCounter(array, listPrefix); +	} +	 +	 +	/** +	 * Normalize a CSV encoded list of value of an key/value pair +	 *  +	 * This method removes all whitespace at the begin or the  +	 * end of CSV values and remove newLine signs at the end of value. +	 * The ',' is used as list delimiter +	 *  +	 * @param value CSV encoded input data +	 * @return normalized CSV encoded data or null if {value} is null or empty +	 */ +	public static String normalizeCSVValueString(String value) { +		String normalizedCodes = null; +		if (StringUtils.isNotEmpty(value)) {			 +			final String[] codes = value.split(CSV_DELIMITER); +			for (final String el: codes) { +				if (normalizedCodes == null) +					normalizedCodes = StringUtils.chomp(el.trim()); +				else +					normalizedCodes += "," + StringUtils.chomp(el.trim()); +				 +			} +		} +		return normalizedCodes; +	} +	 +	 +	/** +	 * Check a String if it is a comma separated list of values +	 *  +	 * This method uses the ',' as list delimiter. +	 *  +	 * @param value CSV encoded input data +	 * @return true if the input data contains a ',' and has more then 1 list element, otherwise false +	 */ +	public static boolean isCSVValueString(String value) { +		if (StringUtils.isNotEmpty(value)) {			 +			final String[] codes = value.split(CSV_DELIMITER); +			if (codes.length >= 2) { +				if (StringUtils.isNotEmpty(codes[1].trim())) +					return true; +				 +			}			 +		} +		 +		return false; +	} +	 +	/** +	 * Convert a CSV list to a List of CSV values +	 * <br><br> +	 * This method removes all whitespace at the begin or the  +	 * end of CSV values and remove newLine signs at the end of value. +	 * The ',' is used as list delimiter +	 *  +	 * @param csv CSV encoded input data +	 * @return List of CSV normalized values, but never null +	 */ +	@Nonnull +	public static List<String> getListOfCSVValues(@Nullable String csv) { +		final List<String> list = new ArrayList<String>(); +		if (StringUtils.isNotEmpty(csv)) {		 +			final String[] values = csv.split(CSV_DELIMITER); +			for (final String el: values) +				list.add(el.trim()); +		 +		} +		 +		return list; +	} +	 +	/** +	 * Convert a List of String elements to a Map of Key/Value pairs +	 * <br> +	 * Every List element used as a key/value pair and the '=' sign represents the delimiter between key and value  +	 *  +	 * @param elements List of key/value elements +	 * @return Map of Key / Value pairs, but never null +	 */ +	public static Map<String, String> convertListToMap(List<String> elements) { +		final Map<String, String> map = new HashMap<String, String>(); +		for (final String el : elements) { +			if (el.contains(KEYVVALUEDELIMITER)) { +				final String[] split = el.split(KEYVVALUEDELIMITER); +				map.put(split[0], split[1]); +				 +			} else +				log.debug("Key/Value Mapper: '" + el + "' contains NO '='. Ignore it."); +			 +		} +		 +		return map; +	} +	 +	/** +	 * This method remove all newline delimiter (\n or \r\n) from input data +	 *  +	 * @param value Input String +	 * @return Input String without newline characters +	 */ +	public static String removeAllNewlineFromString(String value) { +		return value.replaceAll("(\\t|\\r?\\n)+", ""); +		 +	} +	 +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/NodeIteratorAdapter.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/NodeIteratorAdapter.java new file mode 100644 index 00000000..ec57b92a --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/NodeIteratorAdapter.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * Copyright 2017 Graz University of Technology + * EAAF-Core Components has been developed in a cooperation between EGIZ,   + * A-SIT Plus, A-SIT, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/news/understanding-eupl-v12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *   + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + *******************************************************************************/ +/******************************************************************************* + *******************************************************************************/ +/******************************************************************************* + *******************************************************************************/ + + +package at.gv.egiz.eaaf.core.impl.utils; + +import java.util.ListIterator; + +import org.w3c.dom.DOMException; +import org.w3c.dom.Node; +import org.w3c.dom.traversal.NodeFilter; +import org.w3c.dom.traversal.NodeIterator; + +/** + * A <code>NodeIterator</code> implementation based on a + * <code>ListIterator</code>. + *  + * @see java.util.ListIterator + * @see org.w3c.dom.traversal.NodeIterator + *  + */ +public class NodeIteratorAdapter implements NodeIterator { + +  /** The <code>ListIterator</code> to wrap. */ +  private ListIterator nodeIterator; + +  /** +   * Create a new <code>NodeIteratorAdapter</code>. +   * @param nodeIterator The <code>ListIterator</code> to iterate over. +   */ +  public NodeIteratorAdapter(ListIterator nodeIterator) { +    this.nodeIterator = nodeIterator; +  } + +  /** +   * @see org.w3c.dom.traversal.NodeIterator#getRoot() +   */ +  public Node getRoot() { +    return null; +  } + +  /** +   * @see org.w3c.dom.traversal.NodeIterator#getWhatToShow() +   */ +  public int getWhatToShow() { +    return NodeFilter.SHOW_ALL; +  } + +  /** +   * @see org.w3c.dom.traversal.NodeIterator#getFilter() +   */ +  public NodeFilter getFilter() { +    return null; +  } + +  /** +   * @see org.w3c.dom.traversal.NodeIterator#getExpandEntityReferences() +   */ +  public boolean getExpandEntityReferences() { +    return false; +  } + +  /** +   * @see org.w3c.dom.traversal.NodeIterator#nextNode() +   */ +  public Node nextNode() throws DOMException { +    if (nodeIterator.hasNext()) { +      return (Node) nodeIterator.next(); +    } +    return null; +  } + +  /** +   * @see org.w3c.dom.traversal.NodeIterator#previousNode() +   */ +  public Node previousNode() throws DOMException { +    if (nodeIterator.hasPrevious()) { +      return (Node) nodeIterator.previous(); +    } +    return null; +  } + +  /** +   * @see org.w3c.dom.traversal.NodeIterator#detach() +   */ +  public void detach() { +  } + +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/NodeListAdapter.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/NodeListAdapter.java new file mode 100644 index 00000000..69045aaa --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/NodeListAdapter.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright 2017 Graz University of Technology + * EAAF-Core Components has been developed in a cooperation between EGIZ,   + * A-SIT Plus, A-SIT, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/news/understanding-eupl-v12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *   + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + *******************************************************************************/ +/******************************************************************************* + *******************************************************************************/ +/******************************************************************************* + *******************************************************************************/ + + +package at.gv.egiz.eaaf.core.impl.utils; + +import java.util.List; + +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * A <code>NodeList</code> implementation based on a <code>List</code>. + *  + * @see java.util.List + * @see org.w3c.dom.NodeList + */ + +public class NodeListAdapter implements NodeList { +  /** The <code>List</code> to wrap. */ +  private List nodeList; +   +  /** +   * Create a new <code>NodeListAdapter</code>. +   *  +   * @param nodeList The <code>List</code> containing the nodes.  +   */ +  public NodeListAdapter(List nodeList) { +    this.nodeList = nodeList; +  } + +  /** +   * @see org.w3c.dom.NodeList#item(int) +   */ +  public Node item(int index) { +    return (Node) nodeList.get(index); +  } + +  /** +   * @see org.w3c.dom.NodeList#getLength() +   */ +  public int getLength() { +    return nodeList.size(); +  } + +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/Random.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/Random.java new file mode 100644 index 00000000..e236b3a9 --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/Random.java @@ -0,0 +1,176 @@ +/******************************************************************************* + * Copyright 2017 Graz University of Technology + * EAAF-Core Components has been developed in a cooperation between EGIZ,   + * A-SIT Plus, A-SIT, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/news/understanding-eupl-v12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *   + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + *******************************************************************************/ + +package at.gv.egiz.eaaf.core.impl.utils; + + +import java.nio.ByteBuffer; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.apache.commons.codec.binary.Hex; +import org.apache.commons.lang3.ArrayUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.eaaf.core.impl.idp.process.support.SecureRandomHolder; + + +/** + * Random number generator used to generate ID's + * @author Paul Ivancsics + * @version $Id$ + */ +public class Random { +	private static final Logger log = LoggerFactory.getLogger(Random.class); +	 +	private final static char[] allowedPreFix =  +		{'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z', +		 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}; +	private static final DateFormat dateFormater = new SimpleDateFormat("yyyyddMM"); +	 +  /** random number generator used */ +	private static SecureRandom random; +	//private static SeedGenerator seedgenerator;	 +		 +	static { +		try { +			random = SecureRandom.getInstance("SHA256PRNG-FIPS186"); +			 +		} catch (NoSuchAlgorithmException e) { +			log.warn("Can NOT initialize SecureRandom with: 'SHA256PRNG-FIPS186'. Use 'StrongSecureRandom' as backup"); +			random = SecureRandomHolder.getInstance(); +			 +		} +		 +		 +		//random = iaik.security.random.SHA256FIPS186Random.getDefault();		 +	} +	 +	/** +	 * Generate a unique process reference-value [160bit], which always starts with a letter +	 * <br> +	 * This unique ID consists of single letter, a 64bit date String[yyyyddMM], +	 *  and a 88bit random value.   +	 *  +	 * @return 160bit ID, which is hex encoded +	 */ +	public static String nextProcessReferenceValue() {		 +		//pre-process all three parts of a unique reference value +		String now = dateFormater.format(new Date()); //8 bytes = 64bit +		byte[] randValue = nextByteRandom(11); +		char preFix = allowedPreFix[Math.abs(random.nextInt() % allowedPreFix.length)]; +	  +		//generate ID +		String returnValue = preFix + new String(Hex.encodeHex(ArrayUtils.addAll(now.getBytes(), randValue))); // 20 bytes = 160 bits +		if (returnValue.length() > 40) +			return returnValue.substring(0, 40); +		else +			return returnValue; +			 +	} +	 +	 +	 +	/** +	 * Creates a new random number [256bit], and encode it as hex value. +	 *  +	 * @return random hex encoded value [256bit] +	 */ +	public static String nextHexRandom32() { +		return new String(Hex.encodeHex(nextByteRandom(32))); // 32 bytes = 256 bits +		 +	} +	 +	/** +	 * Creates a new random number [128bit], and encode it as hex value. +	 *  +	 * @return random hex encoded value [128bit] +	 */ +	public static String nextHexRandom16() { +		return new String(Hex.encodeHex(nextByteRandom(16))); // 16 bytes = 128 bits +		 +	} +	 +	  /** +	   * Creates a new random number [64bit], to be used as an ID. +	   *  +	   * @return random long as a String [64bit] +	   */ +	public static String nextLongRandom() {	   +		return "".concat(String.valueOf(Math.abs(generateLongRandom(32)))); // 32 bytes = 256 bits	 +			 +	  } +		 +  /** +   * Creates a new random number, to be used as an ID. +   *  +   * @return random long as a String [64bit] +   */ +	@Deprecated +  public static String nextRandom() {	   +	long l = ByteBuffer.wrap(nextByteRandom(32)).getLong(); // 32 bytes = 256 bits +	return "" + Math.abs(l); +		 +  } +   +/** + * Creates a new random byte[] + * 	 + * @param size Size of random number in byte + * @return + */ +public static byte[] nextBytes(int size) { +	return  nextByteRandom(size); +	 +} +   +  public static void seedRandom() { +	  //TODO: implement reflection on IAIK Seed generator	   +//      seedgenerator = iaik.security.random.AutoSeedGenerator.getDefault();   +//	  if (seedgenerator.seedAvailable()) +//		  random.setSeed(seedgenerator.getSeed()); +	   +	  random.setSeed(System.nanoTime()); +  } +   +	private static long generateLongRandom(int size) { +		return ByteBuffer.wrap(nextByteRandom(size)).getLong(); 	 +	} +   +  /** +   * Generate a new random number +   *  +   * @param size Size of random number in byte +   * @return +   */ +  private static synchronized byte[] nextByteRandom(int size) { +	  byte[] b = new byte[size]; +	  random.nextBytes(b);			  +	  return b; +	   +  } +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/SecurePendingRequestIdGenerationStrategy.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/SecurePendingRequestIdGenerationStrategy.java new file mode 100644 index 00000000..f0ef9b38 --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/SecurePendingRequestIdGenerationStrategy.java @@ -0,0 +1,214 @@ +package at.gv.egiz.eaaf.core.impl.utils; + +import java.io.UnsupportedEncodingException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.util.Arrays; +import java.util.Base64; + +import javax.annotation.PostConstruct; +import javax.crypto.Mac; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; + +import org.apache.commons.lang3.StringUtils; +import org.joda.time.DateTime; +import org.joda.time.DurationFieldType; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; + +import at.gv.egiz.eaaf.core.api.idp.IConfiguration; +import at.gv.egiz.eaaf.core.api.utils.IPendingRequestIdGenerationStrategy; +import at.gv.egiz.eaaf.core.exceptions.EAAFConfigurationException; +import at.gv.egiz.eaaf.core.exceptions.EAAFException; +import at.gv.egiz.eaaf.core.exceptions.EAAFIllegalStateException; +import at.gv.egiz.eaaf.core.exceptions.PendingReqIdValidationException; + +/** + * PendingRequestId generation strategy based on signed tokens that facilitates extended token validation + *  + * @author tlenz + * + */ +public class SecurePendingRequestIdGenerationStrategy implements IPendingRequestIdGenerationStrategy { +	private static final Logger log = LoggerFactory.getLogger(SecurePendingRequestIdGenerationStrategy.class); +	 +	@Autowired(required=true) IConfiguration baseConfig; +	 +	public static final String CONFIG_PROP_PENDINGREQUESTID_DIGIST_SECRET = "core.pendingrequestid.digist.secret"; +	public static final String CONFIG_PROP_PENDINGREQUESTID_DIGIST_ALGORITHM = "core.pendingrequestid.digist.algorithm"; +	public static final String CONFIG_PROP_PENDINGREQUESTID_MAX_LIFETIME = "core.pendingrequestid.maxlifetime"; +	 +	public static final String DEFAULT_PENDINGREQUESTID_DIGIST_ALGORITHM = "HmacSHA256"; +	public static final String DEFAULT_PENDINGREQUESTID_MAX_LIFETIME = "300"; +	 +	private static final int ENCODED_TOKEN_PARTS = 3; +	private static final String TOKEN_SEPARATOR = "|"; +	private static final DateTimeFormatter TOKEN_TEXTUAL_DATE_FORMAT = +            DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss SSS"); +		 +	private int maxPendingRequestIdLifeTime = 300; +	private final int maxPendingReqIdSize = 1024; +	private String digistAlgorithm = null; +	private SecretKey key = null; +	private final byte[] salt = "notRequiredInThisScenario".getBytes(); +		 +	@Override +	public String generateExternalPendingRequestId() throws EAAFException { +		try { +			final String toSign = buildInternalToken(Random.nextLongRandom(), DateTime.now()); +			final StringBuilder externalPendingRequestId= new StringBuilder(); +			externalPendingRequestId.append(toSign); +			externalPendingRequestId.append(TOKEN_SEPARATOR); +			externalPendingRequestId.append(Base64.getEncoder().encodeToString(calculateHMAC(toSign)));									 +			return Base64.getUrlEncoder().encodeToString(externalPendingRequestId.toString().getBytes("UTF-8")); +			 +		} catch (final UnsupportedEncodingException e) { +			throw new EAAFException("internal.99", new Object[] {e.getMessage()}, e); +			 +		} +		 +	} + +	@Override +	public String getPendingRequestIdWithOutChecks(String externalPendingReqId) throws PendingReqIdValidationException { +		final String[] tokenElements = extractTokens(externalPendingReqId); +		return tokenElements[1]; +		 +	} +	 +	@Override +	public String validateAndGetPendingRequestId(String externalPendingReqId) throws PendingReqIdValidationException {				 +		try { +			final String[] tokenElements = extractTokens(externalPendingReqId);						 +			final String internalPendingReqId = tokenElements[1]; +			final DateTime timeStamp = TOKEN_TEXTUAL_DATE_FORMAT.parseDateTime(tokenElements[0]); +							 +			log.trace("Checking HMAC from externalPendingReqId ... "); +			final byte[] tokenDigest = Base64.getDecoder().decode(tokenElements[2]); +			final byte[] refDigist = calculateHMAC(buildInternalToken(internalPendingReqId, timeStamp)); +			if (!Arrays.equals(tokenDigest, refDigist)) { +				log.warn("Digest of Token does NOT match"); +				log.debug("Token: {} | Ref: {}", tokenDigest, refDigist); +				throw new  PendingReqIdValidationException(null, "Digest of pendingRequestId does NOT match"); +			 +			}				 +			log.debug("PendingRequestId HMAC digest check successful"); +					 +			log.trace("Checking valid period ... "); +			final DateTime now = DateTime.now(); +			if (timeStamp.withFieldAdded( +					DurationFieldType.seconds(), maxPendingRequestIdLifeTime).isBefore(now)) { +				log.warn("Token exceeds the valid period"); +				log.debug("Token: {} | Now: {}", timeStamp, now ); +				throw new PendingReqIdValidationException(internalPendingReqId, "PendingRequestId exceeds the valid period"); +			 +			} +			log.debug("Token valid-period check successful"); +		 +			return internalPendingReqId; +				 +			 +		} catch (final IllegalArgumentException | EAAFIllegalStateException e) {				 +			log.warn("Token is NOT a valid String. Msg: {}", e.getMessage()); +			log.debug("TokenValue: {}", externalPendingReqId); +			throw new PendingReqIdValidationException(null, "PendingReqId is NOT a valid String", e); +		 +		}    +	} +	 +	@NonNull +	private String[] extractTokens(@Nullable String externalPendingReqId) throws PendingReqIdValidationException { +		if (StringUtils.isEmpty(externalPendingReqId)) { +			log.info("PendingReqId is 'null' or empty"); +			throw new PendingReqIdValidationException(null, "PendingReqId is 'null' or empty"); +			 +		} +		 +		log.trace("RAW external pendingReqId: {}", externalPendingReqId); +		final byte[] externalPendingReqIdBytes = Base64.getUrlDecoder().decode(externalPendingReqId); +		 +		if (externalPendingReqIdBytes.length > maxPendingReqIdSize) { +			log.warn("pendingReqId size exceeds {}", maxPendingReqIdSize); +			throw new PendingReqIdValidationException(null, "pendingReqId exceeds max.size: " + maxPendingReqIdSize); +			 +		} +		 +		final String stringToken = new String(externalPendingReqIdBytes); +		if (StringUtils.countMatches(stringToken, TOKEN_SEPARATOR) == ENCODED_TOKEN_PARTS - 1) { +			final String[] tokenElements = StringUtils.split(stringToken,  +					TOKEN_SEPARATOR, ENCODED_TOKEN_PARTS); +			return tokenElements; +			 +		} else { +			log.warn("PendingRequestId has an unvalid format"); +			log.debug("PendingRequestId: {}", stringToken);				 +			throw new PendingReqIdValidationException(null, "PendingReqId has an unvalid format"); +			 +		} +						 +	} +	 +	 +	@PostConstruct +	private void initialize() throws EAAFConfigurationException { +		log.debug("Initializing " + this.getClass().getName() + " ... "); +		 +		final String pendingReqIdDigistSecret = baseConfig.getBasicConfiguration(CONFIG_PROP_PENDINGREQUESTID_DIGIST_SECRET); +		if (StringUtils.isEmpty(pendingReqIdDigistSecret)) +			throw new EAAFConfigurationException("config.08", new Object[] {CONFIG_PROP_PENDINGREQUESTID_DIGIST_SECRET}); +		 +		digistAlgorithm = baseConfig.getBasicConfiguration(CONFIG_PROP_PENDINGREQUESTID_DIGIST_ALGORITHM, DEFAULT_PENDINGREQUESTID_DIGIST_ALGORITHM);				 +		 +		maxPendingRequestIdLifeTime = Integer.valueOf( +				baseConfig.getBasicConfiguration(CONFIG_PROP_PENDINGREQUESTID_MAX_LIFETIME, DEFAULT_PENDINGREQUESTID_MAX_LIFETIME)); +		 +		try { +			final SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WITHHMACSHA256"); +			final KeySpec spec = new PBEKeySpec(pendingReqIdDigistSecret.toCharArray(), salt, 10000, 128); +			key = keyFactory.generateSecret(spec); +			 +			 +		} catch (NoSuchAlgorithmException | InvalidKeySpecException e) { +			log.error("Can NOT initialize TokenService with configuration object", e); +			throw new EAAFConfigurationException("config.09",  +					new Object[] {	CONFIG_PROP_PENDINGREQUESTID_DIGIST_SECRET,  +									"Can NOT generate HMAC key"},  +						e); +			 +		} +		 +		log.info(this.getClass().getName() + " initialized with digistAlg: {} and maxLifeTime: {}", digistAlgorithm, maxPendingRequestIdLifeTime); +		 +	} +	 +	private String buildInternalToken(String internalPendingReqId, DateTime now) { +		return new StringBuilder() +				.append(TOKEN_TEXTUAL_DATE_FORMAT.print(now)) +				.append(TOKEN_SEPARATOR) +				.append(internalPendingReqId).toString();   +	} +	 +	private byte[] calculateHMAC(String toSign) throws EAAFIllegalStateException { +		try {			 +			final Mac mac = Mac.getInstance(digistAlgorithm); +			mac.init(key); +			return mac.doFinal(toSign.getBytes("UTF-8")); +			 +		} catch (UnsupportedEncodingException | NoSuchAlgorithmException | InvalidKeyException e) { +			log.error("Can NOT generate secure pendingRequestId", e); +			throw new EAAFIllegalStateException(new Object[] {"Can NOT caluclate digist for secure pendingRequestId"}, e); +			 +		} +		 +	} + +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/ServletUtils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/ServletUtils.java new file mode 100644 index 00000000..38e873e2 --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/ServletUtils.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright 2017 Graz University of Technology + * EAAF-Core Components has been developed in a cooperation between EGIZ,   + * A-SIT Plus, A-SIT, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/news/understanding-eupl-v12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *   + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + *******************************************************************************/ + +package at.gv.egiz.eaaf.core.impl.utils; + +import javax.servlet.http.HttpServletRequest; + +public class ServletUtils { +   +	 +  public static String getBaseUrl( HttpServletRequest request ) { +	    if ( ( request.getServerPort() == 80 ) || +	         ( request.getServerPort() == 443 ) ) +	      return request.getScheme() + "://" + +	             request.getServerName() + +	             request.getContextPath(); +	    else +	      return request.getScheme() + "://" + +	             request.getServerName() + ":" + request.getServerPort() + +	             request.getContextPath(); +	  } +   +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/SimplePendingRequestIdGenerationStrategy.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/SimplePendingRequestIdGenerationStrategy.java new file mode 100644 index 00000000..6b8fe9b7 --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/SimplePendingRequestIdGenerationStrategy.java @@ -0,0 +1,38 @@ +package at.gv.egiz.eaaf.core.impl.utils; + +import org.apache.commons.lang3.StringUtils; + +import at.gv.egiz.eaaf.core.api.utils.IPendingRequestIdGenerationStrategy; +import at.gv.egiz.eaaf.core.exceptions.PendingReqIdValidationException; + +/** + * Simple pendingRequestId generation strategy that facilitates no extended validation + *  + * @author tlenz + * + */ +public class SimplePendingRequestIdGenerationStrategy implements IPendingRequestIdGenerationStrategy { + +	@Override +	public String generateExternalPendingRequestId() { +		return Random.nextLongRandom(); +		 +	} + +	@Override +	public String validateAndGetPendingRequestId(String pendingReqId) throws PendingReqIdValidationException { +		return getPendingRequestIdWithOutChecks(pendingReqId); +				 +	} + +	@Override +	public String getPendingRequestIdWithOutChecks(String externalPendingReqId) throws PendingReqIdValidationException { +		if (StringUtils.isEmpty(externalPendingReqId)) +			throw new PendingReqIdValidationException(externalPendingReqId, "PendingRequestId is empty or null"); +		 +		 +		 +		return externalPendingReqId; +	} + +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/StreamUtils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/StreamUtils.java new file mode 100644 index 00000000..530da777 --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/StreamUtils.java @@ -0,0 +1,201 @@ +/******************************************************************************* + * Copyright 2017 Graz University of Technology + * EAAF-Core Components has been developed in a cooperation between EGIZ,   + * A-SIT Plus, A-SIT, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/news/understanding-eupl-v12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *   + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + *******************************************************************************/ +/******************************************************************************* + *******************************************************************************/ +/******************************************************************************* + *******************************************************************************/ + + +package at.gv.egiz.eaaf.core.impl.utils; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintStream; + +/** + * Utility methods for streams. + *  + * @author Patrick Peck + * @version $Id$ + */ +public class StreamUtils { +   +  /** +   * Compare the contents of two <code>InputStream</code>s. +   *  +   * @param is1 The 1st <code>InputStream</code> to compare. +   * @param is2 The 2nd <code>InputStream</code> to compare. +   * @return boolean <code>true</code>, if both streams contain the exactly the +   * same content, <code>false</code> otherwise. +   * @throws IOException An error occurred reading one of the streams. +   */ +  public static boolean compareStreams(InputStream is1, InputStream is2)  +    throws IOException { +       +    byte[] buf1 = new byte[256]; +    byte[] buf2 = new byte[256]; +    int length1; +    int length2; +   +    try { +      while (true) { +        length1 = is1.read(buf1); +        length2 = is2.read(buf2); +         +        if (length1 != length2) { +          return false; +        } +        if (length1 <= 0) { +          return true; +        } +        if (!compareBytes(buf1, buf2, length1)) { +          return false; +        } +      } +    } catch (IOException e) { +      throw e; +    } finally { +      // close both streams +      try { +        is1.close(); +        is2.close(); +      } catch (IOException e) { +        // ignore this +      } +    } +  } +   +  /** +   * Compare two byte arrays, up to a given maximum length. +   *  +   * @param b1 1st byte array to compare. +   * @param b2 2nd byte array to compare. +   * @param length The maximum number of bytes to compare. +   * @return <code>true</code>, if the byte arrays are equal, <code>false</code> +   * otherwise. +   */ +  private static boolean compareBytes(byte[] b1, byte[] b2, int length) { +    if (b1.length != b2.length) { +      return false; +    } +   +    for (int i = 0; i < b1.length && i < length; i++) { +      if (b1[i] != b2[i]) { +        return false; +      } +    } +   +    return true; +  } + +  /** +   * Reads a byte array from a stream. +   * @param in The <code>InputStream</code> to read. +   * @return The bytes contained in the given <code>InputStream</code>. +   * @throws IOException on any exception thrown +   */ +  public static byte[] readStream(InputStream in) throws IOException { + +    ByteArrayOutputStream out = new ByteArrayOutputStream(); +    copyStream(in, out, null); +		   +		/*   +    ByteArrayOutputStream out = new ByteArrayOutputStream(); +    int b; +    while ((b = in.read()) >= 0) +      out.write(b); +     +    */ +    in.close(); +    return out.toByteArray(); +  } + +  /** +   * Reads a <code>String</code> from a stream, using given encoding. +   * @param in The <code>InputStream</code> to read. +   * @param encoding The character encoding to use for converting the bytes +   * of the <code>InputStream</code> into a <code>String</code>. +   * @return The content of the given <code>InputStream</code> converted into +   * a <code>String</code>. +   * @throws IOException on any exception thrown +   */ +  public static String readStream(InputStream in, String encoding) throws IOException { +    ByteArrayOutputStream out = new ByteArrayOutputStream(); +    copyStream(in, out, null); + +    /* +    ByteArrayOutputStream out = new ByteArrayOutputStream(); +    int b; +    while ((b = in.read()) >= 0) +      out.write(b); +      */ +    in.close(); +    return out.toString(encoding); +  } +   +  /** +   * Reads all data (until EOF is reached) from the given source to the  +   * destination stream. If the destination stream is null, all data is dropped. +   * It uses the given buffer to read data and forward it. If the buffer is  +   * null, this method allocates a buffer. +   * +   * @param source The stream providing the data. +   * @param destination The stream that takes the data. If this is null, all +   *                    data from source will be read and discarded. +   * @param buffer The buffer to use for forwarding. If it is null, the method +   *               allocates a buffer. +   * @exception IOException If reading from the source or writing to the  +   *                        destination fails. +   */ +  private static void copyStream(InputStream source, OutputStream destination, byte[] buffer) throws IOException { +    if (source == null) { +      throw new NullPointerException("Argument \"source\" must not be null."); +    } +    if (buffer == null) { +      buffer = new byte[8192]; +    } +     +    if (destination != null) { +      int bytesRead; +      while ((bytesRead = source.read(buffer)) >= 0) { +        destination.write(buffer, 0, bytesRead); +      } +    } else { +      while (source.read(buffer) >= 0); +    }     +  } +   +  /** +   * Gets the stack trace of the <code>Throwable</code> passed in as a string. +   * @param t The <code>Throwable</code>. +   * @return a String representing the stack trace of the <code>Throwable</code>. +   */ +  public static String getStackTraceAsString(Throwable t) +  { +    ByteArrayOutputStream stackTraceBIS = new ByteArrayOutputStream(); +    t.printStackTrace(new PrintStream(stackTraceBIS)); +    return new String(stackTraceBIS.toByteArray()); +  } +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/TransactionIDUtils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/TransactionIDUtils.java new file mode 100644 index 00000000..2e016848 --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/TransactionIDUtils.java @@ -0,0 +1,101 @@ +/******************************************************************************* + * Copyright 2017 Graz University of Technology + * EAAF-Core Components has been developed in a cooperation between EGIZ,   + * A-SIT Plus, A-SIT, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/news/understanding-eupl-v12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *   + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + *******************************************************************************/ +/******************************************************************************* + *******************************************************************************/ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eaaf.core.impl.utils; + + +import at.gv.egiz.eaaf.core.api.IRequest; + +/** + * @author tlenz + * + */ +public class TransactionIDUtils { + +	  //MDC variables for logging +	  public static final String MDC_TRANSACTION_ID = "transactionId"; +	  public static final String MDC_SESSION_ID = "sessionId"; +	  public static final String MDC_SERVICEPROVIDER_ID = "oaId"; +	 +	/** +	 * Set all MDC variables from pending request to this threat context<br> +	 * These includes SessionID, TransactionID, and unique service-provider identifier +	 *  +	 * @param pendingRequest +	 */ +	public static void setAllLoggingVariables(IRequest pendingRequest) { +		setTransactionId(pendingRequest.getUniqueTransactionIdentifier()); +		setSessionId(pendingRequest.getUniqueSessionIdentifier()); +		setServiceProviderId(pendingRequest.getServiceProviderConfiguration().getUniqueIdentifier()); +		 +	} +	 +	/** +	 * Remove all MDC variables from this threat context +	 *  +	 */ +	public static void removeAllLoggingVariables() { +		removeSessionId(); +		removeTransactionId(); +		removeServiceProviderId(); +		 +	} +	 +	 +	public static void setServiceProviderId(String oaUniqueId) {		 +		org.slf4j.MDC.put(MDC_SERVICEPROVIDER_ID, oaUniqueId); +		 +	} +	 +	public static void removeServiceProviderId() { +		org.slf4j.MDC.remove(MDC_SERVICEPROVIDER_ID); +		 +	} +	 +	public static void setTransactionId(String pendingRequestID) {	    +		org.slf4j.MDC.put(MDC_TRANSACTION_ID,  +				"TID-" + pendingRequestID); +				     +	} +		 +	public static void removeTransactionId() { +		org.slf4j.MDC.remove(MDC_TRANSACTION_ID); +		 +	} +		 +	public static void setSessionId(String uniqueSessionId) {	  	 +		org.slf4j.MDC.put(MDC_SESSION_ID,  +				"SID-" + uniqueSessionId); +				     +	} +		 +	public static void removeSessionId() { +		org.slf4j.MDC.remove(MDC_SESSION_ID); +		 +	} +	 +	 +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/X509Utils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/X509Utils.java new file mode 100644 index 00000000..b3fb42c4 --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/X509Utils.java @@ -0,0 +1,62 @@ +package at.gv.egiz.eaaf.core.impl.utils; + +import java.security.cert.X509Certificate; +import java.util.List; + +import javax.security.auth.x500.X500Principal; + +public class X509Utils { + +	  /** +	   * Sorts the Certificate Chain by IssuerDN and SubjectDN. The [0]-Element should be the Hostname, +	   * the last Element should be the Root Certificate. +	   *  +	   * @param certs +	   *          The first element must be the correct one. +	   * @return sorted Certificate Chain +	   */ +	  public static List<X509Certificate> sortCertificates( +		      List<X509Certificate> certs) +		  { +		    int length = certs.size(); +		    if (certs.size() <= 1) +		    { +		      return certs; +		    } + +		    for (X509Certificate cert : certs) +		    { +		      if (cert == null) +		      { +		        throw new NullPointerException(); +		      } +		    } + +		    for (int i = 0; i < length; i++) +		    { +		      boolean found = false; +		      X500Principal issuer = certs.get(i).getIssuerX500Principal(); +		      for (int j = i + 1; j < length; j++) +		      { +		        X500Principal subject = certs.get(j).getSubjectX500Principal(); +		        if (issuer.equals(subject)) +		        { +		          // sorting necessary? +		          if (i + 1 != j) +		          { +		            X509Certificate tmp = certs.get(i + 1); +		            certs.set(i + 1, certs.get(j)); +		            certs.set(j, tmp); +		          } +		          found = true; +		        } +		      } +		      if (!found) +		      { +		        break; +		      } +		    } + +		    return certs; +		} +} | 
