I am trying to programmatically add a user like this below but get an access denied message on the Save. I'm running locally on Windows 7 and the code resides in a console app.
/// <summary>
///
/// </summary>
/// <param name="userName"></param>
/// <param name="password"></param>
/// <param name="description"></param>
public static void CreateUser(string userName, string password, string description)
{
PrincipalContext pc = new PrincipalContext(ContextType.Machine, null);
System.DirectoryServices.AccountManagement.UserPrincipal u = new UserPrincipal(pc);
u.SetPassword(password);
u.Name = userName;
u.Description = description;
u.UserCannotChangePassword = true;
u.PasswordNeverExpires = true;
u.Save();
GroupPrincipal gp = GroupPrincipal.FindByIdentity(pc, "Users");
gp.Members.Add(u);
gp.Save();
}
Any ideas? I tried supplying an administrators username and password and still get the same error.
The console app gets executed like this:
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.UserName = userName;
startInfo.Password = securePassword;
startInfo.LoadUserProfile = true;
startInfo.UseShellExecute = false;
startInfo.FileName = batchPath;
startInfo.Arguments = operationLogID.ToString();
Process.Start(startInfo);
Here is a rough view of how the code is set up:
Console App test harness gets executed in debug mode.
I check for a user and if they don't exist..then I try and create it shown above. This is where the error occurs.
Even if you're logged in as admin, you need to run your console as admin. Here's how to launch a console as admin: http://www.howtogeek.com/howto/windows-vista/run-a-command-as-administrator-from-the-windows-vista-run-box/.
Then find your console app and run it.
Good luck!
-Michael
Related
I'm currently building a mvc5 app hosted on azure which will be in term used throught a WPF app.
As I need to check user group membership I implemented graph API by following the guidance in this article : https://azure.microsoft.com/fr-fr/documentation/samples/active-directory-dotnet-graphapi-web/
It works quite fine but some time after the user logged in the access to the following controller raise an access denied error :
public async Task<ActionResult> Index()
{
string uID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
ActiveDirectoryClient client = AuthenticationHelper.GetActiveDirectoryClient();
IUser adUser = client.Users.Where(u => u.ObjectId == uID).ExecuteAsync().Result.CurrentPage.SingleOrDefault();
IList<Group> groupMembership = new List<Group>();
var userFetcher = (IUserFetcher)adUser;
IPagedCollection<IDirectoryObject> pagedCollection = await userFetcher.MemberOf.ExecuteAsync();
do
{
List<IDirectoryObject> directoryObjects = pagedCollection.CurrentPage.ToList();
foreach (IDirectoryObject directoryObject in directoryObjects)
{
if (directoryObject is Group)
{
var group = directoryObject as Group;
groupMembership.Add(group);
}
}
pagedCollection = await pagedCollection.GetNextPageAsync();
} while (pagedCollection != null);
ViewBag.User = adUser.UserPrincipalName;
ViewBag.UserDN = adUser.DisplayName;
ViewBag.UserGN = adUser.GivenName;
ViewBag.UserMail = adUser.Mail;
ViewBag.UserSN = adUser.Surname;
return View(groupMembership);
}
The exception is raised on GetActiveDirectoryClient(), the code of this method is a strict copy/paste from the article in the link and looks like this :
internal class AuthenticationHelper
{
public static string token;
/// <summary>
/// Async task to acquire token for Application.
/// </summary>
/// <returns>Async Token for application.</returns>
public static async Task<string> AcquireTokenAsync()
{
if (token == null || token.IsEmpty())
{
throw new Exception("Authorization Required. ");
}
return token;
}
/// <summary>
/// Get Active Directory Client for Application.
/// </summary>
/// <returns>ActiveDirectoryClient for Application.</returns>
public static ActiveDirectoryClient GetActiveDirectoryClient()
{
Uri baseServiceUri = new Uri(Constants.ResourceUrl);
ActiveDirectoryClient activeDirectoryClient =
new ActiveDirectoryClient(new Uri(baseServiceUri, Constants.TenantId), async () => await AcquireTokenAsync());
return activeDirectoryClient;
}
}
This code works perfectly right after the user has logged in but after some times the token become null and so the exception is raised.
I'm guessing this is related to some expiration time, so is there's a way to set some auto refresh on the token ?
Thanks !
Thanks for answering, I don't have yet set the [Authorize] tag as I would like to as Azure AD group membership to grant access to controllers and haven't yet figured out how to achieve it :)
It seems that appliying mofifications to the authenticationHelper solved the issue :
public static ActiveDirectoryClient GetActiveDirectoryClient()
{
Uri baseServiceUri = new Uri(Constants.ResourceUrl);
string userObjectID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
AuthenticationContext authContext = new AuthenticationContext(Authority, new NaiveSessionCache(userObjectID));
ClientCredential credential = new ClientCredential(clientId, appKey);
ActiveDirectoryClient activeDirectoryClient = new ActiveDirectoryClient(new Uri(baseServiceUri, Constants.TenantId), async () =>
{
var result = await authContext.AcquireTokenSilentAsync(graphUrl, credential, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
return result.AccessToken;
});
return activeDirectoryClient;
}
I don't know if that's a clean way to do thing it at least it works.
It seems, in my WebAPI project, which uses OWIN, the call always returns NULL:
var appAssembly = Assembly.GetEntryAssembly();
I've also tried:
var entryAssembly = new StackTrace().GetFrames().Last().GetMethod().Module.Assembly;
All that returns is "System.Web".
How do you capture the App Name, Version???
I am trying to capture this information right at Startup for the Web API project:
/// <summary>
/// The OWIN startup class.
/// </summary>
public class Startup
{
/// <summary>
/// The necessary OWIN configuration method.
/// </summary>
/// <param name="app">The app being started under OWIN hosting.</param>
public void Configuration(IAppBuilder app)
{
var appAssembly = Assembly.GetEntryAssembly();
Aspect.Logging.LoggingHandler.Initialize(appAssembly, "Hard Coded App Name!");
Log.Information("Starting App...");
// Order is important here. The security wiring must happen first.
ConfigureAuthentication(app);
// Create web configuration and register with WebAPI.
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
// Configure documentation.
ConfigureDocumentation(config);
// Configure support for static files (e.g. index.html).
app.UseFileServer(new FileServerOptions
{
EnableDefaultFiles = true,
FileSystem = new PhysicalFileSystem(".")
});
// Start the API.
app.UseWebApi(config);
Log.Information("App started.");
}
Use:
var appAssembly = typeof(Startup).Assembly;
Please forgive me if this has already been asked before. I looked around, but my situation didn't come into play on any answered question I came across.
I'm Using SignalR 2.2.0
Setup: I have a WebAPI 2 web application (call it API For short) that holds my hub called ChatHub. I have a MVC site (MVC) that is calling my hub on the API site.
Both of these sites are on the same server just different ports. I am using VS 2013 and when I test locally, my local system also uses the same ports...also the url to the API site is loaded from the web config which is different for Release and Debug and local(so the url is correct and the ports on the server are fine...really on thing wrong is OnDisconnectednot getting fired)
After a lot of trial and error and searching, I finally got a test application up. Everything was working perfect. Then I had to modify my hub fit into the business model. IE Take the in memory list of users and messages and record them in the database (among other things). Everything work perfectly when running locally, however once the sites are published to IIS on the server....The OnDisconnected is never called. On the test app / when running locally, this is almost instantly hit by most browsers. But even after waiting 15+ minutes, the method is still not fired.
Here is my hub:(shorted for clarity)
/// <summary>
/// Chat Hub Class
/// </summary>
public class ChatHubAsync : Hub
{
#region Data Members
IDemographicsProvider DemographicsProvider { get; set;}
IChatRepository Repo { get; set; }
#endregion
#region CTOR
/// <summary>
/// Unity Constructor
/// </summary>
[InjectionConstructor]
public ChatHubAsync()
: this(new ChatRepositoryEF(), DataProviders.Demographics)
{
}
/// <summary>
/// Constructor for Chat Hub
/// </summary>
/// <param name="Repository"></param>
/// <param name="Demographics"></param>
public ChatHubAsync(IChatRepository Repository, IDemographicsProvider Demographics)
{
Repo = Repository;
DemographicsProvider = Demographics;
}
#endregion
#region Methods
/// <summary>
/// On Connected method to call base class
/// </summary>
/// <returns></returns>
public override async Task OnConnected()
{
await base.OnConnected();
}
/// <summary>
/// Connect to Hub
/// </summary>
/// <returns>Void</returns>
public async Task Connect()
{
if (await Repo.GetUser(Context.ConnectionId) == null)
{
await RemoveDuplicates(getGuidFromQueryString("userID"), getGuidFromQueryString("groupID"));
var user = await CreateUser();
await Repo.Connect(user);
await Clients.Caller.onConnected(user);
}
}
/// <summary>
/// Add User To Group
/// </summary>
/// <returns></returns>
public async Task AddToGroup()
{
Guid id = getGroupFromQueryString();
if (id != Guid.Empty)
{
string groupID = id.ToString();
var user = await Repo.GetUser(Context.ConnectionId);
try
{
if(user == null)
{
await Connect();
user = await Repo.GetUser(Context.ConnectionId);
}
await Groups.Add(Context.ConnectionId, groupID);
var users = await Repo.OnlineUsers(id);
var messages = await Repo.RetrieveMessages(id, 20);
var status = await Repo.AddOnlineUserToGroup(Context.ConnectionId, id);
await Clients.Caller.onGroupJoined(user, users, messages, status);
Clients.Group(groupID, Context.ConnectionId).onNewUserConnected(user);
}
catch(Exception E)
{
Console.WriteLine(E.Message);
}
}
}
/// .....More Methods that are irrelevant....
/// <summary>
/// Disconnect from Hub
/// </summary>
/// <param name="stopCalled"></param>
/// <returns></returns>
public override async Task OnDisconnected(bool stopCalled)
{
try
{
var item = await Repo.GetUser(Context.ConnectionId);
if (item != null)
{
if (item.GroupID != null && item.GroupID != Guid.Empty)
{
var id = item.GroupID.ToString();
Repo.Disconnect(Context.ConnectionId);
Clients.OthersInGroup(id).onUserDisconnected(Context.ConnectionId, item.UserName);
Groups.Remove(Context.ConnectionId, id);
}
}
}
catch (Exception E)
{
Console.WriteLine(E.Message);
}
await base.OnDisconnected(stopCalled);
}
#endregion
#region private Messages
private async Task<IOnlineUser> CreateUser()
{
///Code removed
}
private Guid getGroupFromQueryString()
{
return getGuidFromQueryString("groupID");
}
private Guid getGuidFromQueryString(string name)
{
Guid id;
try
{
var item = getItemFromQueryString(name);
if (Guid.TryParse(item, out id))
{
return id;
}
throw new Exception("Not a Valid Guid");
}
catch(Exception E)
{
Console.WriteLine(E.Message);
return Guid.Empty;
}
}
private async Task RemoveDuplicates(Guid User, Guid Group)
{
///Code removed
}
#endregion
}
UPDATE:
I have no idea why, but once I removed the calls to the database (ALL CALLS TO THE DATABASE) and went back to in memory lists, On Disconnected started getting called again.
Added back any call to the database using either straight sql or EntityFramework and the OnDisconnected stopped getting called.
Any ideas why adding database calls would cause the onDisconnected to stop getting called?
In case anyone else is having this issue. The problem was caused by the application not impersonating the correct user....it worked fine in visual studio because when impersonation failed to use the provided user, it used me. Out side of VS it didn't have that option and tried using the machine account which failed to log in. This was fixed by adding the impersonation in the application pool instead of using it in the web config. I was having issues with this earlier in the project when using async calls, but I thought I had those fixed with a few changes to the web config. I worked for all the async calls that I was making, but it was still failing in a few select areas that was causing the OnDisconnect to not fire on the server.
I am working on a requirement where I need to create a site collection in SharePoint using client side api. I know server side we can do it using self service site creation api. Also I know in case of SharePoint Online , we have Microsoft.Online.SharePoint.Client.Tenant.dll that we can use to create site collection However in my case I have a On premise environment (SharePoint 2013) where I need to create a site collection thru client side api. Can you please let me know if there is any API that I can use for this requirement.
Thanks for any Help you can provide on this.
This is not possible to do by using the CSOM, on an on-premise environment.
As you mentioned, it is possible on the SPO environment using the library that you listed (Microsoft.Online.SharePoint.Client.Tenant.dll).
I'm not sure if this will help, but here is code that could create a site inside of the current site collection:
You will also need to add using statements for System.Collections.Generic and System.Text.
// Starting with ClientContext, the constructor requires a URL to the
// server running SharePoint.
ClientContext context = new ClientContext("http://SiteUrl");
WebCreationInformation creation = new WebCreationInformation();
creation.Url = "web1";
creation.Title = "Hello web1";
Web newWeb = context.Web.Webs.Add(creation);
// Retrieve the new web information.
context.Load(newWeb, w => w.Title);
context.ExecuteQuery();
label1.Text = newWeb.Title;
This code was taken directly from here: http://msdn.microsoft.com/en-us/library/fp179912.aspx
How to create site collection via SharePoint 2013 Managed CSOM
Tenant.CreateSite method from Microsoft.Online.SharePoint.Client.Tenant.dll assembly is intended for site collection creation:
/// <summary>
/// Create a new site.
/// </summary>
/// <param name="context"></param>
/// <param name="url">rootsite + "/" + managedPath + "/" + sitename: e.g. "https://auto.contoso.com/sites/site1"</param>
/// <param name="title">site title: e.g. "Test Site"</param>
/// <param name="owner">site owner: e.g. admin#contoso.com</param>
/// <param name="template">The site template used to create this new site</param>
/// <param name="localeId"></param>
/// <param name="compatibilityLevel"></param>
/// <param name="storageQuota"></param>
/// <param name="resourceQuota"></param>
/// <param name="timeZoneId"></param>
internal static void CreateSite(ClientContext context, String url, String owner, String title =null, String template = null, uint? localeId = null, int? compatibilityLevel = null, long? storageQuota = null, double? resourceQuota = null, int? timeZoneId = null)
{
var tenant = new Tenant(context);
if (url == null)
throw new ArgumentException("Site Url must be specified");
if (string.IsNullOrEmpty(owner))
throw new ArgumentException("Site Owner must be specified");
var siteCreationProperties = new SiteCreationProperties {Url = url, Owner = owner};
if (!string.IsNullOrEmpty(template))
siteCreationProperties.Template = template;
if (!string.IsNullOrEmpty(title))
siteCreationProperties.Title = title;
if (localeId.HasValue)
siteCreationProperties.Lcid = localeId.Value;
if (compatibilityLevel.HasValue)
siteCreationProperties.CompatibilityLevel = compatibilityLevel.Value;
if (storageQuota.HasValue)
siteCreationProperties.StorageMaximumLevel = storageQuota.Value;
if (resourceQuota.HasValue)
siteCreationProperties.UserCodeMaximumLevel = resourceQuota.Value;
if (timeZoneId.HasValue)
siteCreationProperties.TimeZoneId = timeZoneId.Value;
var siteOp = tenant.CreateSite(siteCreationProperties);
context.Load(siteOp);
context.ExecuteQuery();
}
//Usage
const string username = "***#***.onmicrosoft.com";
const string password = "***";
const string tenantAdminUrl = "https://***-admin.sharepoint.com/";
const string newSiteCollUrl = "https://contoso.sharepoint.com/sites/finance"
var securedPassword = new SecureString();
foreach (var c in password.ToCharArray()) securedPassword.AppendChar(c);
var credentials = new SharePointOnlineCredentials(username, securedPassword);
using (var context = new ClientContext(tenantAdminUrl))
{
context.Credentials = credentials;
CreateSite(context, newSiteCollUrl,username);
}
It is available in the April 2014 CU
http://blogs.msdn.com/b/vesku/archive/2014/06/09/provisioning-site-collections-using-sp-app-model-in-on-premises-with-just-csom.aspx
I a console application that creates a sub sites under a site collection
The site collection accepts only forms based user.
Now, when i run the console application, its with windows credentials.
I need some way to run the code in console app that creates sub site to run under forms user who is admin to that site collection.
Please let me know your suggestions.
Thanks
You need to create a new Web service within the Central Administration Web application ([12 hive]\AMDISAPI) and add a function that creates subsites.
Here's an example - the hstCreateSubSite function from the SharePoint for Hosters project:
/// <summary>
/// Method to create a Sub site for a site collection
/// </summary>
/// <param name="strSiteURL">url of the sitecollection i.e. "http://www.sitea.com"</param>
/// <param name="subsitePath">the path to the subsite i.e. inventory</param>
/// <param name="strTitle">sub site title</param>
/// <param name="strDesc">sub site description</param>
/// <param name="strTemplate">a valid templateID</param>
/// <param name="nLCID">the LCID for the language i.e. 1033 for english</param>
[WebMethod]
public void hstCreateSubSite(string strSiteURL, string subSitePath, string strTitle, string strDesc, string strTemplate, uint nLCID)
{
SPSite oSite = new SPSite(strSiteURL);
SPWeb oSubSiteWeb = oSite.OpenWeb();
SPWeb oWeb = null;
if (String.IsNullOrEmpty(strDesc)) strDesc = null;
if (String.IsNullOrEmpty(strTitle)) strTitle = null;
try
{
// elevate permissions to allow user to create a new site.
SPSecurity.RunWithElevatedPrivileges(delegate()
{
// the subsite will inherit permissions and will not convert the site if it exists
oWeb = oSubSiteWeb.Webs.Add(subSitePath, strTitle, strDesc, nLCID, strTemplate, false, false);
SPNavigationNodeCollection nodes = oSubSiteWeb.Navigation.TopNavigationBar;
SPNavigationNode navNode = new SPNavigationNode(strTitle, subSitePath);
nodes.AddAsLast(navNode);
oWeb.Navigation.UseShared = true;
// create entry in property bag to store template and url in the subsite.
oWeb.AllowUnsafeUpdates = true;
// add the Templateid to the property bag. This needs to be done becuase
// sites that are created from site templates (.stp) do not retain the templateid.
oWeb.Properties.Add("STP_ID", strTemplate);
oWeb.Properties.Update();
oWeb.AllowUnsafeUpdates = false;
});
}
catch (Exception ex)
{
throw ex;
}
finally
{
//dispose objects
if (oWeb != null)
oWeb.Dispose();
if (oSite != null)
oSite.Dispose();
}
}