How do I get more than the default attributes from the Microsoft Graph API when requesting all users? - rest

I am trying to get all users from one AAD tenant with a specified schema extension.
However, when doing this request:
GraphServiceClient client = new GraphServiceClient(new AuthProv(_authHelper.GetAuthenticationResult().Result));
var userList = new List<User>();
var users = await client.Users.Request().GetAsync();
userList.AddRange(users.CurrentPage);
while (users.NextPageRequest != null)
{
var nextPage = users.NextPageRequest.RequestUrl;
Debug.WriteLine("Call To: " + users.NextPageRequest.RequestUrl);
users = users.NextPageRequest.GetAsync().Result;
userList.AddRange(users);
}
I am receiving a JSON object that looks like:
[{"businessPhones":[],"displayName":"some account name","userPrincipalName":"somemail#email.com","id":"123","givenName":null,"jobTitle":null,"mail":null,"mobilePhone":null,"officeLocation":null,"preferredLanguage":null,"surname":null}, ...]
However, I have customized an own attribute for users so I can retrieve values from that, but that attribute is not sent with the API response.
How can I change the request so that all user attributes are retrieved as a reponse?

Use this new baseUrl: "https://graph.microsoft.com/beta/"
GraphServiceClient client = new GraphServiceClient("https://graph.microsoft.com/beta/",new AuthProv(_authHelper.GetAuthenticationResult().Result),null);

It seems that you were using open extensions. If yes, we need to expand the extensions explicitly.
Here is the code to print the value of open extensions for your reference:
foreach (var user in users.CurrentPage)
{
if (user.Extensions != null&& user.Extensions.CurrentPage!=null)
{
var customProperty = user.Extensions.CurrentPage.FirstOrDefault(ext => ext.Id == "Com.Contoso.Deal");
if (customProperty != null)
Console.WriteLine($"{user.UserPrincipalName}--{customProperty.Id}:{customProperty.AdditionalData["companyName"]}");
}
}
while (users.NextPageRequest != null)
{
var nextPage = users.NextPageRequest.RequestUrl;
users = users.NextPageRequest.GetAsync().Result;
foreach (var user in users.CurrentPage)
{
var customProperty = user.Extensions.CurrentPage.First(ext => ext.Id == "Com.Contoso.Deal");
if (customProperty != null)
Console.WriteLine($"{user.UserPrincipalName}--{customProperty.Id}:{customProperty.AdditionalData["companyName"]}");
}
}
If there are multiple pages of open extension, you also should retrieve it via the NextPageRequest. Please feel free to let me know if you still have the problem.

Related

Power BI REST API ExportToFileInGroup Not Working

I am able to programmatically log in to the PowerBI Client, gather my Workspaces as well as get a specific Report from a specific Workspace. I need to programmatically render that report to a .pdf or .xlsx file. Allegedly this is possible with the ExportToFileInGroup/ExportToFileInGroupAsync methods. I even created a very simple report without any parameters. I can embed this using the sample app from here. So that at least tells me that I have what I need setup in the backend. But it fails when I try to run the ExportToFileInGroupAsync method (errors below code.)
My Code is:
var accessToken = await tokenAcquisition.GetAccessTokenForUserAsync(new string[] {
PowerBiScopes.ReadReport,
PowerBiScopes.ReadDataset,
});
var userInfo = await graphServiceClient.Me.Request().GetAsync();
var userName = userInfo.Mail;
AuthDetails authDetails = new AuthDetails {
UserName = userName,
AccessToken = accessToken,
};
var credentials = new TokenCredentials($"{accessToken}", "Bearer");
PowerBIClient powerBIClient = new PowerBIClient(credentials);
var groups = await powerBIClient.Groups.GetGroupsAsync();
var theGroup = groups.Value
.Where(x => x.Name == "SWIFT Application Development")
.FirstOrDefault();
var groupReports = await powerBIClient.Reports.GetReportsAsync(theGroup.Id);
var theReport = groupReports.Value
.Where(x => x.Name == "No Param Test")
.FirstOrDefault();
var exportRequest = new ExportReportRequest {
Format = FileFormat.PDF,
};
string result = "";
try {
var response = await powerBIClient.Reports.ExportToFileInGroupAsync(theGroup.Id, theReport.Id, exportRequest);
result = response.ReportId.ToString();
} catch (Exception e) {
result = e.Message;
}
return result;
It gets to the line in the try block and then throws the following errors:
An error occurred while sending the request.
Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host..
UPDATE
Relating to #AndreyNikolov question, here is our Embedded capacity:
After this was implemented, no change. Same exact error.
Turns out the issue was on our side, more specifically, security/firewall settings. Here is the exact quote from our networking guru.
"After some more investigation we determined that our firewall was causing this issue when it was terminating the SSL connection. We were able to add a bypass for the URL and it is now working as expected."

