SharePoint CAML query gets (401) Unauthorized exception - sharepoint

I have a query which looks like this:
var site = properties.Site;
var context = new ClientContext(site.Url);
List list = context.Web.Lists.GetByTitle(properties.ListTitle);
var query = new CamlQuery();
query.ViewXml = "<View><Query><Where><Eq><FieldRef Name='FileLeafRef'/>" +
"<Value Type='Text'>" + fieldName + "</Value></Eq></Where></Query></View>";
ListItemCollection itemCollection = list.GetItems(query);
context.Load(itemCollection);
context.ExecuteQuery();
In this scenario properties is an SPItemEventProperties.
Whenever it gets to ExecuteQuery(), it throws an exception: The remote server returned an error: (401) Unauthorized.
What could be the cause of this?

Looking at your code I guess this is RER associated with list item action. It happens that if you run it from host web (rather than app web) or you use CSOM it fails to authencitate back to SharePoint. Most likely when you will try to debug your code you will see that your RER has not received a valid context token (SPRemoteEventProperties.ContextToken is an empty string). Additionally the way you create your context using constructor
var context = new ClientContext(site.Url);
Does not deal with authenticateion. You have token methods to do that.
var context = TokenHelper.CreateRemoteEventReceiverClientContext(properties)
This will return a valid context as long as this event is on app web.
If this does not resolve your problem you will need to switch to use app permissions in your AppManifest and build a proper Realm from your SPHostUrl
var siteUrl = properties.Site.Url;
var siteUri = new Uri(siteUrl);
var realm = TokenHelper.GetRealmFromTargetUrl(siteUri);
var token = TokenHelper.GetAppOnlyAccessToken(TokenHelper.SharePointPrincipal, siteUri.Authority, realm).AccessToken;
using (var context = TokenHelper.GetClientContextWithAccessToken(siteUrl, token))
{
}
Let me know if that helps.

Related

Access Azure Data Explorer with Kusto.Data in Azure Function -- Kusto failed to send request -- local debugging works

I am having the following problem and an extensive search online didn't provide any good results.
When trying to access my Azure Data Explorer Database and querying using the Kusto.Data SDK in an Azure Function, it yields the following error:
Kusto client failed to send a request to the service: 'An unknown, invalid, or unsupported option or level was specified in a getsockopt or setsockopt call.'
However, running the Function on my local machine, everything works fine.
Edit: The function excepts at using (var reader = await queryProvider.ExecuteQueryAsync(Database, query, clientRequestProperties))
EDIT2 - SOLUTION:
You can downgrade the NuGet Kusto.Data Package to Version 9.4.1, this solves the problem and doesn't throw any error anymore. If you still encounter difficulties, you can try to directly access the ADX database via http requests:
const string tenantId = "<tenantId>";
const string client_id = "<clientId>";
const string client_secret = "<client_secret>";
const string Cluster = "<cluster_adress";
const string Database = "<database_name>";
var authUrl = "https://login.microsoftonline.com/<tenantId>/oauth2/token";
var param = new Dictionary<string, string>
{
{"client_id",client_id},
{"grant_type","client_credentials"},
{"client_secret",client_secret},
{"resource","https://help.kusto.windows.net"}
};
var data = new FormUrlEncodedContent(param);
using var authClient = new HttpClient();
var response = await authClient.PostAsync(authUrl, data);
string result = response.Content.ReadAsStringAsync().Result;
//parse result
var resultJson = System.Text.Json.JsonDocument.Parse(result);
//retrieve access token
var accessToken = resultJson.RootElement.GetProperty("access_token");
//-----------------------------------------------------------------------------------------------
var dataXUrl = Cluster + "/v1/rest/query";
var database = Database;
var dataXQuery = "sample_table| where Time > ago(2min)";
var body = new Dictionary<string, string>
{
{"db",database},
{"csl",dataXQuery}
};
using var dataXClient = new HttpClient();
dataXClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.ToString());
dataXClient.DefaultRequestHeaders.Add("Accept", "application/json");
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, dataXUrl);
request.Content = new StringContent(JsonConvert.SerializeObject(body), Encoding.UTF8, "application/json");
var table = await dataXClient.SendAsync(request);
//pretty print
var obj = JsonConvert.DeserializeObject(table.Content.ReadAsStringAsync());
var tableJSON = JsonConvert.SerializeObject(obj, Formatting.Indented);
log.LogInformation("\n\n" + tableJSON);
I am having the same issue on a continuous webjob on an Azure App Service. The Kusto nuget version I am using is 10.1.0
Downgrading to nuget 9.4.1 solved the problem immediately.
FYI - This only seems to affect 10.1.0. The earlier 10.x.x versions should work.
The ADX team believes they will have this fixed in the next nuget version.

