console application - object model - database persmission - sharepoint

I'm trying to run a console application that uses the SharePoint Object Model.
I'm getting the error Cannot open database "dbname" requested by the login. The login failed. Login failed for user 'DOMAIN\userid'.
Some place I have read that the user must have permission to the Content DB.
I can not find an article that explains what permissions to setup. I need this as ammunition to go to my Sys Admin guy to get the permission setup.
Where is there an article that explains that? I have searched google but with no luck.

RunWithElevatedPrivileges doesn't help because it just changes the thread user to the process identity - in a console application, this has no effect because they're the same. The "elevation" in an impersonated web context works because the process identity is the application pool account, which has db_owner on its content database.
If I ask that the account I'm using be given Full Control, under the Policies for Web Application, should that work?
Not according to Ishai Sagi: Object model code and stsadm fail with "Access Denied". In short, it seems db_owner permissions on the content database are required for a user to run object model code (including STSADM) without a web context.

Are you running the console application on the server itself? I assume so.
In this case it is likely to be a permissions issue with the account you are using (RDP?) on the server. The database error side of things can be misleading as you will need to be permissioned within SharePoint itself, which will then give permissions to the database.
I would get your sys admins to create a service account for you to use that can be granted the correct rights. (site collection administrator is often needed, but it depends on the code inside the console app. most do assume site collection admin rights though). you may get more mileage from looking at the application instructions (or if it is your own code just go for site collection admin)
Running a console app is a bit of a major though, so you may have better luck if you give the sysadmins the application to run and instructions... though I doubt you are running this on the prod box.

