Programmatically modify ACL to give application pool all permissions to application (IIS) - iis

I have a process that creates an application and application pool using the Server Manager object in the Microsoft.Web.Administration namespace, the application pool is created first and then the application, assigning the newly created app pool to the application, code below.
protected TResult UseServerManagerWrapper<TResult>(Func<ServerManager, TResult> func)
{
using (var serverManager = new ServerManager())
{
return func(serverManager);
}
}
Application creation function
public void CreateApplication(String siteName, String parentApplicationName, String organisationName, String applicationName, String applicationPoolName)
{
UseServerManagerWrapper(serverManager =>
{
var site = serverManager.Sites[siteName];
var newApplication =
site.Applications.Add(
GetApplicationPath(parentApplicationName, organisationName, applicationName),
this.GetGeneratedApplicationPhysicalPath(siteName, parentApplicationName, organisationName, applicationName));
newApplication.ApplicationPoolName = applicationPoolName;
serverManager.CommitChanges();
return true;
});
}
and app pool creation.
public Boolean CreateApplicationPool(String applicationPoolName)
{
return UseServerManagerWrapper(serverManager =>
{
var appPool = serverManager.ApplicationPools.Add(applicationPoolName);
appPool.ManagedPipelineMode = ManagedPipelineMode.Integrated;
appPool.ManagedRuntimeVersion = "";
serverManager.CommitChanges();
return true;
});
}
This all works fine, the only problem I have is that I have to go into the application folder and manually assign permissions for the application pool.
I can't see anything in the ServerManager documentation that can help me and I can't figure out a way to use the Directory.SetAccessControl Method to give an application pool permissions. Is there anyway to do this in code?
Apologies if I'm using wrong terminology or anything, I'm new to publishing in general. Let me know if you need anymore info.

Ok, so after a lot of searching and some trial and error I've found the resolution and it's nothing to do with the ServerManager object. First of all to get this to work in ASP.NET Core 2.1 (1.x/2.x) I needed the System.IO.FileSystem.AccessControl Nuget and the Namespaces below.
using System.Security.AccessControl;
using System.Security.Principal;
These give the ability to modify the ACL of files and folders and then the CreateApplication function becomes the below.
public void CreateApplication(String siteName, String parentApplicationName, String organisationName, String applicationName, String applicationPoolName)
{
UseServerManagerWrapper(serverManager =>
{
var site = serverManager.Sites[siteName];
var generatedPath = this.GetGeneratedApplicationPhysicalPath(siteName, parentApplicationName, organisationName, applicationName);
var newApplication =
site.Applications.Add(
GetApplicationPath(parentApplicationName, organisationName, applicationName),
generatedPath);
newApplication.ApplicationPoolName = applicationPoolName;
var dInfo = new DirectoryInfo(generatedPath);
var acl = dInfo.GetAccessControl();
var acct = new NTAccount($"IIS APPPOOL\\{applicationPoolName}");
acl.AddAccessRule(new FileSystemAccessRule(acct, FileSystemRights.FullControl, InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit, PropagationFlags.NoPropagateInherit, AccessControlType.Allow));
dInfo.SetAccessControl(acl);
serverManager.CommitChanges();
return true;
});
}
The code between "newApplication.ApplicationPoolName = applicationPoolName" and "serverManager.CommitChanges()" gets the ACL from the newly generated directory giving the ability to modify it and reassign with a new FileSystemAccessRule.

Related

Copying or moving files around in SharePoint Online