creating webjob to get status of webjobs of same app service

I am creating one webjob which needs to send mail of status of webjobs. I am using webjob API aka "https://xyz.scm.ase-03.com/api/triggeredwebjobs" to get the webjobs details. I am getting the response from my local httpclient call but while deploying it as a webjob on azure then I am getting null response. Here is my code:
var result = string.Empty;
var url = "https://domain.dev.xyz.com/api/";
var baseURL = "triggeredwebjobs";
string userPswd = "username " + ":" + "password"; // getting username and password from publish profile.
userPswd = Convert.ToBase64String(Encoding.Default.GetBytes(userPswd));
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(url);
client.Timeout = TimeSpan.FromMinutes(30);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",userPswd );
var response = new HttpResponseMessage();
response = client.GetAsync(baseURL).Result; // Here I am getting null value.
result = response.IsSuccessStatusCode ? (response.Content.ReadAsStringAsync().Result) : response.IsSuccessStatusCode.ToString();
}
I am in doubt that calling self webjobs api url maybe not working so I deployed it to another app service but no luck.
Can anyone let me know where is the issue may be?
Thanks in advance.

How to GET application by appId using Microsoft Graph API via Java using HttpURLConnection

I want to authorize an OAuth JSON Web Token granted by Azure Active Directory, and one thing I need to do is get more information about the application in the token's appId using the Microsoft Graph API.
Microsoft Graph API allows me to GET an app by its id via
https://graph.microsoft.com/beta/applications/{id}
, but NOT by its appId via
https://graph.microsoft.com/beta/applications/{appId}
The best way I see to use Microsoft Graph API to GET an app using its AppId is via a filter like so:
https://graph.microsoft.com/beta/applications?filter=appId eq '{appId}'
The above filter works just fine in the Microsoft Graph Explorer, but when calling the Graph API using a GET request using HttpUrlConnection, my request fails with HTTP Code 400 and message "Bad Request".
It's weird because using the exact same HttpUrlConnection to GET the full range of applications via
https://graph.microsoft.com/beta/applications
works just fine.
Is there something about the filter functionality that I can't use it in a Microsoft Graph API GET request? How should I get info on an app by its AppId?
Here is a snippet of the Java code I am using for my HttpURLConnection:
url = new URL(String.format("https://graph.microsoft.com/beta/applications?filter=appId eq '%s'", appId));
final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Authorization", "Bearer " + result.getAccessToken());
conn.setRequestProperty("Accept", "application/json");
conn.setRequestProperty("Content-Type", "application/json");
final int httpResponseCode = conn.getResponseCode();
if (httpResponseCode == 200 || httpResponseCode == 201) {
BufferedReader in = null;
final StringBuilder response;
try {
in = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
String inputLine;
response = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
} finally {
in.close();
}
final JSONObject json = new JSONObject(response.toString());
return json.toString(4);
} else {
return String.format("Connection returned HTTP code: %s with message: %s",
httpResponseCode, conn.getResponseMessage());
}
In case someone else comes looking for this, if you are using the GraphServiceClient you can do this:
var appId = "some app id";
var response = await _graphClient.Applications
.Request()
.Filter($"appId eq '{appId}'")
.GetAsync();
var azureAddApplication = response.FirstOrDefault() ?? throw new ArgumentException($"Couldn't find App registration with app id {appId}");
In case someone is searching this... the API call should be done with the app's Object ID, not the app ID.
You should URLEncode the query parameters.
String url2=URLEncoder.encode("$filter=appId eq '{applicationId}'");
URL url = new URL("https://graph.microsoft.com/beta/applications?"+url2);