I want to trigger mail when the bot says that it has no answer

I want to trigger mail when the bot says that it has no answer.
I'm using MS bot framework SDk4, and using LUIS and QnA maker also, when the bot reached the to a point where it says that it has no answer , we want a mail to be triggered or add a new item in the sharepoint
If you want to add a no answer to a SharePoint List, I managed to get it working using the csom-node package and Bot Framework v4 / NodeJS. Granted, it's not the most elegant solution, but it works.
Bot.JS
const csomapi = require('../node_modules/csom-node');
settings = require('../settings').settings;
// Set CSOM settings
csomapi.setLoaderOptions({url: settings.siteurl});
Bit further down the page...
// If no answers were returned from QnA Maker, reply with help.
} else {
await context.sendActivity("Er sorry, I don't seem to have an answer.");
console.log(context.activity.text);
var response = context.activity.text;
var authCtx = new AuthenticationContext(settings.siteurl);
authCtx.acquireTokenForApp(settings.clientId, settings.clientSecret, function (err, data) {
var ctx = new SP.ClientContext("/sites/yoursite"); //set root web
authCtx.setAuthenticationCookie(ctx); //authenticate
var web = ctx.get_web();
var list = web.get_lists().getByTitle('YourList');
var creationInfo = new SP.ListItemCreationInformation();
var listItem = list.addItem(creationInfo);
listItem.set_item('Title', response);
listItem.update();
ctx.load(listItem);
ctx.executeQueryAsync();
});
}
Proactive Messaging doesn't really work for email (to prevent spam), so you're better off not using the Bot Framework SDK for the email portion. #Baruch's link, How to send email in ASP.NET C# is good if you're using the C# SDK. Here's one for sending emails in Node.
All you have to do is send the email when QnA Maker doesn't return any results. In this sample, you would do so here:
if (response != null && response.Length > 0)
{
await turnContext.SendActivityAsync(MessageFactory.Text(response[0].Answer), cancellationToken);
}
else
{
await turnContext.SendActivityAsync(MessageFactory.Text("No QnA Maker answers were found."), cancellationToken);
// Add code that sends Notification Email
}
That being said, if you'd like to try a semi-proactive route, you can enable the Email Channel in your bot, then use this:
if (response != null && response.Length > 0)
{
await turnContext.SendActivityAsync(MessageFactory.Text(response[0].Answer), cancellationToken);
}
else
{
await turnContext.SendActivityAsync(MessageFactory.Text("No QnA Maker answers were found."), cancellationToken);
MicrosoftAppCredentials.TrustServiceUrl(#"https://email.botframework.com/", DateTime.MaxValue);
var user = new ChannelAccount(name: "MyUser", id: "<notified Email Address>");
var parameters = new ConversationParameters()
{
Members = new ChannelAccount[] { user },
Bot = turnContext.Activity.Recipient
};
var connector = new ConnectorClient(new Uri("https://email.botframework.com"), "<appId>", "<appPassword>");
var conversation = await connector.Conversations.CreateConversationAsync(parameters);
var activity = MessageFactory.Text("This is a notification email");
activity.From = parameters.Bot;
activity.Recipient = user;
await connector.Conversations.SendToConversationAsync(conversation.Id, activity);
}
The catch is that <notified Email Address> has to send a message to the bot before any notifications will work. If it doesn't, it will return a 401: Unauthorized error. Again, I don't recommend this route.
Note: If you're using the Dispatch sample, you'd place the code here:
private async Task ProcessSampleQnAAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
_logger.LogInformation("ProcessSampleQnAAsync");
var results = await _botServices.SampleQnA.GetAnswersAsync(turnContext);
if (results.Any())
{
await turnContext.SendActivityAsync(MessageFactory.Text(results.First().Answer), cancellationToken);
}
else
{
// PLACE IT HERE
await turnContext.SendActivityAsync(MessageFactory.Text("Sorry, could not find an answer in the Q and A system."), cancellationToken);
}
}

