I've been investigating how to add (and later remove) a user from an Azure AD group using the Microsoft Graph API (the dotnet/C# library available on nuget).
Nuget MS Graph API
Ignoring all else around getting a connected GraphServiceClient etc. I'm trying code very similar to the sample below, not getting any exception (suggesting things are fine) but when I get the group once again via the API, it's not got any members still!
Interestingly (as an aside), when I ask for memberOf property on the user object and tell it to expand it, it comes back null still.
var user = await client.Users[userPrincipalName]
.Request()
.Select("id,memberOf")
.Expand("memberOf")
.GetAsync();
var group = await client.Groups[groupId]
.Request()
.Select("members")
.Expand("members")
.GetAsync();
group.Members.Add(user);
await client.Groups[groupId].Request().UpdateAsync(group);
// userPrincipalName => "test.user#mytenant.com"
// groupId => the object GUID for the group
Does anyone know what I'm doing wrong here? The docs I used to come up with this code were based on the links to the following documents:
API doc for 'group'
Adding a member to a group
Also, I tried to style the approach on the solution suggested here to setting licenses for users:
Assign user license via Graph API
As usual, thanks for any help.
Peter
Additional
I've also tried poking around in the graph API looking at potentially updating the .Members property/resource rather than the group itself:
await client.Groups[groupId].Members.Request(). // <-- Only has GetAsync()
But it only has the GetAync() method available to it.
Updated based on answer
var usersGroups = await client.Users[userPrincipalName].MemberOf.Request().GetAsync();
if (!usersGroups.Any(g => g is Group && g.Id == groupId))
{
// User is not a member of the group, add them.
var user = await client.Users[userPrincipalName].Request().Select("id").GetAsync();
await client.Groups[groupId].Members.References.Request().AddAsync(user);
}
I've added the code snippet above based on the answer, as I think it succinctly answers the issue regarding adding members.
Thanks to Nan Yu for the answer.
To add user to Group ,you could use :
User userToAdd = await graphClient.Users["objectID"].Request().GetAsync();
await graphClient.Groups["groupObjectID"].Members.References.Request().AddAsync(userToAdd);
To get members of a group :
List<ResultsItem> items = new List<ResultsItem>();
// Get group members.
IGroupMembersCollectionWithReferencesPage members = await graphClient.Groups[id].Members.Request().GetAsync();
if (members?.Count > 0)
{
foreach (User user in members)
{
// Get member properties.
items.Add(new ResultsItem
{
Properties = new Dictionary<string, object>
{
{ Resource.Prop_Upn, user.UserPrincipalName },
{ Resource.Prop_Id, user.Id }
}
});
}
}
Get groups the current user is a direct member of ,you could try :
IUserMemberOfCollectionWithReferencesPage memberOfGroups = await graphClient.Users["testnanyu#testbasic1.onmicrosoft.com"].MemberOf.Request().GetAsync();
if (memberOfGroups?.Count > 0)
{
foreach (var directoryObject in memberOfGroups)
{
// We only want groups, so ignore DirectoryRole objects.
if (directoryObject is Group)
{
Group group = directoryObject as Group;
items.Add(new ResultsItem
{
Display = group.DisplayName,
Id = group.Id
});
}
}
}
Related
I need to search by keyword, so that I could retrieve only events that contain this keyword I am searching for.
I already got an answer that solves part of my problem (filter on subject),
but I still need to search on bodypreview too.
I am trying to add my search (or filter) parameter on this piece of code:
...
protected override async void OnAppearing()
{
base.OnAppearing();
// Get start and end of week in user's time zone
// I replaced variables below by pure strings on QueryOption <== DOES WORKS FINE
//var startOfWeek = GetUtcStartOfWeekInTimeZone(DateTime.Today, App.UserTimeZone);
//var endOfWeek = startOfWeek.AddDays(30); //Eloir: original AddDays(7)
var queryOptions = new List<QueryOption>
{
//new QueryOption("$search", "BodyPreview:stackoverflow"), <== DOES NOT WORK
new QueryOption("startDateTime", "01/01/2020 00:00:00"),
new QueryOption("endDateTime", "31/12/2020 23:59:59")
};
var timeZoneString =
Xamarin.Forms.Device.RuntimePlatform == Xamarin.Forms.Device.UWP ?
App.UserTimeZone.StandardName : App.UserTimeZone.DisplayName;
// Get the events
var events = await App.GraphClient.Me.CalendarView.Request(queryOptions)
.Header("Prefer", $"outlook.timezone=\"{timeZoneString}\"")
//.Filter("BodyPreview contains 'stackoverflow'") <== DOES NOT WORK EITHER
.Select(e => new
{
e.Subject,
e.BodyPreview,
e.Start,
e.End
})
.OrderBy("start/DateTime")
.Top(50)
.GetAsync();
// Add the events to the list view
CalendarList.ItemsSource = events.CurrentPage.ToList();
}
...
This code is part of Microsoft docs site:
Build Xamarin apps with Microsoft Graph
CalendarView provides the list of expanded events in the specified time range only and does not support additional filtering.
You can use me/events endpoint to filter by a dateTime and subject or bodyPreview
GET https://graph.microsoft.com/v1.0/me/events?$filter=start/dateTime ge '2021-09-06T08:00' and end/dateTime lt '2021-09-30T08:00' and contains(subject,'planning')
C#
await App.GraphClient.Me.Events.Request()
.Filter("start/dateTime ge '2021-09-06T08:00' and end/dateTime lt '2021-09-30T08:00' and contains(subject,'planning')")
.Select(e => new
{
e.Subject,
e.BodyPreview,
e.Start,
e.End
})
.OrderBy("start/DateTime")
.Top(50)
.GetAsync();
We have a C# application that is using the Microsoft Graph API to display the contents of SharePoint folder to users in a Syncfusion FileManager control. We need to grant permissions to those folders for certain users in order that they can collaborate on files.
We can add specific users using the sharing invitation to add a permission (see https://learn.microsoft.com/en-us/graph/api/driveitem-invite?view=graph-rest-1.0&tabs=http). We also need to be able to remove a user from this permission without deleting the whole link (and therefore any other users using it). I cannot see a way of doing this!
I have also tried using CreateLink (see https://learn.microsoft.com/en-us/graph/api/driveitem-createlink?view=graph-rest-1.0&tabs=http) but get an ‘Invalid Request’ error when trying to ‘Grant’ permission to a user and therefore never get as far as trying to remove an individual user!. The code I am using to try and 'Grant' permission is as follows (the last line produces the error):
public object CreateSharingLink(string itemId, List<string> recipientList, List<string> roles)
{
if (itemId == null) return null;
var type = "edit";
var scope = "organization";
Permission p = GraphClient.Drives[documentLibrary.Id].Items[itemId].CreateLink(type, scope).Request().PostAsync().Result;
return GrantAccessToSharingLink(p.Link.WebUrl, recipientList, roles);
}
public object GrantAccessToSharingLink(string sharingUrl, List<string> recipientList, List<string> roles)
{
List<DriveRecipient> recipients = (recipientList.Select(r => new DriveRecipient { Email = r })).ToList();
string base64Value = System.Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(sharingUrl));
string encodedUrl = "u!" + base64Value.TrimEnd('=').Replace('/', '_').Replace('+', '-');
return GraphClient.Shares[encodedUrl].Permission.Grant(roles, recipients).Request().PostAsync().Result;
}
Any assistance would be much appreciated.
A similar question was asked a while ago but without an answer. Remove GrantedTo user from Permission using Graph API
You can delete only not inherited permissions. Only sharing permissions that are not inherited can be deleted. Here's the related doc.
I am trying to create a commands using discord.net that will get a guild by id and then loop through the channels roles and users allowing me to list the names or permission each role or channel has. However, I've run into a few issues. The way I am currently getting my guild is using
var guild = BotStuff._client.GetGuild(Global.guildid);
After I get the guild I was able to get the number of channels, roles, or users using
var channels = guild.Channels;
and then
Console.WriteLine(channles.Count);
However, my main issue is that I want to be able to loop through the channels or roles and change the permissions or log the name of them, but I am unsure of how to accomplish this. I wasn't able to find documentation for this. Any help is appreciated!
I'll provide you with some examples that would already work with your current variables.
You need to gather the current channels in a guild, however since a Category is still considered a channel, you need to filter it out using the Where method in LINQ. This way, we will only have text and voice channels in our enumerable, which we then just loop through using the foreach keyword.
In the code examples below, you can modify the channel (name, positon, category) and add the permissions for both users and roles as you wanted.
In case you simply want to type their names into the console, just display the Name property in the foreach:
Console.WriteLine(channel.Name)
Change the name of each channel in a guild:
var guild = BotStuff._client.GetGuild(Global.guildid);
var channels = guild.Channels.Where(x => !(x is ICategoryChannel));
foreach (var channel in channels)
{
await channel.ModifyAsync(x =>
{
x.Name = "new name";
});
}
Add a permission overwrite for a user:
var guild = BotStuff._client.GetGuild(Global.guildid);
var channels = guild.Channels.Where(x => !(x is ICategoryChannel));
foreach (var channel in channels)
{
var guilduser = guild.GetUser(554240045800882181);
await channel.AddPermissionOverwriteAsync(guilduser, new OverwritePermissions(sendMessages: PermValue.Allow, manageChannel: PermValue.Allow));
}
This way we will let the user of ID 554240045800882181 send messages and manage the channel. You are able to choose between Allow, Inherit, Deny.
The permission adding method is overloaded and also accepts a role as an argument, so with a small modification we can change the channel permissions for a whole group of people.
var guildrole = guild.GetRole(202853174959144960);
await channel.AddPermissionOverwriteAsync(guildrole, new OverwritePermissions(manageMessages: PermValue.Allow, attachFiles: PermValue.Allow));
The IDs/variables used are just examples, you should understand and modify the code before using it.
For listing channel : (with a bot, else just replace Context.Guild.Channel per u'r getguild variable)
foreach (var channel in Context.Guild.Channels)
{
Console.WriteLine(channel.Name);
}
I'm building a mood logging app using Xamarin forms for a university project but i've hit a wall when trying to return the data contained within a nested child node. Please see my firebase schema below:
BrainBreakApp
MoodLog #1
unique MoodLog id assigned by firebase
Mood Rating
Note
Location
Activities
Unique activity id assigned by firebase
activity logged by user (e.g. Shopping)
Unique activity id assigned by firebase
activity logged by user (e.g. Working)
MoodLog #2
MoodLog #3
etc.
I'm easily able to return all of the data within each mood log, except the data within the 'Activities' child node. I have placed 'shopping' and 'working' in bold because that is the data I need to return.
In my 'MoodLog' model, I have declared the 'Activities' attribute as a list of type 'Activity'; which is a separate model just containing an 'ActivityName' attribute. As my current code stands I need to return all of the activities for that particular log in a list that I can assign to the MoodLog 'Activities' attribute.
I've tried a number of approaches including adding the statement to return the list within the 'Activity' assignment and generating a list of the activities for each emotion log separately and assigning the returned list to 'Activity'; but clearly no luck. The issue i'm continually running into when trying to create a separate list of activities for that emotion log, is being able to access the firebase generated key for that particular log each time, and also being able to return a List<> type rather than a Task List<> type that I can't assign to the 'Activity' attribute.
I appreciate that much of the documentation states to make the schema as flat as possible, and if you can think of a flatter schema that will serve the same purpose that would also be fantastic.
I'm a complete Firebase newbie so any help at all would be very much appreciated!
I'll place my current code below and you will see where I need to add the list of activities:
public async Task<List<EmotionLog>> GetAllLogsForPerson()
{
var allPersons = await GetAllLogs();
await Firebase
.Child(LogChildName)
.OnceAsync<EmotionLog>();
return allPersons.Where(a => a.UserId == personId).ToList();
}
public async Task<List<EmotionLog>> GetAllLogs()
{
return (await Firebase
.Child(LogChildName)
.OnceAsync<EmotionLog>()).Select(item => new EmotionLog
{
UserId = item.Object.UserId,
DateTime = item.Object.DateTime,
Note = item.Object.Note,
Location = item.Object.Location,
MoodRating = item.Object.MoodRating,
NoActivities = item.Object.NoActivities,
Activity =
{
// Need to add list of activities here
}
}).ToList();
}
EDIT:
Please see the code below used to post the emotion log data to the firebase db. I add all of the other log data first in the 'AddLog' method, and then iterate separately through the activities using the 'AddActivity' method, which is called within the 'Add Log' method.
public async Task AddLog(string Id, string Note, string Location, DateTime DateTime, int MoodRating, List<string> ActivityList)
{
await Firebase
.Child(LogChildName)
.PostAsync(new EmotionLog() { UserId = Id, Note = Note, Location = Location, DateTime = DateTime, MoodRating = MoodRating});
await AddActivity(Id, ActivityList);
}
public async Task AddActivity(string Id, List<string> ActivityList)
{
var toAddUserActivities = (await Firebase
.Child(LogChildName)
.OnceAsync<EmotionLog>()).Last(a => a.Object.UserId == Id);
foreach (string item in ActivityList)
{
await Firebase
.Child(LogChildName)
.Child(toAddUserActivities.Key)
.Child(ActivityChildName)
.PostAsync(new Activity() { ActivityName = item });
}
}
The solution was to remove the unique Id assigned to each activity within the database. These were being generating because rather than just adding my activity list to the emotion log as one whole list, i was iterating through the list and adding each of them to the emotion log individually.
Once i removed the code that iterated through the activity list and changed my list to type List, everything worked perfectly :)
I'm using the Tridion.OutboundEmail.ContentManagement API to retrieve and manage contact details.
Retrieving Contacts is working fine, as is pulling back the ExtendedDetails dictionary, but the Keywords TcmUriCollection is always empty.
[Test]
public void GetContacts_via_address_book()
{
var uri = new TcmUri(101, 2, TcmItemTypes.StaticAddressBook);
var addressBook = new StaticAddressBook(uri);
var contacts = addressBook.GetContacts();
foreach (var contact in contacts)
{
var firstName = contact.ExtendedDetails["NAME"].StringValue;
Assert.That(contact.EmailAddress, Is.Not.Empty); // PASS
Assert.That(firstName, Is.Not.Empty); // PASS
Assert.That(contact.Keywords.Count, Is.GreaterThan(0)); // FAIL
}
}
I've also tried the following method:
[Test]
public void GetContacts_via_filter()
{
var uri = new TcmUri(101, 2, TcmItemTypes.StaticAddressBook);
var addressBook = new StaticAddressBook(uri);
var filter = new ContactFilter(UserContext.Current);
var contacts = Contact.GetContacts(filter, addressBook);
foreach (var contact in contacts)
{
var firstName = contact.ExtendedDetails["NAME"].StringValue;
Assert.That(contact.EmailAddress, Is.Not.Empty); // PASS
Assert.That(firstName, Is.Not.Empty); // PASS
Assert.That(contact.Keywords.Count, Is.GreaterThan(0)); // FAIL
}
}
I can even add a keyword to a Contact's Keywords collection and save it, and it appears correctly in Tridion, but when I retrieve the same contact again, the collection is once again empty.
Does anyone have any experience with this API, and/or know what the problem is?
This is because Keywords are not loaded when you get a list of Contacts. Only a subset of the data is available, for performance reasons.
To solve this, you will need to re-load each Contact. Since Contacts are streamed from the database, you cannot do this inside of your loop. So you'll want to build the list of Contacts first and then loop over them and load them in full.
For more info and examples, please see my blog post on the subject:
http://pkjaer.wordpress.com/2011/12/01/looping-through-contacts/