JSOM - Load mutliple SharePoint objects in one request

Is there a way to load multiple objects in one request like this:
var context = new SP.ClientContext.get_current();
this.web = context.get_web();
this.site = context.get_site();
var list = this.web.get_lists().getByTitle(window.sessionStorage.getItem('selectedContentType'));
var query = '<View Scope=\'RecursiveAll\'><Query>' + $('.camlQuery').val() + '</Query></View>';
var camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml(query);
this.items = list.getItems(camlQuery);
context.load(this.site);
context.load(this.items, 'Include(ID,DocIcon,LinkFilename,FileRef,FileLeafRef,Title,ContentType,SupplierPGProduct)');
context.executeQueryAsync(Function.createDelegate(this, get_Data_onSuccess), Function.createDelegate(this, get_Data_onFailure));
}
In this example, I mean this.site and this.items in one async request?
Or do I have to load first this.site and when the request is successful, then load the this.items?
I've found the answer in sharepoint.stackexchange.
Here is the link to the answer.
https://sharepoint.stackexchange.com/a/227984/47825

Root site collection in Javascript

How to get the Root Site collection url when the context is in a child site, in JavaScript or JQuery.
You could use the following using client object model
var clientContext = new SP.ClientContext();
var owebsite = clientContext.get_site.get_rootWeb();
Without client object model you can use the following
var siteCollectionPath= _spPageContextInfo.siteServerRelativeUrl;
var clientContext = new SP.ClientContext.get_current();
this.web = clientContext.get_site().get_rootWeb();
It's working for me.
Do you mean root web or root site collection?
This one will get you the root site collection:
_spPageContextInfo.siteAbsoluteUrl.replace(_spPageContextInfo.siteServerRelativeUrl, _spPageContextInfo.siteServerRelativeUrl == \"/\" ? \"/\": \"\") + \""
From my post: http://www.rdacorp.com/2015/03/alternate-solution-alternate-css-url-sharepoint-2013-online/
Below sample script shows how you can retrieve root web. The onQuerySucceedSample function alerts root site title.
getRootWeb = function () {
//Get and load a reference to the root web of the current site collection.
var ctx = new SP.ClientContext.get_current();
var site = ctx.get_site();
this.rootWeb = site.get_rootWeb();
ctx.load(this.rootWeb);
//Ask SharePoint to pull data for us
ctx.executeQueryAsync(Function.createDelegate(this,this.onQuerySucceed),Function.createDelegate(this, this.onQueryFailed));
};
//Function executed on success
onQuerySucceed = function () {
alert(this.rootWeb.get_title());
};
//Function executed on failure
onQueryFailed = function (sender, args) {
alert('Unable to retrieve data from the SharePoint. Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
};
you can get the url without the client object model by using available information and string parse it down from there. Not as robust as the Client-Object model but significantly less complicated for simple tasks.
window.location.href
...gives you the full window url (e.g. "http://sitecollection.com/sites/mysite/Lists/myList/NewForm.aspx?ContentTypeId=0x01006AC2C39AA621424EBAD9C2AC8A54F8B9007B626ABEEB66E34196C46E13E0CA41A2&ContentTypeName=xxxx")
L_Menu_BaseUrl
Or
_spPageContextInfo.siteServerRelativeUrl
...(as Jinxed points out) will give you the relative url (e.g. "/sites/mysite")

Resources