I see so many people struggling to copy or moving files around in SharePoint online, that I decided to write a small demo console app to show how to do it.
We will be using the CreateCopyJobs method, available on CSOM to copy a folder from one site collection to another. This method can be used to copy or move files between site collections or even on the same SC, betwen different libraries or folders inside a library.
The method works exactly as the UI, when you try to copy or move something in a library.
1 - Create new .NET console app. We will be using PnP, so go to your project NuGet manager and add SharePointPnPCoreOnline
2 - add to the usings of your class the following:
using Microsoft.SharePoint.Client;
using Newtonsoft.Json;
using OfficeDevPnP.Core;
3 - Define the following class to receive the status of the job that we will be checking.
class CopyJobProgress
{
public string Event;
public string JobId;
public string CorrelationId;
}
4 - Now add this sample main method:
static void Main(string[] args)
{
var siteUrl = "https://...-admin.sharepoint.com";
var userName = "admin#...";
var password = "....";
AuthenticationManager authManager = new AuthenticationManager();
using (var ctx = authManager.GetSharePointOnlineAuthenticatedContextTenant(siteUrl, userName, password))
{
var web = ctx.Web;
ctx.Load(web);
ctx.ExecuteQuery();
string sourceFile = "https://....sharepoint.com/sites/<site>/<library>/<file or folder>";
string destinationPath = "https://....sharepoint.com/sites/<site>/<destination library>";
var createJobInfo = ctx.Site.CreateCopyJobs(new string[] { sourceFile }, destinationPath,
new CopyMigrationOptions() { IsMoveMode = false, IgnoreVersionHistory = true,
AllowSchemaMismatch = true, NameConflictBehavior = MigrationNameConflictBehavior.Replace });
ctx.ExecuteQueryRetry();
Dictionary<string, CopyJobProgress> eventsFound = new Dictionary<string, CopyJobProgress>();
bool jobEndFound = false;
while (!jobEndFound)
{
var progress = ctx.Site.GetCopyJobProgress(createJobInfo[0]);
ctx.ExecuteQuery();
foreach (string log in progress.Value.Logs)
{
CopyJobProgress progressRes = (CopyJobProgress)JsonConvert.DeserializeObject(log, typeof(CopyJobProgress));
if (!eventsFound.ContainsKey(progressRes.Event))
{
Console.WriteLine(DateTime.Now + " - " + progressRes.Event + " - CorrelationId: " + progressRes.CorrelationId);
eventsFound[progressRes.Event] = progressRes;
}
if (progressRes.Event == "JobEnd")
{
jobEndFound = true;
}
}
if (!jobEndFound)
System.Threading.Thread.Sleep(2000);
}
Console.WriteLine("Done!");
}
}

Remote Administration IIS not working after sideloading to a device connected with fixed internet

I must access some data from IIS by connecting my IIS to a virtual machine to avoid any further configuration.
The code is running well but after I sideload my app, it seems that my app can't reach my data anymore. For example, I've got a folder called Video in my shared folder and I just changed:
static string adresse ="http://localhost"
into
static string adresse = "http://172.16.1.113";
the app can still run well when I am connected wireless to the network but when I use a device connected with fixed internet I got a message saying it can't connect to the server
public static async Task<List<Uri>> GetMedia()
{
try
{
List<Uri> target = new List<Uri>();
HtmlDocument document = new HtmlDocument();
var httpClient = new HttpClient();
var urlVideos = adresse + "/Videos";
var response = await httpClient.GetAsync(urlVideos);
var result = await response.Content.ReadAsStringAsync();
string htmlString = result;
document.LoadHtml(htmlString);
var collection = document.DocumentNode.DescendantsAndSelf();
foreach (HtmlNode link in collection)
{
if (link.Attributes.Contains("href") && !string.IsNullOrEmpty(link.Attributes["href"].Value.Trim().Trim('/')))
{
target.Add(new Uri(adresse + "" + link.Attributes["href"].Value));
}
}
return target;
}
catch (Exception)
{
string errors = "Proxy.getMedia" + iLine.ToString();
App.ProxyErrors = errors;
throw;
}
}
Any suggestions?
I found a solution I just had to go to app.manifest then capabilities
end enable private networks(Client and Server)

OrganizationServiceProxy: No authentication error when wrong password is setup

