Grant a service principal access to check password expiry of other apps (or own) - azure-active-directory

I'm trying to use the least privilege approach. I know how to grant directory or app reader privilege, but that would open the whole AAD and I want to be more selective. I also figured that an owner of an app could do that, but that would also allow the principal to read and modify the passwords. Is it even possible to grant access to only read password expiry for specific app/service principal?
resource "azurerm_role_assignment" "secret_checker_monitors_app_pwd_expiry" {
role_definition_name = "Reader"
principal_id = azuread_service_principal.checker.object_id
scope = azuread_service_principal.another.object_id
}
I've tried this, but it complains about invalid scope. What would be the correct scope? I suppose some /aad/scope/.../x-y-z-object-id. What would be the correct role name? Or would I need a custom role? Which permission?
Someone tried something similar here and concluded it was not possible. Still hoping...

Use azuread_app_role_assignment instead:
resource "azuread_app_role_assignment" "secret_checker_monitors_app_pwd_expiry" {
app_role_id = azuread_service_principal.msgraph.app_role_ids["Application.Read.All"]
principal_object_id = azuread_service_principal.checker.object_id
resource_object_id = azuread_service_principal.other.object_id
}
resource "azuread_service_principal" "msgraph" {
application_id = data.azuread_application_published_app_ids.well_known.result.MicrosoftGraph
use_existing = true
}
data "azuread_application_published_app_ids" "well_known" {}
The azurerm_role_assignment is for Azure resources. azuread_app_role_assignment was introduced in azuread provider 2.4.

Related

How to add grant type into access token in IdentityServer4?

I need to know the client's grant type (or OAuth Flow type) in my API protected by IdentitySrever4, but not sure how to do that. I am assuming that I need to add the grant type to the access token. Can someone help me by pointing me to instructions/documentations or sample code?
UPDATE
Under standard IdentityServer4 EF model, my SQL Server data store has a ClientGrantTypes table and a ClientClaims table (see screenshot below). I am assuming that I need to create a ClientClaims record that ties into ClientGrantTypes. If it is SQL, you can just join the tables on ClientID, but how do you implement it here to get the Grant Type into access token?
It would be easier to find the answer if you explained the purpose for the requirement. From my experience one common task is to distinguish authorization_code and client_credentials flow use for the same client, but that's easy: the second one does not contain user information (sub and sid claims).
Also don't forget about restricted auth flow combinations in Identityserver (for instance you can't allow both implicit and authorization_code flow for the same client), so one client is usually bound to the only user interactive flow.
Finally, the auth flow is generally not about API. It's only about interaction among IdP and Client. API usually use scopes as general information about clients, so... when you have two clients -- one with implicit grant and the other with authorization_code, you can distinguish which one is in use by setting different scopes.
Isn't that enough?
A check for a particular grant type could be performed in Identityserver the following way:
public class ExtendedClaimsService : DefaultClaimsService{
public override async Task<IEnumerable<Claim>> GetAccessTokenClaimsAsync(
ClaimsPrincipal subject,
ResourceValidationResult resourceResult,
ValidatedRequest request)
{
var outputClaims = (await base.GetAccessTokenClaimsAsync(subject, resourceResult, request)).ToList();
//if (request.Secret.Type == "NoSecret") //this is more or less the same
if ((request as ValidatedTokenRequest)?.GrantType != "client_credentials")
{
//filter out server-side-only scopes here
//or add any custom claim you like
}
return outputClaims;
}
}
Registration: services.AddTransient<IClaimsService, ExtendedClaimsService>(); after services.AddIdentityServer() in your Startup
One option is to have different client definitions for the different flows and then use a ClientClaim to indicate what type of client it is:
To set the client claims in the client definition just set it like:
ClientClaimsPrefix="",
AlwaysSendClientClaims=true,
Claims = new List<ClientClaim>()
{
new ClientClaim("role","admin"),
new ClientClaim("name","joe"),
new ClientClaim("admin","yes"),
new ClientClaim("employmentid","employee"),
new ClientClaim("employeetype","yes"),
new ClientClaim("creditlimit","100000")
}

LDAP Error: The user has insufficient access rights. : LdapErr: DSID-0C09099D, comment: Error processing control,

