Get all sharepoint sites in a site collection to which user has access using Javascript client object model - sharepoint-2010

I want to display all sites in a site collection using JSOM to which user has access to. In other words I only need to find collection of sites to which user has access in a site collection. I am able to get all webs but it doesnt work if user doesnt have permissions to some of web sites.

SP.Web.getSubwebsForCurrentUser Method returns a security trimmed (user has access) collection of sub sites (only one level beneath)
Example
var ctx = SP.ClientContext.get_current();
var webs = ctx.get_web().getSubwebsForCurrentUser(null);
ctx.load(webs);
ctx.executeQueryAsync(
function() {
for(var i=0;i< webs.get_count();i++) {
var web = webs.getItemAtIndex(i);
console.log(web.get_title());
}
},
function(sender,args){
console.log(args.get_message());
}
);
If you are interested in all sub webs within site collection, you could consider the following approach.
function getAllSubwebsForCurrentUser(success,error)
{
var ctx = SP.ClientContext.get_current();
var web = ctx.get_site().get_rootWeb();
var result = [];
var level = 0;
var getAllSubwebsForCurrentUserInner = function(web,result,success,error)
{
level++;
var ctx = web.get_context();
var webs = web.getSubwebsForCurrentUser(null);
ctx.load(webs,'Include(Title,Webs)');
ctx.executeQueryAsync(
function(){
for(var i = 0; i < webs.get_count();i++){
var web = webs.getItemAtIndex(i);
result.push(web);
if(web.get_webs().get_count() > 0) {
getAllSubwebsForCurrentUserInner(web,result,success,error);
}
}
level--;
if (level == 0 && success)
success(result);
},
error);
};
getAllSubwebsForCurrentUserInner(web,result,success,error);
}
Usage
getAllSubwebsForCurrentUser(
function(allwebs){
for(var i = 0; i < allwebs.length;i++){
console.log(allwebs[i].get_title());
}
},
function(sendera,args){
console.log(args.get_message());
});

Hi the following code snippet may help you.
var ctx = SP.ClientContext.get_current();
var web = ctx.get_web();
ctx.load(web);
var webCollection = web.getSubwebsForCurrentUser(null);
ctx.load(webCollection);
ctx.executeQueryAsync(
Function.createDelegate(this,this.onSuccess),
Function.createDelegate(this,this.onError)
);
getSubwebsForCurrentUser - uses a parameter of type SP.SubwebQuery which you may leave as null.
The web collection you get using this code is just of one level. You will not get the subsites of the subsites. For that you need to execute the same statements on every SP.Web object you get - recursively - starting from the root web.

If you can use the API instead then I would suggest you do the following to return all the sub webs for the current user.
using(SPSite site = new SPSite("http://example/site/"))
{
using (SPWeb web = site.OpenWeb())
{
SPWebCollection webCollection = web.GetSubwebsForCurrentUser();
}
}
Note: As pointed out by Helm Sterk in comment below, GetSubwebsForCurrentUser() would not return result which the user is seeking. So above code would not work.

Related

Kentico 12 - How to set individual page to require authentication?

