ServerUnauthorizedAccessException when trying to connect to Project Server through CSOM - c#

I have Microsoft SharePoint 2013 onPremise Farm with Microsoft Project Server 2013 installed on it.
I am trying to build a WebPart which should connect to the Project Server through CSOM from a TeamSite to get Project Information.
This is my super simple code:
string pwaPath = "http://sp2013/PWA";
ProjectContext projContext;
projContext = new ProjectContext(pwaPath);
projContext.Load(projContext.Projects);
projContext.ExecuteQuery();
When trying to execute it i get the following error:
ServerUnauthorizedAccessException
When i add
projContext.Credentials = new NetworkCredential("administrator", "pass", "domain");
It works! But how can i make the code using the logged in user, cause the credentials passed above are the users credentials who is currently logged in.

Your code won't work because farm solution does not execute in the context of the SharePoint user. If you check System.Security.Principal.WindowsIdentity.GetCurrent() you'll see that current identity is IIS built-in IUSR. This is the identity ClientContext will use to issue the request.
If you elevate permissions using SPSecurity.RunWithElevatedPrivileges() code will be executed with app pool identity. This may be enough in some scenarios, but may also be not secure, as all users will be able to access the resources.

Related

How to check if a windows user has access (Windows Integrated Security authentication) to SQL Server Analysis Services (SSAS) Server via impersonation