I want to get incremental changes from Active Directory using C# and for that I am trying to build a solution as mentioned in the following article (using DirSync Control).
https://learn.microsoft.com/en-us/windows/win32/ad/polling-for-changes-using-the-dirsync-control
However, I am facing following problems:
When using following code, I am getting exception that The user has insufficient access rights. The user is part of administrators group.
What more permission needs to be given to that account? And how?
LdapConnection connection = new LdapConnection("adfs.fed.zzz.com");
connection.SessionOptions.ProtocolVersion = 3;
connection.Credential = new System.Net.NetworkCredential("adfsfed\\username", "password");
connection.AuthType = AuthType.Basic;
connection.Bind();
var filter = "(&(objectClass=*))";
var searchRequest = new SearchRequest("", filter, SearchScope.Subtree, properties);
DirSyncRequestControl dirSyncRC = new DirSyncRequestControl(null, DirectorySynchronizationOptions.None);
searchRequest.Controls.Add(dirSyncRC);
var response = connection.SendRequest(searchRequest) as SearchResponse;
If I am using below code, then I am not getting any exception but getting empty result in cookie.
String[] properties = { "objectGUID", "sAMAccountName", "displayName", "mail", "member" };
String filter = "(|(objectClass=group)(objectClass=user))";
DirectorySearcher directorySearcher = new DirectorySearcher(myLdapConnection, filter, properties);
var dSynch = new DirectorySynchronization(System.DirectoryServices.DirectorySynchronizationOptions.None);
directorySearcher.DirectorySynchronization = dSynch;
directorySearcher.SearchScope = System.DirectoryServices.SearchScope.Subtree;
var results = directorySearcher.FindAll();
var cookie = dSynch.GetDirectorySynchronizationCookie();
Considerations:
I have only one Domain Controller
I am system admin. So, I can assign appropriate permissions to the user.
Please help.
• Your user ID will need the "Replicating Directory Changes" permission and should be a member of ‘Domain Administrators’ group to use the DirSync LDAP control extension. But please note that it pretty much can read anything in the directory partition, regardless of standard permissions. Though they cannot change anything.
However - you may have some attributes that are sensitive in your directory. Please refer the powershell script in the below link and execute it with the user ID after giving appropriate permissions using C#. It is a dirsync code that will retrieve even attributes like ‘userAccountControl, userparameters, msexchuseraccountcontrol, pwdlastset, unicodePwd (BLANK, So no hashed domain password is returned), lockouttime, accountexpires, unixuserpassword(Its Hash is returned).
http://dloder.blogspot.com/2012/01/powershell-dirsync-sample.html
Based on the response given by #KartikBhiwapurkar-MT, I figured out the bug.
The error The user has insufficient access rights is completely misleading (User had already Replicating Directory Changes rights and was part of Domain Administrators group). The error was happening in System.DirectoryServices.Protocols is that I was passing out "" as first parameter (distinguishedName)
new SearchRequest("", filter, SearchScope.Subtree, properties);
but it should have been passed as
new SearchRequest("DC=adfs,DC=fed,DC=zzz,DC=com", filter, SearchScope.Subtree, properties);
I was getting empty cookie in System.DirectoryServices because of bug in latest nuget package (6.0.0). At the time of writing this answer, the bug is still open.
Reference to bug

Find out which users have Full Access on a mailbox

I am trying to draw a graph of which Exchange User has which permissions on which Exchange mailboxes, coloring them according to the type of permission.
As of now, I cannot find out all types of permissions that Exchange takes into account.
I can, using EWS, find out who was granted access to a mailbox by the user himself:
foreach(var permission in calendarFolder.Permissions) {
// do sth.
}
But then there is the possibility that an admin grants someone permission over a mailbox by adding him to the "Full Access" permission list.
Where is this list stored? How can I read it, without PowerShell?
You can't using EWS (or any of the Exchange Mailbox API's) you can only access the Folder level DACL's what you need to read is the Mailbox DACL which can only be either accessed via the Exchange Management Shell (Get-MailboxPermissions) or via reading the msexchmailboxsecuritydescriptor from Active Directory.
You can get the AutoMapping Mailboxes http://technet.microsoft.com/en-us/library/hh529943(v=exchg.141).aspx for a particular user using Autodiscover which will generally tell you what Mailbox a particular User has been granted FullAccess to where AutoMapping has been enabled. (But this won't return Mailboxes where Automapping hasn't been set)
AutodiscoverService esService = new AutodiscoverService(ExchangeVersion.Exchange2013);
esService.RedirectionUrlValidationCallback = adAutoDiscoCallBack;
esService.Credentials = ncCred;
GetUserSettingsResponse gsr = esService.GetUserSettings("user#domain.com", UserSettingName.AlternateMailboxes);
AlternateMailboxCollection amCol = (AlternateMailboxCollection)gsr.Settings[UserSettingName.AlternateMailboxes];
foreach (AlternateMailbox am in amCol.Entries){
Console.WriteLine(am.DisplayName);
}
Cheers
Glen