On Kentico 12, the property Security inside the Page doesn't have Access field like the previous version Kentico 11 - Interface Access.
I need to provide this feature, so I was thinking about using overriding the OnAuthentication method like this:
protected override void OnAuthentication(AuthenticationContext filterContext)
{
var isAuthenticated = filterContext.Principal.Identity.IsAuthenticated;
var routePath = filterContext.HttpContext.Request.Path;
var page = DocumentHelper.GetDocuments().Path(routePath).FirstOrDefault();
var allowAccess = (page.HasSecureProperty && isAuthenticated) || !page.HasSecureProperty;
if (allowAccess)
{
base.OnAuthentication(filterContext);
}
else
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary(new { controller = "Account", action = "Signin" })
);
}
}
HasSecureProperty would be the property from the kentico page that admins or editors users can set on the administration panel. I was planning to create this property using custom table and make a interface on the page for the users.
The field IsSecureNode on CMS_Tree seems to be the property that I need and was been used on previous versions, but I couldn't find a way to set on the new admin panel.
Is there another solution to allow users to set authentication on pages? I was concerned about performance since this method will be called on every action. Thank you.
I have done something similar so maybe it will help point you in the right direction.
My entire MVC site requires authentication, so that might be where this differs. In the MVC when I want to get files and check permissions I do something like this:
var files = subcat.Children.WithAllData.WithPermissionsCheck;
On the CMS side, I have a field on the page type that allows a user to select roles and another one for selecting users. I then have a custom event on the document update or insert to update the settings.
Here is the code I use for updating the ACLs:
private void UpdateSettings(TreeNode node)
{
ObjectQuery<RoleInfo> roles = null;
ObjectQuery<UserInfo> users = null;
var columnRoles = node.GetStringValue("Roles", "");
if (columnRoles != "")
{
var rolesConcat = columnRoles.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
var where = "RoleName IN " + "('" + string.Join("','", rolesConcat) + "')";
EventLogProvider.LogInformation("Document Event", "Roles", where);
roles = RoleInfoProvider.GetRoles()
.Where(where);
}
var columnUsers = node.GetStringValue("Users", "");
if (columnUsers != "")
{
var usersConcat = columnUsers.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
var where = "UserName IN " + "('" + string.Join("','", usersConcat) + "')";
EventLogProvider.LogInformation("Document Event", "Users", where);
users = UserInfoProvider.GetUsers()
.Where(where);
}
if (node != null)
{
// Gets the ID of the ACL item that stores the page's permission settings
int nodeACLID = ValidationHelper.GetInteger(node.GetValue("NodeACLID"), 0);
// Deletes the page's ACL item
// Removes the page's permission settings for all users and roles
AclItemInfoProvider.DeleteAclItems(nodeACLID);
node.IsSecuredNode = true;
int allowed = DocumentSecurityHelper.GetNodePermissionFlags(NodePermissionsEnum.Read);
// Prepares a value indicating that no page permissions are denied
int denied = 0;
if (users != null)
foreach (var user in users)
{
// Sets the page's permission for the user (allows the 'Modify' permission)
AclItemInfoProvider.SetUserPermissions(node, allowed, denied, user);
}
if (roles != null)
foreach (var role in roles)
{
// Sets the page's permission for the user (allows the 'Modify' permission)
AclItemInfoProvider.SetRolePermissions(node, allowed, denied, role);
}
}
}
You can use the approach mentioned in the documentation on authorizing live site actions.
I ended up using a custom table with an interface for the user to set if the page requires authentication or not. Since this is an override of OnAuthentication, every page calls this method. I hope there is a better solution using built-in Kentico features. Here is the final code:
protected override void OnAuthentication(AuthenticationContext filterContext)
{
base.OnAuthentication(filterContext);
var routePath = filterContext.HttpContext.Request.Path;
var allowAccess = Authentication.CanEnterPage(filterContext.Principal.Identity.IsAuthenticated, routePath);
if (!allowAccess)
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary(new { controller = "Account", action = "Signin", returnUrl = routePath })
);
}
}
The static method below contains the logic to access the page:
public static bool CanEnterPage(bool isAuthenticated, string routePath)
{
var page = DocumentHelper.GetDocuments().Path(routePath).FirstOrDefault();
if (page == null)
return false;
var pageAccess = PageAccessInfoProvider.GetPageAccesses()
.WhereEquals("PageAccessNodeID", page.NodeID).FirstOrDefault();
// Create a record if pageAccess is null
if (pageAccess == null)
{
pageAccess = CreateRecordsPageAccess(page);
}
var isSecure = pageAccess.PageAccessHasAuthentication;
var allowAccess = isSecure && isAuthenticated || !isSecure;
return allowAccess;
}

Identity Server 4 User Impersonation

