Find out which users have Full Access on a mailbox - active-directory

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

Related

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

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.

Exception in Site.createExternalUser in Apex RESTclass: Site.ExternalUserCreateException: [That operation is only allowed from within an active site.]

I have a Non-Salesforce Auth System which holds usernames and passwords for a few thousand users. I am willing to migrate these users to Salesforce and give access to these users to my Experience Cloud site. I am developing an apex REST Resource which will take username and password as arguments and create a user with that username and password with a community profile. I am planning to call this API from my Non-Salesforce system and migrate all these users. I am using Site.createExternalUser method in this API. I am getting the exception
Site.ExternalUserCreateException: [That operation is only allowed from within an active site.]
The reason I am using Site.createExternalUser is because I don't want to send the welcome email/reset password email to my users since they already have signed up successfully long ago.
I am open to any alternatives for achiving this.
Below is my code:
#RestResource(urlMapping='/createUser/*')
global with sharing class createUserRestResource {
#HttpPost
global static String doPost(){
Contact con=new Contact();
con.Firstname="First";
con.LastName= "Last";
con.Email="first.last#example.com";
con.AccountId='/Add an account Id here./';
insert con;
usr.Username= "usernameFromRequest#example.com";
usr.Alias= "alias123";
usr.Email= "first.last#example.com";
usr.FirstName= "First";
usr.IsActive= true;
usr.LastName= "Last";
usr.ProfileId='/Community User Profile Id/';
usr.EmailEncodingKey= 'ISO-8859-1';
usr.TimeZoneSidKey= 'America/Los_Angeles';
usr.LocaleSidKey= 'en_US';
usr.LanguageLocaleKey= 'en_US';
usr.ContactId = con.Id;
String userId = Site.createExternalUser(usr, con.AccountId, 'Password#1234', false);
return userId;
}
}
You can suppress sending emails out in whole org (Setup -> Deliverability) or in the Community config there will be way to not send welcome emails (your community -> Workspaces -> Administration -> Emails).
Without running on actual Site I don't think you can pull it off in one go. In theory it's simple, insert contact, then insert user. In practice depends which fields you set on the user. If it's Partner community you might be setting UserRoleId too and that's forbidden. See MIXED DML error. In Customer community you might be safe... until you decide to assign them some permission sets too.
You might need 2 separate endpoints, 1 to create contact, 1 to make user out of it. Or save the contact and then offload user creation to #future/Queueable/something else like that.

Delphi IPWorks LDAP PASSWD_CANT_CHANGE