What "domain" should I specify in JNDI login to an Active Directory Server?

I'm wondering what "principal" I should specify to login in to an Active Directory server. Should the principal be a user inside the AD I try to log into? Or it can be a user in the domain I specify as long as the user has privileges to access the AD?
I tried both with credentials error 49. But I can log in to the AD with ldp.exe by using the Administrator account of the server that AD is installed on.
Here is my code. Many thanks for any prompt help.
Hashtable env= new Hashtable(11);
env.put(Context.SECURITY_AUTHENTICATION,"simple"); // Also tried none w/ the same error
// What principal should I use??
env.put(Context.SECURITY_PRINCIPAL,"CN=Ross,OU=Eng,DC=RossInc");//User
//env.put(Context.SECURITY_PRINCIPAL, user + "#" + domain); // Tried w/ the same error
env.put(Context.SECURITY_CREDENTIALS, "ross");//Password
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL,"ldap://myserver:389/DC=RossInc");
DirContext ctx = new InitialDirContext(env); <-- Fails with AuthenticationException: [LDAP: error code 49 - 8009030C
You either can provide:
NT-style login name
Kerberos UPN (implicit UPN)
explicit UPN (if additional UPN suffices have been defined)
More over, NEVER ever perform a simple bind! Either Digest or GSS-API.
According to the following example from Oracle site, the security Principal is a distinguished name.
Here is some code working for me from a computer inside the domain :
Hashtable<String, String> ldapEnv = new Hashtable<String, String>(11);
ldapEnv.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
ldapEnv.put(Context.PROVIDER_URL, "ldap://societe.fr:389");
ldapEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
ldapEnv.put(Context.SECURITY_PRINCIPAL, "cn=administrateur,cn=users,dc=societe,dc=fr");
ldapEnv.put(Context.SECURITY_CREDENTIALS, "test.2011");
ldapContext = new InitialDirContext(ldapEnv);
The principal can be a user inside the AD as long as he has privileges to access the AD.

Winform user authorization via active directory

I have a situation where I am using the following code to verify user membership in AD before executing tasks in my app
using System.Security.Principal;
WindowsIdentity identity = WindowsIdentity.GetCurrent();
WindowsPrincipal principal = new WindowsPrincipal(identity);
return principal.IsInRole("someGroup");
The above code works fine for machines on my domain, however I do have some machines which are not on my domain on which I have the WINFORM application installed. How can I verify the user membership in AD?
Edit - is there a way to prompt the windows login?
Since your computer is not joined to domain at all, we cannot use WindowsIdentity or WindowsPrincipal and then check its IsInRole() method. The IsInRole() method works only if your computer is joined to the domain and it's using your domain machine account to do S4USelf.
You cannot use LogonUser approach too because your computer won't let you create a logon session from an untrusted forest.
I think we can only query the Active Directory directly to get the information we want. The code in your posted Microsoft KB does not work very well as far as I can tell. It's trying to query from memberOf attribute. The group information is not always available from the memberOf attributes.
I just wrote an IsInRole() function using AccountManagement. I guess this is what you want. The IsInRole() function will call a recursive function IsInGroup() to find out all the groups the user belongs to.
private bool IsInRole(string domain, string username, string password, string role)
{
using (var context = new PrincipalContext(ContextType.Domain, domain, username, password))
{
GroupPrincipal group = GroupPrincipal.FindByIdentity(context, IdentityType.SamAccountName, role);
UserPrincipal user = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, username);
return IsInGroup(user, group);
}
}
private bool IsInGroup(Principal principal, GroupPrincipal group )
{
if (principal.IsMemberOf(group))
return true;
foreach (var g in principal.GetGroups())
{
if (IsInGroup(g, group))
return true;
}
return false;
}
To use this IsInRole() function, you need to provide your domain name and domain credentials. If the username and password provided are wrong, you will get an exception.
You need .NET 3.5 SP1 to use AccountManagement API. Also, you may like to pay attention to this hotfix. The AccountManagement API got some bugs if running in some environment. You may need to apply the hotfix.

Resources