Cannot add roles to a group in keycloak-admin-client - spring

Technology.
java11
springboot 2.7.2
docker quay.io/keycloak/keycloak:19.0.2
Prerequisite.
realmService.create(); // 大元のRealmなので先頭に実行
clientScopeService.create(); // clientに設定するのでclientsServiceより先に実行
clientsService.create();
realmRoleService.create();
groupService.create(); // group に roleを設定するのでrealmRoleServiceより後に実行
// groupService.create()
public void create() {
for (GroupEnum groupEnum : GroupEnum.values()) {
GroupRepresentation groupRepresentation = new GroupRepresentation();
groupRepresentation.setName(groupEnum.getName());
List<String> roles = Lists.newArrayList();
roles.add(RoleEnum.READ.getName());
roles.add(RoleEnum.WRITE.getName());
groupRepresentation.setRealmRoles(roles);
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
getGroupsResource().add(groupRepresentation);
}
}
After creating the realm role in No4.
When I try to add a realm role while creating a group in No5, I cannot.
Confirmed that the role has been added before attempting to add it to the group.
Confirming that realm roles can be added to groups in the keycloak GUI.

Related

Error Login failed for user ''.' when i try to connect through azure active directory interceptor in my solution

I am implementing the Azure active directory interceptor to connect my solution through active directory. My username is already added in the azure database and I am using below class as interceptor with like below.
public class AadAuthenticationDbConnectionInterceptor : DbConnectionInterceptor
{
public override InterceptionResult ConnectionOpening();
public override async Task<InterceptionResult> ConnectionOpeningAsync();
}
Its connecting fine through SSMS with the account but when I connect through my solution in visual studio it gives me below error:
Microsoft.Data.SqlClient.SqlException: 'Login failed for user ''.'
Help will be appreciated.
You need to use Azure Identity:
var resourceScope = "https://database.windows.net/.default";
var tokenCredential = new DefaultAzureCredential();
var accessToken = tokenCredential.GetToken(
new TokenRequestContext(scopes: new string[] { resourceScope }) { }
);
using (var connection = new SqlConnection($"Server={dbserver};Initial Catalog={db};Encrypt=True;TrustServerCertificate=True"))
{
connection.AccessToken = accessToken.Token;
connection.Open();
}

Allow multiple Microsoft App IDs for chat bot

I have a chatbot that works on localhost, and it's working great. I then added a new Bot Channels Registration on Azure for testing, and that works fine too. I did it by taking its Microsoft App ID and password and putting it into my appsettings.json file.
However, I need to add another Bot Channels Registration. When I test it on that registration, my bot returns a 401 unauthorized error. It's because that has a new App ID and password. But I already put the App ID and password from my first registration channel. I need both of them to work.
How can I allow my chatbot to accept multiple App IDs and passwords? Or how do I get rid of that level of security completely (ie. Allow ALL App IDs and passwords)?
The answer, as #Mick suggested, is to create a bot adapter for each channel. You can do something like this if you want it really dynamic:
BotController.cs
[HttpPost, HttpGet]
public async Task PostAsync()
{
var credentialProvider = new SimpleCredentialProvider(YourAppId, YourAppPassword); // for each adapter
Adapter = new BotFrameworkHttpAdapter(credentialProvider); // for each adapter
await Adapter.ProcessAsync(Request, Response, Bot);
}
With a custom ICredentialProvider the appid and password can be retrieved from anywhere:
public class MultiCredentialProvider : ICredentialProvider
{
public Dictionary<string, string> Credentials = new Dictionary<string, string>
{
{ "YOUR_MSAPP_ID_1", "YOUR_MSAPP_PASSWORD_1" },
{ "YOUR_MSAPP_ID_2", "YOUR_MSAPP_PASSWORD_2" }
};
public Task<bool> IsValidAppIdAsync(string appId)
{
return Task.FromResult(this.Credentials.ContainsKey(appId));
}
public Task<string> GetAppPasswordAsync(string appId)
{
return Task.FromResult(this.Credentials.ContainsKey(appId) ? this.Credentials[appId] : null);
}
public Task<bool> IsAuthenticationDisabledAsync()
{
return Task.FromResult(!this.Credentials.Any());
}
}
Then, in Startup.cs:
services.AddSingleton<ICredentialProvider, MultiCredentialProvider>();