your user propably don't have permissions to access those lists or webs. You can run your code with elevated privilegies, but it can sometimes give you unexpected results.
Example of how elevated privilegies is used can be found here
Or you can set user unser who's account console app runs as site collection administrator.
Your code updated to run with elevated privilegies can look like this:
private static void DisplayAllLists(string site, string webToOpen)
{
try
{
SPSecurity.RunWithElevatedPrivileges(delegate()
{
using ( SPSite siteCollection = new SPSite(site) )
{
try
{
using (SPWeb web = siteCollection.OpenWeb(webToOpen))
{
SPListCollection lists = web.Lists;
foreach (SPList list in lists)
{
Console.WriteLine(string.Format("List Title: {0}", list.Title));
}
}
}
}
finally
{
siteCollection.RootWeb.Dispose();
Console.ReadLine();
}
}
}
catch (Exception ex)
{
Console.WriteLine("Exception: "+ex.Message);
}
}
Note: This code was written from top of my head, so maybe something is missing..you will have to try it

Related

Redirecting to error page when clicked on the cancel button on admin consent

Hi I am developing web application. I am using Azure active directory for login process. I am working on admin consent. I am able to redirect to admin consent and give the consent. In admin consent page,whenever i clicked on the cancel button in admin consent I am redirecting to error page.
Below is the url I am redirecting when clicked on the admin consent page.
https://mywebsite.net/adminconsent?error=access_denied&error_description=AADSTS65004%3a+The+resource+owner+or+authorization+server+denied+the+request.%0d%0aTrace+ID%3a+7798f669-f82d-4b55-8c9b-1259142e1900%0d%0aCorrelation+ID%3a+82764c15-3e79-4905-840b-952af3dfe6fc%0d%0aTimestamp%3a+2018-09-07+13%3a30%3a42Z
Can someone help me to identify the root cause of the issue? Any help would be appreciated. Thank you.
You're getting the relevant error code back from Azure AD - 65004, telling you the root cause, that Admin has declined to consent. Description is visible in the URL and if you can confirm the meaning of error code by looking it up here -
Sign-in activity report error codes in the Azure Active Directory portal
65004 User declined to consent to access the app. Have the user retry
the sign-in and consent to the app
Update about displaying a meaningful error page
You haven't mentioned what is it that you're using to write your web application. In any case, I tried out a quick ASP.NET MVC web application with similar setup and I clearly get back the response in query string parameters. All you need to do is, read the query string from the URL (I have HttpRequest.QueryString collection in my sample) and check for error/error_description.
Here is a quick sample code on doing that in the MVC controller..
public class AdminConsentController : Controller
{
// GET: AdminConsent
public ActionResult Index()
{
if (Request.QueryString.AllKeys.Contains("error")
&& Request.QueryString.AllKeys.Contains("error_description"))
{
string errorDescription = Request.QueryString["error_description"];
if(errorDescription.Contains("AADSTS65005"))
{
//Do something good about it..
}
}
//if no errors, simply return the view
return View();
}
Since you mention Angular 5.. here's a quick sample for that.
Take a look at this SO post for multiple options
ngOnInit() {
this.param1 = this.route.snapshot.paramMap.get('param1');
this.param2 = this.route.snapshot.paramMap.get('param2');
}
And if you don't want to use anything fancy, plain old window.location should always work from client side. May not be the recommended way though.
window.location.href

Under which account a .net console application which is hosted inside Azure Function app, will be running

I have developed a .net console application which have these main characteristics :-
Integrate with SharePoint online REST API, to retrieve some list items, and modify the items fields.
Will run daily #1 am for example.
I will host this console application inside Azure Function app.
The Azure account does not have any permission on the sharepoint tenant, as the Azure account and the sharepoint online are on different domains.
so i am not sure under which account the console application will be running?
Will it runs under the current Azure account? if this is the case, then this will not work as the azure account is on different domain and does not have any permission on the sharepoint (and it shouldn't have)?
OR
I can define a service account for the Azure function app to run under it, where in this case i can define the service account to be an authorized account inside sharepoint online?
OR
i need to define the username/password inside the console application itself? i do not like to approach, as i will be exposing the password inside the console application. also changing the password for the username, means that we will need to update the console application accordingly..
so can anyone advice on this please?
Thanks
EDIT
code for managing the console application authentication :-
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint.Client;
namespace O365SPProject
{
class Program
{
private class Configuration
{
public static string ServiceSiteUrl = "https://<tenant>.sharepoint.com";
public static string ServiceUserName = "<user>#<tenant>.onmicrosoft.com";
public static string ServicePassword = "xxxxxxxxxx";
}
static ClientContext GetonlineContext()
{
var securePassword = new SecureString();
foreach (char c in Configuration.ServicePassword)
{
securePassword.AppendChar(c);
}
var onlineCredentials = new SharePointOnlineCredentials(Configuration.ServiceUserName, securePassword);
var context = new ClientContext(Configuration.ServiceSiteUrl);
context.Credentials = onlineCredentials;
return context;
}
static void Main(string[] args)
{
var ClientContext=GetonlineContext();
Web web = clientContext.Web;
// do somethings
}
}
}
There are multiple parts to your question, so I'll answer it accordingly.
1. Which option out of the 3 you mentioned (or if there is a different better option :)), should you use to manage your configuration data/service account identity
OPTION 4 (similar to your option 2 with subtle difference):
You should take your service account identity and configuration data out of your console application completely and pass them in through "Application Settings" for your Azure Function App.
This option is similar to the option 2 you had in your question, as you keep the information outside of console app code
I can define a service account for the Azure function app to run under
it, where in this case i can define the service account to be an
authorized account inside sharepoint online?
but difference is that I am not saying that you will be able to define a service account for your Azure function app to run under (because you can't control the account that Azure function will run under, Microsoft infrastructure takes care of it), instead you will pass it to your console app as a secure configuration data and your console app will use it. More on security/encryption later while comparing the options.
I actually took your console application code from question, created a console app and used it in a timer triggered Azure function to get it working. So these steps are from a working sample. I used the "Microsoft.SharePointOnline.CSOM" nuget package in my console app, and had to upload some of the dependency dlls along with exe in order for it to run. Feel free to ask for more details on doing this part if you run into issues.
Adding Application Settings - Navigate your Azure Function App and Click on "Application Settings"
Add Settings for all items that you want to take out of your console application and control from outside. I did it for all 3 items I saw, but this is up to you.
Then change your code to use these settings. I have shown the exact code changes at the end.
OPTION 5
Registering a new application in Azure AD to represent your Azure function.
You should register a new application in your Azure AD and use this identity to access SharePoint online.
You will need to grant permissions to SharePoint online for this application (NOTE: permission assignment will not be as granular or detailed as in case of your service account approach, I'll explain more while comparing the options)
You will need to associate a certificate with your AzureAD application to help in authentication.
While authenticating to SharePoint online, you will not be directly able to use the SharePointOnlineCredentials class as in your code today, but instead send the bearer token in 'Authorization' header for the http request.
Here is blog post that walks through detailed steps involved in this option 5.
NOTE: This blog still leaves out the certificate details like password in function code at the end, which will not be ideal and you will need to move it out to App Settings or Azure Key Vault ideally.
2. Which account will the .NET console application run under and a Quick Comparison of all options
It's an arbitrary IIS App Pool account, as pointed out by #Mitch Stewart, other SO posts and is evident in the output I get for my function, it's exact value in my run came out to be "IIS APPPOOL\mawsFnPlaceholder0_v1 ". See the image at the bottom. You already have some good info shared on this, so I'll not repeat. Only thing I'll add is that this account will be controlled by the infrastructure hosting your function app and will be designed more towards taking care of isolation/other concerns in a shared infrastructure where many function apps can run, so trying to control/change it may not be the way to go right now.
Option 1 (from your question) - Giving permissions to an IIS app pool account for your SharePoint Online site, especially when you don't control the account may not be a good idea.
Option 2 (from your question) - It would have been better than the other 2 options you mentioned, but you can't really control this account.
Option 3 (from your question)- Embedding this information deep into your console application will be a maintenance issue as well as not the most secure option unless you start reading form a vault etc. Maintenance issues will remain no matter what you do because it's embedded in compiled code, which it shouldn't be.
Option 4 - This is better than previous 3 options, because it separates the concern of code from configuration and identity information, no recompilation needed for updates. Also note that whatever you store in App Settings configurations is encrypted by default (with good governance of key rotation) and is the recommended way. These values are decrypted only just before execution of your app and loaded into process memory. Look detailed discussion in this link, I have also given a small relevant excerpt below -
Provide documentation about encrypt/decrypt settings
Even with this option you could store them in a key vault and then your setting would be the URL of the key vault secret that has the actual information.
Option 5 - This option makes use of Azure AD based identity to authenticate with SharePoint Online which is good part.
It does come with some additional effort and some limitations though, so you will need to consider if these limitations are acceptable or not in your scenario:
Permissions for SharePoint online will not be as granular/detailed as a user being given permissions from inside SharePoint Users/Groups interfaces (no site/list/folder/item level specific permissions etc). In this approach, you will give the permissions as part of setting up Azure AD application and you will only get generic options like these (shown in screenshot below)
Microsoft has some well documented limitations in this scenario, which you can read here: What are the limitations when using app-only
So overall, I would suggest you choose option 4 or option 5, or a combination of both for your implementation depending on which limitations are acceptable in your scenario.
3. Code Changes to use App Settings
Just the important Change
public static string ServiceSiteUrl = Environment.GetEnvironmentVariable("ServiceSiteUrl");
public static string ServiceUserName = Environment.GetEnvironmentVariable("ServiceUserName");
public static string ServicePassword = Environment.GetEnvironmentVariable("ServicePassword");
Full Code in a working Sample (I replaced do something with reading the title and Url for SharePoint Web object):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint.Client;
using System.Security;
using System.Security.Principal;
namespace O365SPProject
{
class Program
{
private class Configuration
{
public static string ServiceSiteUrl = Environment.GetEnvironmentVariable("ServiceSiteUrl");
public static string ServiceUserName = Environment.GetEnvironmentVariable("ServiceUserName");
public static string ServicePassword = Environment.GetEnvironmentVariable("ServicePassword");
}
static ClientContext GetonlineContext()
{
var securePassword = new SecureString();
foreach (char c in Configuration.ServicePassword)
{
securePassword.AppendChar(c);
}
var onlineCredentials = new SharePointOnlineCredentials(Configuration.ServiceUserName, securePassword);
var context = new ClientContext(Configuration.ServiceSiteUrl);
context.Credentials = onlineCredentials;
return context;
}
static void Main(string[] args)
{
var ClientContext = GetonlineContext();
ClientContext.Load(ClientContext.Web);
ClientContext.ExecuteQuery();
Console.WriteLine("This app found web title as: {0} and URL as: {1}",
ClientContext.Web.Title, ClientContext.Web.Url);
Console.WriteLine("Console app is running with identity {0}", WindowsIdentity.GetCurrent().Name);
}
}
}
OUTPUT on executing Azure Function
The SharePoint REST API supports OAuth. Here's a promising article. Although, this might be a bit much for you intentions. Alternatively, you can try using basic auth (username + password). To guard against plain text passwords, you can store them in Azure Key Vault.
Edit
The current user of an Azure function is the identity of the IIS app pool.

Cannot access site groups with user with manage hierarchy permissions

I have a custom form that lists the site groups and the users in each group.
the form has twi drop down lists: one to display the site's group and the other to display the users in that group.
when I log to the form with the administrator user it works fine.
But if I log in with a user with manage hierarchy permission level, it omly displays the info of the domain groups and if I try to access a sharepoint group I get an access denied error.
I use run with elevated permissions in my code
I really don't know what to do in this
thanks.
Two common mistakes when using RunWithElevatedPrivileges is:
Using the SPContext.Current.Web (or Site etc) won't change the identity of the web object, it is already in memory.
Declaring the SPWeb outside the delegate, with similar results of mistake 1
That said, try something like:
Guid siteId = SPContext.Current.Site.Id;
SPSecurity.RunWithElevatedPrivileges(() =>
using (SPSite elevatedSite = new SPSite(siteId))
using (SPWeb elevatedWeb = elevatedSite.RootWeb)
{
//impl
});

SPFarm.Local.Solutions.Add - Exception - "Access Denied"

Here is my code snippet:
SPSecurity.RunWithElevatedPrivileges(delegate()
{
SPSolution newSolution = SPFarm.Local.Solutions.Add(#fullPath);
});
The stacktrace and innerexception give no further clues. The Exception.Source says Microsoft.SharePoint.
SPFarm.Local.CurrentUserIsAdministrator() returns TRUE for the userid.
The userid is in the Farm Administrators group.
Any ideas?
EDIT
I have changed my code to the following and still get the Access Denied error:
private void AddSolution()
{
SPSolution newSolution = SPFarm.Local.Solutions.Add(#fullPath);
}
SPSecurity.CodeToRunElevated elevatedAddSolution = new SPSecurity.CodeToRunElevated(AddSolution);
SPSecurity.RunWithElevatedPrivileges(elevatedAddSolution);
Your main problem might just be that you are not DBO of a sharepoint database (_Config if I'm not wrong). Adding a solution to a farm is something that require more rights than just access to the farm.
Be sure that the user running this is Farm Administrator and DBO of the proper database.
If you still have problem... try running
stsadm -o addsolution -filename
"myWsp.wsp"
If you have the proper right, it will give you the proper error.
Have you tried declaring the delegate outside of the call to RunWithElevatedPriviliges?
Edit: Ignore below as you appear to have checked permissions.
RunWithElevatedPriviliges will use the identity of the application pool which SP is running under. Have you ensured this account has sufficient privileges in your environment?
I do believe that the issue you are having is due to the fact that you are using the static member to access the SPFarm object. I think that it is similar to running the SPcontext static class which will still run under the security context of the logged on user and not under the elevated privledges context (which is the local application pool identity).
Try this instead inside your delegate:
SPFarm spFarm = SPWebService.AdministrationService.Farm;
SPSolution newSolution = spFarm.Solutions.Add(#fullPath);
EDIT:
Since the above didn't help then your issue probably has to do with database permissions to the config database. The RunWithElevatedPriviliges will run under the application pool's identity that the code is running under. Adding a solution to your farm affects the configuration database so your application pool identity will need access to the config database. As a test try adding the app pool identity to the config db and give it dbo permissions. If that fixes the issue then you will need to find the minimum amount of permissions that each of your app pool accounts will need to add solutions (do not leave as dbo)

SharePoint WebPart Permissions

Hi I am using the SharePoint namespace for a webpart and I encounter some permission errors when I try to use the System account. Is there a way I can use a defined user instead of the system account?
Right now I have:
SPUserToken sysToken = SPContext.Current.Site.SystemAccount.UserToken;
using (SPSite site = new SPSite(_SPSite, sysToken))
I want to be able to use an account on the domain instead of the System account, thanks for any advice.
You may need to use RunWithElevatedPermissions to get access to the System account to work, as per the following blog post:
http://solutionizing.net/2009/01/06/elegant-spsite-elevation/
You can use the SPUserCollection
SPContext.Current.Site.RootWeb.AllUsers
to get all of the users on the site, and get the SPUser from there. Once you have the SPUser you can get the UserToken.
What are you trying to do? If you don't use a token, the web will be opened with the same permissions as the current user
/* runs as user requesting the web part */
SPSite site = SPContext.Current.Web.site
or you can wrap it in the RunWithElevatedPrivileges delegate
/* runs with admin privileges */
SPSecurity.RunWithElevatedPrivileges(
delegate()
{
using (SPSite site = new SPSite(SPContext.Current.Web.Site.Url))
{
//do stuff
}
}
);

Resources