Appreciate your help
Thanks
Thong Huynh
Hi Anurag,
1) The REST API provides the org.camunda.bpm.engine.rest.security.auth.AuthenticationProvider for developing custom authentication providers.
http://docs.camunda.org/api-references/rest/#!/overview/authentication
2) The camunda webapplication ships with the org.camunda.bpm.webapp.impl.security.filter.SecurityFilter implementation which can be configured using a JSON-based configuration:
{
"pathFilter": {
"deniedPaths" : [
{ "path": "/api/engine/.*", "methods" : "*" },
{ "path": "/api/cockpit/.*", "methods" : "*" },
{ "path": "/app/tasklist/{engine}/.*", "methods" : "*" },
{ "path": "/app/cockpit/{engine}/.*", "methods" : "*" }
],
"allowedPaths" : [
{ "path": "/api/engine/engine/", "methods" : "GET" },
{ "path": "/api/{app:cockpit}/plugin/{engine}/static/.*", "methods" : "GET" },
{ "path": "/api/{app:cockpit}/plugin/{plugin}/{engine}/.*", "methods" : "*", "authorizer" : "org.camunda.bpm.webapp.impl.security.filter.EngineRequestAuthorizer" },
{ "path": "/api/engine/engine/{engine}/.*", "methods" : "*", "authorizer" : "org.camunda.bpm.webapp.impl.security.filter.EngineRequestAuthorizer" },
{ "path": "/app/{app:cockpit}/{engine}/.*", "methods" : "*", "authorizer" : "org.camunda.bpm.webapp.impl.security.filter.ApplicationRequestAuthorizer" },
{ "path": "/app/{app:tasklist}/{engine}/.*", "methods" : "*", "authorizer" : "org.camunda.bpm.webapp.impl.security.filter.ApplicationRequestAuthorizer" }
]
}
}
You could also completely replace the org.camunda.bpm.webapp.impl.security.filter.SecurityFilter with a custom filter in the WEB-INF/web.xml file.
Cheers,
Daniel Meyer
We have a similar requirement to the OP, but using Oracle Access Manager (OAM). This is basically a plug-in for Apache which checks incoming requests against a policy in OAM. If that URL matches a pattern that requires authentication, and the user is not currently authenticated then they are redirected to the login page hosted by OAM. OAM then handles the login, and once successful redirects the user's browser back to the original URL they were trying to access. They are now authenticated, so the plugin allows the request to proceed as per normal to the application server.
The plug-in also adds HTTP header properties to the request which lets the application know some details about the currently logged in user. In our case, this is the user id (email address), the first name and surname. If these are present in the incoming request to the application, the application can assume that the user has been authenticated. Naturally, you need to have a mechanism in place to stop users from bypassing the Apache server and going directly to the app server and spoofing the headers.
Behind the scenes, the OAM component has its own session cookie so that it can link incoming requests back to a session on the OAM server which maintains whether the user is authenticated, expiry times etc. Because this is a domain level cookie, the user will be seen as signed on to all web applications that are protected by OAM in that domain.
OAM also handles the problem of Integrated Windows Authentication without us having to do anything in our applications.
We also use Spring Security for our own applications, and have a custom security filter that checks the HTTP request for the necessary headers and creates the required Spring UserDetails object.
public class IamSecurityFilter extends AbstractPreAuthenticatedProcessingFilter {
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
// Get OAM HTTP header from request
final String userName = request.getHeader(HTTP_HEADER_ATTRIBUTES_USER_NAME);
final String cNumber = request.getHeader(HTTP_HEADER_ATTRIBUTES_CNUM);
final String customerID = request.getHeader(HTTP_HEADER_ATTRIBUTES_CUSTID);
final String firstName = request.getHeader(HTTP_HEADER_ATTRIBUTES_FIRST_NAME);
final String lastName = request.getHeader(HTTP_HEADER_ATTRIBUTES_LAST_NAME);
if (ANONYMOUS_USER_STRING.equals(userName) || StringUtils.isEmpty(userName) || StringUtils.isEmpty(cNumber)){
SecurityContextHolder.clearContext();
return null;
} else if (StringUtils.isEmpty(customerID) || StringUtils.isEmpty(firstName) || StringUtils.isEmpty(lastName)) {
SecurityContextHolder.clearContext();
return null;
} else if (customerID.equals(USER_NOT_AUTHENTICATED_IN_11G) || cNumber.equals(USER_NOT_AUTHENTICATED_IN_11G)) {
SecurityContextHolder.clearContext();
return null;
}
UserProfile sessionUser = (UserProfile) request.getSession().getAttribute(SessionKeys.USER_ATTRIBUTE);
if (sessionUser == null || !userName.equals(sessionUser.getUsername())) {
UserProfile requestUser = new UserProfile(userName, null, cNumber, firstName, lastName, customerID);
request.getSession().setAttribute(SessionKeys.USER_ATTRIBUTE, requestUser);
return requestUser;
} else {
return sessionUser;
}
}
protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
return "N/A";
}
}
public class UserProfile implements UserDetails, Serializable, Cloneable {
...
}
The Spring Security UserDetails interface specifies the method Collection<GrantedAuthority> getAuthorities() which is used for authorisation. In our implementation, these are group memberships in the directory. These can either be passed in the HTTP header (if there aren't many) or looked up in the directory once the user is authenticated.
This is then wired up in our Spring context like so:
<security:http disable-url-rewriting="true" create-session="ifRequired">
<security:anonymous enabled="true"/>
<security:custom-filter position="PRE_AUTH_FILTER" ref="securityFilter"/>
<security:form-login login-page="/login/success"/>
</security:http>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="preauthAuthProvider"/>
</security:authentication-manager>
<bean id="preauthAuthProvider"
class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<property name="preAuthenticatedUserDetailsService" ref="userService"/>
</bean>
<bean id="iamSecurityFilter" class="sso.web.security.IamSecurityFilter" lazy-init="true">
<property name="authenticationManager" ref="authenticationManager"/>
</bean>
And in web.xml:
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
So a summary of our requirements for single sign on would be:
- Pass authentication details as HTTP header properties
- Pass authorisation details (group membership) as HTTP header properties or through a separate LDAP lookup
- Integrate with Spring Security so that Spring Security mechanisms can be used inside our process applications if required
Thanks for the opportunity to contribute ideas.
David
Thanks for the response. I was going to start looking into the SecurityFilter, but then I saw this post which mentioned you were looking at Spring Security. As this is what we use already in our non Camunda web applications to integrate with OAM, I thought I would provide this as some background on how we use it. From the quick look I have had at the security in 7.0.0, I'm pretty sure we could get it to work with OAM, but if you were looking at moving to Spring Security then most of the work is done already. Is this still on the roadmap?
Initially, we would be looking to add this single sign on capability to the Camunda web applications. We would also like the option to have it in our custom process applications as well, but we could already secure any web UI components (like external forms) using the method I described above.
Thanks,
David
19-Jan-2016 16:51:29.816 SEVERE [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Error deploying web application directory /root/camunda/server/apache-tomcat-8.0.24/webapps/camunda
java.lang.IllegalStateException: ContainerBase.addChild: start: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/camunda]]
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:729)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:701)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:717)
at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:1101)
at org.apache.catalina.startup.HostConfig$DeployDirectory.run(HostConfig.java:1786)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:724)
Did I make some mistakes?
Thanks :)
--
You received this message because you are subscribed to a topic in the Google Groups "camunda BPM users" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/camunda-bpm-users/c7zVqsiOCCI/unsubscribe.
To unsubscribe from this group and all its topics, send an email to camunda-bpm-us...@googlegroups.com.
To post to this group, send email to camunda-...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/camunda-bpm-users/d48d6bdd-abc1-4c9e-b86b-9ed1ead34b81%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
<repositories> <repository> <id>camunda-bpm-nexus</id> <name>camunda-bpm-nexus</name> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>true</enabled> </snapshots> <url>https://app.camunda.com/nexus/content/groups/public</url> </repository> </repositories>