I am using Delphi 10.3 and IPWorks LDAP component. I can modify most attributes without any issues, such as unicodePwd, givenName, and mail. However, for the userAccountControl attribute of a user, I am unable to set ADS_UF_PASSWD_CANT_CHANGE, of course after successfully binding as an administrator with secure connection and supplying correct DN, because if the connection is not secure, it is impossible to modify the password:
const
ADS_UF_NORMAL_ACCOUNT = 512;
ADS_UF_DONT_EXPIRE_PASSWD = 65536;
ADS_UF_PASSWD_CANT_CHANGE = 64;
ADS_UF_LOCKOUT = 16;
ipaLDAP1.DN := searchResultDN;
ipaLDAP1.AttrCount := 1;
ipaLDAP1.AttrType[0] := 'userAccountControl';
ipaLDAP1.AttrValue[0] := IntToStr(ADS_UF_NORMAL_ACCOUNT + ADS_UF_DONT_EXPIRE_PASSWD + ADS_UF_LOCKOUT + ADS_UF_PASSWD_CANT_CHANGE);
ipaLDAP1.AttrModOp[0] := amoReplace;
ipaLDAP1.Modify();
It is strange that I can not modify ADS_UF_PASSWD_CANT_CHANGE. It doesn't take effect on the user. When I check the user, this attribute is still unchecked. I don't understand why.
userAccountControl is a bitmask, so you should be using the or operator to combine flags, not the + operator.
But, more importantly, according to How to use the UserAccountControl flags to manipulate user account properties:
PASSWD_CANT_CHANGE
Note: You cannot assign this permission by directly modifying the UserAccountControl attribute. For information about how to set the permission programmatically, see the "Property flag descriptions" section.
Where the "Property flag descriptions" section says:
PASSWD_CANT_CHANGE - The user cannot change the password. This is a permission on the user's object. For information about how to programmatically set this permission, visit the following Web site:
Modifying User Cannot Change Password (LDAP Provider)
That page, in turn, says:
The ability of a user to change their own password is a permission that can be grant or denied. To deny this permission, set two ACEs in the security descriptor discretionary access control list (DACL) of the user object with the ADS_ACETYPE_ACCESS_DENIED_OBJECT ace type. One ACE denies the permission to the user and another ACE denies the permission to the Everyone group. Both ACEs are object-specific deny ACEs that specify the GUID of the extended permission for changing passwords. To grant this permission, set the same ACEs with the ADS_ACETYPE_ACCESS_ALLOWED_OBJECT ace type.
The following procedure describes how to modify or add ACEs for this permission.
To modify or add the ACEs for this permission
Bind to the user object.
Obtain the IADsSecurityDescriptor object from the ntSecurityDescriptor property of the user object.
Obtain an IADsAccessControlList interface for the security descriptor from the IADsSecurityDescriptor.DiscretionaryAcl property.
Enumerate the ACEs for the object and search for the ACEs that have the change password GUID ({AB721A53-1E2F-11D0-9819-00AA0040529B}) for the IADsAccessControlEntry.ObjectType property and "Everyone" or "NT AUTHORITY\SELF" for the IADsAccessControlEntry.Trustee property.
Note: The "Everyone" and "NT AUTHORITY\SELF" strings are localized based on the language of the first domain controller in the domain. Because of this, the strings should not be used directly. The account names should be obtained at run time by calling the LookupAccountSid function with the SID for "Everyone" ("S-1-1-0") and "NT AUTHORITY\SELF" ("S-1-5-10") well-known security principals. The GetSidAccountName, GetSidAccountName_Everyone, and GetSidAccountName_Self C++ example functions shown in Reading User Cannot Change Password (LDAP Provider) demonstrate how to do this.
Modify the IADsAccessControlEntry.AceType property of the ACEs that were found to ADS_ACETYPE_ACCESS_DENIED_OBJECT if the user cannot change their password or ADS_ACETYPE_ACCESS_ALLOWED_OBJECT if the user can change their password.
If the "Everyone" ACE is not found, create a new IADsAccessControlEntry object that contains the property values shown in the table below and add the new entry to the ACL with the IADsAccessControlList.AddAce method.
If the "NT AUTHORITY\SELF" ACE is not found, create a new IADsAccessControlEntry object with the same property values shown in the table below except the Trustee property contains the account name for SID "S-1-5-10" ("NT AUTHORITY\SELF"). Add the entry to the ACL with the IADsAccessControlList.AddAce method.
To update the ntSecurityDescriptor property of the object, call the IADs.Put method with the same IADsSecurityDescriptor obtained in Step 2.
Commit the local changes to the server with the IADs.SetInfo method.
If either of the ACEs were created, you must reorder the ACL so that the ACEs are in the correct order. To do this, call the GetNamedSecurityInfo function with the LDAP ADsPath of the object and then the SetNamedSecurityInfo function with the same DACL. This reordering will occur automatically when the ACEs are added.
The following table lists the IADsAccessControlEntry object property values.
AccessMask
ADS_RIGHT_DS_CONTROL_ACCESS
AceType
ADS_ACETYPE_ACCESS_DENIED_OBJECT if the user cannot change their password or ADS_ACETYPE_ACCESS_ALLOWED_OBJECT if the user can change their password.
AceFlags
0
Flags
ADS_FLAG_OBJECT_TYPE_PRESENT
ObjectType
"{AB721A53-1E2F-11D0-9819-00AA0040529B}" which is the change password GUID in string form.
InheritedObjectType
Not used
Trustee
Account name for SID "S-1-1-0" (Everyone).
There is a fairly lengthy code example provided on the same page.

