Programmatically adding role with database permissions - c#

I'm trying to add a role to my SSAS DB, but I'm having issues when I try to set the database permissions.
The following code will run without error
var server = new Microsoft.AnalysisServices.Server();
server.Connect("localhost\\SQLSERVER2012");
var database = server.Databases["LRTabDB"];
var role = database.Roles.Add(database.Roles.GetNewName("DataHubRole"));
var databasePermission = new Microsoft.AnalysisServices.DatabasePermission
{
RoleID = role.ID,
ID = role.Name,
Name = role.Name,
Read = Microsoft.AnalysisServices.ReadAccess.Allowed
};
database.DatabasePermissions.Add(databasePermission);
var member = new Microsoft.AnalysisServices.RoleMember { Name = "NETWORK SERVICE" };
role.Members.Add(member);
role.Update();
databasePermission.Update();
database.Update();
But when I attempt to look at the Role in SQL Server Management Studio I get the following error message
Cannot show requested dialog.
Additional information:
Key cannot be null.
Parameter name: key (System)
If I remove the creation of the databasePersmisson object then I can view the role in Management Studio. But ultimately I need to set the database permissions for the role. I've also attempted getting the permissions, but the following just returns null.
database.DatabasePermissions.FindByRole(role.ID);
I've also tried using the following to create the database permissions and got the same results.
var databasePermissions = database.DatabasePermissions.Add(
database.DatabasePermissions.GetNewName(role.Name));
Finally I've also tried using a different Name and ID for the database permission from the role but again that does not help.
I assume there is some step that I'm missing, but the examples I've found for doing this don't shed any light on the problem.
https://bennyaustin.wordpress.com/2011/05/24/ssas-using-amo-to-secure-analysis-service-cube/

I don't know how can I get your error, but I've done something, what works for me.
Here is the code:
Server server = new Server();
server.Connect("Data source=" + cubeServerName + ";Timeout=7200000;Integrated Security=SSPI;");
Database db = server.Databases.FindByName(cubeName);
if (db.Roles.Find("MyRole") == null)
{
Role newRole = db.Roles.Add("MyRole");
RoleMember r = new RoleMember(userDomain + "\\" + userName);
newRole.Members.Add(r);
newRole.Update();
}
DatabasePermission dbperm;
var role = db.Roles.Find("MyRole");
dbperm = db.DatabasePermissions.Add(role.ID);
dbperm.Process = true; //I want to add process permission
dbperm.Update();
I used tutorial from msdn.

Related

SMO not copying role memberships

Hello I'm trying to use SMO to create an exact copy of one DB in the same server. I'm able to copy the schema, the data and the users, but I just noticed that the roles for the users are not copied between the databases. Their roles look like this:
Original DB
Cloned DB
My current transfer object configuration looks like this:
Transfer t = new Transfer(database);
t.Options.DriAll = true;
t.CopySchema = true;
t.CopyData = true;
t.CopyAllDatabaseScopedCredentials = true;
t.CopyAllRoles = true;
t.CopyAllUsers = true;
t.PreserveDbo = true;
t.PreserveLogins = true;
t.DestinationServer = connection.DataSource;
t.DestinationDatabase = dbname;
t.TransferData();
But the roles are not copied, and because of that, when the user tries to read/write anything I get the 'Log in failed' error for that user, is there a config that I'm missing? Or is this not possible using SMO?
Thanks for the help!
Try t.Options.IncludeDatabaseRoleMemberships = true

Creating term fails in SP 2013