How to find out permissions for each tree element(folder or file) for another user using sharepoint api?

I want to get answer for question:
Does userA can read the folder/tree ?
Application uses another user to communicate with sharpoint via sharepoint API.
I've googled the following request:
http://aissp2013/sites/Team/_api/web/lists/getbytitle('L2')/EffectiveBasePermissions
But it doesn't resolve my problem because it responds with permissions for current user!
Is there way to get permissions for another user for concrete element(folder or file or library)
Is there way to get permissions for another user for all elements and sub-elements ?
To get permissions per user SP.ListItem.getUserEffectivePermissions method is intended:
for SP.Folder
Url /_api/web/getFolderByServerRelativeUrl('<folder-rel-url>')/ListItemAllFields/getusereffectivepermissions(#u)?#u='<account>'
Method: Get
for SP.File
Url /_api/web/getFileByServerRelativeUrl('<file-rel-url>')/ListItemAllFields/getusereffectivepermissions(#u)?#u='<account>'
Method: Get
Note: account parameter needs to be provided in claims format
Regarding the question:
Does userA can read the folder/tree ?
the following example demonstrates how to retrieve permissions for a folder
const accountName = "i:0#.f|membership|<name>#<tenant>.onmicrosoft.com";
let endpointUrl = _spPageContextInfo.webAbsoluteUrl + "/_api/web/getFolderByServerRelativeUrl('/Shared Documents/Achive')/ListItemAllFields/getusereffectivepermissions(#u)?#u='" + encodeURIComponent(accountName) + "'";
const content = await executeJson(endpointUrl);
let roles = parseBasePermissions(content.d.GetUserEffectivePermissions);
if(roles.viewListItems)
console.log(`${accountName} has been granted permissions.`);
where
function parseBasePermissions(value)
{
let permissions = new SP.BasePermissions();
permissions.initPropertiesFromJson(value);
let result = {};
for(var levelName in SP.PermissionKind.prototype) {
if (SP.PermissionKind.hasOwnProperty(levelName)) {
var permLevel = SP.PermissionKind.parse(levelName);
if(permissions.has(permLevel))
result[levelName] = true;
else
result[levelName] = false;
}
}
return result;
}
is used to parse permission mask into roles
and
async function executeJson(url,options) {
options = options || {};
options.method = options.method || 'GET';
options.headers = options.headers || {};
options.headers["Accept"] = "application/json;odata=verbose";
options.headers["Content-Type"] = "application/json;odata=verbose";
if(options.method == "POST") {
options.headers["X-RequestDigest"] = document.getElementById("__REQUESTDIGEST").value;
}
if (options.body) {
options.body = JSON.stringify(options.body);
}
const rawResponse = await fetch(url,options);
const content = await rawResponse.json();
return content;
}
to perform REST request

.net core 2.0 external login - extra profile information

I have a .net core 2.0 app and am implementing external login providers like google, twitter, and facebook. I have the requirement to get the user's display name and profile picture, and can't find any documentaion of how to achieve this in .net core 2.0.
I add the authentication like this post: https://learn.microsoft.com/en-us/aspnet/core/security/authentication/social/
Here are my twitter login and callback functions...
[HttpGet]
[Route("/api/security/login/type/socialmedia/twitter")]
public IActionResult GetTwitterLogin(string redirect_uri)
{
ClientCallback = redirect_uri;
string redirectUrl = "/api/security/login/type/socialmedia/twittercallback";
var properties = SignInManager.ConfigureExternalAuthenticationProperties("Twitter", redirectUrl);
return Challenge(properties, "Twitter");
}
[HttpGet]
[Route("/api/security/login/type/socialmedia/twittercallback")]
public async Task<HttpResponseMessage> GetTwitterCallBackAsync()
{
var info = await SignInManager.GetExternalLoginInfoAsync();
var result = await SignInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor: true);
if (result.Succeeded)
{
}
else
{
}
Response.StatusCode = (int)HttpStatusCode.OK;
return null;
}
It looks like you can get some items from info.Principal.Claims, but nothing for the user's display name or profile picture.
How do you get the display name or profile picture for the various login providers?
I finally figured this out...you need to add claims when you configure the authentication. These claims look at the resulting json response and pulls items from it. The pertinent lines are the ClaimActions items.
services.AddAuthentication()
.AddTwitter(twitterOptions =>
{
twitterOptions.ConsumerKey = cfg.SystemConfig["TwitterConsumerKey"];
twitterOptions.ConsumerSecret = cfg.SystemConfig["TwitterConsumerSecret"];
twitterOptions.SaveTokens = true;
twitterOptions.RetrieveUserDetails = true;
twitterOptions.ClaimActions.MapJsonKey("display-name", "name");
twitterOptions.ClaimActions.MapJsonKey("profile-image-url", "profile_image_url_https");
})
.AddFacebook(facebookOptions =>
{
facebookOptions.AppId = cfg.SystemConfig["FacebookClientId"];
facebookOptions.AppSecret = cfg.SystemConfig["FacebookClientSecret"];
facebookOptions.SaveTokens = true;
facebookOptions.ClaimActions.MapJsonKey("display-name", "name");
})
.AddGoogle(googleOptions =>
{
googleOptions.ClientId = cfg.SystemConfig["GoogleClientId"];
googleOptions.ClientSecret = cfg.SystemConfig["GoogleClientSecret"];
googleOptions.SaveTokens = true;
googleOptions.ClaimActions.MapJsonSubKey("profile-image-url", "image", "url");
googleOptions.ClaimActions.MapJsonKey("display-name", "displayName" );
});
After getting the login information in your callback using
var info = await SignInManager.GetExternalLoginInfoAsync();
If populated successfully you can query the claims and find the values
var profileImageClaim = info.Principal.Claims.Where(x => x.Type == "profile-image-url").FirstOrDefault();
Facebook images are different from google and twitter and can be found using...
var claim = info.Principal.Claims.Where(x => x.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier").FirstOrDefault();
var url = "http://graph.facebook.com/" + claim.Value + "/picture";
In ASP.NET Core 2.0, FacebookOptions uses extension methods on ClaimActions to map the profile data returned by UserInformationEndpoint.
ClaimActions.MapJsonKey(ClaimTypes.DateOfBirth, "birthday");
In the mapping above, "birthday" is a top-level property in the Facebook Graph API response that's mapped to the value represented by the claim ClaimTypes.DateOfBirth.
To grab the profile picture you would do the same thing, but since the picture in the Graph API response is a nested JSON object, you would have to use MapCustomJson()
services.AddAuthentication()
.AddFacebook(options =>
{
// ...other options omitted
options.Fields.Add("picture");
options.ClaimActions.MapCustomJson("urn:facebook:picture",
claim => (string)claim.SelectToken("picture.data.url"));
})
Here, claim is a NewtonSoft JObject that uses JPath syntax to select the nested property value and cast it to a string.
The profile picture URL will now appear in your Claims list.

DotNetOpenAuth Get Facebook Email Address

I have the following code where its grabbing First/Last name. I realize that email is an extended permission, but what would I need to modify to request extended permissions?
How do I get the email of an authenticated Facebook user through the DotNetOpenAuth?
fbClient = new FacebookClient
{
ClientIdentifier = ConfigurationManager.AppSettings["facebookAppID"],
ClientSecret = ConfigurationManager.AppSettings["facebookAppSecret"],
};
IAuthorizationState authorization = fbClient.ProcessUserAuthorization();
if (authorization == null)
{
// Kick off authorization request
fbClient.RequestUserAuthorization();
}
else
{
var request = WebRequest.Create("https://graph.facebook.com/me?access_token=" + Uri.EscapeDataString(authorization.AccessToken));
using (var response = request.GetResponse())
{
using (var responseStream = response.GetResponseStream())
{
var graph = FacebookGraph.Deserialize(responseStream);
// unique id for facebook based on their ID
FormsAuthentication.SetAuthCookie("fb-" + graph.Id, true);
return RedirectToAction("Index", "Admin");
}
}
}
return View("LogOn");
Add the following bits:
var scope = new List<string>();
scope.Add("email");
fbClient.RequestUserAuthorization(scope);
If you are using VS2012 built in oauth providers you just need to update your oauth package. See the last post on the following link: http://forums.asp.net/t/1847724.aspx/1. The only email I can't retrieve is MS Live. Currently I use facebook, google, and yahoo.