Access secure RESTful url from another application - restful-url

Am using resteasy-servlet and secured url with JASS authentication
eg:
<login-config>
<auth-method>FORM</auth-method>
<realm-name>WebSecurityDomain</realm-name>
<form-login-config>
<form-login-page>Login.jsp</form-login-page>
<form-error-page>error.jsp</form-error-page>
</form-login-config>
</login-config>
<security-role>
<role-name>
SecurityAdmin
</role-name>
</security-role>
<security-constraint>
<web-resource-collection>
<web-resource-name>Secure Pages</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>SecurityAdmin</role-name>
</auth-constraint>
</security-constraint>
Now if I want to access some url from above application using externa resource. Url response returns Login.jsp.
How can I authenticate and access url.
I have even tried SecurityInterceptor but its not even accessing this function.
eg:
public ServerResponse preProcess(HttpRequest request, ResourceMethodInvoker methodInvoked)
throws Failure, WebApplicationException {
...
return null;
}

Well as am using FORM-based authentication I can't use rest calls from backend. To use rest call I need to pass SESSIONID which is generated inside Browser.
So am doing a POST call to login to service I need and after that if I use any call from Browser it automatically adds correct SESSIONID and let me use call

Related

Restrict access to java-melody monitoring url

Is there a way I can restrict access to /monitoring url generated by Java-Melody plugin in Grails using Shiro roles?
Update: a little bit more details. It's no problem so secure most Grails ressources with shiro. But in case of the java melody plugin, it seems that the melody filter is executed before the shiro filter gets executed. This renders shiro useless.
There are some solutions which say that this might be fixed through a change in the web.xml, but this is not a quick hit and I (rdmueller) didn't manage to make it work yet. The web.xml plugin also seems to promise some help, but I don't want to add another plugin just to secure one plugin.
Some older statements found on the web state that this problem should be already solved through the usage of the loadAfter list in this file: https://github.com/javamelody/grails-melody-plugin/blob/master/GrailsMelodyGrailsPlugin.groovy - but it seems that this only worked for older versions of Grails.
Update2: In order to make it easier to propose a solution, I've create a Grails 2.2.4 sample: https://github.com/rdmueller/SO30739581
just clone the project, do a grailsw run-app and navigate to
http://localhost:8080/SO30739581/dbdoc
and you'll get a login screen via shiro. Navigate to
http://localhost:8080/SO30739581/monitoring
and you'll get the melody screen without being logged in :-(
I ended up doing so by making changes to web.xml for HTTP authentication. Add this to you web.config file.
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>Monitoring</realm-name>
</login-config>
<security-role>
<role-name>monitoring</role-name>
</security-role>
<security-constraint>
<web-resource-collection>
<web-resource-name>Monitoring</web-resource-name>
<url-pattern>/monitoring</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>monitoring</role-name>
</auth-constraint>
</security-constraint>
Then add a user and role to your tomcat-users.xml
<user username="yourusername" password="yourpassword" roles="monitoring"/>
I assume you're using Grails 2.x, you could hardcode it this way :
<!-- language: java-->
// grails-app/conf/MonitoringFilters.groovy
import org.apache.shiro.SecurityUtils
class MonitoringFilters {
def dependsOn = [ShiroSecurityFilters]
def filters = {
myMonitoringArea(uri: "/monitoring") {
before = {
SecurityUtils.subject.hasRole('ADMIN')
}
}
}
}
This is not a "quick hit", but the following approach should work with Shiro or whatever security framework your Grails app uses.
In web.xml, add the following elements above any existing <filter> elements:
<filter>
<filter-name>melodyFilter</filter-name>
<filter-class>com.your.package.MelodyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>melodyFilter</filter-name>
<url-pattern>/monitoring/*</url-pattern>
</filter-mapping>
This will call com.your.package.MelodyFilter any time the /monitoring/* url pattern is invoked.
Next, you'll need to create a MelodyFilter Java class in /src/java/com/your/package/MelodyFilter.java.
In the body of the doFilter method, you may call a Grails service method to perform any desired security checks, as follows:
package com.your.package;
import com.my.grails.app.MyService;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
public class MelodyFilter implements Filter {
#Override
public void destroy() { }
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
String uri = ((HttpServletRequest)request).getRequestURI();
HttpSession session = ((HttpServletRequest)request).getSession(false);
ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(session.getServletContext());
// replace MyService with your actual service
MyService myService = (MyService)ctx.getBean("myService");
// replace isUserAuthorized with your actual service method;
// session and uri params included to demonstrate how to pass them
// your argument list can be whatever your service method requires
boolean authorized = myService.isUserAuthorized(session, uri);
if (authorized) { chain.doFilter(request,response); }
else {
request.setAttribute("error", "User is not authorized to access " + uri);
request.getRequestDispatcher("/someController/someAction").forward(request, response);
}
}
#Override
public void init(FilterConfig filterConfig) throws ServletException { }
}
Then simply implement myService.isUserAuthorized() to perform whatever security checks you desire.
I have verified this technique works in Grails-2.3.6 with grails-melody:1.59.0
Just to list all available options:
the shiro-protect-any - plugin seems to work, but IMHO, it seems to be to be a bit too complicated and the plugin is "not fully tested" (says the author)...

Spring Security OAuth2 (google) web app in redirect loop

I am trying to build a Spring MVC application and securing it with Spring Security OAuth2 and the provider is Google. I was able to get the web app working without security and with form login. However, I am not able to get OAuth with google to work. Google app setup is fine as I can get the call backs etc to work with a non Spring Security app.
My security config is as follows:
<?xml version="1.0" encoding="UTF-8"?>
<b:beans xmlns:sec="http://www.springframework.org/schema/security"
xmlns:b="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-3.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
<sec:http use-expressions="true" entry-point-ref="clientAuthenticationEntryPoint">
<sec:http-basic/>
<sec:logout/>
<sec:anonymous enabled="false"/>
<sec:intercept-url pattern="/**" access="isFullyAuthenticated()"/>
<sec:custom-filter ref="oauth2ClientContextFilter" after="EXCEPTION_TRANSLATION_FILTER"/>
<sec:custom-filter ref="googleAuthenticationFilter" before="FILTER_SECURITY_INTERCEPTOR"/>
</sec:http>
<b:bean id="clientAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint"/>
<sec:authentication-manager alias="alternateAuthenticationManager">
<sec:authentication-provider>
<sec:user-service>
<sec:user name="user" password="password" authorities="DOMAIN_USER"/>
</sec:user-service>
</sec:authentication-provider>
</sec:authentication-manager>
</b:beans>
The OAuth2 protected resource is as follows:
#Configuration
#EnableOAuth2Client
class ResourceConfiguration {
#Autowired
private Environment env;
#Resource
#Qualifier("accessTokenRequest")
private AccessTokenRequest accessTokenRequest;
#Bean
public OAuth2ProtectedResourceDetails googleResource() {
AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();
details.setId("google-app");
details.setClientId(env.getProperty("google.client.id"));
details.setClientSecret(env.getProperty("google.client.secret"));
details.setAccessTokenUri(env.getProperty("google.accessTokenUri"));
details.setUserAuthorizationUri(env.getProperty("google.userAuthorizationUri"));
details.setTokenName(env.getProperty("google.authorization.code"));
String commaSeparatedScopes = env.getProperty("google.auth.scope");
details.setScope(parseScopes(commaSeparatedScopes));
details.setPreEstablishedRedirectUri(env.getProperty("google.preestablished.redirect.url"));
details.setUseCurrentUri(false);
details.setAuthenticationScheme(AuthenticationScheme.query);
details.setClientAuthenticationScheme(AuthenticationScheme.form);
return details;
}
private List<String> parseScopes(String commaSeparatedScopes) {
List<String> scopes = newArrayList();
Collections.addAll(scopes, commaSeparatedScopes.split(","));
return scopes;
}
#Bean
public OAuth2RestTemplate googleRestTemplate() {
return new OAuth2RestTemplate(googleResource(), new DefaultOAuth2ClientContext(accessTokenRequest));
}
#Bean
public AbstractAuthenticationProcessingFilter googleAuthenticationFilter() {
return new GoogleOAuthentication2Filter(new GoogleAppsDomainAuthenticationManager(), googleRestTemplate(), "https://accounts.google.com/o/oauth2/auth", "http://localhost:9000");
}
}
The custom authentication filter which I have written to throw a Redirect exception to get the OAuth2 authorization is as follows:
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
try {
logger.info("OAuth2 Filter Triggered!! for path {} {}", request.getRequestURI(), request.getRequestURL().toString());
logger.info("OAuth2 Filter hashCode {} request hashCode {}", this.hashCode(), request.hashCode());
String code = request.getParameter("code");
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
logger.info("Code is {} and authentication is {}", code, authentication == null ? null : authentication.isAuthenticated());
// not authenticated
if (requiresRedirectForAuthentication(code)) {
URI authURI = new URI(googleAuthorizationUrl);
logger.info("Posting to {} to trigger auth redirect", authURI);
String url = "https://www.googleapis.com/oauth2/v2/userinfo?access_token=" + oauth2RestTemplate.getAccessToken();
logger.info("Getting profile data from {}", url);
// Should throw RedirectRequiredException
oauth2RestTemplate.getForEntity(url, GoogleProfile.class);
// authentication in progress
return null;
} else {
logger.info("OAuth callback received");
// get user profile and prepare the authentication token object.
String url = "https://www.googleapis.com/oauth2/v2/userinfo?access_token=" + oauth2RestTemplate.getAccessToken();
logger.info("Getting profile data from {}", url);
ResponseEntity<GoogleProfile> forEntity = oauth2RestTemplate.getForEntity(url, GoogleProfile.class);
GoogleProfile profile = forEntity.getBody();
CustomOAuth2AuthenticationToken authenticationToken = getOAuth2Token(profile.getEmail());
authenticationToken.setAuthenticated(false);
Authentication authenticate = getAuthenticationManager().authenticate(authenticationToken);
logger.info("Final authentication is {}", authenticate == null ? null : authenticate.isAuthenticated());
return authenticate;
}
} catch (URISyntaxException e) {
Throwables.propagate(e);
}
return null;
}
The filter chain sequence from the Spring web app is as follows:
o.s.b.c.e.ServletRegistrationBean - Mapping servlet: 'dispatcherServlet' to [/]
o.s.b.c.e.FilterRegistrationBean - Mapping filter: 'metricFilter' to: [/*]
o.s.b.c.e.FilterRegistrationBean - Mapping filter: 'oauth2ClientContextFilter' to: [/*]
o.s.b.c.e.FilterRegistrationBean - Mapping filter: 'googleOAuthFilter' to: [/*]
o.s.b.c.e.FilterRegistrationBean - Mapping filter: 'org.springframework.security.filterChainProxy' to: [/*]
o.s.b.c.e.FilterRegistrationBean - Mapping filter: 'org.springframework.security.web.access.intercept.FilterSecurityInterceptor#0' to: [/*]
o.s.b.c.e.FilterRegistrationBean - Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
o.s.b.c.e.FilterRegistrationBean - Mapping filter: 'applicationContextIdFilter' to: [/*]
o.s.b.c.e.FilterRegistrationBean - Mapping filter: 'webRequestLoggingFilter' to: [/*]
The redirect to Google works fine and I get the callback to the filter and the authentication is successful. However after that, the request results in a redirect and it invokes the filter again (the request is the same, I have checked the hasCode). On the second call the authentication in the SecurityContext is null. As part of the first authentication call the Authentication object was populated in the security context, so why does it disappear?
I am working with Spring Security for the first time so may have made newbie mistake.
After playing around with Spring Security configuration and the filters I was finally able to get this working. I had to make couple of important changes
I used a standard Spring OAuth2 filter (org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter) instead of the custom filter I was using.
Change the intercept URL of the authentication filter to be /googleLogin and added an authentication entry point which redirects to this URL on authentication failure.
Overall the flow is as follows
Browser accesses / and the request passes through the OAuth2ClientContextFilter and OAuth2ClientAuthenticationProcessingFilter as the context does not match. The configured context path for login is /googleLogin
The security interceptor FilterSecurityInterceptor detects that the the user is anonymous and throws an access denied exception.
Spring security's ExceptionTranslationFilter catches the access denied exception and asks the configured authentication entry point to handle it which issues a redirect to /googleLogin.
For the request /googleLogin, the filter OAuth2AuthenticationProcessingFilter tries to access the Google protected resource and an UserRedirectRequiredException is thrown which is translated into a HTTP redirect to Google (with the OAuth2 details) by OAuth2ClientContextFilter.
On successful authentication from Google the browser is redirected back to /googleLogin with the OAuth code. The filter OAuth2AuthenticationProcessingFilter handles this and creates an Authentication object and updates the SecurityContext.
At this point the user is fully authenticated and redirect to / is issued by the OAuth2AuthenticationProcessingFilter.
FilterSecurityInterceptor allows the request to proceed as the SecurityContext contains an Authentication object which is authenticated.
Finally the application page which is secured using an expression like isFullyAuthenticated() or similar is rendered.
The security context xml is as follows:
<sec:http use-expressions="true" entry-point-ref="clientAuthenticationEntryPoint">
<sec:http-basic/>
<sec:logout/>
<sec:anonymous enabled="false"/>
<sec:intercept-url pattern="/**" access="isFullyAuthenticated()"/>
<!-- This is the crucial part and the wiring is very important -->
<!--
The order in which these filters execute are very important. oauth2ClientContextFilter must be invoked before
oAuth2AuthenticationProcessingFilter, that's because when a redirect to Google is required, oAuth2AuthenticationProcessingFilter
throws a UserRedirectException which the oauth2ClientContextFilter handles and generates a redirect request to Google.
Subsequently the response from Google is handled by the oAuth2AuthenticationProcessingFilter to populate the
Authentication object and stored in the SecurityContext
-->
<sec:custom-filter ref="oauth2ClientContextFilter" after="EXCEPTION_TRANSLATION_FILTER"/>
<sec:custom-filter ref="oAuth2AuthenticationProcessingFilter" before="FILTER_SECURITY_INTERCEPTOR"/>
</sec:http>
<b:bean id="oAuth2AuthenticationProcessingFilter" class="org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter">
<b:constructor-arg name="defaultFilterProcessesUrl" value="/googleLogin"/>
<b:property name="restTemplate" ref="googleRestTemplate"/>
<b:property name="tokenServices" ref="tokenServices"/>
</b:bean>
<!--
These token classes are mostly a clone of the Spring classes but have the structure modified so that the response
from Google can be handled.
-->
<b:bean id="tokenServices" class="com.rst.oauth2.google.security.GoogleTokenServices">
<b:property name="checkTokenEndpointUrl" value="https://www.googleapis.com/oauth2/v1/tokeninfo"/>
<b:property name="clientId" value="${google.client.id}"/>
<b:property name="clientSecret" value="${google.client.secret}"/>
<b:property name="accessTokenConverter">
<b:bean class="com.rst.oauth2.google.security.GoogleAccessTokenConverter">
<b:property name="userTokenConverter">
<b:bean class="com.rst.oauth2.google.security.DefaultUserAuthenticationConverter"/>
</b:property>
</b:bean>
</b:property>
</b:bean>
<!--
This authentication entry point is used for all the unauthenticated or unauthorised sessions to be directed to the
/googleLogin URL which is then intercepted by the oAuth2AuthenticationProcessingFilter to trigger authentication from
Google.
-->
<b:bean id="clientAuthenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<b:property name="loginFormUrl" value="/googleLogin"/>
</b:bean>
Also the Java Config for the OAuth2 resources is as follows:
#Configuration
#EnableOAuth2Client
class OAuth2SecurityConfiguration {
#Autowired
private Environment env;
#Resource
#Qualifier("accessTokenRequest")
private AccessTokenRequest accessTokenRequest;
#Bean
#Scope("session")
public OAuth2ProtectedResourceDetails googleResource() {
AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();
details.setId("google-oauth-client");
details.setClientId(env.getProperty("google.client.id"));
details.setClientSecret(env.getProperty("google.client.secret"));
details.setAccessTokenUri(env.getProperty("google.accessTokenUri"));
details.setUserAuthorizationUri(env.getProperty("google.userAuthorizationUri"));
details.setTokenName(env.getProperty("google.authorization.code"));
String commaSeparatedScopes = env.getProperty("google.auth.scope");
details.setScope(parseScopes(commaSeparatedScopes));
details.setPreEstablishedRedirectUri(env.getProperty("google.preestablished.redirect.url"));
details.setUseCurrentUri(false);
details.setAuthenticationScheme(AuthenticationScheme.query);
details.setClientAuthenticationScheme(AuthenticationScheme.form);
return details;
}
private List<String> parseScopes(String commaSeparatedScopes) {
List<String> scopes = newArrayList();
Collections.addAll(scopes, commaSeparatedScopes.split(","));
return scopes;
}
#Bean
#Scope(value = "session", proxyMode = ScopedProxyMode.INTERFACES)
public OAuth2RestTemplate googleRestTemplate() {
return new OAuth2RestTemplate(googleResource(), new DefaultOAuth2ClientContext(accessTokenRequest));
}
}
I had to override some of the Spring classes as the format of the token from Google and the one expected by Spring don't match. So there is some custom handiwork required there.
Not to impose a better answer, but another reason for this redirect looping is the session cookie handling when the session cookie sameSite attribute is set to Strict. Then, if the authority service has broken the redirect chain, the session cookie will not be transmitted.
See: How can I redirect after OAUTH2 with SameSite=Strict and still get my cookies?

Spring Security - check if web url is secure / protected

Is there a way to "ask" spring security if the current request is secure? Because even if I am authenticated I want to detect if I am in a secure protected URL or in a anonymous / public page
Thanks in advance!
Spring Security provides JSP tag support for this. For example:
<sec:authorize url="/admin">
This content will only be visible to users who are authorized to access the "/admin" URL.
</sec:authorize>
Thymeleaf provides a Spring Security Dialect that has direct support for checking URL authorization with Spring Security. For example:
<div sec:authorize-url="/admin">
This will only be displayed if authenticated user can call the "/admin" URL.
</div>
If your technology does not support performing the check directly, you can easily use the WebInvocationPrivilegeEvaluator (this is the object that the JSP taglib and Thymeleaf use). For example, you can #Autowire an instance of WebInvocationPrivilegeEvaluator and use it directly. Obviously the syntax will vary depending on where you use it (i.e. GSP, Freemarker, etc), but here is an example in straight Java code.
#Autowired
WebInvocationPrivilegeEvaluator webPrivs;
public void useIt() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
boolean hasAdminAccess = webPrivs.isAllowed("/admin", authentication);
boolean hasAdminPostAccess = webPrivs.isAllowed(null, "/admin", "POST", authentication);
}
I think you can use your own implementation for AccessDecisionVoter then just simply override Vote method and compare intercept url using filterInvocation.getRequestUrl(); method.
#Override
public int vote(Authentication authentication, FilterInvocation filterInvocation,
Collection<ConfigAttribute> attributes) {
String requestUrl = filterInvocation.getRequestUrl();
....
}
We can mark it is as secure channel so converted to https:// url.
<intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY" requires-channel="https" />
We can then use request.getScheme() to identify it.
I used org.springframework.security.version 3.1.4.RELEASE

JSF 2 and the Basic HTTP authentication

We have an web app what uses Basic HTTP authentication.
web.xml
...
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>file</realm-name>
</login-config>
...
Indirect usage somewhere in the deep code space ...
User getLogedUser(HttpServletRequest request)
And I have to rewrite it in JSF 2, but I have no clue how can use the this authentication method in JSF 2. (i could not find the way how can i get 'HttpServletRequest request')
Google did not throw any useful hit on his first page
Thanks for the answers in advance.
The raw HttpServletRequest is in JSF available by ExternalContext#getRequest().
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
HttpServletRequest request = (HttpServletRequest) ec.getRequest();
// ...
However, I think it's better to change the getLoggedUser() method to take the remote user instead.
User getLoggedUser(String remoteUser)
so that you can just feed either ExternalContext#getRemoteUser() or HttpServletRequest#getRemoteUser() to it. This also decouples the method from any Servlet API or JSF API dependency.
I guess you are looking for ExternalContext.getRemoteUser()
which returns the user name.
Usage:
FacesContext.getCurrentInstance().getExternalContext().getRemoteUser();

Spring security : Redirect to previous url after logout

I've got a web app that uses spring security. I'm wanting to redirect the user back to the same page they were on before log out when they log out.
Is there an easy way to do this?
Not sure which Spring version this question was referring to - but there is a useReferer property on the standard org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler since Spring 3.0.
So all you need to do is configure it like this and the logout will redirect to wherever the user came from:
<bean id="logoutSuccessHandler" class="org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler">
<property name="useReferer" value="true"/>
</bean>
<security:http>
<security:logout logout-url="/logout" success-handler-ref="logoutSuccessHandler" />
</security:http>
What you need is a Simple LogoutSuccessHandler
#Component
public class CustomLogoutSuccessHandler extends
SimpleUrlLogoutSuccessHandler implements LogoutSuccessHandler {
#Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse
response, Authentication authentication)
throws IOException, ServletException {
if (authentication != null) {
System.out.println(authentication.getName());
}
response.setStatus(HttpStatus.OK.value());
response.sendRedirect(request.getHeader("referer"));
}
And later call it in your configure method i.e
.logout().logoutSuccessHandler(customLogoutSuccessHandler)
This will redirect you to referer URL.
You can add a new filter in the filter chain of the spring security. That new filter will be applied to the /logout URL. When going trough this filter you can save the current page in a field variable. And when returning through the filter. You can redirect the request to the saved URL. I think this can help. You can get the current page URL by using the Referer header in the Request object.
Just a thought:
How about we keep a stack of visited pages. may be at max 3 entries. and redirects user from logout.
Configure a Filter or extend spring security filter to maintain a stack in session about last two visited URLs
On logout configure a servlet as logout-success-url .
Now get the URL from session stack and now invalidate the session and redirect user to that page
Also you can make use of referrer header as Vijay has mentioned

Resources