I am trying to create a term in a SP 2013 term set. I am able to read the terms from the term set with the given credentials.
Settings:
The ServiceAccountLogonName user is one of the Term Store Admins
Code:
var siteUrl = ConfigHelper.GetValue("SharepointSiteUrl");
var clientContext = new ClientContext(siteUrl);
clientContext.Credentials = new NetworkCredential(ConfigHelper.GetValue("ServiceAccountLogonName"), ConfigHelper.GetValue("ServiceAccountPassword"));
var taxonomySession = TaxonomySession.GetTaxonomySession(clientContext);
var termStore = taxonomySession.TermStores.GetById(new Guid("dae0745c07ae40d6bf4b6c52e3172d6a"));
var termSet = termStore.GetTermSet(new Guid("f40eeb54-7c87-409d-96c7-75ceed6bff60"));
foreach (var tagName in tagNames)
{
var newTerm = termSet.CreateTerm(tagName, Thread.CurrentThread.CurrentUICulture.LCID, Guid.NewGuid());
newTerm.CreateLabel(tagName, Thread.CurrentThread.CurrentUICulture.LCID, true);
termStore.CommitAll();
clientContext.Load(newTerm);
clientContext.ExecuteQuery();
}
Error message when ExecuteQuery is run:
Failed to read from or write to database. Refresh and try again. If the problem
persists, please contact the administrator.
Does the credential need any specific rights on the term store or term set?
Is there anything else I am missing here?
Useful tutorial:
https://github.com/OfficeDev/TrainingContent/blob/master/O3656/O3656-8%20Developing%20advanced%20Taxonomy%20Scenarios%20in%20Office%20365/Lab.md
Make sure that your user has permissions to term store.
Go to central administration ==> manage service application ==> click on line (not link) of your service application ==> choose "Permissions" ribbon button and add your account.
Also make sure that your term set ist open for term creation. You can check the "IsOpenForTermCreation" of you termSet variable.
Third. Use Guid.NewGuid() instead of new Guid() in CreateTerm line.
Had time for some tests:
foreach (var tagName in tagNames)
{
var newTerm = termSet.CreateTerm(tagName, Thread.CurrentThread.CurrentUICulture.LCID, Guid.NewGuid());
}
termStore.CommitAll();
clientContext.Load(termStore);
clientContext.ExecuteQuery();
This worked for me after I've added my user to service application permissions.

C# Nested impersonation - impersonate a user whilst already impersonating another

