Hey, are you interested in collaborating with other technophiles and artists on projects, presenting/attending workshops on the awesome toys/art/hacks you've created - or will create !! -- or using space and equipment you might not have at home?
I have just the place in mind!! :) I'm a member of Site 3 coLaboratory - we're opening shop at Bloor and Ossington next month - and we're looking for members, associate members and cool collaborators!
Our members are former Toronto Hacklab folk, crazy artists, engineers,
physicists, special-effects guys, pyrotechs, leather and metalworkers.
If this seems interesting, contact me!
Wednesday, June 9, 2010
Wednesday, May 5, 2010
low tech security
We're not great at choosing and remembering secure passwords, admit it?!
While my wordly combinations of aussie slang / l33t are contributing (marginally) to making my online life more secure ;) - I've been embarrassingly compromised more than once. Thus I would give anything for an easy way to meet the ridiculous password requirements and not get tripped up by my nonsensical security answers!
Enter the grid solution! I've seen this in commercial enterprise, but this example is a free, low-tech password vault.
There are some people drawbacks; starting at corner/edges, reusing passwords. However, assuming you're the kind of person who chooses this solution, let's assume you'd avoid this ;)
If you turn the solution about, and start challenging with grid co-ordinates, you're coming close to infringing on patented 2nd-factor authentication from Entrust. Although if you go down that road, why don't we start challenging with connect-the-dots, next item in the series, etc ;D
In personal news, I discovered the game NETHACK! today.
While my wordly combinations of aussie slang / l33t are contributing (marginally) to making my online life more secure ;) - I've been embarrassingly compromised more than once. Thus I would give anything for an easy way to meet the ridiculous password requirements and not get tripped up by my nonsensical security answers!
Enter the grid solution! I've seen this in commercial enterprise, but this example is a free, low-tech password vault.
There are some people drawbacks; starting at corner/edges, reusing passwords. However, assuming you're the kind of person who chooses this solution, let's assume you'd avoid this ;)
If you turn the solution about, and start challenging with grid co-ordinates, you're coming close to infringing on patented 2nd-factor authentication from Entrust. Although if you go down that road, why don't we start challenging with connect-the-dots, next item in the series, etc ;D
In personal news, I discovered the game NETHACK! today.
Sunday, March 14, 2010
Security in web application design
Security is something I think about constantly. It's part of the "unwritten" requirements. We usually only receive requirements for functionality, i.e. "the system must ..." but the unspoken agreement is that the functions will be usuable and secure.
Usability is a whole article, let's focus on where security fits into user-interaction end of design.
What are we concerned about? User Input, malicious or otherwise.
Starting with web input, three things we can do: reduce, restrict and sanitize.
Reduce: data from forms or URL parameters is not trusted, don't allow it unless you need to! Users are a trusting bunch, don't let them down: ENFORCE SSL. Don't even allow http access.
Restrict: Input can be restricted to a certain set; length, character format, value options. Value options are safest, instead of directly passing on input, convert it to one or more values in a server-side list. Examples include address fields, product codes, etc.
Setting javascript or form validation, e.g. using maxlength or specifying select options, means nothing if we don't enforce validation on the server!
Compare expectations to input instead of using it directly;
if ("expectedData".equals(request.getParameter("data")) {}
Not:
String data = request.getParameter("data");
Sanitize: If you must pass on user input, then sanitize. Here's a simple trick, use Commons Validation library: check every field for alphanumeric only.
There's a list of XSS protection tricks at OWASP.
Authentication
Do you use cookies for session authentication? If so, are you using them well?
Use the maximum amount of cookie information to protect your users.
Usability is a whole article, let's focus on where security fits into user-interaction end of design.
What are we concerned about? User Input, malicious or otherwise.
Starting with web input, three things we can do: reduce, restrict and sanitize.
Reduce: data from forms or URL parameters is not trusted, don't allow it unless you need to! Users are a trusting bunch, don't let them down: ENFORCE SSL. Don't even allow http access.
Restrict: Input can be restricted to a certain set; length, character format, value options. Value options are safest, instead of directly passing on input, convert it to one or more values in a server-side list. Examples include address fields, product codes, etc.
Setting javascript or form validation, e.g. using maxlength or specifying select options, means nothing if we don't enforce validation on the server!
Compare expectations to input instead of using it directly;
if ("expectedData".equals(request.getParameter("data")) {}
Not:
String data = request.getParameter("data");
Sanitize: If you must pass on user input, then sanitize. Here's a simple trick, use Commons Validation library: check every field for alphanumeric only.
There's a list of XSS protection tricks at OWASP.
Authentication
Do you use cookies for session authentication? If so, are you using them well?
Use the maximum amount of cookie information to protect your users.
- Restrict the cookie to your own domain
- use the secure flag (of course you're using HTTPS)
- restrict to HTTP only, requires JDK 6 - see http://www.owasp.org/index.php/HTTPOnly
Sunday, March 7, 2010
if I didn't know how good I was at math, would I have been as good at anything?
I'm drinking coffee and reading Scientific American - two of life's inestimable pleasures - and I came across the article "Numbers War: School Battles Heat Up Again in the Traditional versus Reform-Math Debate"
Imagine if they stopped teaching algebra, geometry, and polynomials in high schools - would it be a real detriment to society?
Imagine the fun we could have if we looked at the topics which have reference to real world situations? The wave function can be made fun to learn with the right teaching!
My personal beef with mathematical education is the lack of choice. By age 12-13 families and students are mostly sure which broad path to look forward to: academic or applied. Aren't they?
I cannot however, underestimate the ways of thinking that only became my tools to use after bashing on through some difficult concepts - and only by repetition.
I'm not in favour of simplifying education, but in teaching children what they need to know, and how to find out what they don't yet know.
Teach them to learn.
Imagine if they stopped teaching algebra, geometry, and polynomials in high schools - would it be a real detriment to society?
Imagine the fun we could have if we looked at the topics which have reference to real world situations? The wave function can be made fun to learn with the right teaching!
My personal beef with mathematical education is the lack of choice. By age 12-13 families and students are mostly sure which broad path to look forward to: academic or applied. Aren't they?
I cannot however, underestimate the ways of thinking that only became my tools to use after bashing on through some difficult concepts - and only by repetition.
I'm not in favour of simplifying education, but in teaching children what they need to know, and how to find out what they don't yet know.
Teach them to learn.
Thursday, January 21, 2010
Finding the directories that are eating up the hard disk
By user home:
du -k /home/ | sort -n | tail -10
All. This generally takes forever:
cd /
du -k | sort -n | tail -10
du -k /home/ | sort -n | tail -10
All. This generally takes forever:
cd /
du -k | sort -n | tail -10
Sunday, November 8, 2009
Web Application Configuration - custom settings per target environment
The issue comes up in every new project - how to create a custom build or customized configuration for each environment. There will be different configurations for JDBC connections, SOAP endpoints, etc. loaded as ResourceBundles, Spring properties, and so on.
Stack overflow has a discussion on configuration patterns where most of the potential solutions are represented. Many externalize the files (not packaged in the war/ear) to a hardcoded file path or to a location on the appserver classpath. Other solutions have multiple properties files and rely on the build script to replace the main application.properties.
These are good solutions, elements of each have been used in projects I've worked on. The externalized properties file or jar is often more hassle in reality, as the build guys are often not the developers - and every extra deploy step is more application downtime.
The build script option is also something I wish to avoid. I want my maven pom to speak for itself with no extra plugins or build hassles.
In my experience, the best solution is multiple properties files, one per environment, bundled in the ear/war and a single set-and-forget JVM property.
The key is letting the application choose a config based on where it's deployed. It's a bad to use IP or hostname for such choices, so choose something that's done once, but is still within the developers/build guys control.
When you setup each application server: set a JVM custom property called "ENV_NAME" that contains the environment name indicator. e.g. local, dev, stage, prod.
This value can then be used anywhere in your J2EE application, from front-end to back as simply as:
Make testing easier by loading custom props! Add a static block to your base TestCase class, and call:
Pretty clunky so far, but now we can take advantage of the Spring property configurator to load environment specific files. (This version was tested on spring 2.5, but have been using this setup since 1.2).
Create a default myapp-config.properties in the classpath root. This is the fallback.
Add a folder for each target environment to your src classpath.
Copy myapp-config.properties with customized values for the target environment to a new folder matching ENV_NAME; e.g. /dev/myapp-config.properties containing the dev JDBC settings.
I'm a little behind the times already, because I think spring3 can already handle this sort of thing - but not every app is ready to go with spring3 yet.
Added bonus example called in the constructor; loading a customized log4j setup file.
This one class demonstrates using ENV_NAME - a static System Property set on the JVM startup, thus available at any time - to load a custom log4j configuration for the environment. The same method can be used to load a custom ResourceBundle or static properties.
It is important to have a default/fallback configuration, this should be the production configuration. If the ENV_NAME is missing, then it's likely the production resources are firewalled from accidental dev/uat access, and if the ENV_NAME is missing in production then the default configuration is the correct file.
Stack overflow has a discussion on configuration patterns where most of the potential solutions are represented. Many externalize the files (not packaged in the war/ear) to a hardcoded file path or to a location on the appserver classpath. Other solutions have multiple properties files and rely on the build script to replace the main application.properties.
These are good solutions, elements of each have been used in projects I've worked on. The externalized properties file or jar is often more hassle in reality, as the build guys are often not the developers - and every extra deploy step is more application downtime.
The build script option is also something I wish to avoid. I want my maven pom to speak for itself with no extra plugins or build hassles.
In my experience, the best solution is multiple properties files, one per environment, bundled in the ear/war and a single set-and-forget JVM property.
The key is letting the application choose a config based on where it's deployed. It's a bad to use IP or hostname for such choices, so choose something that's done once, but is still within the developers/build guys control.
When you setup each application server: set a JVM custom property called "ENV_NAME" that contains the environment name indicator. e.g. local, dev, stage, prod.
This value can then be used anywhere in your J2EE application, from front-end to back as simply as:
if ("dev".equals(System.getProperty("ENV_NAME")) {
// do what you need for dev env.
}Make testing easier by loading custom props! Add a static block to your base TestCase class, and call:
System.setProperty("ENV_NAME", "local")Pretty clunky so far, but now we can take advantage of the Spring property configurator to load environment specific files. (This version was tested on spring 2.5, but have been using this setup since 1.2).
Create a default myapp-config.properties in the classpath root. This is the fallback.
Add a folder for each target environment to your src classpath.
Copy myapp-config.properties with customized values for the target environment to a new folder matching ENV_NAME; e.g. /dev/myapp-config.properties containing the dev JDBC settings.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<!-- Configurer that replaces ${..} placeholders with values from properties
Customized to load properties file from classpath, based on path from JVM ENV_NAME value
e.g. ENV_NAME=dev Configurer will look for classpath:/dev/myapp-registration.properties -->
<bean id="propertyConfigurer" class="com...myapp.util.EnvNameBasedConfigurer">
<property name="ignoreResourceNotFound" value="true" />
<property name="locations">
<list>
<value>myapp-config.properties</value>
</list>
</property>
</bean>
I'm a little behind the times already, because I think spring3 can already handle this sort of thing - but not every app is ready to go with spring3 yet.
Added bonus example called in the constructor; loading a customized log4j setup file.
/**
* This class extends the Spring Property Configurer for the
* purpose of loading environment specific property files. This
* implementation relies on a JVM system propery named
* "ENV_NAME" the value sets up which classpath
* folder name property files will be loaded.
*
* For example if ENV_NAME == dev, the resource[s] will be
* loaded from "classpath:/dev/resourcename"
*
* The WebSphere administrator should set ENV_NAME as a custom JVM
* property in each server instance Process Definition.
* JBoss users should alter JAVA_OPTS setting in the run.bat files.
* Standard (not enforced) should be: local, dev, uat, or prod.
*
* @author s.n.beynon
* @version $Revision: 1.4 $ $Date: 2009/11/03 16:46:52 $
*/
public class EnvNameBasedConfigurer extends PropertyPlaceholderConfigurer {
private static Logger log = null;
public static final String SEP = "/";
public static final String APPNAME = "myapp";
private static final String LOGSUFFIX = "-log4j.xml";
public EnvNameBasedConfigurer() {
super();
setup();
}
/**
* @return String env name; dev, uat, prod - default return blank
*/
public static String env() {
return System.getProperty("ENV_NAME", "");
}
/*
* normally would do this setup stuff in an initializing servlet
* call here because we want our custom log config setup before
* spring loads our configuration.
*/
private void setup() {
System.out.println("spring and log4j configuration loading.");
try {
// standard environment log setup (unix log path)
String logconfig = APPNAME + LOGSUFFIX;
// configure different logging based on ENV here.
// developers all use win32; appname-win-log4j.xml
// dev/uat/prod are unix; appname-envname-log4j.xml
if (System.getProperty("os.name").indexOf("Win") > -1) {
logconfig = APPNAME + "-win" + LOGSUFFIX;
}
else if (!"".equals(env())) {
//
logconfig = APPNAME + "-" + env() + LOGSUFFIX;
}
// from classpath load the standard log4j.xml
URL url = Loader.getResource(logconfig);
// custom environment log config not found, fallback to default
if (url == null) logconfig = APPNAME + LOGSUFFIX;
System.out.println("Configuring log from config file: " + url.getFile());
DOMConfigurator.configure(url);
log = Logger.getLogger(EnvNameBasedConfigurer.class);
} catch (Throwable t) {
t.printStackTrace();
}
}
/**
* Override the Property Loader resource configuration based on System
* Property value "ENV_NAME". If ENV_NAME is set, property file will be
* loaded from a directory named by that value (in the classpath).
*
For example, System.setProperty("local") will load files
* from the "local" directory.
If that directory does not
* contain the requested class path resource, will fall back to original
* name.
*
* @see org.springframework.core.io.support.PropertiesLoaderSupport#setLocation(org.springframework.core.io.Resource)
*/
public void setLocation(Resource resource) {
Resource localized = new ClassPathResource(env() + SEP + resource.getFilename());
if ("".equals(env())) {
log.warn("Expected to find system property ENV_NAME - Not Found!");
localized = new ClassPathResource(resource.getFilename());
}
if (localized.exists()) {
super.setLocation(localized);
return;
}
// fall back to what was supplied to this method
super.setLocation(resource);
}
/**
* Override the Property Loader resource configuration based on System
* Property value "ENV_NAME". If ENV_NAME is set, property files will be
* loaded from a directory named by that value (in the classpath).
*
For example, System.setProperty("local") will load files
* from the "local" directory.
If that directory does not
* contain the requested class path resource, will fall back to original
* name.
*
* @see org.springframework.core.io.support.PropertiesLoaderSupport#setLocations(org.springframework.core.io.Resource[])
*/
public void setLocations(Resource[] resources) {
log.debug("EnvNameBasedConfigurer setLocations from classpath for ENV_NAME:" + env());
if ("".equals(env())) {
log.warn("Expected to find system property ENV_NAME - Not Found!");
}
Resource[] envResources = new Resource[resources.length];
for (int i = 0; i < resources.length; i++) {
log.debug("setLocations " + resources[i]);
try {
Resource localized = new ClassPathResource(env() + SEP
+ resources[i].getFilename());
if (localized.exists()) {
envResources[i] = localized;
}
else {
envResources[i] = new ClassPathResource(resources[i].getFilename());
}
log.debug(envResources[i]);
} catch (Exception e) {
log.warn(e);
}
}
super.setLocations(envResources);
}
}
This one class demonstrates using ENV_NAME - a static System Property set on the JVM startup, thus available at any time - to load a custom log4j configuration for the environment. The same method can be used to load a custom ResourceBundle or static properties.
It is important to have a default/fallback configuration, this should be the production configuration. If the ENV_NAME is missing, then it's likely the production resources are firewalled from accidental dev/uat access, and if the ENV_NAME is missing in production then the default configuration is the correct file.
Tuesday, November 3, 2009
struts2 regex validation for a phone number
Struts2 bundled (built-in) validator regex type could really have some better examples!
Here's a basic phone number regex sample for a standard North American telephone number e.g. 123-456-7890
Key point is to use start/end pattern indicators ^ and $ - otherwise 123-456-78901234 etc. will also match!
Here's a basic phone number regex sample for a standard North American telephone number e.g. 123-456-7890
Key point is to use start/end pattern indicators ^ and $ - otherwise 123-456-78901234 etc. will also match!
<field name="person.phone">
<field-validator type="required" circuit="true">
<message>${getText("error.phone.required")}</message>
</field-validator>
<field-validator type="regex">
<param name="expression"><![CDATA[^[\d]{3}-[\d]{3}-[\d]{4}$]]></param>
<message>${getText("error.phone.format")}</message>
</field-validator>
</field>
Saturday, October 24, 2009
simon willison on redis
Proving again and old-school c is still fast and relevant, he talks about a "data structure" storage server (that which is not a database, but still a means to store data). Any programmer can see why this would be a valuable hook for the debugging process (think HSQL, MockDB, etc).
The fascinating tidbit at the end of the article is what got me interested however:
hurl.it
"Hurl makes HTTP requests. Enter a URL, set some headers, view the response, then share it with others. Perfect for demoing and debugging APIs. "
Sounds like a great addition to a system test toolkit for anyone who works on web apps.
The fascinating tidbit at the end of the article is what got me interested however:
hurl.it
"Hurl makes HTTP requests. Enter a URL, set some headers, view the response, then share it with others. Perfect for demoing and debugging APIs. "
Sounds like a great addition to a system test toolkit for anyone who works on web apps.
Sunday, October 11, 2009
WebSphere Trust Access Interceptor
Background - What is a TAI?
To correlate between external authentication (e.g. SSO with Oracle AM WebGate, IBM TAM, etc.) and the WebSphere Application Server (WAS) User Registry identity, use a Trust Association Interceptor within the WebSphere security layer.
Lightweight Third Party Authentication (LTPA) is the mechanism by which WAS supports SSO. With LTPA a token is created with the user data and expiry time, signed by the LTPA keys (which why multiple nodes in a cell must have their LTPA keys in sync). The LTPA token can be forwarded by authenticated resources. This token passes to other servers, in the same cell or in a different cell, either through cookies (for Web resources) or through the authentication layer Security Authentication Service (SAS) or CSIv2 for EJBs.
A TAI is an identity provider, it takes information from an HTTP request, and, assuming trust is validated, provides either a principal (to be filled in by the User Registry) or a fully populated subject. In either case the user must exist in the WAS User Registry. The option for the TAI to populate the Subject allows the implementer to override existing group membership in the WAS LDAP.
Trust Interceptors operate completely within the WebSphere security layer, usually loaded on server startup from AppServer lib ext directory. The interceptor is only executed when an authenticated resource is requested via HTTP(S), i.e. the web.xml has a security constraint for that URL. If the user has already authenticated to any participating cell in the WebSphere deployment, they will have a valid LTPA token for the resource; the interceptor will skip.
This interceptor implementation is an adjunct to the security system; fallback to the usual authentication mechanism is always possible. Make note that a TAI is available for web authentication only (as opposed to JAAS); there is no intercepting programmatic login or access to Enterprise Beans.
The negotiation stage is only executed only if the target is defined as protected by the implementing class. Targets are evaluated in the isTargetInterceptor method.
For more information, this article is dated now, but a great start to WAS security.
Implementation
I have implemented TAIs for WAS v6.0, v6.1 and v7.0. Slight differences, but I will cover my implementation for v6.1.
Basically, implement the interface com.ibm.wsspi.security.tai.TrustAssociationInterceptor from the websphere_apis.jar.
Order of execution is:
1. initialize(Properties props) - on server startup.
2. boolean isTargetInterceptor (HttpServletRequest req) - on each request - if true, continue to negociate.
3. TAIResult negotiateValidateandEstablishTrust (HttpServletRequest req, HttpServletResponse res)
TAI Result holds either a string user id to be retrieved from the Registry, or the fully qualified Subject.
Initialize
Load your TAI settings, a list of regex patterns to intercept, the name of the SSO Request Header, etc. Will be loaded from the referenced properties file or from the console custom properties. Console settings override.
isTargetInterceptor
Should the target URL be intercepted? For this, setup a Map (cache) of URLs, and from the properties compare to set of Regex Patterns to intercept.
Negotiate Trust
Quick and easy - direct mapping to a WAS registry ID. The simplest way, if the SSO header exists map it to an existing user.
Another option for TAI's using Basic Authentication:
For our actual implementation, we break user types into two paths by the application URL. getRealm is a custom function not shown here, it translates URLs to just a context root and maps to a list of Anonymous/MustExistInRegistry type apps.
Anonymous user mapping. Each user with a valid header is allowed in, and if they don't exist the user is created.
Must-exist users are higher security and must exist in the WAS registry before access.
Reading the User Registry - creating a Shared State from scratch. This give the TAI the option to alter user groups/roles.
Updating and creating users in the TAI requires a privileged request. The entire class to setup users and update properties is included here:
To correlate between external authentication (e.g. SSO with Oracle AM WebGate, IBM TAM, etc.) and the WebSphere Application Server (WAS) User Registry identity, use a Trust Association Interceptor within the WebSphere security layer.
Lightweight Third Party Authentication (LTPA) is the mechanism by which WAS supports SSO. With LTPA a token is created with the user data and expiry time, signed by the LTPA keys (which why multiple nodes in a cell must have their LTPA keys in sync). The LTPA token can be forwarded by authenticated resources. This token passes to other servers, in the same cell or in a different cell, either through cookies (for Web resources) or through the authentication layer Security Authentication Service (SAS) or CSIv2 for EJBs.
A TAI is an identity provider, it takes information from an HTTP request, and, assuming trust is validated, provides either a principal (to be filled in by the User Registry) or a fully populated subject. In either case the user must exist in the WAS User Registry. The option for the TAI to populate the Subject allows the implementer to override existing group membership in the WAS LDAP.
Trust Interceptors operate completely within the WebSphere security layer, usually loaded on server startup from AppServer lib ext directory. The interceptor is only executed when an authenticated resource is requested via HTTP(S), i.e. the web.xml has a security constraint for that URL. If the user has already authenticated to any participating cell in the WebSphere deployment, they will have a valid LTPA token for the resource; the interceptor will skip.
This interceptor implementation is an adjunct to the security system; fallback to the usual authentication mechanism is always possible. Make note that a TAI is available for web authentication only (as opposed to JAAS); there is no intercepting programmatic login or access to Enterprise Beans.
The negotiation stage is only executed only if the target is defined as protected by the implementing class. Targets are evaluated in the isTargetInterceptor method.
For more information, this article is dated now, but a great start to WAS security.
Implementation
I have implemented TAIs for WAS v6.0, v6.1 and v7.0. Slight differences, but I will cover my implementation for v6.1.
Basically, implement the interface com.ibm.wsspi.security.tai.TrustAssociationInterceptor from the websphere_apis.jar.
Order of execution is:
1. initialize(Properties props) - on server startup.
2. boolean isTargetInterceptor (HttpServletRequest req) - on each request - if true, continue to negociate.
3. TAIResult negotiateValidateandEstablishTrust (HttpServletRequest req, HttpServletResponse res)
TAI Result holds either a string user id to be retrieved from the Registry, or the fully qualified Subject.
Initialize
Load your TAI settings, a list of regex patterns to intercept, the name of the SSO Request Header, etc. Will be loaded from the referenced properties file or from the console custom properties. Console settings override.
public int initialize(Properties props) throws WebTrustAssociationFailedException {
// read the Resource Bundle and add to static properties
// *console values* will overwrite props file.
try {
ResourceBundle rb = Constants.getResourceBundle();
Enumeration keys = rb.getKeys();
while (keys.hasMoreElements()) {
String key = (String) keys.nextElement();
log.debug("Prop from " + key + "=" + rb.getObject(key));
taiProps.put(key, rb.getObject(key));
}
} catch (Exception e) {
// check the constants RESOURCE_PKG/RESOURCES values to see
// if the properties files exist!
log.warn("Did not find/load iaatai_en.properties " + e.getMessage());
// No rethrow, console might be used to set properties.
}
try {
// read properties from console config add to static list
if (props != null) {
props.list(System.out);
taiProps.putAll(props);
}
// setup url patterns and header names from props.
configureFromProperties();
} catch (Throwable t) {
log.error("Initialization error (props not loaded)", t);
}
// return zero for success - any other value is failure.
return 0;
}
isTargetInterceptor
Should the target URL be intercepted? For this, setup a Map (cache) of URLs, and from the properties compare to set of Regex Patterns to intercept.
public boolean isTargetInterceptor(HttpServletRequest request) {
log.info("isTargetInterceptor: Check URI " + request.getRequestURI());
try {
// if we can get away without regexing, do so
if (targetUris.contains(request.getRequestURI())) {
log.debug("Found uri in cache");
return true;
}
// iterate over the patterns (values)
Iterator urlPatterns = targetUrlPatterns.values().iterator();
while (urlPatterns.hasNext()) {
Pattern p = (Pattern) urlPatterns.next();
// log.debug("Test URI pattern: " + p.pattern());
Matcher m = p.matcher(request.getRequestURI());
if (m.matches()) {
log.debug("Matched Pattern " + p.pattern());
targetUris.add(request.getRequestURI());
return true;
}
}
} catch (Exception e) {
log.error("ERROR: isTargetInterceptor", e);
}
log.info("TAI will not intercept this target URI");
return false;
}
Negotiate Trust
Quick and easy - direct mapping to a WAS registry ID. The simplest way, if the SSO header exists map it to an existing user.
public TAIResult negotiateValidateandEstablishTrust(HttpServletRequest request,
HttpServletResponse response) throws WebTrustAssociationFailedException {
log.debug("negotiateValidateandEstablishTrust starting.");
String ssoId = null;
TAIResult result = TAIResult.create(401);
ssoId = request.getHeader(Constants.HEADER_USER_ID);
if (ssoId != null && !"".equals(ssoId)) {
return TAIResult.create(HttpServletResponse.SC_OK, wasId);
}
// no header, return the default result, unauthorized 401
return result;
}
Another option for TAI's using Basic Authentication:
public TAIResult negotiateValidateandEstablishTrust(HttpServletRequest req,
HttpServletResponse resp) throws WebTrustAssociationFailedException {
log.debug("negotiateValidateandEstablishTrust");
String basicAuth = req.getHeader("Authorization");
TAIResult tairesult;
if (basicAuth == null || basicAuth.equals("") || !basicAuth.startsWith("Basic ")) {
tairesult = challengeClient(resp);
}
else {
String userId = null;
String passwd = "";
try {
String basicDecoded = new String(Base64.decode(basicAuth.substring(6), '='),
"UTF-8");
int i = basicDecoded.indexOf(':');
if (i == -1) {
userId = basicDecoded;
}
else {
userId = basicDecoded.substring(0, i);
passwd = basicDecoded.substring(i + 1);
}
log.debug("negotiateValidateandEstablishTrust userID:" + userId);
if (userId != null && (passwd == null || passwd.length() == 0)) {
log.warn("negotiateValidateandEstablishTrust FAILED, no password for "
+ userId);
tairesult = challengeClient(resp);
}
else {
Subject subject = doBasicWASLogin(userId, passwd);
tairesult = TAIResult.create(200, userId, subject);
}
} catch (LoginException e) {
log.error("negotiateValidateandEstablishTrust " + userId, e);
tairesult = challengeClient(resp);
} catch (UnsupportedEncodingException e) {
tairesult = null;
}
}
log.info("negotiateValidateandEstablishTrust " + tairesult.getStatus());
return tairesult;
}
private TAIResult challengeClient(HttpServletResponse resp)
throws WebTrustAssociationFailedException {
resp.setHeader("WWW-Authenticate", wwwAuthenticateHeader);
resp.setStatus(401);
return TAIResult.create(401);
}
private Subject doBasicWASLogin(String user, String pass) throws LoginException {
Subject subject = null;
CallbackHandler callbackhandler = WSCallbackHandlerFactory.getInstance()
.getCallbackHandler(user, pass);
LoginContext logincontext = new LoginContext(basicLoginTarget, callbackhandler);
log.debug("doBasicWASLogin built LoginContext");
logincontext.login();
log.debug("doBasicWASLogin login done");
return logincontext.getSubject();
}
For our actual implementation, we break user types into two paths by the application URL. getRealm is a custom function not shown here, it translates URLs to just a context root and maps to a list of Anonymous/MustExistInRegistry type apps.
Anonymous user mapping. Each user with a valid header is allowed in, and if they don't exist the user is created.
Must-exist users are higher security and must exist in the WAS registry before access.
public TAIResult negotiateValidateandEstablishTrust(HttpServletRequest request,
HttpServletResponse response) throws WebTrustAssociationFailedException {
log.debug("negotiateValidateandEstablishTrust starting.");
String ssoId = null;
String wasId = null;
String realm = "";
String lang = "en";
// http authenticate response
TAIResult result = TAIResult.create(401);
try {
// headers will be set by GA runtime, actual header
// name is configured via properties.
ssoId = request.getHeader(Constants.HEADER_USER_ID);
lang = request.getHeader(Constants.HEADER_LANG_ID);
if (ssoId == null
&& "true".equals(taiProps.getProperty("DEBUG_HEADER_VALUE", "false"))) {
log.warn("SSO_ID null, but DEBUG_HEADER_VALUE found to be true.");
ssoId = "debuguser " + new Random().nextInt();
}
if (ssoId == null) {
// SSO *not* protecting this resource, no SSO_ID header &
// DEBUG_HEADER_VALUE not set.
log.warn("TAI cannot trust, no header. Should we be intercepting this URL?");
throw new WebTrustAssociationUserException("Null SSO user");
}
// retrieve the name of the application, used to decide if anon/mustexist
realm = getRealm(request);
wasId = mapToAnonOrMustExistId(ssoId, realm);
// Anonymous users will be auto-created
log.debug(realm + ":requires that users exist? " + mustexistRealms);
boolean mustExist = (mustexistRealms != null && mustexistRealms.contains(realm));
// Setup the sharedState user credentials
Map sharedState = null;
// Using the sharedState map is an example of creating the subject
// to change the user credentials or state. It may not be necessary
// in your implementation. If not, simply return an ID WAS will
// recognize:
// TAIResult.create(HttpServletResponse.SC_OK, wasId);
UserRegistryLookup userRegLookup = new UserRegistryLookup();
try {
// lookup the user, group, etc
sharedState = userRegLookup.getSharedState(wasId, lang, realm);
} catch (EntryNotFoundException enf) {
log.warn("No match for wasId " + wasId);
// Check for another chance - create the user
// then attempt to read sharedState for newly created.
if (!ismustexist) {
createNewUser(ssoId, wasId, lang, realm);
log.debug("WAS user created, return TAIResult OK.");
return TAIResult.create(HttpServletResponse.SC_OK, wasId);
}
}
if (sharedState != null) {
// Create the subject and add credentials
Subject authenticatedPrincipal = new Subject();
authenticatedPrincipal.getPublicCredentials().add(sharedState);
// N.B. the principal field (arg1) is unnecesary when
// providing the Subject object.
log.debug("Return the authenticated principal. " + wasId);
return TAIResult.create(HttpServletResponse.SC_OK, wasId,
authenticatedPrincipal);
}
} catch (Throwable e) {
// most actions do not throw, this must be a catastrophic failure
log.error("Trust ERROR for:" + ssoId + "::" + wasId, e);
throw new WebTrustAssociationFailedException(e.getMessage());
}
// return the default result, unauthorized 401
return result;
}
Reading the User Registry - creating a Shared State from scratch. This give the TAI the option to alter user groups/roles.
public Map getSharedState(final String user, final String lang, final String realm)
throws NamingException, RemoteException, EntryNotFoundException,
CustomRegistryException {
Hashtable secTable = new Hashtable();
InitialContext _ctx = VmmServiceManager.getInitialContext();
// Retrieve the local UserRegistry implementation.
// For v6.0 this is WMM-UR (WMM custom registry)
// Impl: com.ibm.websphere.security._UserRegistry_Stub
// v6.1 Use com.ibm.ws.wim.registry.WIMUserRegistry
UserRegistry reg = (UserRegistry) _ctx.lookup("UserRegistry");
log.info("Got User Registry "
+ (reg != null ? reg.getRealm() : "NULL"));
if (reg == null) {
log.error("Failed connnection to WMM User Registry ");
return null;
}
// Retrieves the user registry uniqueID based on the uid specified
// in the NameCallback. Unique ID is the full DN.
String uniqueID = reg.getUniqueUserId(user);
log.debug("From user registry: uniqueID " + uniqueID);
// read the WAS user ID from registry userID@realm
String wsUserId = WSSecurityPropagationHelper
.getUserFromUniqueID(uniqueID);
log.debug("wsUserId from WSSecurityPropagationHelper "
+ wsUserId);
// Retrieves display name from the user registry based on the
// WAS UserId.
String securityName = reg.getUserSecurityName(wsUserId);
// display name from user registry
log.debug("securityName from user registry " + securityName);
// Retrieves the groups associated with the uniqueID.
List groupList = null;
try {
groupList = reg.getUniqueGroupIds(wsUserId);
log.debug("found groupList " + groupList);
} catch (Exception e) {
log.warn("ERROR retrieving groupList " + e.getMessage());
groupList = new ArrayList();
}
// N.B. This is the ideal place to override group membership.
// Example, set all users in the group name "Realmusers".
try {
String usersGroupUniqueId = reg.getUniqueGroupId(realm + "users");
groupList.add(usersGroupUniqueId);
} catch (Exception e) {
log.warn("ERROR finding users group " + realm + " " + e);
}
// Creates the java.util.Hashtable with the information that you
// gathered from the UserRegistry implementation.
log.debug("Now creating the subject shared state. " + realm
+ " " + uniqueID);
secTable.put(AttributeNameConstants.WSCREDENTIAL_USERID, securityName);
secTable.put(AttributeNameConstants.WSCREDENTIAL_UNIQUEID, uniqueID);
secTable.put(AttributeNameConstants.WSCREDENTIAL_SECURITYNAME,
securityName);
// secTable.put(AttributeNameConstants.WSCREDENTIAL_REALM,
// reg.getRealm());
secTable.put(AttributeNameConstants.WSCREDENTIAL_GROUPS, groupList);
// Adds a cache key that is used as part of the lookup mechanism for
// the created Subject. The cache key can be an object, but should have
// an implemented toString() method. Make sure that the cacheKey
// contains enough information to scope it to the user and any
// additional attributes that you are using. If you do not specify this
// property the Subject is scoped to the returned WSCREDENTIAL_UNIQUEID,
// by default.
secTable.put(AttributeNameConstants.WSCREDENTIAL_CACHE_KEY, realm
+ uniqueID);
return secTable;
}
Updating and creating users in the TAI requires a privileged request. The entire class to setup users and update properties is included here:
public class VmmServiceManager implements VmmService {
private static final Logger log = Logger.getLogger(VmmServiceManager.class);
/**
* Provider URL for JNDI lookup - default provider for WAS - first Node only.
*/
public static String providerURL = "corbaloc:iiop:localhost:10031";
/**
* Constructor for Virtual Member Manager Service Manager.
*/
public VmmServiceManager() {
super();
}
/**
* @return InitialContext for the current environment.
* @throws NamingException
*/
public static InitialContext getInitialContext() throws NamingException {
Hashtable env = getEnv();
if (env.size() == 2) {
return new InitialContext(env);
}
return new InitialContext();
}
/**
* @return Hashtable containing INITIAL_CONTEXT_FACTORY and
* PROVIDER_URL
* @throws NamingException
*/
private static Hashtable getEnv() throws NamingException {
ResourceBundle rb = Constants.getResourceBundle();
// look for a server specific provider url
String serverid = System.getProperty(Constants.SERVER_NAME);
log.info("getInitialContext SERVER_NAME=" + serverid);
String providerURL = null;
try {
providerURL = rb.getString("provider.url." + serverid);
} catch (Exception e) {
// no providerURL for this serverid (serverid may have been null).
try {
// look in properties for the default provider URL
providerURL = rb.getString("provider.url");
} catch (Exception e2) {
// no providerURL, WAS will use the local provider.
}
}
log.info("Init Ctx lookup using providerURL=" + providerURL);
Hashtable env = new Hashtable(2);
if (providerURL != null) {
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.ibm.websphere.naming.WsnInitialContextFactory");
env.put(Context.PROVIDER_URL, providerURL);
}
return env;
}
/**
* @return LocalServiceProvider Wim Client com.ibm.websphere.wim.Service
*/
public LocalServiceProvider getLocalServiceProvider() {
//http://publib.boulder.ibm.com/infocenter/wasinfo/v6r1/index.jsp?topic=
// /com.ibm.websphere.javadoc.doc/vmm/com/ibm/websphere/wim/client/
// LocalServiceProvider.html
try {
// holds reference to "ejb/com/ibm/websphere/wim/ejb/WIMServiceHome"
LocalServiceProvider service = new LocalServiceProvider(getEnv());
return service;
} catch (Exception e) {
log.error("LocalServiceProvider setup fail.", e);
}
return null;
}
/**
* @param userId
* @param lang
* @param containerDn
* @return boolean success flag
*/
public boolean createUser(final String userId, final String lang,
String containerDn) {
LocalServiceProvider service = null;
try {
service = getLocalServiceProvider();
if (service == null)
return false;
// containerDn has to be null - empty string is bad.
if (containerDn != null && containerDn.trim().length() == 0) {
containerDn = null;
}
DataObject root = SDOHelper.createRootDataObject();
// API entity type might be "PersonAccount" or "Person"
// this is unclear to me. using the SchemaConstants field.
// leave the 2nd param blank to use the default container.
DataObject entity = SDOHelper.createEntityDataObject(root, containerDn,
SchemaConstants.DO_PERSON_ACCOUNT);
entity.set(Constants.UID, userId);
entity.set(Constants.CN, userId);
entity.set(Constants.SN, userId);
List displayName = new ArrayList();
displayName.add(userId);
entity.set(Constants.DISPLAY_NAME, displayName);
entity.set(Constants.PREFERRED_LANGUAGE, lang);
log.info("PersonAccount object assembled " + entity);
// Assemble a logged-in subject with admin (create-user) rights.
Subject adminSubject = retrieveAdminSubject();
log.info("VMM AdminSubject logged in " + adminSubject);
root = doSecureCreate(adminSubject, root);
log.info("PersonAccount create completed: " + root);
return true;
} catch (Throwable t) {
log.error("PersonAccount create failed", t);
}
return false;
}
/**
* @param uniqueId
* @param lang
* @return boolean success flag
*/
public boolean updateUser(final String uniqueId, final String lang) {
LocalServiceProvider service = null;
try {
service = getLocalServiceProvider();
if (service == null)
return false;
DataObject root = SDOHelper.createRootDataObject();
DataObject entity = SDOHelper.createEntityDataObject(root, null,
SchemaConstants.DO_PERSON_ACCOUNT);
entity.createDataObject(SchemaConstants.DO_IDENTIFIER).set(
SchemaConstants.PROP_UNIQUE_NAME, uniqueId);
List displayName = new ArrayList();
displayName.add(lang);
entity.set(Constants.DISPLAY_NAME, displayName);
entity.set(Constants.PREFERRED_LANGUAGE, lang);
log.info("PersonAccount object for update " + entity);
// Assemble a logged-in subject with admin (update-user) rights.
Subject adminSubject = retrieveAdminSubject();
log.info("VMM AdminSubject logged in " + adminSubject);
root = doSecureUpdate(adminSubject, root);
log.info("PersonAccount update completed: " + root);
return true;
} catch (Throwable t) {
log.error("PersonAccount update failed", t);
}
return false;
}
/**
* @param adminSubject
* @param userRoot
* @return DataObject
* @throws PrivilegedActionException
*/
@SuppressWarnings("unchecked")
protected DataObject doSecureCreate(final Subject adminSubject, final DataObject userRoot)
throws PrivilegedActionException {
return (DataObject) WSSubject.doAs(adminSubject,
new java.security.PrivilegedExceptionAction() {
public Object run() throws PrivilegedActionException {
// Subject is associated with the current thread context
return AccessController.doPrivileged(new CreateUserPrivileged(
userRoot));
}
// Subject is re-associated with the current thread context
}); // end doAs admin.
}
@SuppressWarnings("unchecked")
protected DataObject doSecureUpdate(final Subject adminSubject, final DataObject userRoot)
throws PrivilegedActionException {
return (DataObject) WSSubject.doAs(adminSubject,
new java.security.PrivilegedExceptionAction() {
public Object run() throws PrivilegedActionException {
// Subject is associated with the current thread context
return AccessController.doPrivileged(new UpdateUserPrivileged(
userRoot));
}
// Subject is re-associated with the current thread context
}); // end doAs
}
/**
* Create new user in the WebSphere Repository. This private class runs
* inside PrivilegedAction; must implement PrivilegedAction interface.
*/
class CreateUserPrivileged implements PrivilegedExceptionAction {
private DataObject rootWithNewUser;
/**
* @param containsUserObj
*/
public CreateUserPrivileged(DataObject containsUserObj) {
this.rootWithNewUser = containsUserObj;
}
/**
* Cannot throw specific exceptions on this method signature, so return
* the DataObject, or general Exception with cause.
*
* @return Object DataObject if success, null otherwise.
* @throws Exception
* RemoteException, WIMExcpetion
* @see java.security.PrivilegedExceptionAction#run()
*/
public Object run() throws Exception {
// Subject cut off from the current thread context.
LocalServiceProvider service = getLocalServiceProvider();
if (service == null)
return null;
log.debug("Connected to WIM service " + service.getClass().getName());
return service.create(rootWithNewUser);
}
}
/**
* Update user in the WebSphere Repository. This private class runs inside
* PrivilegedAction; must implement PrivilegedAction interface.
*/
class UpdateUserPrivileged implements PrivilegedExceptionAction {
private DataObject rootWithUser;
/**
* @param containsUserObj
*/
public UpdateUserPrivileged(DataObject containsUserObj) {
this.rootWithUser = containsUserObj;
}
/**
* Cannot throw specific exceptions on this method signature, so return
* the DataObject, or general Exception with cause.
*
* @return Object DataObject if success, null otherwise.
* @throws Exception
* RemoteException, WIMExcpetion
* @see java.security.PrivilegedExceptionAction#run()
*/
public Object run() throws Exception {
// Subject cut off from the current thread context.
LocalServiceProvider service = getLocalServiceProvider();
if (service == null)
return null;
log.debug("Connected to WIM service " + service.getClass().getName());
return service.update(rootWithUser);
}
}
/**
* @return Subject logged-in administrator subject.
* @throws NamingException
*/
private Subject retrieveAdminSubject() throws NamingException {
// hard-set the locale to ensure the expected file is always found.
// see also, IaaTrustAssociationInterceptor notes.
ResourceBundle rb = Constants.getResourceBundle();
final String wasAdminUser = rb.getString("admin.user");
final String wasAdminPasswd = rb.getString("admin.pass");
// log.debug("REMOVE ME LATER " + wasAdminUser + " " +
// wasAdminPasswd);
// This will only work with admin credentials, see props
Subject adminSubject = null;
LoginContext lc = null;
try {
lc = new LoginContext("WSLogin", new WSCallbackHandlerImpl(
wasAdminUser, wasAdminPasswd));
lc.login();
adminSubject = lc.getSubject();
} catch (Throwable t) {
log.error("LoginContext error " + t.getMessage());
// everything falls apart
}
return adminSubject;
}
Thursday, October 8, 2009
Axis SSL for a non-trusted Certificate - Dev, Testing, only.
The problem is the Entrust 2048 SSL certificates which were recently issued, every platform I use normally has no trouble accepting them (no imported certificates required!), but deploying on the IBM WebSphere 6.1 JDK/Server is nothing but pain.
My errors without an import - "No trusted certificate found":
My errors after attempting to import to trust.p12 - "End user tried to act as a CA"
No matter what I import or try, I keep seeing "End user tried to act as a CA". Well if that's the way it's going to be IBM-TrustManager, that's the way I'm going to play.
My solution, though designed and tested on IBM runtimes is JDK independent.
My first play was to switch to xfire, which is just about the coolest/simplest SOAP implementor out there - and I know it uses the commons httpclient package.
Use Easy SSL TrustManager implementation, so easy to override.
EasySSLTrustManager is designed to accept self-signed certs, not those that are completely borked. So for me, it was required that I copy the httpclient contrib source and add a quick try/catch around the checkClientTrust and checkServerTrusted. This way I still see errors, but the ssl communications proceeds unhindered.
Then I tried to do the same for an Axis client. There are a few misleading pages on the Axis wiki; which I completely disregarded in favour of control by AxisProperties.
I'm running on Axis 1.4, but my stubs were generated years ago and I don't want to bother regenerating.
Here's the great site that set me on the "Axis client even with self-signed SSL cert" path.
My implementation, however, is much simpler, and reuses the EasySSLTrustManager :)
We create a TrustAllSSLSocketFactory that implements Axis' SecureSocketFactory
Now we tell Axis about it's new SSL Socket Factory.
I chose to do so in the WSProxy class initializing method, but it can exist any where before the call.
My errors without an import - "No trusted certificate found":
00000026 WSX509TrustMa E CWPKI0022E: SSL HANDSHAKE FAILURE: A signer with SubjectDN "blah" was sent from target host:port "blah:443". The signer may need to be added to local trust store "../base_v61/profiles/was61profile1/config/cells/CellName/nodes/NodeName/trust.p12" located in SSL configuration alias "NodeDefaultSSLSettings". The extended error message from the SSL handshake exception is: "No trusted certificate found".
My errors after attempting to import to trust.p12 - "End user tried to act as a CA"
WSX509TrustMa E CWPKI0022E: SSL HANDSHAKE FAILURE: A signer with SubjectDN "blah" was sent from target host:port "blah:443". The signer may need to be added to local trust store "../base_v61/profiles/was61profile1/config/cells/CellName/nodes/NodeName/trust.p12" located in SSL configuration alias "NodeDefaultSSLSettings". The extended error message from the SSL handshake exception is: "End user tried to act as a CA".
No matter what I import or try, I keep seeing "End user tried to act as a CA". Well if that's the way it's going to be IBM-TrustManager, that's the way I'm going to play.
My solution, though designed and tested on IBM runtimes is JDK independent.
My first play was to switch to xfire, which is just about the coolest/simplest SOAP implementor out there - and I know it uses the commons httpclient package.
Use Easy SSL TrustManager implementation, so easy to override.
EasySSLTrustManager is designed to accept self-signed certs, not those that are completely borked. So for me, it was required that I copy the httpclient contrib source and add a quick try/catch around the checkClientTrust and checkServerTrusted. This way I still see errors, but the ssl communications proceeds unhindered.
Then I tried to do the same for an Axis client. There are a few misleading pages on the Axis wiki; which I completely disregarded in favour of control by AxisProperties.
I'm running on Axis 1.4, but my stubs were generated years ago and I don't want to bother regenerating.
Here's the great site that set me on the "Axis client even with self-signed SSL cert" path.
My implementation, however, is much simpler, and reuses the EasySSLTrustManager :)
We create a TrustAllSSLSocketFactory that implements Axis' SecureSocketFactory
import java.io.IOException;
import java.security.SecureRandom;
import java.util.Hashtable;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import org.apache.axis.components.net.JSSESocketFactory;
import org.apache.axis.components.net.SecureSocketFactory;
import org.apache.commons.httpclient.contrib.ssl.EasyX509TrustManager;
/**
* Custom SSL socket factory to ignore certificate errors.
*
* Based loosely on org.apache.axis.components.net.SunJSSESocketFactory
* Praise to open source libraries.
*/
public class TrustAllSSLSocketFactory extends JSSESocketFactory implements
SecureSocketFactory {
/**
* Constructor TrustAllSSLSocketFactory
* @param attributes
*/
@SuppressWarnings("unchecked")
public TrustAllSSLSocketFactory(Hashtable attributes) {
super(attributes);
}
/**
* Init the SSL socket factory with our own Trust Manager.
*
* This overrides the parent class to provide our SocketFactory
* implementation.
*
* @throws IOException
*/
protected void initFactory() throws IOException {
try {
SSLContext context = getContext();
sslFactory = context.getSocketFactory();
} catch (Exception e) {
if (e instanceof IOException) {
throw (IOException) e;
}
throw new IOException(e.getMessage());
}
}
/**
* Gets a custom SSL Context with no keystore and a Trust All Manager.
*
* @return SSLContext
* @throws Exception
*/
protected SSLContext getContext() throws Exception {
try {
// congifure a local SSLContext to use created keystores
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, new TrustManager[] { new EasyX509TrustManager(null) },
new SecureRandom());
return sslContext;
} catch (Exception e) {
throw new Exception("Error creating context for SSLSocket!", e);
}
}
}
Now we tell Axis about it's new SSL Socket Factory.
I chose to do so in the WSProxy class initializing method, but it can exist any where before the call.
public class SampleWSProxy implements SampleWS {
private String _endpoint = null;
private com.example.application.webservice.ISampleWS iSampleWS = null;
public ISampleWSProxy() {
_initISampleWSProxy();
}
private void _initISampleWSProxy() {
try {
// this Axis default class only works for self-signed certs,
// or certs loaded by custom keystore. NB. Has version for Sun JDK also.
// AxisProperties.setProperty("axis.socketSecureFactory",
// "org.apache.axis.components.net.IBMJSSESocketFactory");
// this works for all certs - even when an exception is thrown
// connection ignores the invalid cert and moves on :)
AxisProperties.setProperty("axis.socketSecureFactory",
"full.package.to.TrustAllSSLSocketFactory");
String socketSecureFactory = AxisProperties
.getProperty("axis.socketSecureFactory");
System.out.println("axis.socketSecureFactory : " + socketSecureFactory);
iSampleWS = (new ISampleWSServiceLocator())
.getSampleWS();
if (iSampleWS != null) {
if (_endpoint != null) {
((javax.xml.rpc.Stub) iSampleWS)._setProperty(
"javax.xml.rpc.service.endpoint.address", _endpoint);
}
else {
_endpoint = (String) ((javax.xml.rpc.Stub) iSampleWS)
._getProperty("javax.xml.rpc.service.endpoint.address");
}
// Staging has a test/generic provisioning web service
// with default username password as: admin
// callers may/must override! :)
((javax.xml.rpc.Stub) iSampleWS)._setProperty(
javax.xml.rpc.Stub.USERNAME_PROPERTY, "admin");
((javax.xml.rpc.Stub) iSampleWS)._setProperty(
javax.xml.rpc.Stub.PASSWORD_PROPERTY, "admin");
}
} catch (javax.xml.rpc.ServiceException serviceException) {
}
}
... end of _initISampleWSProxy .. followed by available web service methods ...
}
Subscribe to:
Posts (Atom)