Error accessing Google Calendar using OAuth2.0. and service account: "Invalid impersonation prn email address."

I am trying to use Google Calendar API to access the calendar of various users in our organization calendars using OAuth2.0 and a service account but I get an error
"invalid_request" "Invalid impersonation prn email address.".
In the Google console I have:
- Created a project
- Created a service account and enabled "Domain wide delegation" and given the "Project Owner" role, then got a P12 key.
- In Security > Advanced settings > Authentication > Manage API client access I have given the serviceaccount access to https://www.googleapis.com/auth/calendar.readonly.
using System;
using System.Windows.Forms;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Calendar.v3;
using Google.Apis.Services;
using System.Security.Cryptography.X509Certificates;
namespace Google_Calendar
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button2_Click(object sender, EventArgs e)
{
string GoogleCertificate = #"testcalendar-209521-772939e76cae.p12"; // keyfile filename
string GoogleEmail = #"myserviceaccount#testcalendar-209521.iam.gserviceaccount.com"; // serviceaccount mail
string GoogleUser = "MyServiceAccount"; // serviceaccount name
string[] Scopes = new string[] { "https://www.googleapis.com/auth/calendar.readonly" };
X509Certificate2 certificate = new X509Certificate2(GoogleCertificate, "notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential( new ServiceAccountCredential.Initializer(GoogleEmail)
{
Scopes = Scopes,
User = GoogleUser
}.FromCertificate(certificate));
CalendarService service = new CalendarService(new BaseClientService.Initializer() { HttpClientInitializer = credential, ApplicationName = "testcalendar" });
string CalenderID = "mathias#mydomain.com";
var CalRequest = service.Events.List(CalenderID);
CalRequest.TimeMin = DateTime.Now.AddMonths(-1); //optional parameter
CalRequest.TimeMax = DateTime.Now.AddMonths(+1); //optional parameter
do
{
var events = CalRequest.Execute(); // here I get the error
foreach (var item in events.Items)
{
// do stuff
}
CalRequest.PageToken = events.NextPageToken;
} while (CalRequest.PageToken != null);
}
}
}
Any ideas what the problem might be? I think the problem is in my settings in Google somewhere. Do I miss a step there?
With some help from Google support I solved the problem(s).
1: Where I had used the service account user
string GoogleUser = "MyServiceAccount";
I should have used an impersonate user
string GoogleUser = "MyAdminUser";
2: When I added the scopes on my Admin Console, I added it by using the Service Account email, which then got translated visually to the ClientID of my Project and everything seemed to be ok. But it was not. When I instead used the ClientID everything worked correct.

How to add/manage user claims at runtime in IdentityServer4

