Can't add 'otherMails' with Graph-Tester - azure-active-directory

I am trying to add another email address to a User.
PATCH https://graph.microsoft.com/v1.0/users/user#domain.de
Body:
{
"givenName":"Meier",
"surname":"Meeier",
"otherMails":["emaissssl#domain.de"]
}
Response: Success - Statuscode 204
The result is givenName changed to Meier, surname changed to Meeier, but the email doesn't get added to otherMails[].
A related question is, can I change the primary address of the User?
Edit: I get the same behavior, if i also include the current address:
{
"givenName": "Meier",
"surname": "Meeier",
"otherMails": ["user#domain.de", "emaissssl#domain.de"]
}

You need spesific permissions in AAD to update otherMails. From the documentation:
Updating another user's businessPhones, mobilePhone, or otherMails property is only allowed on users who are non-administrators or assigned one of the following roles: Directory Readers, Guest Inviter, Message Center Reader, and Reports Reader. For more details, see Helpdesk (Password) Administrator in Azure AD available roles. This is the case for apps granted either the User.ReadWrite.All or Directory.ReadWrite.All delegated or application permissions.
The wording is a little poor but basically, if the User record you are updating is an Administrator or assigned any of the mentioned roles (Directory Readers, Guest Inviter, Message Center Reader, or Reports Reader), it will ignore the change request.
As for changing the primary email address, that isn't possible. The primary email address is automatically constructed based on the mailNickname and the default domain for the tenant (mailNickname#default.dom).

Related

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.

Get email address of logged-in user if multiple emails are associated with same account

Often, users associates multiple email addresses with the same account. But question is, is there any way to know using which email user has logged in into the system?
Note : We are strictly using email for log in and not username.
Mongodb users emails array structure:
"emails" : [
{
"address" : "xyz#abc.com",
"verified" : true
},
{
"address" : "prq#abc.com",
"verified" : true
},
{
"address" : "jkl#abc.com",
"verified" : true
}
],
Problem statement -
One need to order X item, while putting order in system we need email address of logged in user to save in this particular order. So that one can receive notifications related to this particular order.
If we save email address of logged in user to custom database field while logging in it may work but issue is if another user owning same account logs in then second users email will be updated to database and his/her email will get save in order placed by user 1.
Thanks in advance.
The best way to achieve this is to store the last email used for each user while logging in in a custom field in the user model.
Using the Account.onLogin hook on the server side or your custom login form you can have the user's last email used. Then you should save this email address for each user in a custom field such as user.lastEmail.
If you do so and need then the lastEmail on the user side, do not forget to publish the custom field to your client such as described in the documentation.

DocuSign for Salesforce: Recipient Sorting

I have a custom button that explicitly needs to sort a hard coded recipient with a specific DocuSign role and Sort Order and at the same time load in all contact roles from the related opportunity.
The code below brings in all contact roles (good) but the role of the 'Any Recipient' (which is not a (Salesforce contact role) is defined as 'Customer' (bad) when it should be defined as 'Legal'.
In the (Salesforce) DocuSign Admin | Envelopes settings the roles are defined as 'Customer' and 'Legal' where customer is the first entry and legal is the second. It appears that the sorting of the recipients is based on the the first entry in DocuSign roles because when I make the first entry 'Legal' and second entry 'Customer' I get the anticipated results.
CRL='Email~frasuy#email.com;FirstName~Any;LastName~Signer;Role~Signer 2;RoutingOrder~2,LoadDefaultContacts~1';
When using the code below with the DocuSign role entries of Customer (1st) and Legal (2nd) the mappings are working but none of the contact roles are added (bad) as recipients just the entry defined in CRL (good) with the correct DocuSign role - Legal. The LoadDefaultContacts~1 is not respected since none are brought in as recipients.
CRL='Email~frasuy#email.com;FirstName~Any;LastName~Signer;Role~Signer 2;RoutingOrder~2,LoadDefaultContacts~1';
CCRM='Signer 1~Customer;Signer 2~Legal';
CCTM='Signer 1~Signer;Signer 2~Signer';
How do I bring in all contact roles but explicitly define the DocuSign role for the CRL recipient?
Keep in mind the CCRM parameter is used to map a Salesforce role to a DocuSign Role. In this scenario, a "ContactRole to DocuSign Template Role".
Your current button logic is saying "Only pull contacts with the contact role of Signer 1 and Signer 2".
CCRM is typically irrelevant if you're not using DocuSign Server Templates. To answer your question:
1. You would need to reference all the Contact Roles within CCRM
and CCTM
2. All DocuSign roles must be different because you can't merge two
contacts with the same role
My example below references all the Salesforce default contact roles:
CES='Stackoverflow Example';
CRL='FirstName~Any;LastName~Signer;Email~{!URLENCODE("frasuy#email.com")};RoutingOrder~2;Role~Signer 2,LoadDefaultContacts~1';
CCRM='Decision Maker~Customer;Signer 2~Legal;Business User~Customer2;Economic Buyer~Customer3;Economic Decision Maker~Customer4;Evaluator~Customer5;Executive Sponsor~Customer6;Influencer~Customer7;Technical Buyer~Customer8;Other~Customer9';
CCTM='Decision Maker~Signer;Signer 2~Signer;Business User~Signer;Economic Buyer~Signer;Economic Decision Maker~Signer;Evaluator~Signer;Executive Sponsor~Signer;Influencer~Signer;Technical Buyer~Signer;Other~Signer';

is FB's db (Oracle?) of "available username" in 'real-time' or gets updated dayly/wekly or never?

Scenario:
there is FB user with Facebook (FB) ID and personal profile.
that FB ID also has one Page.
Now that ID wants to assign an available username "Myusername" to its Page (not assigning that available "Myusername" to ID itself).
Error occurred and available "MyUsername" got assigned to ID .
In order to correct that, and to release "MyUsername" to the availability status,
ID's username "MyUsername" is changed to "NewReplacingUseername" .
However, when then trying to assign "MyUsername" (expected to be now AGAIN availabe) to the ID's Page, FB returns "Username not availabe".
However,
https://graph.facebook.com/MyUsername
returns
{
"error": {
"message": "(#803) Some of the aliases you requested do not exist: MyUsername",
"type": "OAuthException"
}
}
which should mean that "MyUsername" IS available !
Any help ?
========================
#OffBySome
thanks for pointing to that FAQ.
But that FAQ use terminology
"securing" a username
and
"diffrent account".
But this is NOT "different" account.
Page belong to the same ID account to which username was initially (arroneously) given but later that username to ID itself was replaced with NewUserName in order to free original Username to be given to the Page of same account to which ID belongs to.
As for "securing" , it is not anymore "secured" as it was replaced with NewUsername.
I need tech confirmation: Does FB store FOREVER any 'username' ever typed, even in error,
even if an account, who typed it in the first place, REPLACED it with something different, i.e. it is not used anymore / it is abandon ?
And, once again, this is within SAME account !
You cannot transfer a user name. Once it has been assigned it cannot be re-assigned so that is why you are getting that error message. This is documented on their FAQ:
Can I transfer my username to another account? Once you have secured a
username, it is not possible to transfer it to a different account on
Facebook. Also, when an account is removed from the site, its username
will be unavailable. Facebook does this for security reasons, namely
to prevent username squatting.

Resources