GAE Namespace with Spring MVC + Spring Security + Datastore - google-app-engine

How to setup Google namespace filter with Spring Security?
I want to set the namespace once user is logged-in using user id.
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException{
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(authentication.isAuthenticated())
{
User currentUser = (User) authentication.getPrincipal();
NamespaceManager.set(currentUser.getId().toString());
}
else{
NamespaceManager.set("");
}
chain.doFilter(req,res);
}
current 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>
<filter>
<filter-name>NamespaceFilter</filter-name>
<filter-class>com.etailerp.security.NamespaceFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>NamespaceFilter</filter-name>
<url-pattern>/app/*</url-pattern>
</filter-mapping>
current application.xml
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/errors/**" access="permitAll" />
<intercept-url pattern="/app/**" access="hasAnyRole('ROLE_USER','ROLE_ADMIN')" />
<intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" />
<form-login login-page="/" authentication-success-handler-ref="authenticationSuccessHandler"
authentication-failure-url="/login.html?login_error=true"/>
<logout logout-success-url="/"/>
<custom-filter ref="namespaceFilter" before="FIRST" />
</http>

Spring Security documentation states:
You can also use the after or before attributes if you want your filter to be inserted before or after another filter in the stack. The names "FIRST" and "LAST" can be used with the position attribute to indicate that you want your filter to appear before or after the entire stack, respectively.
From my point of view using FIRST as a value of attribute before is illegal. I would suggest avoiding adding such a functionality via filter and rather use e.g. custom org.springframework.security.web.authentication.AuthenticationSuccessHandler implementation for that purpose.

Related

AngularJS $http GET request returning 404 though corresponding Java method is executed

Firing AngularJS Rest URL as a GET request from AngularJS, its
returning 404 though its hitting the required method on server
side (spring implementation) and producing the desired Result
Below is the method firing the angularjs request
getCredentials : function(){
return $http.get('/helloRest/getUsername');
}
The above method results in the below URL
URL : http://localhost:8083/helloRest/getUsername
below is the stack traces
<body>
<h1>HTTP Status 404 - /helloRest/getUsername</h1>
<div class="line"></div><p><b>type</b> Status report</p><p>
<b>message</b>
<u>/helloRest/getUsername</u></p><p>
<b>description</b>
<u>The requested resource is not available.</u></p>
<hr class="line"><h3>Apache Tomcat/8.0.37</h3></body></html>
web.xml
<web-app>
<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>
<servlet>
<servlet-name>test</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>test</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/test-servlet.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
</web-app>
Below is test-server.xml(dispatcher servlet)
<beans>
<context:annotation-config/>
<mvc:annotation-driven />
<context:component-scan base-package="com.org.simpro"/>
<jpa:repositories base-package="com.org.simpro"></jpa:repositories>
<bean id="hibernateJpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
</bean>
<bean id="HelloWorldRestController" class="com.org.simpro.controller.HelloWorldRestController">
<property name="userService" ref="userServiceImpl"></property>
</bean>
<bean id= "userServiceImpl" class="com.org.simpro.service.UserServiceImpl">
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:#localhost:1521:xe"/>
<property name="username" value="HR"/>
<property name="password" value="hr"/>
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter"></property>
<property name="packagesToScan" value="com.org.simpro.model" />
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle9Dialect</prop>
<!--
<prop key="javax.persistence.schema-generation.database.action">none</prop>
<prop key="hibernate.ejb.use_class_enhancer">true</prop>
//jpa hibernate properties
hibernate.dialect=org.hibernate.dialect.H2Dialect
hibernate.format_sql=true
hibernate.hbm2ddl.auto=create-drop
hibernate.ejb.naming_strategy=org.hibernate.cfg.ImprovedNamingStrategy
hibernate.show_sql=false
-->
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">create-drop</prop>
<prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<import resource="security-context.xml"/>
</beans>
Below is the security-context.xml
<security:http auto-config="true" use-expressions="false">
<security:form-login login-page="/login"
login-processing-url="/authenticate"
authentication-failure-url="/login?error=true"
username-parameter="username" password-parameter="password"/>
<security:intercept-url pattern="/" access="ROLE_USER"/>
<security:intercept-url pattern="/authenticate" access="ROLE_USER"/>
<security:csrf disabled="true"/>
</security:http>
<security:authentication-manager>
<security:authentication-provider>
<security:user-service>
<security:user name="user" password="password"
authorities="ROLE_USER"/>
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>
</beans>
Below is the method which the URL is hitting
#RequestMapping(value="/getUsername",method=RequestMethod.GET)
public void authenticateUser(HttpServletRequest hreq){
System.out.println("credentials are");
Principal principalObject = (Principal) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
System.out.println("security"+principalObject.toString());
}
The AngularJS $http request is returning me 404,though the above java
method is executing successfully. I should get 200 instead of 404
as the server response, Could you kindly check this
#RequestMapping(value="/helloRest")
public class HelloWorldRestController {
public UserService getUserService() {
return userService;
}
public void setUserService(UserService userService) {
this.userService = userService;
}
#RequestMapping(value="/getUsername",method=RequestMethod.GET)
public void authenticateUser(){
System.out.println("credentials are");
Principal principalObject =(Principal) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
System.out.println("security"+principalObject.toString());
}
#Autowired
UserService userService; //Service which will do all data retrieval/manipulation work
#RequestMapping(value = "/pullusers/", method = RequestMethod.GET )
public ResponseEntity<Iterable<User_ex>> listAllUser_exs() {
Iterable<User_ex> users = userService.findAllUsers();
System.out.println(users.toString());
//return null;
/*if(users.isEmpty()){
return new ResponseEntity<List<User>>(HttpStatus.NO_CONTENT);//You many decide to return HttpStatus.NOT_FOUND
}*/
return new ResponseEntity<Iterable<User_ex>>(users, HttpStatus.OK);
}
//-------------------Retrieve Single User--------------------------------------------------------
#RequestMapping(value = "/user/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<User_ex> getUser(#PathVariable("id") long id) {
System.out.println("Fetching User with id " + id);
User_ex user = userService.findById(id);
if (user == null) {
System.out.println("User with id " + id + " not found");
return new ResponseEntity<User_ex>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<User_ex>(user, HttpStatus.OK);
}
//-------------------Create a User--------------------------------------------------------
#RequestMapping(value = "/persistuser/", method = RequestMethod.POST)
public ResponseEntity<Void> createUser(#RequestBody User_ex user, UriComponentsBuilder ucBuilder) {
System.out.println("Creating User " + user.getName());
if (userService.isUserExist(user)) {
System.out.println("A User with name " + user.getName() + " already exist");
return new ResponseEntity<Void>(HttpStatus.CONFLICT);
}
userService.saveUser(user);
HttpHeaders headers = new HttpHeaders();
//headers.setLocation(ucBuilder.path("/user/{id}").buildAndExpand(user.getId()).toUri());
return new ResponseEntity<Void>(headers, HttpStatus.CREATED);
}
//-------------------check if particular user exists or not----------------------------------------------------------
#RequestMapping(value="/authenticate", method=RequestMethod.POST,produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<User_ex> isCurrentUserExists(String username, String password){
System.out.println("A User with name " + username +password+ "exist");
User_ex user = userService.isUserLoginExist(username,password);
if(user != null){
return new ResponseEntity<User_ex>(user,HttpStatus.OK);
}
return null;
}
//------------------- Update a User --------------------------------------------------------
#RequestMapping(value = "/updateuser/{id}", method = RequestMethod.PUT)
public ResponseEntity<User_ex> updateUser(#PathVariable("id") long id, #RequestBody User_ex user) {
System.out.println("Updating User " + id);
User_ex currentUser = userService.findById(id);
if (currentUser==null) {
System.out.println("User with id " + id + " not found");
return new ResponseEntity<User_ex>(HttpStatus.NOT_FOUND);
}
currentUser.setName(user.getName());
currentUser.setAge(user.getAge());
currentUser.setSalary(user.getSalary());
userService.updateUser(currentUser);
return new ResponseEntity<User_ex>(currentUser, HttpStatus.OK);
}
//------------------- Delete a User --------------------------------------------------------
#RequestMapping(value = "/removeuser/{id}", method = RequestMethod.DELETE)
public ResponseEntity<User_ex> deleteUser(#PathVariable("id") long id) {
System.out.println("Fetching & Deleting User with id " + id);
User_ex user = userService.findById(id);
if (user == null) {
System.out.println("Unable to delete. User with id " + id + " not found");
return new ResponseEntity<User_ex>(HttpStatus.NOT_FOUND);
}
userService.deleteUserById(id);
return new ResponseEntity<User_ex>(HttpStatus.NO_CONTENT);
}
//------------------- Delete All Users --------------------------------------------------------
#RequestMapping(value = "/removeusers/", method = RequestMethod.DELETE)
public ResponseEntity<User_ex> deleteAllUsers() {
System.out.println("Deleting All Users");
userService.deleteAllUsers();
return new ResponseEntity<User_ex>(HttpStatus.NO_CONTENT);
}
}
try,
#RequestMapping(value="/getUsername",method=RequestMethod.GET)
public ResponseEntity authenticateUser(){
System.out.println("credentials are");
Principal principalObject =(Principal) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
System.out.println("security"+principalObject.toString());
return new ResponseEntity(HttpStatus.OK);
}
Blindly guessing :
Your configuration is :
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/test-servlet.xml</param-value>
</context-param>
But you gave the dispatcher servlet' name as test-server.xml while it should be test-servlet.xml
The controller class is not being put under the com.org.simpro
One (or both) of those security-context.xml and test-servlet.xml is not being put under the WEB-INF.
Check your maven config. (pom.xml) whether or not the following dependencies exist :
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring.security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring.security.version}</version>
</dependency>
I don't see how you load your secuty config via (in your web.xml) :
<!-- Loads Spring Security config file -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/security-context.xml
</param-value>
</context-param>
It's possibly normal behavior since there's no view resolver configured in your dispatcher servlet, check your server's console instead to check the output for the getUsername.
Well, too many things to guess...