I am struggling to implement a Impersonation feature into the Identity Server 4 Service. I understand that there's a lot of people who are against implementing it the way I want to but I really need the full redirect back to the SSO server in order to generate a new list of claims. The user that is being impersonated will have a completely different set of Rules associated with them as claims, so it must come from the IdSrvr.
I have read through https://github.com/IdentityServer/IdentityServer4/issues/853, and also IdentityServer4 - How to Implement Impersonation
Here's what I've attempted so far, We did this perfectly inside of Identity Server 3 with ACR values and the Pre-Auth even on the UserService.
This controller method I am calling from one of the Clients of my identity server:
public IActionResult Impersonate(string userIdToImpersonate, string redirectUri)
{
return new ChallengeResult(OpenIdConnectDefaults.AuthenticationScheme, new AuthenticationProperties(){RedirectUri = redirectUri, Items = { {"acr_values", $"userIdToImpersonate:{userIdToImpersonate}"}}});
}
Here is my OnRedirectToProvider:
OnRedirectToIdentityProvider = context =>
{
if (context.Properties.Items.ContainsKey("acr_values"))
{
context.ProtocolMessage.AcrValues = context.Properties.Items["acr_values"].ToString();
}
return Task.CompletedTask;
}
This is where i start to get lost, at the moment, I've inherited from the AuthorizeInteractionResponseGenerator class and implemented my own with the override to the ProcessLoginAsync (this is the only thing i could find that was close to the pre-auth event previously)
protected override async Task<InteractionResponse> ProcessLoginAsync(ValidatedAuthorizeRequest request)
{
if (!request.IsOpenIdRequest) return await base.ProcessLoginAsync(request);
var items = request.GetAcrValues();
if (items.Any(i => i.Contains("userIdToImpersonate")) && request.Subject.IsAuthenticated())
{
//handle impersonation
var userIdToImpersonate = items.FirstOrDefault(m => m.Contains("userIdToImpersonate")).Split(':').LastOrDefault();
request.Subject = await _signInManager.ImpersonateAsync(userIdToImpersonate);
//var userToImpersonate = await _signInManager.UserManager.FindByIdAsync(userIdToImpersonate);
//if (userToImpersonate == null) return await base.ProcessLoginAsync(request);
//var userBeingImpersonated = await _signInManager.UserManager.FindByIdAsync(userIdToImpersonate);
//var currentUserIdentity = await _signInManager.CreateUserPrincipalAsync(userBeingImpersonated);
//var currentClaims = currentUserIdentity.Claims.ToList();
//currentClaims.Add(new Claim(IdentityServiceClaimTypes.ImpersonatedById, request.Subject.IsBeingImpersonated() ? request.Subject.GetClaimValue(IdentityServiceClaimTypes.ImpersonatedById) : _signInManager.UserManager.GetUserId(request.Subject)));
//request.Subject = new ClaimsPrincipal(new ClaimsIdentity(currentClaims));
//return await base.ProcessLoginAsync(request);
return new InteractionResponse();
}
else
{
return await base.ProcessLoginAsync(request);
}
}
As you can see, i've tried a couple different things here, When not using OIDC as a authentication scheme, and my IdServer/Site is the same site, I had a function that impersonation worked with. Which is where _signInManager.ImpersonateAsync(...) is. Here is that Implementation:
public async Task<ClaimsPrincipal> ImpersonateAsync(string userIdToImpersonate)
{
var userBeingImpersonated = await UserManager.FindByIdAsync(userIdToImpersonate);
if (userBeingImpersonated == null) return null;
var currentUserIdentity = await CreateUserPrincipalAsync(userBeingImpersonated);
var currentClaims = currentUserIdentity.Claims.ToList();
currentClaims.Add(new Claim(IdentityServiceClaimTypes.ImpersonatedById, Context.User.IsBeingImpersonated() ? Context.User.GetClaimValue(IdentityServiceClaimTypes.ImpersonatedById) : UserManager.GetUserId(Context.User)));
//sign out current user
await SignOutAsync();
//sign in new one
var newIdentity = new ClaimsPrincipal(new ClaimsIdentity(currentClaims));
await Context.SignInAsync(IdentityConstants.ApplicationScheme, newIdentity);
return Context.User;
}
In an effort to simply 'replace' who was signing in, or at least who the identity server was thinking was signing in, i just replaced Request.Subject with the Impersonation Result. This doesn't actually change anything that I can find, at least not on my client app. If i use the redirect URI of 'https://localhost:44322/signin-oidc' (localhost because i'm running the sites locally), I get a "Correlation failed at signin-oidc redirect" message. If anyone has implemented something like this or done anything similar I would greatly appreciate the help getting this complete.
Suggestions welcome for completely different implementations, this was just my best stab at what worked flawlessly with idsrvr3.

How to obtain information about the owner of a Dropbox shared file via the API?

