I am trying to return a list of roles back to a mobile client device from the WL server
In the createIdentity method of my LoginModule I added the following code
HashMap<String, Object> customAttributes = new HashMap<String, Object>();
customAttributes.put("AuthenticationDate", new Date());
Set<String> groups = new HashSet<String>();
groups.add("Managers");
groups.add("Users");
UserIdentity identity = new UserIdentity(loginModule, USERNAME, "Fred Flintstone", groups, customAttributes, PASSWORD);
The display Name "Fred Flintstone" gets returned to the mobile device, the custom attributes get returned, but the group information seems to get lost somewhere.
I get the following displayed in the mobile device logs
"BasicAuthRealm":{"userId":"user1","attributes":{"AuthenticationDate":"Thu Nov 14 22:39:35 EST 2013"},"isUserAuthenticated":1,"displayName":"Fred Flintstone"},"WL-Authentication-Success":{"BasicAuthRealm":{"userId":"user1","attributes":{"AuthenticationDate":"Thu Nov 14 22:39:35 EST 2013"},"isUserAuthenticated":1,"displayName":"Fred Flintstone"}},
I am running WL 6.0.0.1 Enterprise edition and running against a Liberty server v8.5.5.0
Any ideas?
The groups object is not sent back to the client after the user successfully authenticates. The only parts of the UserIdentity object that are sent back are the name, displayName, and the attributes. I do not know the reason that the groups aren't sent back. Perhaps the objects purpose was only meant for the server and was never intended to be used by the client.
The unfortunate but easy workaround is to add any information you need to know about your group to your attributes object.
Related
I am attempting to create a c# Windows service that periodically captures information from Microsoft Graph. This always fails with a "AADSTS700016: Application not found in the directory of our Microsoft 365 account."
Prior to trying to write this service, I created a test program to do same access. I set up an application in Azure Active Directory with a secret. When I run the exact same code in the this test program with the correct tenant ID, client ID and the secret, the program works fine from whatever computer I run it from.
However, the c# service always fails with the error noted above and detailed below. Can't be an issue with permissions as access IDs and secret are the same. I have even tried creating a separate application in AAD but get the same error.
Common c# statements:
var scopes = new[] { "https://graph.microsoft.com/.default" };
// Multi-tenant apps can use "common",
// single-tenant apps must use the tenant ID from the Azure portal
// using Azure.Identity;
var options = new TokenCredentialOptions
{
AuthorityHost = AzureAuthorityHosts.AzurePublicCloud
};
var clientSecretCredential = new ClientSecretCredential(
tenantId, clientID, clientSecret, options); ;
graphClient = new GraphServiceClient(clientSecretCredential, scopes);
if (graphClient == null) throw new Exception("Unable able to obtain a GraphClient for this pass");
var groups = await graphClient.Groups.Request().Select(x => new { x.Id, x.DisplayName }).GetAsync();
Any help appreciated. I am sure it is something simple, but clueless at the moment.
Inner Exception 1:
AuthenticationFailedException: ClientSecretCredential authentication failed: AADSTS700016: Application with identifier 'c62d4eb9-587d-4b7f-a4d8-0640747f0958' was not found in the directory xxxx. This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You may have sent your authentication request to the wrong tenant.
Trace ID: c8bfac15-c9d6-407e-89e7-36f21fb18300
Correlation ID: 9c8d25ad-c275-43c0-93c1-d295608e9f92
Timestamp: 2022-08-13 15:52:36Z
And just like that, I found the error. Good old global vs local variable name. I hope no one spent too much time on this.
I am using ReportExecutionServiceSoapClient in .Net Core i got the latest version of .net Core and tried to get a report from reporting services to work. after I've used the WCF connection service I was able to add the code with looks like bellow
// Instantiate the Soap client
ReportExecutionServiceSoap rsExec = new ReportExecutionServiceSoapClient(ReportExecutionServiceSoapClient.EndpointConfiguration.ReportExecutionServiceSoap);
// Create a network credential object with the appropriate username and password used
// to access the SSRS web service
string historyID = null;
TrustedUserHeader trustedUserHeader = new TrustedUserHeader();
ExecutionHeader execHeader = new ExecutionHeader();
// Here we call the async LoadReport() method using the "await" keyword, which means any code below this method
// will not execute until the result from the LoadReportAsync task is returned
var taskLoadReport = rsExec.LoadReportAsync(reportPath, historyID);
// By the time the LoadReportAsync task is returned successfully, its "executionInfo" property
// would have already been populated. Now the remaining code in this main thread will resume executing
string deviceInfo = null;
string format = "EXCEL";
// Now, similar to the above task, we will call the RenderAsync() method and await its result
var taskRender = await rsExec.RenderAsync(renderReq);
When it hist renderAsync all falls apart because the credentials for the service are not set anywhere. I've tried to Login async with no success. Also I've tried to set the credentials with SetExecutionCredentialsAsync but I've got and error saying "The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server was 'NTLM'." I don't know how to change that for ReportExecutionServiceSoapClient.
I have read some posts in which Microsoft guys says that the authentication with a soap is not resolved but for me it seems so close to be true. I feel like I am missing something.
Technology stack: VS 2017, .net Core web api, ssrs 2016, sql server 2016 standard
How can I authenticate the user for this call?
I know this is an old question but I had the same issue and stumbled onto the answer.
After creating the ReportExecutionServiceSoap object you can specify the username and password in the ClientCredentials. I've had success with this using the Basic client credential type. Be sure you are using HTTPS, otherwise your password is sent in plaintext to the reporting server. I also recommend storing the user/password in a secure place and not code.
BasicHttpBinding rsBinding = new BasicHttpBinding();
rsBinding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
rsBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
EndpointAddress rsEndpointAddress = new EndpointAddress("https://servername/ReportServer/ReportExecution2005.asmx");
var rsExec = new ReportExecutionServiceSoapClient(rsBinding, rsEndpointAddress);
rsExec.ClientCredentials.UserName.UserName = "username";
rsExec.ClientCredentials.UserName.Password = "pass";
I am debugging confirmation email flow when signing up a new User in Asp.Net Core web application with Identity Server 4.
Since I had already signed up with my actual email, to reuse it, I modified the UserName and Email in AspNetUsers table using SQL Update to some random value.
Now when I am signing up with the original email again. I am getting a duplicate user error
result = await _userManager.CreateAsync(user, model.Password);
I have already:
Cleared browser cache.
Closed local IIS Express
Restarted Visual Studio.
Used_userManager.DeleteAsync() after updating the UserName and Email back to original values but this gives an Microsoft.AspNetCore.Identity.IdentityError with description Optimistic concurrency failure, object has been modified.
On running this query on Sql Server
select * from INFORMATION_SCHEMA.COLUMNS where COLUMN_NAME in ( 'UserName' , 'Email')
I get the following:
I know that this is not a good practice to mess with backend, but this is development environment and I could continue my work with another email.
I would request readers to help in understanding how the User could be safely scorched to be able to reuse the email.
Appreciate your time
I agree with Kyle's comment and to further speed up your debug process you should note that if you use gmail to do this you can debug this process using one email.
from google/gmails perspective myaccount#gmail.com == my.acount#gmail.com == m.y.a.c.c.ount#gmail.com etc etc just try it out, google disregards all period characters in the email. you can enumerate/exhaust ~2^8 emails (in this example) if you just enumerate through the local-part of the e-mail address. but from your applications side, myaccount#gmail.com is not the same as my.account#gmail.com, ie they are different user accounts. Basically you can use one email to test out this feature of yours without having to delete the user.
Here is how I did it and finally got passed the pesky "concurrency failure" error message... This works in ASP.NET CORE 2.2
Obtain the user object through the FindByName method first.
Remove the user from their assigned Role (in this case I hard coded "Admin" because that is the role I'm interested in but fill in your own), then delete the user.
//Delete user.
//Obtain the user object through the FindByName method first.
//Remove the user from their assigned Role, then delete the user.
var userManager = serviceProvider.GetRequiredService<UserManager<ApplicationUser>>();
ApplicationUser delAppUser = new ApplicationUser
{
Email = "SomeEmailForindividualAdminUser",
UserName = "SomeUsernameindividualAdminUser"
};
Task <ApplicationUser> taskGetUserAppUser = userManager.FindByNameAsync(delAppUser.UserName);
taskGetUserAppUser.Wait();
Task<IdentityResult> taskRemoveFromRoleAppUser = userManager.RemoveFromRoleAsync(taskGetUserAppUser.Result, "Admin");
taskRemoveFromRoleAppUser.Wait();
Task<IdentityResult> taskDeleteAppUser = userManager.DeleteAsync(taskGetUserAppUser.Result);
taskDeleteAppUser.Wait();
We use Apache Shiro to authenticate and authorize users using our active directory.
Authenticating the user and mapping groups works just fine using the following config:
adRealm = org.apache.shiro.realm.activedirectory.ActiveDirectoryRealm
adRealm.searchBase = "OU=MYORGANIZATION,DC=MYDOMAIN,DC=COM"
adRealm.groupRolesMap = "CN=SOMEREADGROUP":"read","CN=SOMEMODIFYGROUP":"modify","CN=SOMEADMINGROUP":"admin"
adRealm.url = ldaps://my.ad.url:636
adRealm.systemUsername= systemuser
adRealm.systemPassword= secret
adRealm.principalSuffix= #myorganization.mydomain.com
I can authenticate in Shiro using the following lines:
String user = "someuser";
String password = "somepassword";
Subject currentUser = SecurityUtils.getSubject ();
if (!currentUser.isAuthenticated ()){
UsernamePasswordToken token = new UsernamePasswordToken (user,
password);
token.setRememberMe (true);
currentUser.login (token);
}
We now want to get more user information from our ActiveDirectory. How can I do that using Apache Shiro? I was not able to find anything about it in the documentation.
In the source code of ActiveDirectoryRealm I found this line:
NamingEnumeration answer = ldapContext.search(searchBase, searchFilter, searchArguments, searchCtls);
So the first part of the answer is clear: use the ldapContext to search something in it. But how can I retrieve the LdapContext?
It depends on what you are trying to do. Are you just trying to reuse the context to run a query for something other then authentication or authorization? Or are you trying to change the behavior of the query in the AD realm?
If the latter, you would need to extend the ActiveDirectoryRealm and override the queryForAuthorizationInfo() method.
Are you implementing something that is custom for your environment?
(updated)
A couple things:
The realm has access to the LdapContext in the two touch points: queryForAuthenticationInfo() and queryForAuthorizationInfo(), so if you extend the AD realm or AbstractLdapRealm you should already have it. You could change the query to return other info and add the extra info to your Principal. Then you have access to that info directly from your Subject object.
Your realms, are not required to be singletons.
If you want to do some other sort of user management (email all users with a given role, create a user, etc). Then you could create a LdapContextFactory in your shiro.ini, and use the same instance for multiple objects.
[main]
...
ldapContextFactory = org.apache.shiro.realm.ldap.JndiLdapContextFactory
ldapContextFactory.systemUsername = foobar
ldapContextFactory.systemPassword = barfoo
adRealm = org.apache.shiro.realm.activedirectory.ActiveDirectoryRealm
adRealm.ldapContextFactory = $ldapContextFactory
...
myObject = com.biz.myco.MyObject
myObject.ldapContextFactory = $ldapContextFactory
This would work well if myObject is interacting with other Shiro components, (responding to events, etc), but less so if you need access to it from another framework. You could work around this by some sort of static initialization that builds creates the ldapContextFactory, but in my opinion, this is where the sweet spot of using the shiro.ini ends, and where using Guice or Spring shines.
I've spend hours installing a custom login service in embedded Jetty 9.1.0.v20131115 and RESTEasy 3.0.5.Final. My login service will look users up in a database and assign them roles. It looks something like this:
final Constraint restConstraint = new Constraint();
restConstraint.setName(Constraint.__BASIC_AUTH);
restConstraint.setRoles(new String[]{"user", "admin");
restConstraint.setAuthenticate(true);
final ConstraintMapping restConstraintMapping = new ConstraintMapping();
restConstraintMapping.setConstraint(restConstraint);
restConstraintMapping.setPathSpec("/api/*");
final ConstraintSecurityHandler restSecurityHandler = new ConstraintSecurityHandler();
final LoginService myLoginService = new MyLoginService();
restSecurityHandler.setAuthenticator(new BasicAuthenticator());
restSecurityHandler.setRealmName(myLoginService.getName());
restSecurityHandler.addConstraintMapping(restConstraintMapping);
restSecurityHandler.setLoginService(myLoginService);
I have users joe-user who has the role of user, and jane-admin who has both user and admin roles. I have a REST GET resource named my-resource marked with:
#RolesAllowed("admin")
When I do an HTTP GET on my-resource, the browser correctly requests credentials, and I can login as either joe-user or jane-admin. The problem is that either user is allowed to GET my-resource!!
I've traced through some of the Jetty code, and indeed, as a result of my login service above, Jetty asks the login user which roles is supported. Unfortunately, Jetty will accept any of the roles I've specified in restConstraint.setRoles(new String[]{"user", "admin"), regardless of the user.
Apparently it is the RESTEasy layer that is supposed to recognize the #RolesAllowed("admin") annotation and validate the user. But how do I get RESTEasy to do that?
With a little help from the RESTEasy documentation, I found out that in order for RESTEasy to honor the #RolesAllowed annotations, one must turn on the resteasy.role.based.security context parameter switch in the web.xml file; or programatically, as I am doing:
final ServletHolder servletHolder = new ServletHolder(new HttpServlet30Dispatcher());
servletHolder.setInitParameter("javax.ws.rs.Application", MyApplication.class.getName());
servletHolder.setInitParameter("resteasy.role.based.security", String.valueOf(true));
contextHandler.addServlet(servletHolder, "/api/*");