I'm creating Organization service proxy object using following way:
[ThreadStatic]
public static OrganizationServiceProxy OrgServiceProxy;
// ...
sLog.DebugFormat("Get AuthenticationProviderType...");
AuthenticationProviderType _crmAuthType = this.GetServerType(parameters.DiscoveryUri);
sLog.DebugFormat("Get AuthenticationProviderType - DONE!");
// ...
sLog.Info("Perform metadata download (ServiceConfigurationFactory.CreateConfiguration)...");
IServiceConfiguration<IOrganizationService> _crmServiceConfiguration = ServiceConfigurationFactory.CreateConfiguration<IOrganizationService>(parameters.OrgServiceUri);
sLog.Info("Perform metadata download (ServiceConfigurationFactory.CreateConfiguration) - DONE");
// ...
// enable proxy types
var behavior = new ProxyTypesBehavior() as IEndpointBehavior;
behavior.ApplyClientBehavior(_crmServiceConfiguration.CurrentServiceEndpoint, null);
// ...
public OrganizationServiceProxy GetServiceProxy(ICRMConnectionParameters parameters)
{
// ...
ClientCredentials clientCreds = new ClientCredentials();
clientCreds.Windows.ClientCredential.UserName = parameters.UserName;
clientCreds.Windows.ClientCredential.Password = parameters.Password;
clientCreds.Windows.ClientCredential.Domain = parameters.Domain;
sLog.DebugFormat("Setup client proxy...");
OrgServiceProxy = new OrganizationServiceProxy(_crmServiceConfiguration, clientCreds);
sLog.DebugFormat("Setup client proxy - DONE.");
return OrgServiceProxy;
}
Just note here that AuthenticationProviderType and IServiceConfiguration are statically cached. This code above is part of class named CRMConnection.
I have one more abstract class (ProxyUser) which contains following property:
private CRMConnection conn;
// ...
protected OrganizationServiceProxy OrgServiceProxy
{
get
{
//return orgService;
return this.Conn.GetServiceProxy();
}
}
protected CRMConnection Conn
{
get
{
conn = conn ?? new CRMConnection();
return conn;
}
}
In another class that inherits ProxyUser I have method with following code:
ColumnSet columnSet = new ColumnSet();
ConditionExpression condition1 = new ConditionExpression("new_id", ConditionOperator.NotNull);
FilterExpression filter = new FilterExpression(LogicalOperator.And);
filter.AddCondition(condition1);
QueryExpression query = new QueryExpression()
{
EntityName = new_brand.EntityLogicalName,
ColumnSet = columnSet,
Criteria = filter,
NoLock = true
};
EntityCollection res = OrgServiceProxy.RetrieveMultiple(query);
And now we come to the point :)
If I setup correct parameters - organization service url, discovery service url, username, password and domain, everything works as expected. BUT, in case when wrong password is set, in line below, service is simply unresponsive. It doesn't happen anything.
EntityCollection res = OrgServiceProxy.RetrieveMultiple(query);
Of course, I'm expecting authentication failed error. Any suggestions what I'm missing here?
Thanks in advance!
I solved this problem with adding line below in GetServiceProxy method - when ClientCredentials are created:
clientCreds.SupportInteractive = false;
I figured this out after I moved whole logic in console app. When wrong password is set and app is in debug mode, I'm getting windows login prompt. Then I found this answer.

Client Object Model Sharepoint How to get the Instance ID

So In my sharepoint site contents page i have an application.
Can anyone tell me how to get the instance id of it.
so that i can invoke the link :
http://testingwebcompany.sharepoint.com/_layouts/15/appredirect.aspx?instance_id={ <>}
I can't seem to get it when I'm searching ClientContext.Web.Lists.
Thanks
I got it, it seems that the instance id is auto generated. the real url of the application is located when looping through ClientContext.Web.Webs[].Title == "Application Name" then retrieving the ClientContext.Web.Webs[].Url.
The following example demonstrates how to retrieve an App by its title
public static class WebExtensions
{
public static IEnumerable<AppInstance> GetAppInstanceByTitle(this Web web,string appTitle)
{
var ctx = web.Context;
var apps = AppCatalog.GetAppInstances(ctx, web);
var result = ctx.LoadQuery(apps.Where(a => a.Title == appTitle));
return result;
}
}
Usage
using (var ctx = new ClientContext(webUri))
{
var appTitle = "\"Napa\" Office 365 Development Tools";
var result = ctx.Web.GetAppInstanceByTitle(appTitle);
ctx.ExecuteQuery();
var app = result.FirstOrDefault();
if (app != null) Console.WriteLine(app.Id); // print App Instance Id
}