Spring REST call gives 302 redirect

I am using angularjs to make a REST call to my tomcat backend managed by Spring. But my server code never seems to get the call and the browser gets a 302 with "index.html" appended at the end of the url. For example, if my initial call was "localhost:8080/api/search/someString", browser receives a 302 redirect to "localhost:8080/api/search/someString/index.html", which then redirects to "localhost:8080/api/search/someString/index.html/index.html".
This keeps happening until a "Too many redirects" error occurs and it fails. Any ideas anyone?
Here is my web.xml:
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
<url-pattern>/api/*</url-pattern>
<url-pattern>/page/*</url-pattern>
</servlet-mapping>
Angular call:
search: function (searchStr) {
return $http.get("/api/search/"+searchStr);
}
Rest Controller:
#RestController
public class SearchResource {
#Autowired
private ItemService itemService;
#RequestMapping(value = "/api/search/{searchStr}", method = RequestMethod.GET)
public List<Item> getItemsBasedOnSearchString(#PathVariable String searchStr) {
return itemService.getAllItems();
}
public ItemService getItemService() {
return itemService;
}
public void setItemService(ItemService itemService) {
this.itemService = itemService;
}
}
Sometimes you can make the stupidest of mistakes! I changed the RequestMapping annotation from "/api/search" to "/search" and it works.
Thanks.

WebAPI CORS with Windows Authentication - allow Anonymous OPTIONS request

I have a WebAPI 2 REST service running with Windows Authentication. It is hosted separately from the website, so I've enabled CORS using the ASP.NET CORS NuGet package. My client site is using AngularJS.
So far, here's what I've been through:
I didn't have withCredentials set, so the CORS requests were returning a 401. Resolved by adding withCredentials to my $httpProvider config.
Next, I had set my EnableCorsAttribute with a wildcard origin, which isn't allowed when using credentials. Resolved by setting the explicit list of origins.
This enabled my GET requests to succeed, but my POST issued a preflight request, and I hadn't created any controller actions to support the OPTIONS verb. To resolve this, I've implemented a MessageHandler as a global OPTIONS handler. It simply returns 200 for any OPTIONS request. I know this isn't perfect, but works for now, in Fiddler.
Where I'm stuck - my Angular preflight calls aren't including the credentials. According to this answer, this is by design, as OPTIONS requests are designed to be anonymous. However, the Windows Authentication is stopping the request with a 401.
I've tried putting the [AllowAnonymous] attribute on my MessageHandler. On my dev computer, it works - OPTIONS verbs do not require authentication, but other verbs do. When I build and deploy to the test server, though, I am continuing to get a 401 on my OPTIONS request.
Is it possible to apply [AllowAnonymous] on my MessageHandler when using Windows Authentication? If so, any guidance on how to do so? Or is this the wrong rabbit hole, and I should be looking at a different approach?
UPDATE:
I was able to get it to work by setting both Windows Authentication and Anonymous Authentication on the site in IIS. This caused everything to allow anonymous, so I've added a global filter of Authorize, while retaining the AllowAnonymous on my MessageHandler.
However, this feels like a hack...I've always understood that only one authentication method should be used (no mixed). If anyone has a better approach, I'd appreciate hearing about it.
I used self-hosting with HttpListener and following solution worked for me:
I allow anonymous OPTIONS requests
Enable CORS with SupportsCredentials set true
var cors = new EnableCorsAttribute("*", "*", "*");
cors.SupportsCredentials = true;
config.EnableCors(cors);
var listener = appBuilder.Properties["System.Net.HttpListener"] as HttpListener;
if (listener != null)
{
listener.AuthenticationSchemeSelectorDelegate = (request) => {
if (String.Compare(request.HttpMethod, "OPTIONS", true) == 0)
{
return AuthenticationSchemes.Anonymous;
}
else
{
return AuthenticationSchemes.IntegratedWindowsAuthentication;
}};
}
I have struggled for a while to make CORS requests work within the following constraints (very similar to those of the OP's):
Windows Authentication for all users
No Anonymous authentication allowed
Works with IE11 which, in some cases, does not send CORS preflight requests (or at least do not reach global.asax BeginRequest as OPTIONS request)
My final configuration is the following:
web.config - allow unauthenticated (anonymous) preflight requests (OPTIONS)
<system.web>
<authentication mode="Windows" />
<authorization>
<allow verbs="OPTIONS" users="*"/>
<deny users="?" />
</authorization>
</system.web>
global.asax.cs - properly reply with headers that allow caller from another domain to receive data
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
if (Context.Request.HttpMethod == "OPTIONS")
{
if (Context.Request.Headers["Origin"] != null)
Context.Response.AddHeader("Access-Control-Allow-Origin", Context.Request.Headers["Origin"]);
Context.Response.AddHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, MaxDataServiceVersion");
Context.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
Context.Response.AddHeader("Access-Control-Allow-Credentials", "true");
Response.End();
}
}
CORS enabling
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// all requests are enabled in this example. SupportsCredentials must be here to allow authenticated requests
var corsAttr = new EnableCorsAttribute("*", "*", "*") { SupportsCredentials = true };
config.EnableCors(corsAttr);
}
}
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
}
This is a much simpler solution -- a few lines of code to allow all "OPTIONS" requests to effectively impersonate the app pool account. You can keep Anonymous turned Off, and configure CORS policies per normal practices, but then add the following to your global.asax.cs:
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
if (Context.Request.HttpMethod == "OPTIONS" && Context.User == null)
{
Context.User = System.Security.Principal.WindowsPrincipal.Current;
}
}
In our situation:
Windows Authentication
Multiple CORS origins
SupportCredentials set to true
IIS Hosting
we found that the solution was elsewhere:
In Web.Config all we had to do was to add runAllManagedModulesForAllRequests=true
<modules runAllManagedModulesForAllRequests="true">
We ended up to this solution by looking into a solution on why the Application_BeginRequest was not being triggered.
The other configurations that we had:
in Web.Config
<authentication mode="Windows" />
<authorization>
<allow verbs="OPTIONS" users="*" />
<deny users="?"/>
</authorization>
in WebApiConfig
private static string GetAllowedOrigins()
{
return ConfigurationManager.AppSettings["CorsOriginsKey"];
}
public static void Register(HttpConfiguration config)
{
//set cors origins
string origins = GetAllowedOrigins();
var cors = new EnableCorsAttribute(origins, "*", "*");
config.EnableCors(cors);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
BTW "*" cors origin is not compatible with Windows Authentication / SupportCredentials = true
https://learn.microsoft.com/en-us/aspnet/web-api/overview/security/enabling-cross-origin-requests-in-web-api#pass-credentials-in-cross-origin-requests
I solved it in a very similar way but with some details and focused on oData service
I didn't disable anonymous authentication in IIS since i needed it to POST request
And I've added in Global.aspx (Adding MaxDataServiceVersion in Access-Control-Allow-Headers) the same code than above
protected void Application_BeginRequest(object sender, EventArgs e)
{
if ((Context.Request.Path.Contains("api/") || Context.Request.Path.Contains("odata/")) && Context.Request.HttpMethod == "OPTIONS")
{
Context.Response.AddHeader("Access-Control-Allow-Origin", Context.Request.Headers["Origin"]);
Context.Response.AddHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept,MaxDataServiceVersion");
Context.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
Context.Response.AddHeader("Access-Control-Allow-Credentials", "true");
Context.Response.End();
}
}
and WebAPIConfig.cs
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
var cors = new EnableCorsAttribute("*", "*", "*");
cors.SupportsCredentials = true;
config.EnableCors(cors);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
and AngularJS call
$http({
method: 'POST',
url: 'http://XX.XXX.XXX.XX/oData/myoDataWS.svc/entityName',
withCredentials: true,
headers: {
'Content-Type': 'application/json;odata=verbose',
'Accept': 'application/json;odata=light;q=1,application/json;odata=verbose;q=0.5',
'MaxDataServiceVersion': '3.0'
},
data: {
'#odata.type':'entityName',
'field1': 1560,
'field2': 24,
'field3': 'sjhdjshdjsd',
'field4':'wewewew',
'field5':'ewewewe',
'lastModifiedDate':'2015-10-26T11:45:00',
'field6':'1359',
'field7':'5'
}
});
Dave,
After playing around with the CORS package, this is what caused it to work for me: [EnableCors(origins: "", headers: "", methods: "*", SupportsCredentials=true)]
I had to enable SupportsCredentials=true. Origins,Headers, and Methods are all set to "*"
disable anonymous authentication in IIS if you don't need it.
Than add this in your global asax:
protected void Application_BeginRequest(object sender, EventArgs e)
{
if ((Context.Request.Path.Contains("api/") || Context.Request.Path.Contains("odata/")) && Context.Request.HttpMethod == "OPTIONS")
{
Context.Response.AddHeader("Access-Control-Allow-Origin", Context.Request.Headers["Origin"]);
Context.Response.AddHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
Context.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
Context.Response.AddHeader("Access-Control-Allow-Credentials", "true");
Context.Response.End();
}
}
Make sure that where you enable cors you also enable the credential usage, like:
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
var cors = new EnableCorsAttribute("*", "*", "*");
cors.SupportsCredentials = true;
config.EnableCors(cors);
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
As you can see I enable CORS globally and using the application BeginRequest hook I authenticate all the OPTIONS requests for the api (Web Api) and the odata requests (if you use it).
This works fine with all browsers, in the client side remember to add the xhrFiled withCredentials like shown below.
$.ajax({
type : method,
url : apiUrl,
dataType : "json",
xhrFields: {
withCredentials: true
},
async : true,
crossDomain : true,
contentType : "application/json",
data: data ? JSON.stringify(data) : ''
}).....
I'm trying to find another solution avoiding to use the hook but without success until now,
I would use the web.config configuration to do something like the following:
WARNING THE CONFIGURATION BELOW DOESN'T WORK!
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
<authentication mode="Windows" />
<authorization>
<deny verbs="GET,PUT,POST" users="?" />
<allow verbs="OPTIONS" users="?"/>
</authorization>
</system.web>
<location path="api">
<system.web>
<authorization>
<allow users="?"/>
</authorization>
</system.web>
</location>
Other solutions I found on the web didn't work for me or seemed too hacky; in the end I came up with a simpler and working solution:
web.config:
<system.web>
...
<authentication mode="Windows" />
<authorization>
<deny users="?" />
</authorization>
</system.web>
Project properties:
Turn on Windows Authentication
Turn off Anonymous Authentication
Setup CORS:
[assembly: OwinStartup(typeof(Startup))]
namespace MyWebsite
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll);
This requires Microsoft.Owin.Cors assembly that is avaliable on NUget.
Angular initialization:
$httpProvider.defaults.withCredentials = true;
This is my solution.
Global.asax*
protected void Application_BeginRequest(object sender, EventArgs e)
{
if(!ListOfAuthorizedOrigins.Contains(Context.Request.Headers["Origin"])) return;
if (Request.HttpMethod == "OPTIONS")
{
HttpContext.Current.Response.Headers.Remove("Access-Control-Allow-Origin");
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", Context.Request.Headers["Origin"]);
HttpContext.Current.Response.StatusCode = 200;
HttpContext.Current.Response.End();
}
if (Request.Headers.AllKeys.Contains("Origin"))
{
HttpContext.Current.Response.Headers.Remove("Access-Control-Allow-Origin");
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", Context.Request.Headers["Origin"]);
}
}

Tomcat displaying welcome-page before filter for jsp is executed

I am using Tomcat 6.0.36 and the welcome-page is /Login.jsp
I have a filter in place so that it can display a different login page for mobile devices.
It works with URL mywebsite.com/Login.jsp, but the filter is bypassed when the URL is just mywebsite.com.
Is there a way to force it to execute?
I have found this page but it doesn't work in my case:
How to map a filter for welcome-file in web.xml for Tomcat?
Thanks
My web.xml:
<welcome-file-list>
<welcome-file>/Login.jsp</welcome-file>
</welcome-file-list>
...
<filter>
<display-name>LoginPageFilter</display-name>
<filter-name>LoginPageFilter</filter-name>
<filter-class>filters.LoginPageFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LoginPageFilter</filter-name>
<url-pattern>/Login.jsp</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
The filter - I had deleted it and put one quickly back together
public class LoginPageFilter implements Filter
{
public LoginPageFilter() { }
public void init ( FilterConfig fConfig ) throws ServletException { }
public void doFilter ( ServletRequest request, ServletResponse response,
FilterChain chain ) throws IOException,
ServletException
{
System.out.println ( "Filter being executed" );
chain.doFilter(request, response);
}
public void destroy() { }
}
If the URL is
http://localhost:8080/gymfit/Login.jsp
then the message is printed to the console.
When the URL is
http://localhost:8080/gymfit/
the same page is displayed but the message is not printed out to the console
look at this line, this means only the request to '/Login.jsp' will the filter being executed
<url-pattern>/Login.jsp</url-pattern>
if you want to apply this filter to all the path, change the config to:
<url-pattern>/*</url-pattern>

invalid Assertion for SAML 2.0 Bearer Assertion for SalesForce.com

I am trying to do "SAML 2.0 Bearer Assertion for SalesForce"
I am getting {"error":"invalid_grant","error_description":"invalid assertion"}
Is there any way to validate bearer assertion at salesforce?
I did following in my code
String environment = "https://login.salesforce.com/services/oauth2/token?saml=MgoTx78aEPRbRaz0CkRqjaqrhP3sCa7w7.Y5wbrpGMNT07zKRYwcNWf0zs";
Map<String, String> map = new HashMap<String, String>();
HttpClient httpclient = new HttpClient();
PostMethod post = new PostMethod(environment);
post.addParameter("grant_type", "urn:ietf:params:oauth:grant-type:saml2-bearer");
post.addParameter("client_assertion", Base64.encode(samlResponse.getBytes()));
post.addParameter("client_assertion_type","urn:ietf:params:oauth:client_assertion_type:saml2-bearer");
post.addParameter("format", "json");
String accessToken= null;
String instanceUrl = null;
try {
httpclient.executeMethod(post);
JSONObject authResponse = new JSONObject(new JSONTokener(new InputStreamReader(post.getResponseBodyAsStream())));
System.out.println(authResponse.toString());
} catch (Exception e) {
e.printStackTrace();
} finally {
post.releaseConnection();
}
I have generated following assertion
<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID="s2d3a451cf30560ca819118cf5785e722ea6da7b64" IssueInstant="2012-03-06T12:34:13Z"
Version="2.0">
<saml:Issuer>http://localhost:8080/opensso
</saml:Issuer>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
<ds:Reference URI="#s2d3a451cf30560ca819118cf5785e722ea6da7b64">
<ds:Transforms>
<ds:Transform
Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<ds:DigestValue>seHyxsFzsHCs0GaY7usF0DfMV58=
</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue> signature.....</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate> certificate.....</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</ds:Signature>
<saml:Subject>
<saml:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
NameQualifier="http://localhost:8080/opensso" SPNameQualifier="https://saml.salesforce.com">deepakmule</saml:NameID>
<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<saml:SubjectConfirmationData
NotOnOrAfter="2012-03-06T12:44:13Z"
Recipient="https://login.salesforce.com/?saml=MgoTx78aEPRbRaz0CkRqjaqrhP3sCa7w7.Y5wbrpGMNT07zKRYwcNWf0zs" />
</saml:SubjectConfirmation>
</saml:Subject>
<saml:Conditions NotBefore="2012-03-06T12:34:13Z"
NotOnOrAfter="2012-03-06T12:44:13Z">
<saml:AudienceRestriction>
<saml:Audience>https://saml.salesforce.com</saml:Audience>
</saml:AudienceRestriction>
</saml:Conditions>
<saml:AuthnStatement AuthnInstant="2012-03-06T12:34:13Z"
SessionIndex="s27fb03a2b73bd8dc6846851bed7885b85e1d9ed6f">
<saml:AuthnContext>
<saml:AuthnContextClassRef> urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport
</saml:AuthnContextClassRef>
</saml:AuthnContext>
</saml:AuthnStatement>
<saml:AttributeStatement>
<saml:Attribute Name="userid">
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">deepakmule</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
</saml:Assertion>
Are you trying to use the same IDP as you have configured for SSO for your Org, or are you trying to use the certificate added to a Remote Access application?
If you're trying to use your SSO configuration, then this looks pretty good - I'd check the SAML assertion validator
If you're trying to use the Bearer flow with a Remote Access application, then I'd look at the following
1) The Issuer should be the your Consumer Key from the remote access app ( the oauth client_id )
2) Post to our regular token endpoint
3) Use the salesforce username as the subject
4) Shorten the lifetime of your assertion to a minute

Resources