For a SQL Server instance, to check if a windows user is present and has any access or not one can try various ways as detailed here.
I'm looking for something similar for SQL Server Analysis Services (SSAS) server.
I went into properties of SSAS Server from right-click context menu and on Security tab I can see that there are several windows users already configured:
Is there any way to check from a client application (written in C#) by making some sort of test connection or does SSAS also maintains some metadata database of its own like master database in SQL Server instance (DB engine) which can be queried. I checked the Databases node in SSAS server but I don't see any default databases there:
In the client application I'm working upon, I've windows user name and password as input. In my client application there is a simple winform with two text boxes to take AD user name and password which need to be connected to a SSAS Server. My gut feel is that password is of no relevance here as SSAS supports only Windows integrated authentication mode. My client application would be running under an account which already has access to SSAS server I'm trying to connect.
Update: After getting help from #Vaishali, I'm able to figure out that it is possible to make a test connection to an SSAS server using ADOMD.Net.
Now, the problem here is that the connection string implicitly uses the AD account of the user with which I'm running the client application to connect to the SSAS server. I don't think it would be possible mention an windows AD account user name and password explicitly in the ADOMD.Net connection strings while using Windows Integrated authentication. Even connection strings of SQL Server don't allow mentioning the windows username and password explicitly in the connection string as mentioned here.
Update 2: I have got a lead from one of my friends that it is possible to fire some MDX query on SSAS to get user access details.
Update 3: SSAS server supports only Windows Integrated Security mode of authentication unlike SQL Server DB engine which also supports userid-password based SQL authentication. So, some form of impersonation would be required to fire MDX queries on behalf of other user for which I'm trying to check access on SSAS server through Windows Integrated Security only.
Hmphh...It was quite a journey to really be able to nail it through ADOMD.Net.
Core methodology: The core philosophy is the fact that connection to SSAS server supports only Windows Integrated Security based authentication. The SQL authentication like we do for sa user in SQL Server isn't supported in SSAS.
So, the basic idea was to try to connect to the SSAS server using Windows Integrated Security based authentication and fire an MDX query in the context of the user we are trying to check. If the query gets executed successfully then the user has access. If the query execution returns an error/exception then the user doesn't have access.
Please note that just to be able to open a connection to the SSAS server is not an indicator of user-access due to reasons described here. You must fire a query to check access.
For ADOMD.Net until v12.x:
Now, we know that Windows Integrated Security based authentication always takes the user details from the user-context under which the application/process is running. You can not pass the user credentials in the connection string of ADOMD.Net connection. Here is the code I wrote to accomplish it. You need to refer to Microsoft.AnalysisServices.AdomdClient.dll in your C# project.
using Microsoft.AnalysisServices.AdomdClient;
public static int IsSsasAccessibleToUser(string ssasServerName)
{
var hasAccess = 0;
try
{
using (var adomdConnection = new AdomdConnection($"provider=olap;datasource={ssasServerName};Catalog=myDatabaseName"))
using (var adomdCommand = new AdomdCommand())
{
adomdCommand.CommandText = "SELECT [CATALOG_NAME] AS [DATABASE],CUBE_CAPTION AS [CUBE/PERSPECTIVE],BASE_CUBE_NAME FROM $system.MDSchema_Cubes WHERE CUBE_SOURCE = 1";
adomdCommand.Connection = adomdConnection;
adomdConnection.Open();
adomdCommand.ExecuteNonQuery();
Log("ExecuteNonQuery call succeeded so the user has access");
hasAccess = 1;
}
}
catch (Exception ex)
{
Log("There was an error firing query on the database in SSAS server. so user doesn't have access");
}
return hasAccess;
}
Now, to leverage Windows Integrated Security based authentication we can run this code in two ways:
Out-Proc Impersonation : Put this code inside a console application. Use the "Run as different user" option in the context menu when we right click the exe. Put the credentials of the user Y (let's say) so that application starts in the context of user Y for which we need to validate the access on SSAS server. ADOMD.Net will use user Y's identity while connecting using Windows Integrated Security for SSAS server. If code succeeds the user has access.
In-Proc Impersonation: The other case could be that you are running the application as user X but you want to test the access of user Y. Here effectively you require in-place impersonation while running the above code. For achieving it I used a famous NuGet package "Simple Impersonation" which uses the default .Net library classes WindowsImpersonationContext and WindowsIdentity . Creator of this NuGet package had first posted a great answer here.
Observation in SQL Server Profiler: After you've impersonated user Y, you will clearly see the MDX query getting fired in the context of user Y if you capture the session as shown below:
Caveats and concerns:
One issue that I faced while using this in-proc impersonation is that it doesn't work if the SSAS server is located on the same machine where the application code is running. This is due to the inherent behavior of native LogonUser API (using LOGON32_LOGON_NEW_CREDENTIALS LogonType) which is called during impersonation calls by the NuGete package. You can try other logon types as detailed here which suites you need.
You require password of the user as well along with the domain name and user name to do impersonation.
For ADOMD.Net v13.x onwards
Then, I came across this ChangeEffectiveUser API documentation on MSDN here. But, intellisense wasn't showing this API. Then I found out this API got added in ADOMD.Net with SQL Server 2016 release. There are various ways to get the latest release:
C:\Program Files\Microsoft.NET\ADOMD.NET\130\Microsoft.AnalysisServices.AdomdClient.dll
I'm not sure who dumps this file at this location. Is it part of Microsoft.Net extensions or SQL Server installation.
In Installation folder of Microsoft SQL Server. I got it at path - C:\Program Files\Microsoft SQL Server\130\Setup Bootstrap\Update Cache\KB3182545\ServicePack\x64\Microsoft.AnalysisServices.AdomdClient.dll
NuGet package here. For some weird reason best known to MS the NuGet package of v13.x of ADOMD.Net has been named Unofficial.Microsoft.AnalysisServices.AdomdClient. Not sure why they introduced a separate NuGet package with Unofficial prefix when this should have been simply the next version of the already existing NuGet package Microsoft.AnalysisServices.AdomdClient present here.
So the new API ChangeEffectiveUser present in latest version on AdomdConnection clas can be used easily to impersonate any user as below:
adomdConnection.Open();
//impersonate the user after opening the connection
adomdConnection.ChangeEffectiveUser("domainName\UserNameBeingImpersonated");
//now the query gets fired in the context of the impersonated user
adomdCommand.ExecuteNonQuery();
Observing Impersonation in SQL Server Profiler: Although one peculiar observation I had in the SQL Server Profiler is that the logs of query being fired still shows the name of the original user with which your application process is running.
So to check whether impersonation is happening or not I removed the access rights of the user domainName\UserNameBeingImpersonated from SSAS server. After that, when I ran the above code again then it resulted in exception whose message clearly states that - the user domainName\UserNameBeingImpersonated doesn't have permission on the SSAS server or the database doesn't exist. This error message clearly suggests that impersonation is working.
Advantages and Backward compatibility of this approach:
Although the API is very recent as it came up with SQL Server 2016 but I was able to use it successfully with SSAS server 2014 as well. So it looks fairly backward compatible.
This API works irrespective of whether your SSAS server is local or remote.
You just require the domain name and user name for doing impersonation. No password require.
What to do if we simply want to check the access on the SSAS server without involving any database present on the SSAS server?
Change the connection string to not involve any database. Remove the Catalog key as following connection string - "provider=olap;datasource={ssasServerName};"
Fire the following query instead to check access - SELECT * FROM $System.discover_locks in the code snippet shown initially in the post.
If you wish to check if user has accessibility to SSAS server, one option you can try with C# is: try connecting SSAS with given user credential, if you succeed, you have access.
If you are looking for roles and security mapped to individual cube database, following link will be usefull.
http://www.lucasnotes.com/2012/09/list-ssas-user-roles-using-powershell.html#comment-form
C# code lines:
import library Microsoft.AnalysisServices.AdomdClient;
and code lines would be:
DataSet ds = new DataSet();
AdomdConnection myconnect = new AdomdConnection(#"provider=olap;datasource=.\SQL20f12");
AdomdDataAdapter mycommand = new AdomdDataAdapter();
mycommand.SelectCommand = new AdomdCommand();
mycommand.SelectCommand.Connection = myconnect;
try
{
myconnect.Open();
}
catch
{
MessageBox.Show("error in connection");
}
Hope this works for you.

Authenticating with Exchange Web Services from server after setting up impersonation context

I have the following setup for an app I want to use on the local network:
Silverlight Client App -> Web Server (data objects etc) | -> Exchange
| -> SQL Database
Everything else works fine, I can get data from the DB, etc (might be worth noting I'm using SQL auth for SQL not windows auth), but when I try to get calendar entries for the current user from exchange using Exchange Web Services it gives me a 401 unauthorised error.
This is all running on my local dev server (on my machine) which is running under a classic ASP.NET 4 app pool using the default ApplicationPoolIdentity. I'm not coming in via SSL, just over port 81
I can connect to get my own calendar entries, but for other users I get the error
If I try it without the impersonation code, I get a Connection did not succeed. Please try again later error since I assume that EWS will be using ApplicationPoolIdentity which doesn't have a domain logon.
I've used the following code to impersonate (ignore any potentially unhandled exceptions/security holes for now I just want to get it working!):
var impersonationContext = ((System.Security.Principal.WindowsIdentity)System.Threading.Thread.CurrentPrincipal.Identity).Impersonate();
// Use default credentials
service.UseDefaultCredentials = true;
// Get the target folder ID using the email address
var folder = new FolderId(WellKnownFolderName.Calendar, new Mailbox(emailAddress));
// Get the appointments
var response = service.FindAppointments(folder, view);
impersonationContext.Undo();
// Return list of appointment entities
return response.Items;
Looking at System.Security.Principal.GetCurrent() shows that the user changes to the identity of the user I want to impersonate after the Impersonate method is called. The AuthenticationType is Kerberos and the ImpersonationLevel is Impersonate. As far as I can see this should work but it looks like my web server doesn't want to successfully auth with exchange.
Am I missing something in my setup or in exchange?
This ended up working in the end - it ended up being some exchange settings and using the current threads principal instead of the system principal

Does TFS API require Visual Studio?

I am trying to check in, check out, GetLatest, etc. with the TFS API using C#.
For the development PC and an Admin user this works flawlessly.
however, on a dedicated maschine without VS 2010 installed and the TFS user being a non-Admin this does nto work.
I get the following error:
*
Access to the registry key
'HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\10.0' is denied.
2012-10-08 14:58:30 [...] error : at
Microsoft.Win32.RegistryKey.Win32Error(Int32 errorCode, String str)
at Microsoft.Win32.RegistryKey.CreateSubKey(String subkey,
RegistryKeyPermissionCheck permissionCheck, RegistrySecurity
registrySecurity) at
Microsoft.TeamFoundation.Client.UIHost.get_UserRegistryRoot() at
Microsoft.TeamFoundation.VersionControl.Client.Workstation.get_GetLatestOnCheckout()
*
I wonder if this has to do with Administrator rights?
The TFS "user" is actually a website running under that under account, so it is and is not supposed to be an Admin.
I tried running a test console app with my own credentials on the same maschine, and it works. So this is a credentials.
Can anyone help?
Is it enough to make the website user account an Admin?
And: what does it want to access the registry when creating the local workspace?
We've seen this kind of error when you are running a website under the application pool identity but the application pool identity is set to not load the user profile (and therefore has no HKCU registry access).
In IIS, under the advanced settings, set "Load User Profile"=True
See http://geekswithblogs.net/ProjectLawson/archive/2009/05/05/iis-system.web.aspnethostingpermission-exception-on-windows-7-rc.aspx
.. for more info.

AzMan gives different results to different servers

We have 2 web-servers which are theoretically identical, but are producing different results when performing an AzMan authorisation check.
We have the same web-site running on both machines (literally the same web-site - it's been XCOPYed from one to the other, and it runs under the same service account). All this web-site does is perform an authorisation check against an AzMan database (sitting on a separate SQL server).
However, on the working web-site (WebA) this check returns 0 (i.e. "user is authorised"), while on the broken web-site (WebB) this check returns 5 (i.e. "user is NOT authorised"). We are expecting 0 on both web-sites. The same user is accessing both web-sites, from the same PC.
Does anyone have any ideas for things we can check?
Environment details
Windows Server 2008 R2
Same AD domain
IIS 7.5
.NET 3.5
AzMan Database runs on SQL Server 2005/Windows Server 2008 R2.
Code
AzAuthorizationStoreClass authStore = new AzAuthorizationStoreClass();
// initialise the store
authStore.Initialize(0, "mssql://Driver={SQL Server};Server={OURDBSERVER};Trusted_Connection={Yes};/OURDATABASE/OURAPPLICATION", null);
// open the store
IAzApplication2 authApp = authStore.OpenApplication2("OURAPPLICATION", null);
// get the identity of the user NOT the service account
WindowsIdentity identity = Thread.CurrentPrincipal.Identity as WindowsIdentity;
// and from that derive the token
ulong userToken = (ulong)identity.Token.ToInt64();
// get the context based on the token
IAzClientContext3 clientContext =
(IAzClientContext3)authApp.InitializeClientContextFromToken(userToken, null);
// get the operation object based on the id
IAzOperation2 azManOperation = (IAzOperation2)authApp.OpenOperation(operationId, null);
// generate an audit identifier
string auditIdentifer =
string.Format("{0}{1} : O:{2}", "{the_correct_id}", identity.Name, operationId);
uint accessResult = clientContext.AccessCheck2(auditIdentifer, string.Empty, azManOperation.OperationID);
return accessResult.ToString();
Many thanks,
RB.
Thanks to David Hall for pointing me in the right direction.
Investigation showed that both web-sites were enabled for both Windows authentication and anonymous access. However, on one web-site the user was being logged in correctly, while on the broken web-site it was falling back to anonymous mode.
Disabling anonymous access fixed this problem by ensuring the user logs in to both web-sites.
However, this leaves another question of why the browser logs in anonymously on one web-site but not the other - one for ServerFault I think.
In our case, we were using ASP.NET impersonation with Windows Authentication and not Anonymous. Tt was working on the Windows 7 Enterprise x64 Development machine and not on the Windows Server 2008 R2 x64 test server. Both Application Pools were set up exactly the same with the same domain account credentials.
It turns out that ASP.NET impersonation was the root cause of the issue. After disabling ASP.NET impersonation, the App Pool account was now being used as the credentials to connect to the AzMan store successfully. The same issue was occurring when connecting to an AzMan store in Active Directory or SQL Server.
For clarity, the error I was getting was: Value does not fall within the expected range. from AzAuthorizationStoreClass.Initialize()
My final connection string was:
<add name="AzPolicyStore" connectionString="mssql://Driver={SQL
Server};Server=sqlserver\instance;/DatabaseName/AzStore" />

Error HRESULT E_FAIL when creating Exchange mailbox (CDOEXM.IMailboxStore.CreateMailbox)

I am trying to automate the process of creating an Exchange Mailbox for AD users and am running into an issue. When calling the CreateMailbox method I am receiving the error "Error HRESULT E_FAIL has been returned from a call to a COM component". I have installed and referenced the Exchange Management Tools and am using impersonation for permissions.
Here is the code:
ActiveDs.IADsUser adUser = (ActiveDs.IADsUser)user.NativeObject;
adUser.AccountDisabled = !Active;
user.CommitChanges();
//Set Password
user.Invoke("SetPassword", Password);
user.CommitChanges();
//Create Mailbox
IMailboxStore mailbox;
mailbox = (IMailboxStore)adUser;
mailbox.CreateMailbox("LDAP://CN=StandardUsers,CN=StandardUsers,CN=InformationStore,CN=xxxxx," +
"CN=Servers,CN=First Administrative Group,CN=Administrative Groups," +
"CN=xxxxx Main,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=xxxxx,DC=com");
user.CommitChanges();
My original response was kinda wack; I totally misread the code. What you have is similar to what we had working in production back during Exch 2003. We called the code from a web service that ran as an account that had admin privileges in AD and Exchange.
Anyhow, this is what MSDN is saying now link:
Important Because of issues with multiple-hop authentication and unexpected results observed in multithreaded runtime environments, Microsoft® does not support using CDO for Exchange Management (CDOEXM) in Microsoft ASP.NET pages, ASP Web pages or in Web services. To create Web-based Microsoft Exchange management applications that use CDOEXM functionality, Microsoft recommends wrapping CDOEXM in a Distributed Component Object Model (DCOM) package and calling that package from your application.

Categories