How to add a new group in Active Directory using LDAP in C#

I have scenerio to create new groups in Active Directory using LDAP and C#.
Please provide the suggestions
This article on CodeProject is a really good starting point:
Howto: (Almost) Everything In Active Directory via C#
To create a group, you need to:
bind to a container where you want to create the group inside of
create the group and define some properties
Code:
public void Create(string ouPath, string name)
{
if (!DirectoryEntry.Exists("LDAP://CN=" + name + "," + ouPath))
{
try
{
// bind to the container, e.g. LDAP://cn=Users,dc=...
DirectoryEntry entry = new DirectoryEntry("LDAP://" + ouPath);
// create group entry
DirectoryEntry group = entry.Children.Add("CN=" + name, "group");
// set properties
group.Properties["sAmAccountName"].Value = name;
// save group
group.CommitChanges();
}
catch (Exception e)
{
Console.WriteLine(e.Message.ToString());
}
}
else { Console.WriteLine(path + " already exists"); }
}
Some addition info on setting the Group Scope and Group Type, the enums are:
public enum GroupType : uint
{
GLOBAL = 0x2,
DOMAIN_LOCAL = 0x4,
UNIVERSAL = 0x8,
SECURITY = 0x80000000
}
SECURITY ( Shortened from ADS_GROUP_TYPE_SECURITY_ENABLED ) is combined with the first 3 enums to give you the 6 possible options, without it a group will be a Distribution group.
The values are set as an int, which with the security flag goes into negatives, so unchecked() needs to be used.
Alternatively you could create an enum for the combined values.
GLOBAL | SECURITY = 0x80000002 = -2147483646
DOMAIN_LOCAL | SECURITY = 0x80000004 = -2147483644
UNIVERSAL | SECURITY = 0x80000008 = -2147483640
The value is stored in the 'groupType' property:
var groupType = unchecked((int)(GroupType.UNIVERSAL | GroupType.SECURITY));
group.Properties["groupType"].Value = groupType;
group.CommitChanges();
Take a look at this link: http://msdn.microsoft.com/en-us/library/ms180903(v=vs.80).aspx
I think you might be looking for this part of the code:
// Bind to the domain that this user is currently connected to.
DirectoryEntry dom = new DirectoryEntry();
// Find the container (in this case, the Consulting organizational unit) that you
// wish to add the new group to.
DirectoryEntry ou = dom.Children.Find("OU=Consulting");
// Add the new group Practice Managers.
DirectoryEntry group = ou.Children.Add("CN=Practice Managers", "group");
// Set the samAccountName for the new group.
group.Properties["samAccountName"].Value = "pracmans";
// Commit the new group to the directory.
group.CommitChanges();
I just got through solving this problem for a .NET Core 2.0 app - here is an updated solution for those using .NET Core 2.0+.
This utilizes the NuGet package System.DirectoryServices.Protocols:
try
{
string adminUsername = "myAdminUser";
string namingContext = "CN=Test123,DC=MyCompany,DC=com";
string hostNameAndSSLPort = "192.168.123.123:636";
string adminuser = $"CN={adminUsername},{namingContext}";
string adminpass = "password";
using (LdapConnection connection = new LdapConnection(hostNameAndSSLPort))
{
LdapSessionOptions options = connection.SessionOptions;
options.ProtocolVersion = 3;
options.SecureSocketLayer = true;
connection.AuthType = AuthType.Basic;
NetworkCredential credential = new NetworkCredential(adminuser, adminpass);
connection.Credential = credential;
connection.Bind();
string rolesContext = $"CN=Roles,{namingContext}";
string nameOfNewGroup = "MyGroup";
string groupDN = $"CN={nameOfNewGroup},{rolesContext}";
string dirClassType = "group";
AddRequest addRequest = new AddRequest(groupDN, dirClassType);
AddResponse addResponse = (AddResponse)connection.SendRequest(addRequest);
Console.WriteLine($"A {dirClassType} with a dn of\n {groupDN} was added successfully. The server response was {addResponse.ResultCode}");
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
Also there are a lot of great code examples in this sample project provided by Microsoft.

Resources