How to find that "NT AUTHORITY" it is not an AD server?

I am trying to read an msExchMailboxSecurityDescriptor, to find whether it contains a Full Access to another person. The access control entries contain the trustees in the netbios format (DOMAIN\Username).
SecurityDescriptor secDesc = (SecurityDescriptor)userDirectoryEntry.Properties["msExchMailboxSecurityDescriptor"].Value;
AccessControlList usrAcl = (AccessControlList)secDesc.DiscretionaryAcl;
foreach (AccessControlEntry ace in (IEnumerable)usrAcl)
{
var netbiosDn = ace.Trustee.Split('\\')[0];
var netbiosUser = ace.Trustee.Split('\\')[1];
// now, the problem:
UserPrincipal user = UserPrincipal.FindByIdentity(
new PrincipalContext(ContextType.Domain, netbiosDn),
netbiosUser
);
This works until the last line, where I have to connect to the correct AD server and get some user info. Obviously, this fails when there is no server available for that domain, like any of the "NT AUTHORITY" or "BUILTIN" "domains". It does not only fail, it needs quite some time until it does.
How on earth would I distinguish which ones are AD domains, where I can connect to the AD server, and which ones aren't?
Some example users I may find in the Security Descriptor, just for you to get a feel for the problem:
CONTOSO\Alex
CONTOSO\Michael
SUBDOMAIN\Kirk
TRUSTED\George
NTPD\ChiefBrown
NT AUTHORITY\SELF
NT INSTANS\INTERAKTIV
BUILTIN\Администраторы
BUILDING2\Владимир
VORDEFINERT\Administrator
Take a look at SecurityIdentifier.IsWellKnown
You can pass various values, including WellKnownSidType.NTAuthoritySid to determine what kind of SID you have.
(See also this PowerShell code on translating into readable names.)

app on GAE | Restricted Google account authentications | listed google accounts not all |

I am quite new to this Google app engine. I am learning things every day.
I am have a forum on google app engine.
But I want is to be having private or restricted parts.
Some features should be locked for certain google account users, which are in some sort of access control list.
I plain words I can say, only those user who are in list of access can see the forum rest will be redirect to the "contact to admin" page.
As I am new I wanna know that is it something possible.
if yes, how can I achieve it ?
Thanks,
Alok
If you are using the built-in Users API, you can check users.is_current_user_admin() as an access control mechanism. Administrators can be managed via the dashboard.
If you need more granular, application-specific authorization logic, generally you would create a User model in the datastore that references the built-in UserProperty and also holds a list of roles or whatever else you need to check authorization.
To follow up Drew's reply, I use a similar system in my app, so my server code has something like the following class definition (simplified here for clarity)
class myUser(db.Model):
user = db.UserProperty(required=True)
rights = db.StringProperty(required=True, choices=set(["public", "private"]))
created = db.DateTimeProperty(auto_now_add=True)
lastaccess = db.DateTimeProperty(auto_now=True)
and then I have code like this where I handle queries
def checkUserRights(user):
q = db.GqlQuery("SELECT * from myUser WHERE user = :1", user)
u = q.get()
if not u:
# create a new 'public access' user if we haven't seen this person before
u = myUser(user=user, rights="public")
# always update the user record after the source is fetched (updates the lastaccess field)
db.put( u )
return u.rights
rights = checkUser(users.get_current_user())
if isPrivateArea and rights == "private":
....
This way I create a user for EVERY visitor, and then I have an admin interface to change the rights of selected users - you may decide, for example, to not create a record for every visitor
def checkUserRights(user):
q = db.GqlQuery("SELECT * from myUser WHERE user = :1", user)
u = q.get()
if not u:
# grant default public rights to anyone...
return "public"
# always update the user record after the source is fetched (updates the lastaccess field)
db.put( u )
return u.rights
This is, of course, on a page where the app.yaml specifies "login: required"

Resources