In our sharepoint farm we have two groups:
Users - Contains ALL users of our application (including administrators)
Admins - Contains users with admin privileges
I'm currently trying to deny access to System Pages (_layouts/ files) for all users in the "Users" group to prevent modifying lists etc. without going through the corresponding webpart UI.
To do this I added a permission policy with DENY on View Application Pages. This works as its supposed to for normal users, but the problem is it also blocks the administrators from accessing lists etc.
I tried granting everything to the Admins group, but the deny still overrides it preventing access.
How can I grant access to the admins when their user is blocked in another group?
You could use JSOM solution, check user groups and admin by JSOM(sample script from this thread).
Below script contains logic to check is user admin.
function IsCurrentUserMemberOfGroups(groups, OnComplete) {
var currentContext = new SP.ClientContext.get_current();
var currentWeb = currentContext.get_web();
var currentUser = currentWeb.get_currentUser();
var isAdmin = currentUser.get_isSiteAdmin();
var oGroups = currentUser.get_groups();
currentContext.load(isAdmin);
currentContext.load(currentUser);
currentContext.load(oGroups);
currentContext.executeQueryAsync(OnSuccess, OnFailure);
function OnSuccess(sender, args) {
var userInGroup = false;
console.log('Admin check' + isAdmin);
if (!isAdmin) {
var groupEnumerator = oGroups.getEnumerator();
while (groupEnumerator.moveNext()) {
var oGroup = groupEnumerator.get_current();
var groupTitle = oGroup.get_title();
console.log(groupTitle);
$.each(groups, function (index, value) {
if (value == groupTitle) {
console.log('user in group ' + groupTitle);
userInGroup = true;
}
});
if (userInGroup)
break;
}
}
OnComplete(userInGroup);
}
function OnFailure(sender, args) {
OnComplete(false);
}
}
Then, redirect user to a friendly page.
Add the script to master page or add as javascript link as it need to be run globally.
SharePoint Custom JS file Best Practice
Related
We are implementing MFA using Microsoft Azure. We run wso2 identity server 5.10.4.
What I would like to see happen, is when a user logs in, if they have certain memberOf AD roles, Federation happens. If not, it just uses basic auth/normal log in.
I've been approaching this problem using Adaptive Authentication custom scripting. On a service provider, I created two login steps. Step 1 is basicauth. Step 2 is our MS federated authenticator.
var arrayOfRoles = ["employee","student"];
var onLoginRequest = function(context) {
executeStep(1, {
onSuccess: function (context) {
// Extracting authenticated subject from the first step
var memberOfClaim = 'http://wso2.org/claims/employeeType';
var user = context.currentKnownSubject;
var roles = context.currentKnownSubject.localClaims[memberOfClaim];
foundRole = '-1';
var arrayOfRolesLen = arrayOfRoles.length;
for (var i = 0; i < arrayOfRolesLen; i++) {
searchRole = roles.indexOf(arrayOfRoles[i]);
if (searchRole >= 0) {
foundRole = searchRole;
}
}
if (foundRole >= 0 ) {
Log.info(user.username + ' found a role, indexof=' + foundRole);
// Step 2 is MFA
executeStep(2);
}
}
});
};
The script correctly finds the person's AD memberOf values, and I'm able to execute step 2 using this script. The problem is that a person logs in once to wso2, then if the roles are matched and exectuteStep(2) is invoked, they are prompted with another log in screen for wso2.
How can I prevent a second log in when a person matches the conditions for step 2? Or is this the wrong approach to making a role based decision about when to authenticate using basicauth and when to authenticate using federation?
edit:
Responding to some comments below about using identity-first.
If I setup three steps 1) id first, 2) basic 3) federated, I am prompted for the username again. Step one, a username prompt from wso2. Step two, a username and password prompt. Ditto with step 3.
One difference I see in the below screenshots, is that there is a 'username and password' authenticator. I don't have that available to me in the drop down boxes. Just jwt-basic, and basic. My script using three auth steps looks like this:
var arrayOfRoles = ["PCC_EMPLOYEE_ACTIVE","PCC_STUDENT_CREDIT"];
var onLoginRequest = function(context) {
executeStep(1, {
onSuccess: function (context) {
// Extracting authenticated subject from the first step
var memberOfClaim = 'http://wso2.org/claims/employeeType';
var user = context.currentKnownSubject;
var roles = context.currentKnownSubject.localClaims[memberOfClaim];
foundRole = '-1';
var arrayOfRolesLen = arrayOfRoles.length;
for (var i = 0; i < arrayOfRolesLen; i++) {
searchRole = roles.indexOf(arrayOfRoles[i]);
if (searchRole >= 0) {
foundRole = searchRole;
}
}
Log.info('found role is equal to: ' + foundRole);
if (foundRole >= 0 ) {
Log.info(user.username + ' found a role, indexof=' + foundRole);
// Step 3 is Azure idp.
executeStep(3);
} else {
// Step 2 is basic auth.
executeStep(2);
}
}
});
};```
You may try "identifier First" login with hasRole() to implement your flow.
i.e.
Step1 = Identifier
Step2 = Basic
Step3 = Azure
Let me explain what you have written here using the script.
So you have two auth steps.
Basic auth (username and password)
Federated IDP (Azure)
If you have not enabled adaptive script, after the basic auth step (step1), the user needs to authenticate against the FIDP (step2).
According to the current script that you have written, all the users will have a username and password authentication step (step1). Upon the success of that step, FIDP authentication will be enabled (step2) if the user has a certain set of roles.
Now consider a user who has those specified roles. When that user logins, first the user will see the username password login page. After that, the user will see the azure login page if the user has no active session in Azure (If SSO is enabled, the user might not see the second screen). So it is expected to see two login screens.
So what you have done here is correct, but the user experience is kind of weird. A similar scenario is explained here.
Edited: The better approach is to use the identifier first login method. You can read it here. Please find the approach that I have tried.
I have added 3 steps as follows.
Next I have added a role based adaptive script.
With this approach if I have the role admin, I will only see a input box to provide my password.
But for google it will show google sign-in. If you already have a session then only the consent will be seen. We cannot skip this since this is not in our control.
I want Use this link:
https://docs.wso2.com/display/IS570/Adaptive+Authentication
I want Role1 Can't Login to my Application and Write this Code:
and it's Don't Work and Role1 can login to my application.
How can Write this script?
// Role-Based from Template...
// This script will step up authentication for any user belonging
// to one of the given roles
// If the user has any of the below roles, authentication will be stepped up
var rolesToStepUp = ['Role1'];
function onLoginRequest(context) {
executeStep(1, {
onSuccess: function (context) {
// Extracting authenticated subject from the first step
var user = context.currentKnownSubject;
// Checking if the user is assigned to one of the given roles
var hasRole = hasAnyOfTheRoles(user, rolesToStepUp);
if (hasRole) {
Log.info(user.username + ' Has one of Roles: ' + rolesToStepUp.toString());
return false;
}
}
});
}
// End of Role-Based.......
If you didn't get the log Log.info(user.username + ' Has one of Roles: ' + rolesToStepUp.toString()); check your condition. If your Role is an internal role, change rolesToStepUp variable as follows and try out.
var rolesToStepUp = ['Internal/Role1'];
Have a look on https://stackoverflow.com/a/66013019/10055162 for more details
In order to fail the user login, just returning false doesn't work. Use sendError utility function (https://docs.wso2.com/display/IS570/Adaptive+Authentication+JS+API+Reference#AdaptiveAuthenticationJSAPIReference-sendError(url,parameters)).
Check user-age-based script for sendError usage (https://docs.wso2.com/display/IS570/Configuring+User-Age-Based+Adaptive+Authentication)
We use to create email delegates through Google Email Settings API, but after the deprecation of OAuth 1.0 we were no longer able to authenticate properly. After doing some research I think we should create a service account, delegate domain-wide access for that service account, then authenticate using it. However I can't seem to get it to work, all I receive from Google is 401 unauthorized. Does someone know what I am doing wrong? Here is most of the code, I'm using .Net/c# and I'm using Google Apps for business.
ServiceAccountCredential credential = new ServiceAccountCredential(new ServiceAccountCredential.Initializer("serviceAccountEmail")
{
Scopes = new[] { "https://apps-apis.google.com/a/feeds/emailsettings/2.0/ " },
User = "admin email string"
}.FromCertificate({X509 certificate from service account p12 file}));
credential.RequestAccessTokenAsync(System.Threading.CancellationToken.None).Wait(-1);
GoogleMailSettingsService service = new GoogleMailSettingsService("domain name", "appname");
service.SetAuthenticationToken(credential.Token.AccessToken);
service.CreateDelegate("delegator", "delegate");
For those who may need this answer in the future, I was able to provide a solution through the following. For reference I am running a web app using MVC framework, but the solution could be tweaked for a console or GUI standalone app as well.
Basically, I was able to authenticate the GoogleMailSettingsService.Service.RequestFactory with a GOAuth2RequestFactory object.
For instance:
GoogleMailSettingsService service = new GoogleMailSettingsService("domain", "applicationName");
service.RequestFactory = new GOAuth2RequestFactory("service", "AppName", new OAuth2Parameters() { AccessToken = AuthorizationCodeWebApp.AuthResult.Credential.Token.AccessToken });
Now for the AuthorizationCodeWebApp.AuthResult I implemented the following:
public async Task<ActionResult> DelegationMenu(CancellationToken cancellationToken)
{
var result = await new AuthorizationCodeMvcApp(this, new AppFlowMetadata()).AuthorizeAsync(cancellationToken);
if (result.Credential == null)
return new RedirectResult(result.RedirectUri); //Will redirect to login page for Google Admin to authenticate.
Session["AuthResult"] = result;
return View();
}
public class AppFlowMetadata : FlowMetadata
{
private static readonly IAuthorizationCodeFlow flow =
new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = "ClientId",
ClientSecret = "ClientSecret"
},
Scopes = new[] { "https://apps-apis.google.com/a/feeds/emailsettings/2.0/" },
DataStore = new FileDataStore("C:\\OAuth2.0Tokens")
});
public override string GetUserId(Controller controller)
{
var user = controller.Session["user"];
if (user == null)
{
user = Guid.NewGuid();
controller.Session["user"] = user;
}
return user.ToString();
}
public override IAuthorizationCodeFlow Flow
{
get { return flow; }
}
}
A service account isn't required for this action. The Email Settings API, within the Admin SDK, allows a Super Admin to set a delegation for an account within the domain without the need to impersonate the user via a service account.
Check out this section of the Developers site for more information on this API. You can also test this on the OAuth Playground and add delegates right from there.
I have created some Sitecore users who are not administrators and assigned them few roles. When these users access the Sitecore portal as default they are not shown hidden items and they have to go to view tab and configure it manually. Is there a way I can configure these users to view hidden items by default by doing some configurations to a user role shared between these users.
This information is retrieved by Sitecore.Shell.UserOptions.View.ShowHiddenItems property which gets this data from UserProfile (or from RegistryCache if the profile was already loaded).
User profile information is stored for every user separately and saved in database in binary column. There is no way of getting this option from user role.
Still you can write a script that will loop through all users in the role you mentioned and set the value in profile of those users:
public static void SetHiddenItemsValue(User user)
{
string key = "/Current_User/UserOptions.View.ShowHiddenItems";
string value = "true";
if (!(user != null))
return;
key = StringUtil.Left(key, 250);
key = key.Replace("Current_User", user.Name);
user.Profile[key] = value;
user.Profile.Save();
RegistryCache registryCache = CacheManager.GetRegistryCache(Sitecore.Context.Site);
if (registryCache == null)
return;
registryCache.Clear();
}
An alternative option from Maras is you could possibly hook into the security:loggedin event and set that value.
Your class needs to inherit from Sitecore.Pipelines.LoggedIn.LoggedInProcessor
That'll need to do something like the following:
public override void Process(LoggedInArgs args)
{
var user = Sitecore.Security.Accounts.User.FromName(args.Username, true);
var key = "/" + args.Username + "/UserOptions.View.ShowHiddenItems";
// if user needs to be in a specific role only, check that here
// if (user.IsInRole("yourrolename"))
if (String.IsNullOrEmpty(user.Profile[key]))
{
user.Profile[key] = "true";
user.Profile.Save();
}
}
Hi
I am devolping an application with multiple sites and each site has their own extranet, and this is all working beautifully, using Sitecore 6.4.
Now I need the editors (not admins) of each site to be able to create extranet users that is only able to access the extranet connected to the site, is this even possible?
Basically I am looking for at structure like this:
Sitecore\Editor (Local extranet admin)
Extranet\user
I would think you could make an Extranet Role for each of you "extranets", eg. Site1Admin.
And then make a page that enables them to create a user, giving that user the basic roles it needs.
This is code for Sitecore 6.0, though it should be the same for 6.4 afaik:
Sitecore.Security.Accounts.User user;
if (!Sitecore.Context.IsLoggedIn)
{
string domainUser = Sitecore.Context.Domain.GetFullName("youruser");
string txtPassword = "yourpass";
string txtEmail = "youremail";
if (Sitecore.Security.Accounts.User.Exists(domainUser))
return;
MembershipCreateStatus status;
Membership.CreateUser(domainUser, txtPassword, txtEmail, "Never?", "Always!", true, out status);
if (!status.Equals(MembershipCreateStatus.Success))
{
throw new MembershipCreateUserException(status.ToString());
}
user = //something to load the user, probably in Sitecore.Security.Accounts.User
}
var role = "extranet\\Site1User";
var roles = Roles.GetRolesForUser(); //this is probably only for the current context user
if (!roles.Contains(role))
{
try
{
Roles.AddUsersToRole(new string[] { "youruser" }, role);
}
catch(System.Configuration.Provider.ProviderException)
{
// do nothing, just move on
}
}
}
This is kinda simple, is based on some code I tried to hack together from some working code, that created a user and logged him in and should be adjusted to what you are doing, as there are probably some errors.