I have a fairly odd requirement to be able to impersonate a user, when I'm already impersonating another, using C#.
I'm writing an app to allow the management of Active Directory users. This app will provide the ability for anyone in the company to view and maintain certain details about themselves (some of which will not actually be saved to Active Directory, but some of which will), for managers to be able to view and maintain details about their team, and for HR to be able to view and maintain details about anyone.
For obvious reasons I don't want to develop or test this against the live domain. We have recently ported all users over to this domain from another domain, which means I can actually test against the old domain without affecting anything. However, to enable me to do this I have to impersonate my old account on the old domain, which I do on loading the application.
Although for me everything will work fine as I'm setup as a domain admin, going forward obviously not all users will be domain admins, and won't be able to write to AD under their own account, and therefore we have another domain admin user setup specifically for this application, whenever data needs to be saved to AD that user is impersonated. This was working great before when I was testing against an Active Directory I'd setup on a virtual machine because I was logging onto the local domain, however that didn't allow me to step through the code in Visual Studio so debugging was slow, and hence I've stopped using that virtual machine and am using this old domain. Now I'm already impersonating another user (i.e. my old domain account), when it then tries to impersonate the domain admin user it fails with an "System.Security.SecurityException: Access is denied." exception. The line this fails on is just writing out some debugging information using "WindowsIdentity.GetCurrent().Name".
If I change my code so I'm actually logging in using the new domain admin rather than my old account, the first time it goes through it logs in successfully (so the credentials are correct), however when it then goes through and tries to do the same again to write to AD it fails with the above exception. Therefore I think it must be a problem with trying to do a nested impersonate.
Is it possible to do a nested impersonate?
Below is the code I'm using:
private static WindowsImpersonationContext ImpersonateUser(out string result, string sUsername,
string sDomain, string sPassword)
{
// initialize tokens
var pExistingTokenHandle = new IntPtr(0);
var pDuplicateTokenHandle = new IntPtr(0);
// if domain name was blank, assume local machine
if (sDomain == "")
{
sDomain = Environment.MachineName;
}
try
{
result = null;
const int logon32ProviderDefault = 0;
// create token
const int logon32LogonInteractive = 2;
// get handle to token
var bImpersonated = LogonUser(sUsername, sDomain, sPassword,
logon32LogonInteractive,
logon32ProviderDefault,
ref pExistingTokenHandle);
// did impersonation fail?
if (!bImpersonated)
{
var nErrorCode = Marshal.GetLastWin32Error();
result = "LogonUser() failed with error code: " + nErrorCode + "\r\n";
}
// Get identity before impersonation
result += string.Format("Before impersonation: {0}\r\n", WindowsIdentity.GetCurrent().Name);
var bRetVal = DuplicateToken(pExistingTokenHandle, (int)SecurityImpersonationLevel.SecurityImpersonation,
ref pDuplicateTokenHandle);
// did DuplicateToken fail?
if (bRetVal)
{
// create new identity using new primary token
var newId = new WindowsIdentity(pDuplicateTokenHandle);
var impersonatedUser = newId.Impersonate();
// check the identity after impersonation
result += "After impersonation: " + WindowsIdentity.GetCurrent().Name + "\r\n";
return impersonatedUser;
}
else
{
var nErrorCode = Marshal.GetLastWin32Error();
CloseHandle(pExistingTokenHandle); // close existing handle
result += "DuplicateToken() failed with error code: " + nErrorCode + "\r\n";
return null;
}
}
finally
{
// close handle(s)
if (pExistingTokenHandle != IntPtr.Zero)
{
CloseHandle(pExistingTokenHandle);
}
if (pDuplicateTokenHandle != IntPtr.Zero)
{
CloseHandle(pDuplicateTokenHandle);
}
}
}
When this is called for the nested impersonation which fails, "bImpersonated" is actually "true", as is bRetVal, which suggests its worked, however when it gets to "WindowsIdentity.GetCurrent().Name" it fails with the exception above.
I hope this makes sense, and would appreciate any assistance.

Accessing AD (active directory) Properties

I access the AD properties thru the below method. It works fine in my Local VHD (where I'm the domain/local/enterprise Admin) - but the same doesn't work when I access from a Domain user(who has only local admin access).
But the same Domain user(only with local admin access) access all the AD property details using the ADExplorer(SysInternal) tools.
Is it because that is unmanaged code and have Windows APIs to access and in .Net I need domain admin or some privilege ?
Or is there another way - which I'm missing in .Net to access the AD Properties without having an extra domain-level-privilege ??
public void getCurrentUserADDetails(string UserName)
{
string ladpQueryStr = "LDAP://sp.com";
DirectoryEntry dirEntry = new DirectoryEntry(ladpQueryStr);
DirectorySearcher srch = new DirectorySearcher(dirEntry);
srch.Filter = "(cn=" + UserName.ToLowerInvariant().Trim() + ")";
srch.PropertiesToLoad.Add("name");
srch.PropertiesToLoad.Add("memberOf");
srch.PropertiesToLoad.Add("prop123");
SearchResult searcResult = srch.FindOne();
if (searcResult != null)
{
ResultPropertyCollection propertiesCollection = searcResult.Properties;
List<DisplayClass> grdDataList = new List<DisplayClass>();
foreach (string strKey in propertiesCollection.PropertyNames)
{
DisplayClass dispC = new DisplayClass();
dispC.pName = strKey;
dispC.pValue = Convert.ToString(propertiesCollection[strKey][0]);
grdDataList.Add(dispC);
}
dataGridView1.DataSource = grdDataList;
}
}
This is going to run in ASP.Net
thanks in advance :)
I assume you're using integrated authentification - in order for this to work you have to setup account delegation, unless you're running your application on a domain controller. This is a pretty tricky process, but there are a ton of info in Google.
By using Explicit authentication and changing the Search filter, i got the results.
DirectoryEntry dirEntry = new DirectoryEntry(path, username, password, AuthenticationType);

