I have created a simple Provider Hosted App ( straight out of the Visual Studio 2013 box )
The App has been granted "Read List Permissions" in the app manifest.
When the app is deployed SharePoint Online asks the User to select the list that the app can access. This should give it permission to read one specific list on the Host Web.
I cannot figure out how the MVC Provider Hosted Web part of the application actually determines which SharePoint list the end user has granted it permission to read.
Presumable it gets some sort of token to tell it which list it can read? Or is their a CSOM/JSOM call that will reveal which list it can access?
To determine which list the user has granted permission to read you could consider the following approach:
use List.EffectiveBasePermissions property to retrieve the effective
permissions on the list that are assigned to the current user
use BasePermissions.Has method by specifying PermissionKind.ViewListItems (or PermissionKind.OpenItems) to determine whether current user could access List
Example
1)For current user
using (var ctx = new ClientContext(webUri))
{
var web = ctx.Web;
ctx.Load(web.Lists,
lists => lists.Include(list => list.Title,
list => list.EffectiveBasePermissions));
ctx.ExecuteQuery();
var currentUserLists = web.Lists.Where(l => l.EffectiveBasePermissions.Has(PermissionKind.OpenItems));
}
2)For any user
using (var ctx = new ClientContext(webUri))
{
var web = ctx.Web;
//load lists
ctx.Load(web.Lists);
ctx.ExecuteQuery();
//load lists permissions for a specified user
var listsPermissionsResults = web.Lists.ToDictionary(list => list, list => list.GetUserEffectivePermissions(loginName));
ctx.ExecuteQuery();
//filter lists where user has been granted permissions to open list
var userLists = new List<List>();
foreach (var result in listsPermissionsResults)
{
var list = result.Key;
var listPermissions = result.Value.Value;
if (listPermissions.Has(PermissionKind.OpenItems))
{
userLists.Add(list);
}
}
}
Related
In the OneDrive user interface, it is possible to update a drive item so that the permissions no longer inherit from the parent (Stop Inheriting Permissions). Is it possible to do the same through the Graph API? I can seem to find any call that seems to allow for that. In the documentation for Delete Permissions, it says:
"Only sharing permissions that are not inherited can be deleted. The inheritedFrom property must be null."
We need to be able to programatically set the permissions on drive folders and items and one of the things we have to do is to stop inheritance from parent.
Regards,
LT
i know it's a long time question but here what you can do :
Get the directory item Id
Get the permissions id list
=> https://graph.microsoft.com/v1.0/me/drive/items/{itemId}/permissions
Loop over all permissionIds and delete them
Post a new Invite with all the users/groups you want
The "inherited" is only a list of allow users/groups ... if you delete all of them, your folder become a "only you" folder permission (no inheritance)
I use that in my application, it's not the most simple but it works
I am also struggling with the Graph Api invite-endpoint https://learn.microsoft.com/en-us/graph/api/driveitem-invite?view=graph-rest-1.0&tabs=http
I have not found an better solution than #Dadv.
My job is to copy an Sharepoint folder to another folder, including any subfolders.
And then make sure that subfolders get the exactly same permissions as the source subfolders.
Unfortunaly this has to be 2 seperate tasks, when using the Graph Api, using /copy endpoint to copy the subfolders, and for the permissions I use a combination of Delete / Update / Add to syncronize the permissions.
Rather complicated with lots of roundtrips to the API, for such a simple job.
Now I hope to hear from someone who knows of an simpler way of doing the permission part.
Here is some pseudo code to show visualize the syncronization of the permissions.
// Now the targetFolders exists --> syncronize permissions
var targetFolderGraphUr = "https://graph.microsoft.com/v1.0/drives/<id>/items/<item-id>";
// First remove all permissions not existing in the sourcefolder
foreach (var targetPermission in targetFolderPermissions)
{
if (!sourceFolderPermissions.FirstOrDefault(p => p.GrantedTo?.User?.DisplayName == targetPermission.GrantedTo?.User?.DisplayName))
{
var deleteUrl = $"{targetFolderGraphUr}/permissions/{targetPermission.Id}";
var deleteResult = await myHttpClient.DeleteAsync(deleteUrl);
}
}
// Then apply permissions from template: Update (Patch) or Add (using /invite)
foreach (var sourcePermission in sourceFolderPermissions)
{
var pcPerm = targetFolderPermissions.FirstOrDefault(p => p.GrantedTo?.User?.DisplayName == sourcePermission.GrantedTo?.User?.DisplayName);
// Update
if (pcPerm != null)
{
bool isUpdateNeeded = CheckIfUpdateNeeded(...);
if (isUpdateNeeded)
{
var updateUrl = $"{targetFolderGraphUr}/permissions/{sourcePermission.Id}";
var updateResult = await myHttpClient.PatchAsync(updateUrl, sourcePermission);
}
}
else
{
// Add
var userId = sourcePermission.GrantedTo?.User?.Id;
var invitePermission = new InvitePermission()
{
Recepients = GetRecepientArray( userId ), // returns a list of recipients, setting objectId = userId
ObjectId = userId,
Roles = sourcePermission.Roles,
RequireSignIn = true,
SendInvitation = false,
};
var inviteUrl = $"{targetFolderGraphUr}/invite";
var inviteResult = await myHttpClient.PostAsync(inviteUrl, invitePermission);
// response is 200 OK, but no permissions are added.
// Using Dadv's workarround, deleting all permissions on targetfolder first, then it works.
// (not shown in this example)
}
}
TFS 2012 and later as well as VSTS have a concept of a Team Administrator. I've looked all over the API for a simple way to set and retrieve the value through code to make it easier to provision these settings, but could not find it.
Reflectoring through the Server Object Model for the web UI gives hints for how to do it, but it relies on a number of private methods to get this done. Especially the part that calculates the Security Scope Token is hidden magic.
It took quite a bit of digging to find this old blogpost from 2013 which details how to do this, and I don't seem to be the only person who got stumped by the private methods. In the end they also ended up using Reflection to call the private method to retrieve the token:
This functionality is now available through the TFS Team Tools:
TFS Team Tools
Retrieve
Find the security group matching the team, use it to calculate the team's token, get the people who are part of that special security namespace:
public List<string> ListTeamAdministrators(string team, out string message)
{
// Retrieve the default team.
TeamFoundationTeam t = this.teamService.ReadTeam(this.projectInfo.Uri, team, null);
List<string> lst = null;
message = "";
if (t == null)
{
message = "Team [" + team + "] not found";
}
else
{
// Get security namespace for the project collection.
ISecurityService securityService = this.teamProjectCollection.GetService<ISecurityService>();
SecurityNamespace securityNamespace =
securityService.GetSecurityNamespace(FrameworkSecurity.IdentitiesNamespaceId);
// Use reflection to retrieve a security token for the team.
var token = GetTeamAdminstratorsToken(t);
// Retrieve an ACL object for all the team members.
var allMembers = t.GetMembers(this.teamProjectCollection, MembershipQuery.Expanded)
.ToArray();
AccessControlList acl =
securityNamespace.QueryAccessControlList(token, allMembers.Select(m => m.Descriptor), true);
// Retrieve the team administrator SIDs by querying the ACL entries.
var entries = acl.AccessControlEntries;
var admins = entries.Where(e => (e.Allow & 15) == 15).Select(e => e.Descriptor.Identifier);
// Finally, retrieve the actual TeamFoundationIdentity objects from the SIDs.
var adminIdentities = allMembers.Where(m => admins.Contains(m.Descriptor.Identifier));
lst = adminIdentities.Select(i => i.DisplayName).ToList();
}
return lst;
}
private static string GetTeamAdminstratorsToken(TeamFoundationTeam team)
{
return IdentityHelper.CreateSecurityToken(team.Identity);
}
Set
Setting works in a similar fashion. Grab the token and then add the users unique identifier to the Access Control List:
IdentityDescriptor descriptor = GetMemberDescriptor(memberId);
securityNamespace.SetPermissions(token, descriptor, 15, 0, false);
Remove
And removing a person from the list is then, of course, easy to guess;
IdentityDescriptor descriptor = GetMemberDescriptor(memberId);
securityNamespace.RemovePermissions(token, descriptor, 15);
I am porting an application to azure and in that app we use Active Directory to authenticate users like the following:
var user = model.UserName.Split('\\');
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, user[0]))
{
if (pc.ValidateCredentials(user[1], model.Password, ContextOptions.Negotiate))
{
using (var adUser = UserPrincipal.FindByIdentity(pc, IdentityType.SamAccountName, user[1]))
{
if (!MembershipService.ValidateUser(model.UserName, model.Password))
{
using (var userDb = new UsersDbContext())
{
if (userDb.aspnet_Users.Count(u => u.UserName.ToLower().Contains(model.UserName)) <= 0)
MembershipService.CreateUser(model.UserName, model.Password, adUser.EmailAddress);
else
{
var msUser = Membership.GetUser(model.UserName);
msUser.ChangePassword(msUser.ResetPassword(), model.Password);
}
}
}
FormsService.SignIn(model.UserName, model.RememberMe);
foreach (var role in Roles.GetAllRoles())
{
using (var group = GroupPrincipal.FindByIdentity(pc, role))
{
if (group != null)
{
if (adUser.IsMemberOf(group))
{
if (!Roles.IsUserInRole(model.UserName, role))
Roles.AddUserToRole(model.UserName, role);
}
else
{
if (Roles.IsUserInRole(model.UserName, role))
Roles.RemoveUserFromRole(model.UserName, role);
}
}
}
}
}
}
}
This works fine on our web-server which is connected to our domain server.
Now I set up an Windows Azure Active Directory and configured it to be synced with our On-Premise AD which also works.
But I am now struggeling on finding a way to connect my PrincipalContext to the WAAD.
Is this even possible and how? If not, what is the alternative?
I only found examples using Single-Sign-On which does this redirection to the MS login page we do NOT want to use, because we have a mixed authentication and depending on the entered user name it either uses the ASP.NET Membership or pulls the user and groups from AD (and actually creates an ASP.NET Membership user as seen above).
No.
You can't really use PrincipalContext with WAAD. Have to explicitly state here that you cannot currently (Jan. 2014) do direct user authentication against WAAD. You will need to rewrite some parts of your application to be compatible:
Authentication happens only on the WAAD side, your code cannot do user+password validation. This also happens on WAAD provided login page. You have limited control on how this page looks like and can customize it via Premium features of WAAD.
You can create users and reset user password using the WAAD Graph API.
Explore the Graph API for additional operations you might need (i.e. ask for user's group membership, direct reports, etc.)
You will have to switch from Windows Authentication to Federated Authentication. Depending on what VS version you are using this might be easy or tough. For VS 2012 there is Identity and Access Tool extension. While in 2013 authentication can only be configured when you create the project and cannot be altered afterwards. But you can copy configuration changes from other project over. You need changes in web.config file along with what is initialized in global.asax. Check it here - although about VS 2013 RC, the process is same in RTM.
I would like to display a list of active users on my application's dashboard.
All of my users are employees and access the application through their Active Directory credentials.
I have used UserPrincipal to get the details of the current user, but can this be done for all of the current users?
You can use a PrincipalSearcher and a "query-by-example" principal to do your searching:
// create your domain context
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain))
{
// define a "query-by-example" principal - here, we search for all "enabled" UserPrincipal
UserPrincipal qbeUser = new UserPrincipal(ctx);
qbeUser.IsEnabled = true;
// create your principal searcher passing in the QBE principal
PrincipalSearcher srch = new PrincipalSearcher(qbeUser);
// find all matches
foreach(var found in srch.FindAll())
{
// do whatever here - "found" is of type "Principal" - it could be user, group, computer.....
}
}
If you haven't already - absolutely read the MSDN article Managing Directory Security Principals in the .NET Framework 3.5 which shows nicely how to make the best use of the new features in System.DirectoryServices.AccountManagement. Or see the MSDN documentation on the System.DirectoryServices.AccountManagement namespace.
I have web application that uses the Authorize attribute with roles specified to restrict access to some pages:
[Authorize(Roles = "AD_group1, AD_group2")]
The question is - is there any way I can get some kind of an Active Directory groupId for authorized user (no matter int or string)?
upd:
Basic idea is to store some table in database, containing templates which should be separate for every group. e.g. users in group1 can have some templates for fast answer to typical questions while group2 doesn't have any of them, or have some other templates
If you're on .NET 3.5 and up, you should check out the System.DirectoryServices.AccountManagement (S.DS.AM) namespace. Read all about it here:
Managing Directory Security Principals in the .NET Framework 3.5
MSDN docs on System.DirectoryServices.AccountManagement
Basically, you can define a domain context and easily find users and/or groups in AD:
// set up domain context
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain))
{
// find a user
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, "SomeUserName");
// or if you want the currently logged in user - you can also use:
// UserPrincipal user = UserPrincipal.Current;
if(user != null)
{
// get all groups the user is a member of
foreach(GroupPrincipal group in user.GetAuthorizationGroups())
{
string distinguishedName = group.DistinguishedName;
Guid groupGuid = group.Guid;
}
}
}
The new S.DS.AM makes it really easy to play around with users and groups in AD!