I am writing an application where the users are sharing specific files via Dropbox. I am using the REST API. Lets say A shares a file to B. I know that A can share the file by adding B as a member to a file(add_file_member) and that B can check files shared with him by list_received_files. But I can't find a way that B can see who shared the file. It seems reasonable to have access to this info. Is there a way to obtain some account id or a display info for the user A from B perspective?
The SharedFileMetadata object returned by /sharing/list_received_files doesn't include this information, but I'll be sure to pass this along as a feature request.
However, you can use /sharing/list_file_members to list the members of the shared file, as well as their access level (e.g., owner, editor, etc.).
Example for C# Dropbox SDK.
public async Task<string> GetSharedFileOwnerID(Dropbox.Api.Files.Metadata data, Dropbox.Api.DropboxClient DropboxClient)
{
string owner = "";
var file = data.AsFile;
if ((file.HasExplicitSharedMembers ?? false) || (file.SharingInfo != null))
{
// search the real file owner
var list = await DropboxClient.Sharing.ListFileMembersAsync(file.Id);
var list_continue = false;
do
{
var item = list.Users.First(i => i.AccessType.IsOwner);
if (item != null)
{
owner = item.User.AccountId;
break;
}
list_continue = (list.Cursor != null);
if (list_continue)
{
list = await DropboxClient.Sharing.ListFileMembersContinueAsync(list.Cursor);
}
}
while (list_continue);
}
return owner;
}
public async Task<string> GetSharedFoldeOwnerID(Dropbox.Api.Files.Metadata data, Dropbox.Api.DropboxClient DropboxClient)
{
string owner = "";
var folder = data.AsFolder;
if (folder.SharedFolderId != null)
{
// search the real folder owner
var list = await DropboxClient.Sharing.ListFolderMembersAsync(folder.SharedFolderId);
var list_continue = false;
do
{
var item = list.Users.First(i => i.AccessType.IsOwner);
if (item != null)
{
owner = item.User.AccountId;
break;
}
list_continue = (list.Cursor != null);
if (list_continue)
{
list = await DropboxClient.Sharing.ListFolderMembersContinueAsync(list.Cursor);
}
}
while (list_continue);
}
return owner;
}
C# Dropbox SDK is NuGet package.
Use Install-Package Dropbox.Api for install it.

breeze.js not honoring the "noTracking" option when end point returns multiple result sets

Consider this breze query:
return EntityQuery.from('myAPI')
.noTracking(true)
.using(manager).execute()
.then(querySucceeded)
.fail(queryFailed);
My API is defined like this:
[HttpGet]
public object myAPI()
{
// var userId = get the users id from auth ticket
var userPref = _contextProvider.Context.UserPreferences.Where(u => u.userId == userId);
var userOptions = _contextProvider.Context.UserOptions.Where(u => u.userId == userId);
return new
{
userPref,
userOptions
};
}
I know I can get access to the raw data, which is great. But in addition to this, the entities are created in the entity manager, which I would prefer they not be. This works fine for apis that return IQueryable. Is there a different syntax for noTracking for web apis that returns multiple result sets?
thanks
I can't reproduce the error you describe. I have a similar DocCode test that passes which references Breeze v1.5.3.
Here is the pertinent NorthwindController method:
[HttpGet]
public object Lookups()
{
var regions = _repository.Regions;
var territories = _repository.Territories;
var categories = _repository.Categories;
var lookups = new { regions, territories, categories };
return lookups;
}
And here's the passing QUnit test:
asyncTest('object query (e.g., lookups) w/ "no tracking" does not add to cache', function () {
expect(2);
var em = newNorthwindEm();
EntityQuery.from('Lookups')
.noTracking(true)
.using(em).execute()
.then(success).fail(handleFail).fin(start);
function success(data) {
var lookups = data.results[0];
var hasLookups = lookups &&
lookups.categories && lookups.regions && lookups.territories;
ok(hasLookups, 'Expected a lookups object w/ categories, regions and territories');
var cached = em.getEntities();
var len = cached.length;
equal(0, len, 'Expected ZERO cached entities of any kind and got ' + len);
}
});
If I comment out the noTracking(true) clause, the test fails and tells me that there are 65 entities in cache ... as predicted.
What am I missing?

MS Dynamics CRM. Get users who current record shared with

I have a entity record which is shared with or more users. I would like to unshare this record when Deactivate it. I want to do that in Plugin. But I can't understand how to get all users from sharing list who have access to this record. How to do that?
Here is my code snippet:
protected void ExecutePostPersonSetStateDynamicEntity(LocalPluginContext localContext)
{
if (localContext == null)
{
throw new ArgumentNullException("localContext");
}
var context = localContext.PluginExecutionContext;
var targetEntity = (Entity)context.InputParameters["EntityMoniker"];
var state = (OptionSetValue)context.InputParameters["State"];
var columns = new ColumnSet(new[] { "statecode" });
var retrivedEntity = localContext.OrganizationService.Retrieve(targetEntity.LogicalName, targetEntity.Id, columns);
if (state.Value == 1)
{
RevokeAccessRequest revokeRequest = new RevokeAccessRequest()
{
Target = new EntityReference(personEntity.LogicalName, personEntity.Id),
Revokee = new EntityReference(neededEntity.LogicalName, needed.Id)
};
// Execute the request.
}
}
As you can see, I need an entity "neededEntity", I don't know how to get it from "targetEntity" or "retrievedEntity".
You need to use a RetrieveSharedPrincipalsAndAccessRequest
http://msdn.microsoft.com/en-us/library/microsoft.crm.sdk.messages.retrievesharedprincipalsandaccessrequest.aspx
You can start from the included example, basically inside the foreach you call your RevokeAcessRequest