Can't query AD (get a DirectoryServicesCOMException)

I'm attempting to query AD in an ASP.Net (4.0) application that is running on Windows Server 2008 R2 (IIS7 installed). (It also fails when running as a 2.0 application as well)
This is nothing new for me, as I've done this many times before. I wrote a small ASP.Net program that runs fine on my own machine (Windows XP with IIS6), but fails when run on the 2008 box.
(The result is that you see a list of groups the user is a member of in a textbox)
(on button_click)
var userName = txtUserName.Text;
if (userName.Trim().Length == 0)
{
txtResults.Text = "-- MISSING USER NAME --";
return;
}
var entry = new DirectoryEntry("LDAP://blah.blah/DC=blah,DC=blah",
"cn=acct, dc=blah, dc=blah",
"pass");
var search = new DirectorySearcher(entry);
search.Filter = "(SAMAccountName=" + userName + ")";
search.PropertiesToLoad.Add("memberOf");
var groupsList = new StringBuilder();
var result = search.FindOne();
if (result != null)
{
int groupCount = result.Properties["memberOf"].Count;
for (int counter = 0; counter < groupCount; counter++)
{
groupsList.Append((string)result.Properties["memberOf"][counter]);
groupsList.Append("\r\n");
}
}
txtResults.Text = groupsList.ToString();
When I run this code I get the following error on search.FindOne():
System.DirectoryServices.DirectoryServicesCOMException (0x8007203B): A local error has occurred.
at System.DirectoryServices.DirectoryEntry.Bind(Boolean throwIfFail)
at System.DirectoryServices.DirectoryEntry.Bind()
at System.DirectoryServices.DirectoryEntry.get_AdsObject()
at System.DirectoryServices.DirectorySearcher.FindAll(Boolean findMoreThanOne)
at System.DirectoryServices.DirectorySearcher.FindOne()
at WebApplication1._Default.btnSearch_Click(Object sender, EventArgs e)
We've done a lot of research with this and twiddled every IIS7 setting we can think of, but no go so far. Any clues?
Change the username parameter from "cn=xxx, dc=yyy, dc=zzz" to "Domain\Username"
You can also change the IIS Application Pool to run a domain account with the query priveleges you are searching for.
I have a few other comments as well:
Make sure the first entry for the DirectoryEntry constructor includes the container for the users as well. This should help the DirectorySearcher to work more reliably.
I believe the second parameter in the DirectoryEntry constructor should be the user name, not the AD query path.
You should set the AuthenticationType property as well. With Server 2008, by default, this needs to be set to AuthenticationTypes.Secure | AuthenticationTypes.ServerBind | AuthenticationTypes.Sealing. I'd guess that 2008R2 has a simliar requirement.
I see that the question is rather old, but after struggling with this I thought to mention that it is indeed possible to use the LDAP-style of the username (in opposite to the DNS style). This works well for me:
string connString = "LDAP://MyDomain/CN=blah,DC=blah,DC=blah";
string username = "CN=MyAdmin,CN=Users,CN=blah,DC=blah,DC=blah";
string password = "myLittleSecret";
DirectoryEntry root = new DirectoryEntry(
connString,
username,
password,
AuthenticationTypes.None);
Where MyAdmin is a member in the Administrators role.
One little thing that took me a while to find is the AuthenticationTypes.None parameter that is needed if you do not want to communicate over SSL. Surely, you want to do this in production, but for development purposes it may be OK to skip the encryption.
Environment: Windows 7
I was also getting this exception when tried to query the active directory:
SearchResult result = srch.FindOne();
To resolve this, just put the above code inside Security.RunWithElevatedPrivileges().
Final Solution:
SPSecurity.RunWithElevatedPrivileges(delegate()
{
result = srch.FindOne();
});

Categories

Resources