I want to add members inside specific shared drive (Team drive).
i have tried with inserting permission request as below:
var listPermisssion = new List<TeamDrivePermissionDetailsData>() { };
TeamDrivePermissionDetailsData permission = new TeamDrivePermissionDetailsData()
{
Role = role,
TeamDrivePermissionType ="member" ,
};
listPermisssion.Add(permission);
Permission tdrivePermission = new Permission();
tdrivePermission.EmailAddress = who;
tdrivePermission.TeamDrivePermissionDetails = listPermisssion;
var requestPermission = service.Permissions.Create(tdrivePermission, sharedDriveId);
requestPermission.Execute();
i got this error :
Error:'Google.Apis.Requests.RequestError
The permission role field is required. [400]
thnks for the help.
The method Permissions:create requires the specification of role and type as described in the documentation
member is not a valid option for type - it should be one of the following options:
user
group
domain
anyone
role expects one of the following options:
owner
organizer
fileOrganizer
writer
commenter
reader
Related
I came across few similar articles but it didn't help to fix the problem.
This is the code snippet i am using:
private void GetUsersFromGroup(
PrincipalContext principalContext,
string groupName,
bool isAdminGroup,
IList<User> users,
HashSet<string> userIds)
{
log.Info($"Attempting to find {groupName} group");
GroupPrincipal group = GroupPrincipal.FindByIdentity(principalContext, groupName);
log.Info($"Successfully found {groupName} group");
log.Info($"Attempting to read users from group {group.DistinguishedName}");
var addedUserIds = new List<string>();
foreach (var userPrincipal in group.Members.OfType<UserPrincipal>())
{
if ((userPrincipal.Enabled ?? false) && !userIds.Contains(userPrincipal.UserPrincipalName))
{
users.Add(new User(userPrincipal.UserPrincipalName, userPrincipal.Sid.Value, userPrincipal.DisplayName, userPrincipal.EmailAddress, isAdminGroup));
userIds.Add(userPrincipal.UserPrincipalName.ToLower());
addedUserIds.Add(userPrincipal.UserPrincipalName);
}
}
log.Info($"Successfully read users from group {group.DistinguishedName}. Users read: {string.Join(", ", addedUserIds)}");
}
Error details
System.DirectoryServices.AccountManagement.PrincipalOperationException: While trying to resolve a cross-store reference, the SID of the target principal could not be resolved. The error code is 1788.
at System.DirectoryServices.AccountManagement.ADStoreCtx.ResolveCrossStoreRefToPrincipal(Object o)
at System.DirectoryServices.AccountManagement.ADUtils.DirectoryEntryAsPrincipal(DirectoryEntry de, ADStoreCtx storeCtx)
at System.DirectoryServices.AccountManagement.ADDNLinkedAttrSet.get_CurrentAsPrincipal()
at System.DirectoryServices.AccountManagement.PrincipalCollectionEnumerator.MoveNext()
at System.Linq.Enumerable.d__95`1.MoveNext()
Please advise.
Thank you.
It's likely that the group contains an account from another domain that doesn't exist anymore.
When a group contains a user from an external trusted domain (not in the same AD forest) it stores a "foreign security principal" object, which contains the SID of the object. The domain that the account is on really has no idea that the account is added to a group on another domain. So if that account is deleted, the group is not automatically updated.
When you look at the Members collection, it tries to look up the account and give you a UserPrincipal object, but it cannot because the account doesn't exist anymore.
For some accounts of my company I can't get the email signature. I use the following code to retrieve signatures:
OWAConfig = UserConfiguration.Bind(_service, "OWA.UserOptions", WellKnownFolderName.Root, UserConfigurationProperties.Dictionary);
Normally it works fine but for some accounts, it throws :
The specified object was not found in the store., The configuration object was not found. Name = OWA.UserOptions.
That error will occur for accounts where they have never logged onto OWA before.
The UserConfiguration object won't exist for those accounts and you won't be able to bind to it in code.
You can check for the UserConfiguration object first and then create it, if it doesn't exist:
SearchFilter sf = new SearchFilter.IsEqualTo(ItemSchema.ItemClass, "IPM.Configuration.OWA.UserOptions");
ItemView iv = new ItemView(1);
iv.Traversal = ItemTraversal.Associated;
FindItemsResults<Item> fiResults = Root.FindItems(sf, iv);
UserConfiguration OWAConfig =null;
if (fiResults.Items.Count == 0)
{
OWAConfig = new UserConfiguration(service);
OWAConfig.Save("OWA.UserOptions", Root.ParentFolderId);
}
Note: Creating a new UserConfiguration object may cause issues as it doesn't set the first time OWA logon settings, Regional settings etc.
You will need to test the affected accounts for this and rectify them accordingly.
My project requires me to programmatically access TFS servers we don't administer and to get real time information about the fields in the WorkItemTypes. I can get the field names and most of the information I need by looking at the FieldDefinition in the WorkItemType's FieldDefinitions collection.
public WitType(WorkItemType type)
{
this.Fields = new List<string>();
foreach (FieldDefinition f in type.FieldDefinitions)
{
Fields.Add(f.Name);
}
}
One thing missing is the IsRequired property. I need to be able to tell if a field is required.
I have tried running a work item story query
WorkItemCollection workItemCollection = workItemStore.Query
foreach (WorkItem workItem in workItemCollection)
foreach (Field field in workItem.Fields)
{
textBox1.Text += field.Name + " is required? " + field.IsRequired.ToString();
}
and then checking the IsRequired property of the Field item in the WorkItem's Fields collection.
Only problem is that for a given work item type one work item says Title is required, then the next work item will have the IsRequired property = false.
Is there a way to determine if a WorkItem field is required without resorting to the WIT xml file? If not, is there a way to programmatically access the WIT xml file?
I needed to perform a similar task, and the following was the only way I could figure out how to accomplish it.
As mentioned by others, WorkItem validation is defined in the WorkItemType's template. Fields can have different validation requirements based on the WorkItem's current state and even the current user's permissions.
Therefore, you need to create/retrieve a WorkItem instance using the user's credentials. If your application is impersonating the current user (i.e. in an ASP.NET application using Windows Authentication and impersonation), then you can simply use Option 1, where you use the TFS API to get the WorkItem, without impersonating.
If you're application is not impersonating the user, when you can use Option 2, where you use the TFS impersonation feature, to make calls on-behave of a user. This requires granting the "Make Requests on behave of others" permission in TFS to the application's identity (i.e. in ASP.NET the application pool's identity). See the following link for more information:
http://blogs.microsoft.co.il/blogs/shair/archive/2010/08/23/tfs-api-part-29-tfs-impersonation.aspx
The following code is an example on how to do Option 1 and Option 2.
// Set the following variables accordingly
string workItemTypeName = "Bug";
string teamProjectName = "My Project";
string usernameToImpersonate = "joesmith";
string tfsTeamProjectCollectionUrl = "http://mydomain.com:8080/tfs/ProjectCollectionName";
// OPTION 1: no impersonation.
// Get an instance to TFS using the current thread's identity.
// NOTE: The current thread's identity needs to have the "" permision or else you will receive
// a runtime SOAP exception: "Access Denied: [username] needs the following permission(s) to perform this action: Make requests on behalf of others"
TfsTeamProjectCollection tfs = new TfsTeamProjectCollection( new Uri( tfsTeamProjectCollectionUrl ) );
IIdentityManagementService identityManagementService = tfs.GetService<IIdentityManagementService>();
// OPTION 2: impersonation. Remove the following two lines of code if you don't need to impersonate.
// Get an instance to TFS impersonating the specified user.
// NOTE: This is not needed if the current thread's identity is that of the user
// needed to impersonate. Simple use the ablve TfsTeamProjectCollection instance
TeamFoundationIdentity identity = identityManagementService.ReadIdentity( IdentitySearchFactor.AccountName, usernameToImpersonate, MembershipQuery.None, ReadIdentityOptions.None );
tfs = new TfsTeamProjectCollection( tfs.Uri, identity.Descriptor );
WorkItem workItem = null;
WorkItemStore store = tfs.GetService<WorkItemStore>();
// Determine if we are creating a new WorkItem or loading an existing WorkItem.
if( workItemId.HasValue ) {
workItem = store.GetWorkItem( workItemId.Value );
}
else {
Project project = store.Projects[ teamProjectName ];
WorkItemType workItemType = project.WorkItemTypes[ workItemTypeName ];
workItem = new WorkItem( workItemType );
}
if( workItem != null ) {
foreach( Field field in workItem.Fields ) {
if( field.IsRequired ) {
// TODO
}
}
}
When I try to update the Name field (corresponds to the CN) on UserPrincipal (Principal, really), I get an error "The server is unwilling to process the request" on the call to UserPrincipal.Save().
I've checked to make sure there isn't another object in the same OU with the same Name (CN).
The PrincipalContext I'm operating at is the domain root (not exactly at the OU level where the user account exists).
What reason might there be for this error? Is it something that might be security policy related (even though I'm able to update all the other fields)?
using (var context = new PrincipalContext(ContextType.Domain, ConfigurationManager.AppSettings["domain"], ConfigurationManager.AppSettings["rootDN"], ContextOptions.Negotiate, ConfigurationManager.AppSettings["username"], ConfigurationManager.AppSettings["password"])) {
var user = UserPrincipal.FindByIdentity(context, IdentityType.Sid, "..."); // SID abbreviated
user.Name = "Name, Test";
user.Save();
}
The user I am using to create the PrincipalContext has the security rights to modify AD objects. If I update any other of the other fields (e.g. Surname, GivenName), everything works fine.
EDIT:
I've been able to accomplish what I need to do (using ADSI), but I have to run the following code under impersonation. The impersonation code is ugly, and the code below breaks away from the other way I'm updating AD data (using DirectoryServices.AccountManagement), so I'd like to get a better solution.
using (var companyOU = new DirectoryEntry("LDAP://" + company.UserAccountOU)) {
companyOU.Invoke("MoveHere", "LDAP://" + user.DistinguishedName, "cn=Name\, Test");
}
This is a cleaner way
using (var context = new PrincipalContext(ContextType.Domain))
{
var group = GroupPrincipal.FindByIdentity(context, groupName);
group.SamAccountName = newGroupName;
group.DisplayName = newGroupName;
group.Save();
var dirEntry = (DirectoryEntry)group.GetUnderlyingObject();
dirEntry.Rename("CN=" + newGroupName);
dirEntry.CommitChanges();
}
The only way I've found to do this is in the EDIT section in my question. Basically, you cannot use the UserPrincipal class. There is something special about the CN attribute, and you need to drop down a level and use DirectoryEntry, an LDAP string, and invoke the "MoveHere" ADSI command to rename the user account.
This has to be obtained from a remote machine. The following query works not for SIDs, but for group and account names.
"SELECT GroupComponent FROM Win32_GroupUser WHERE PartComponent = \"Win32_UserAccount.Domain='" + accountDomain + "',Name='" + accountName + "'\""
The Win32_Group objects it returns come in the forms of strings, and they only have domain and name (even though Win32_Group has a SID property).
I have this sinking feeling I'll have to:
Turn the SID into an account name by querying Win32_SID;
Perform the query above;
Turn each of the resulting group names into SIDs by querying Win32_Group.
Can you use the System.DirectoryServices.AccountManagement namespace classes?
using (var context = new PrincipalContext( ContextType.Domain ))
{
using (var user = UserPrincipal.FindByIdentity( context, accountName ))
{
var groups = user.GetAuthorizationGroups();
...iterate through groups and find SIDs for each one
}
}
It should work with ContextType.Machine, though you'd need to specify the machine name and have appropriate privileges.
using (var context = new PrincipalContext( ContextType.Machine,
"MyComputer",
userid,
password ))
{
...
}
There's a nice MSDN article (longish, though) on using the new .NET 3.5 account management namespace.
Yes there is but some methods depend on having a domain.
See this page for how to convert a SID
to a user id using P/Invoke and the Windows API, or with .NET 2.0+ and no P/Invoke.
using System.Security.Principal;
// convert the user sid to a domain\name
string account = new SecurityIdentifier(stringSid).Translate(typeof(NTAccount)).ToString();
If you have AD and
the user id in there then use the DirectorySearcher
method or Account Management APIs to find the groups.
Otherwise use the method outlined in
this article to get local
groups.
Now use the API suggested by #tvanfosson to iterate the groups and get the SIDs. Or follow the info below.
In an ASP.NET application it is possible to use code like this to access group info provided a user is authenticated by Windows and not Forms authentication. In this example I've left an interesting note about exceptions that are thrown in that environment but it may apply to other users:
public List<string> GetGroupsFromLogonUserIdentity()
{
List<string> groups = new List<string>();
HttpRequest request = HttpContext.Current.Request;
if (request.LogonUserIdentity.Groups != null)
{
foreach (IdentityReference group in request.LogonUserIdentity.Groups)
{
try
{
groups.Add(group.Translate(typeof(NTAccount)).ToString());
}
catch (IdentityNotMappedException)
{
// Swallow these exceptions without throwing an error. They are
// the result of dead objects in AD which are associated with
// user accounts. In this application users may have a group
// name associated with their AD profile which cannot be
// resolved in the Active Directory.
}
}
}
return groups;
}
LogonUserIdentity is based on the WindowsIdentity class. You could modify my code sample to use WindowsIdentity and function in a non-Web application. Once you iterate over a group you should be able to do something like this to get the SecurityIdentifier:
SecurityIdentifier secid = group as SecurityIdentifier;