I am trying to use IdentityServer4 in a new project. I have seen in the PluralSight video 'Understanding ASP.NET Core Security' that IdentityServer4 can be used with claims based security to secure a web API. I have setup my IdentityServer4 as a separate project/solution.
I have also seen that you can add an IProfileService to add custom claims to the token which is returned by IdentityServer4.
One plan is to add new claims to users to grant them access to different parts of the api. However I can't figure out how to manage the claims of the users on the IdentityServer from the api project. I assume I should be making calls to IdentotyServer4 to add and remove a users claims?
Additionally is this a good approach in general, as I'm not sure allowing clients to add claims to the IdentityServer for their own internal security purposes makes sense - and could cause conflicts (eg multiple clients using the 'role' claim with value 'admin'). Perhaps I should be handling the security locally inside the api project and then just using the 'sub' claim to look them up?
Does anyone have a good approach for this?
Thanks
Old question but still relevant. As leastprivilege said in the comments
claims are about identity - not permissions
This rings true, but identity can also entail what type of user it is (Admin, User, Manager, etc) which can be used to determine permissions in your API. Perhaps setting up user roles with specific permissions? Essentially you could also split up Roles between clients as well for more control if CLIENT1-Admin should not have same permissions as CLIENT2-Admin.
So pass your Roles as a claim in your IProfileService.
public class ProfileService : IProfileService
{
private readonly Services.IUserService _userService;
public ProfileService(Services.IUserService userService)
{
_userService = userService;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
try
{
switch (context.Client.ClientId)
{
//setup profile data for each different client
case "CLIENT1":
{
//sub is your userId.
var userId = context.Subject.Claims.FirstOrDefault(x => x.Type == "sub");
if (!string.IsNullOrEmpty(userId?.Value) && long.Parse(userId.Value) > 0)
{
//get the actual user object from the database
var user = await _userService.GetUserAsync(long.Parse(userId.Value));
// issue the claims for the user
if (user != null)
{
var claims = GetCLIENT1Claims(user);
//add the claims
context.IssuedClaims = claims.Where(x => context.RequestedClaimTypes.Contains(x.Type)).ToList();
}
}
}
break;
case "CLIENT2":
{
//...
}
}
}
catch (Exception ex)
{
//log your exceptions
}
}
// Gets all significant user claims that should be included
private static Claim[] GetCLIENT1Claims(User user)
{
var claims = new List<Claim>
{
new Claim("user_id", user.UserId.ToString() ?? ""),
new Claim(JwtClaimTypes.Name, user.Name),
new Claim(JwtClaimTypes.Email, user.Email ?? ""),
new Claim("some_other_claim", user.Some_Other_Info ?? "")
};
//----- THIS IS WHERE ROLES ARE ADDED ------
//user roles which are just string[] = { "CLIENT1-Admin", "CLIENT1-User", .. }
foreach (string role in user.Roles)
claims.Add(new Claim(JwtClaimTypes.Role, role));
return claims.ToArray();
}
}
Then add [Authorize] attribute to you controllers for your specific permissions. This only allow specific roles to access them, hence setting up your own permissions.
[Authorize(Roles = "CLIENT1-Admin, CLIENT2-Admin, ...")]
public class ValuesController : Controller
{
//...
}
These claims above can also be passed on authentication for example if you are using a ResourceOwner setup with custom ResourceOwnerPasswordValidator. You can just pass the claims the same way in the Validation method like so.
context.Result = new GrantValidationResult(
subject: user.UserId.ToString(),
authenticationMethod: "custom",
claims: GetClaims(user));
So like leastprivilege said, you dont want to use IdentityServer for setting up permissions and passing that as claims (like who can edit what record), as they are way too specific and clutter the token, however setting up Roles that -
grant them access to different parts of the api.
This is perfectly fine with User roles.
Hope this helps.

Spring ACL adding ACE when the current user has no permission on the ACL

Short Question: When a new user signs up on my website I need to add a read permission to a domain object. Spring ACLs does a check using the current user's permissions to see if a permission can be added. This will always fail because the user has just signed up and has no permissions on the object that they need a read permission on. Is there a way to skip the security check in certain situations?
Long question: The website I'm working on has an invite system that is done using tokens and emails. User A creates an organization and becomes the owner of that organization. User A can then invite User B to the organization by email, an email will be sent telling them to signup, etc. When User B gets the invite they sign up and the token is looked up. This token has a relation to an organization and at this point I try to give the user a ReadPermission but I get the error:
"org.springframework.security.acls.model.NotFoundException: Unable to locate a matching ACE for passed permissions and SIDs"
Is there a way around this security check?
Or
How far into Spring Security do I need to go to change this setup?
Found the answer to this when reading spring docs. You can use a Runnable and DelegatingSecurityContextExecutor to run the required call as a different user.
User newUser = getCurrentUser();
User notOriginalUser = accountsService.findUserById(someId);
SecurityContext context = SecurityContextHolder.createEmptyContext();
Authentication authentication = new UsernamePasswordAuthenticationToken(notOriginalUser, "doesnotmatter", authoritiesList);
context.setAuthentication(authentication);
SimpleAsyncTaskExecutor delegateExecutor = new SimpleAsyncTaskExecutor();
DelegatingSecurityContextExecutor executor = new DelegatingSecurityContextExecutor(delegateExecutor, context);
class PermissionRunnable implements Runnable {
String u;
Organization o;
PermissionRunnable(Organization org, String username) {
o = org;
u = username;
}
public void run() {
permissionsService.setReadableByPrincipal(o, new PrincipalSid(u));
}
}
Runnable originalRunnable = new PermissionRunnable(org, newUser.getUsername());
executor.execute(originalRunnable);

Resources