I've created a custom return url parser to determine which tenant is calling the login page, but when loading the login page I get a null reference on one of the returnUrlparsers.
at IdentityServer4.Services.ReturnUrlParser.ParseAsync(String returnUrl) in C:\local\identity\server4\IdentityServer4\src\IdentityServer4\Services\ReturnUrlParser.cs:line 38
at IdentityServer4.Services.DefaultIdentityServerInteractionService.GetAuthorizationContextAsync(String returnUrl) in C:\local\identity\server4\IdentityServer4\src\IdentityServer4\Services\DefaultIdentityServerInteractionService.cs:line 54 at IdentityServer4.Quickstart.UI.AccountController.BuildLoginViewModelAsync(String returnUrl) in D:\git\identity-server\IdentityServer4\src\Fifthplay.IdentityServer4\Quickstart\Account\AccountController.cs:line 346
I've created a custom implementation of the IReturnUrlParser
public class MyReturnUrlParser: IReturnUrlParser
{
private IdentityServerConfigurationContext _identityServicecontext { get; set; }
private ILogger<MyReturnUrlParser> _logger;
public MyReturnUrlParser(IdentityServerConfigurationContext context, ILogger<MyReturnUrlParser> logger)
{
_identityServicecontext = context;
_logger = logger;
}
public bool IsValidReturnUrl(string returnUrl)
{
return true;
}
public Task<AuthorizationRequest> ParseAsync(string returnUrl)
{
if(string.IsNullOrEmpty(returnUrl))
return null;
... doing some custom stuff
return Task.FromResult(authorizationRequest);
}
registered it to DI
services.AddTransient<IReturnUrlParser, MyReturnUrlParser>();
When the Login page is loaded I see that the "MyReturnUrlparser" is hit, it return a null because it is just browsing, to the login page but then afterwards a null reference is thrown.
https://github.com/IdentityServer/IdentityServer4/blob/c2255fae51ecac3048f7281fc7590bee085f30f2/src/Services/Default/ReturnUrlParser.cs
My next guess is that for some reason the default URL parser is null when looping through all available ReturnUrl parsers. Did anybody experience this behavior before? Or am I missing something obvious?
The default URL parser can't be null because you would have got a NullReferenceException on line 36 in ReturnUrlParser.cs:
if (parser.IsValidReturnUrl(returnUrl))
Have you pasted the whole stack trace? Is the exception thrown inside the `ParseAsync' method?
One possibility is that a URL parser is returning true for a URL that it shouldn't process and is falling over inside the ParseAsync because it is encountering something unexpected. I notice that you always return true from IsValidReturnUrl which means that your URL parser will be parsing all URLs.
You could try moving your register line:
services.AddTransient<IReturnUrlParser, MyReturnUrlParser>();
before the call services.AddIdentityServer() so that your parser is first in the collection of parser:
Related
I am trying to integrate Hystrix javanica into my existing java EJB web application and facing 2 issues with running it.
When I try to invoke following service it always returns response from fallback method and I see that the Throwable object in fallback method has "com.netflix.hystrix.exception.HystrixTimeoutException" exception.
Each time this service is triggered, HystrixCommad and fallback methods are called multiple times around 50 times.
Can anyone suggest me with any inputs? Am I missing any configuration?
I am including following libraries in my project.
project libraries
I have setup my aspect file as follows:
<aspectj>
<weaver options="-verbose -showWeaveInfo"></weaver>
<aspects>
<aspect name="com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect"/>
</aspects>
</aspectj>
Here is my config.properties file in META-INF/config.properties
hystrix.command.default.execution.timeout.enabled=false
Here is my rest service file
#Path("/hystrix")
public class HystrixService {
#GET
#Path("clusterName")
#Produces({ MediaType.APPLICATION_JSON })
public Response getClusterName(#QueryParam("id") int id) {
ClusterCmdBean clusterCmdBean = new ClusterCmdBean();
String result = clusterCmdBean.getClusterNameForId(id);
return Response.ok(result).build();
}
}
Here is my bean class
public class ClusterCmdBean {
#HystrixCommand(groupKey = "ClusterCmdBeanGroup", commandKey = "getClusterNameForId", fallbackMethod = "defaultClusterName")
public String getClusterNameForId(int id) {
if (id > 0) {
return "cluster"+id;
} else {
throw new RuntimeException("command failed");
}
}
public String defaultClusterName(int id, Throwable e) {
return "No cluster - returned from fallback:" + e.getMessage();
}
}
Thanks for the help.
If you want to ensure you are setting the property, you can do that explicitly in the circuit annotation itself:
#HystrixCommand(commandProperties = {
#HystrixProperty(name = "execution.timeout.enabled", value = "false")
})
I would only recommend this for debugging purposes though.
Something that jumps out to me is that Javanica uses AspectJ AOP, which I have never seen work with new MyBean() before. I've always have to use #Autowired with Spring or similar to allow proxying. This could well just be something that is new to me though.
If you set a breakpoint inside the getClusterNameForId can you see in the stack trace that its being called via reflection (which it should be AFAIK)?
Note you can remove commandKey as this will default to the method name. Personally I would also remove groupKey and let it default to the class name.
I have an issue finding the exception cause in FallBackFactory, mine is a old application, so i can not use spring cloud approach (with annotations etc..)
I found the below solution, but still not working for me:
Issue in getting cause in HystrixFeign client fallback
Here is the code i have:
public static class ProfileFallbackFactory implements ProfileProxy, FallbackFactory<ProfileFallbackFactory> {
final Throwable cause;
public ProfileFallbackFactory() {
this(null);
}
ProfileFallbackFactory(Throwable cause) {
this.cause = cause;
}
#Override
public ProfileFallbackFactory create(Throwable cause) {
LOG.info("Profile fallback create "+cause);
return new ProfileFallbackFactory(cause);
}
public Profile getProfile(String id) {
}
instance creation:
profileProxy = new HystrixFeign.Builder().setterFactory(new CustomSetterFactory())
.decode404()
.decoder(new GsonDecoder(gsonWithDateFormat))
.encoder(new GsonEncoder(gsonWithDateFormat))
.errorDecoder(new profileProxyErrorDecoder())
.target(ProfileProxy.class,profileServiceUrl, (FallbackFactory<ProfileFallbackFactory>)new ProfileFallbackFactory());
There is a logger added in ProfileProxyErrorDecoder class, but this logger is not found in logs. I can see com.netflix.hystrix.exception.HystrixRuntimeException in server logs
Can someone please point me where i am going wrong?
I am using Angular in an ASP.NET Core with ASP.NET Identity application.
I have the following controller action
[HttpGet("users/{userId:int:min(1)}/notes"), Authorize]
public async Task<IActionResult> GetNotesBy(userId) {
var data = _service.getNotesBy(userId);
return Ok(data);
} // GetNotesBy
I would like to restrict the access to the API so:
If a user is authenticated than it can only access its notes.
I want to prevent an authenticate user with ID=X to access the notes of a user with ID=Y. How can I block an user in this situation?
This is what resource based authorization is aimed at.
As resource based authorization requires the actual resource it needs to happen imperatively, inside your controller.
The following is for ASP.NET Core RC1.
So, let's assume your getNotesBy returns a Notes class, and you have a few operations, read, write, update, delete.
First we need to define the operations. There's a suitable base class in Microsoft.AspNet.Authorization.Infrastructure, OperationAuthorizationRequirement. So we'd do something like this.
public static class Operations
{
public static OperationAuthorizationRequirement Create =
new OperationAuthorizationRequirement { Name = "Create" };
public static OperationAuthorizationRequirement Read =
new OperationAuthorizationRequirement { Name = "Read" };
public static OperationAuthorizationRequirement Update =
new OperationAuthorizationRequirement { Name = "Update" };
public static OperationAuthorizationRequirement Delete =
new OperationAuthorizationRequirement { Name = "Delete" };
}
So now we have our operations, we think about how we handle authorization. You have two ways operations can succeed, if the current user owns the notes, or the current user is an admin. This equates to two handlers for a single requirement/operation.
The admin one is easy, it would look something like this;
public class AdminAuthorizationHander :
AuthorizationHandler<OperationAuthorizationRequirement, Notes>
{
protected override void Handle(AuthorizationContext context,
OperationAuthorizationRequirement requirement,
Document resource)
{
var isSuperUser = context.User.FindFirst(c => c.Type == "Superuser" &&
c.Value == "True");
if (isSuperUser != null)
{
context.Succeed(requirement);
return;
}
}
}
Here we're looking for a Superuser claim with a value of True. If that's present we succeed the requirement. You can see from the method signature we're taking the OperationAuthorizationRequirement and a resource, the Notes class. This handler doesn't limit itself to a single operation, admins have rights to every operation.
Now we can write the handler which looks for the actual user.
public class NotesAuthorizationHandler :
AuthorizationHandler<OperationAuthorizationRequirement, Notes>
{
protected override void Handle(AuthorizationContext context,
OperationAuthorizationRequirement requirement,
Notes resource)
{
if (context.User.Name == resource.Owner)
{
context.Succeed(requirement);
}
}
}
Here we are writing something that will work for all resources, and checks an Owner property on the resource against the name of the current user.
So we have two handlers now for a single requirement, the OperationAuthorizationRequirement.
Now we need to register our handlers. In startup.cs you register handlers in DI in the ConfigureServices() method. After the call to services.AddAuthorization() you need to put your handlers into DI. You would do this like so;
services.AddSingleton<IAuthorizationHandler, AdminAuthorizationHandler>();
services.AddSingleton<IAuthorizationHandler, NotesAuthorizationHandler>();
You can adjust the scope from Singleton to whatever you like if you are taking things like a DbContext.
Finally we're almost ready to call this, but first you need to change your controller constructor to take an instance of IAuthorizationService. Once you have that you can call AuthorizeAsync() and away you go.
[Authorize]
public class NotesController : Controller
{
IAuthorizationService _authorizationService;
public NotesController(IAuthorizationService authorizationService)
{
_authorizationService = authorizationService;
}
[HttpGet("users/{userId:int:min(1)}/notes"), Authorize]
public async Task<IActionResult> GetNotesBy(userId)
{
var resource = _service.getNotesBy(userId);
if (await authorizationService.AuthorizeAsync(User, resource, Operations.Read))
{
return Ok(data);
}
return new ChallengeResult();
}
}
So what you are doing is getting your resource, and then authorizing the current user against it and the operation. When this happens all handlers which can handle that resource and operation will get called. As there are multiple handlers any one can succeed and allow access.
I have a Spring controller defined like this with 2 request mappings, one using localDAO and the other using dependencyDAO. LocalDAO classes exist in my project and DependencyDAO classes are imported via maven dependency:
#RestController
#PreAuthorize("hasRole('USER')")
public class MyController
#Autowired
private localDAO LocalDAO; // dao classes exist in my project
#Autowired
private DependencyDAO dependencyDAO; // dao classes imported via maven dependency
...
#RequestMapping("/getUsingLocalDAO")
private String getUsingLocalDAO(
#JsonProperty("param") String param) {
localDAO.get(param) ... // <== this never null
}
#RequestMapping("/getUsingDependencyDAO")
private String getUsingDependencyDAO(
#JsonProperty("param") String param) {
dependencyDAO.get(param) ... // <== this always null
}
...
My dao beans are defined in another class:
#Configuration
public class DaoBeans {
#Bean
public LocalDAO localDAO() throws Exception {
return new LocalDAOImpl();
}
#Bean
public DependencyDAO dependencyDAO () throws Exception {
return new DependencyDAOImpl();
}
...
I am doing an $http.post from Angular like this:
$http.post('getUsingLocalDAO', $.param($scope.parameters), {
headers : {
"content-type" : "application/x-www-form-urlencoded"
}
}).success(function(data) {
...
}).error(function(data) {
...
$http.post('getUsingDependencyDAO', $.param($scope.parameters), {
headers : {
"content-type" : "application/x-www-form-urlencoded"
}
}).success(function(data) {
...
}).error(function(data) {
...
Both posts are identical except for the method they execute.
When stepping through the debugger I can see all the dao beans being created.
When I call getUsingLocalDAO everything works as expected.
But, when I call getUsingDependencyDAO every #Autowired object is null.
I believe I am getting different instances of MyController. One managed by Spring and one not; or at least not instantiated properly.
I make these calls in succession. It doesn't matter what order they are in.
I tried injecting the servlet context via #Autowired to get the bean manually but it is always null in getUsingDependencyDAO as well.
I tried using application context aware and although I see the context setter being set in the debugger the context is always null in getUsingDependencyDAO.
If I wrap the two calls in a third request mapping like so everything works well (no null objects).
#RequestMapping("/getUsingBothDAO")
private String getUsingBothDAO(
#JsonProperty("param") String param) {
getLocalDAO(param);
getDependencyDAO(param);
...
}
I am using Spring-Boot 4.1.5. My project is 100% annotation driven and has no .xml configurations. The only difference between the two request mappings is that one uses a bean from a dependency and one does not.
I have been searching for an answer to this problem for 3 days and have not found anything close to what I am experiencing.
Can anyone shed some light as to what I am doing wrong? Any help would be greatly appreciated.
Thanks.
Ok, I solved the problem. My example code above is not entirely accurate. The request method that was giving me nulls was defined as a private method while the one that worked was defined as public as its supposed to be. Originally the private method was not a request method and that modifier remained after the change. I changed it to public and everything is working.
It was just coincidence that the private method was from an imported project. It's curious that Spring did not throw an error that the request mapping didn't exist on the private method or something to that effect.
Thanks to anyone who looked at this and was trying to figure it out.
I've got a spring boot app with angular on the frontend.
I'm using ui-router with html5 mode and I would like spring to render the same index.html on all unknown routes.
// Works great, but it also overrides all the resources
#RequestMapping
public String index() {
return "index";
}
// Seems do be the same as above, but still overrides the resources
#RequestMapping("/**")
public String index() {
return "index";
}
// Works well but not for subdirectories. since it doesn't map to those
#RequestMapping("/*")
public String index() {
return "index";
}
So my question is how can i create a fallback mapping but that lets through the resources?
The simplest way I found was implementing a custom 404 page.
#Configuration
public class MvcConfig {
#Bean
public EmbeddedServletContainerCustomizer notFoundCustomizer(){
return new NotFoundIndexTemplate();
}
private static class NotFoundIndexTemplate implements EmbeddedServletContainerCustomizer {
#Override
public void customize(ConfigurableEmbeddedServletContainer container) {
container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/"));
}
}
}
Neil McGuigan propopes a HandlerInterceptor, but I wasn't able to understand how that would be implemented. I't would be great to see how this would be implemented, as single page applications using html5 history push state will want this behaviour. And I have not really found any best practices to this problem.
Define an entry point for all the urls in your web.xml file like following:
<error-page>
<error-code>404</error-code>
<location>/Error_404</location>
</error-page>
This would catch all 404 i.e page not found errors and throw /Error_404 url, catch it in your controller and push to desired place.
You could handle all of non-matched requests in 404 handler. Take a look at this, there are several options
Another thing you could do is to override DefaultAnnotationHandlerMapping, and to add a sort of catch-all controller by setting the defaultHandler property.
public void setDefaultHandler(Object defaultHandler)
Set the default handler for this handler mapping. This handler will be returned if no specific mapping was found.
Default is null, indicating no default handler.
try to use #ExceptionHandler in your controller, change Exception.class by the class of the exception who you want to handle.
#ExceptionHandler(value = {Exception.class})
public String notFoundErrorHandler() {
return "index";
}