I would like to ask , How can I hide some columns (ex. price,client's Mobile etc...)from group (such as Home visitors )?
Note: I'm using sharepoint 2010 foundation.
SharePoint 2010 doesn't have field level security, so you can't totally prevent different groups from seeing that data.
What you could do is create different forms/views for the different groups, and then only give them links to those form/view pages depending on the groups. However, if they know the right URL, they'd be able to type that in and see the other views.
I do not know of anything in the Enterprise version that adds field level audiences, security, or trimming. We recently completed a project that had "For Admin use only" fields. As Andy described, we used multiple forms to accomplish this. The only difference is that we protected against URL spoofing by having the Admin forms inherit from a custom class that checked the identity of the user:
public class AdminEditFormPage : WebPartPage
{
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
CheckRights();
}
private void CheckRights()
{
SPWeb web = SPContext.Current.Web;
SPGroup group = web.SiteGroups[Groups.FarmAdministrators];
bool flag = (group != null) && group.ContainsCurrentUser;
if (!flag)
{
SPUtility.HandleAccessDenied(new UnauthorizedAccessException());
}
}
}
Related
We have used RBAC to implement simple role based permissions for CRUD, but now we need to also add a 'visibility' functionality which makes it possible to limit content visibility (R) to only registered users or only the content owners.
So, how can we limit content visibility on different levels, for example
PUBLIC: anybody can see the content, including anonymous
INTERNAL: only registered users can see the content
PRIVATE: only the creator can see the content
What would be the best way to implement this, it looks like RBAC does not have a straightforward way of dealing with this.
I think that the problem can be solved by using defaultScope in models. Thus, before giving the content, we can check the current role of the user data and give the necessary conditions.
public static function find()
{
$userRoleArray = \Yii::$app->authManager->getRolesByUser(Yii::$app->user->getId());
$userRole = current($userRoleArray)->name;
if ($userRole == 'admin') {
return parent::find()->where("Your condition");
} elseif ($userRole == 'moderator') {
return parent::find()->where("Your condition");
}
}
you can make a permission function and run in each function that will take user role as argument and returns true or redirect to not allowed page.
Here is something I tried but you can modify according to your need.
public function allowUser($min_level) {
//-1 no login required 0..3: admin level
$userRole = //get user role;
$current_level = -1;
if (Yii::$app->user->isGuest)
$current_level = 0;
else
$current_level = userRole;
if ($min_level > $current_level) {
$this->redirect(array("/pages/not-allowed"),true);
}
}
I am working on an MVC 4 intranet application and am using Windows authentication. I would like to add to the user object that the authentication method uses (#User) and get that data from active directory (such as email, phone number, etc).
I know I can create a custom Authorize attribute and add it to the controller that all of my other controllers inherit from, but I don't know if this is the right method to do what I want.
My end goal is simple, I want #User object to have additional properties that are populated via Active Directory. Thanks for any help you can offer.
I was just about to add my own question to StackOverflow with my solution to help others with this issue, when I saw your existing question. It seems like this would be a very common thing, but the information about how to do it only is spread out between multiple sources and hard to track down. There's not just one complete resource, so hopefully this will help you and others.
The best way to do this is use a UserPrincipal extension. Basically, you're subclassing UserPrincipal from System.DirectoryServices.AccountManagement and adding your own additional properties. This is enabled via the ExtensionGet and ExtensionSet (somewhat magical) methods.
[DirectoryRdnPrefix("CN")]
[DirectoryObjectClass("user")]
public class UserPrincipalExtended : UserPrincipal
{
public UserPrincipalExtended(PrincipalContext context) : base(context)
{
}
public UserPrincipalExtended(PrincipalContext context, string samAccountName, string password, bool enabled)
: base(context, samAccountName, password, enabled)
{
}
[DirectoryProperty("title")]
public string Title
{
get
{
if (ExtensionGet("title").Length != 1)
return null;
return (string)ExtensionGet("title")[0];
}
set
{
ExtensionSet( "title", value );
}
}
[DirectoryProperty("department")]
public string Department
{
get
{
if (ExtensionGet("department").Length != 1)
return null;
return (string)ExtensionGet("department")[0];
}
set
{
ExtensionSet("department", value);
}
}
public static new UserPrincipalExtended FindByIdentity(PrincipalContext context, string identityValue)
{
return (UserPrincipalExtended)FindByIdentityWithType(context, typeof(UserPrincipalExtended), identityValue);
}
public static new UserPrincipalExtended FindByIdentity(PrincipalContext context, IdentityType identityType, string identityValue)
{
return (UserPrincipalExtended)FindByIdentityWithType(context, typeof(UserPrincipalExtended), identityType, identityValue);
}
}
The two attributes on the class need to be customized to your instance of AD. The value for DirectoryRdnPrefix needs to be the RDN (relative distinguished name) in AD, while the value for DirectoryObjectClass needs to be the directory object type name in AD for a userObject class. For a typical AD Domain Services setup, they should both be as used in the code presented above, but for an LDS setup, they could be different. I've added two new properties that my organization uses, "title" and "department". From that, you can get an idea of how to add any other property you like: basically you just create a property using the template I've provided here. The property can be named anything you like, but the string value passed to DirectoryProperty and inside the code block should match up to a property name from AD. With that in place, you can use PrincipalContext with your subclass instead of UserPrincipal to get back a user object with the properties you need added.
UserPrincipalExtended user = UserPrincipalExtended.FindByIdentity(
new PrincipalContext(ContextType.Domain), User.Identity.Name);
And access your property like any other on the UserPrincipal instance:
// User's title
user.Title
If you're unfamiliar with System.DirectoryServices.AccountManagement.UserPrincipal, there's a few user properties baked in: GivenName, Surname, DisplayName, etc. In particular to your circumstance, since you mentioned phone and email specifically, there's VoiceTelephoneNumber and EmailAddress. You can see the full list in the MSDN docs. If all you need is the built-in information, you don't need to extend UserPrincipal as I showed above. You would just do:
UserPrincipal user = UserPrincipal.FindByIdentity(
new PrincipalContext(ContextType.Domain), User.Identity.Name);
But, 9 times out of 10, the built-ins won't be enough, so it's good to know how to get the rest easily.
Finally, I didn't want to have to add #using lines to any view that uses this, so I went ahead and added the namespaces to my Views folder's web.config. That part is important, it needs to be added to the Views folder's web.config, not the project's (and each Area's individual Views folder if you're utilizing Areas).
<system.web.webPages.razor>
...
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
...
<add namespace="System.DirectoryServices.AccountManagement" />
<add namespace="Namespace.For.Your.Extension" />
</namespaces>
</pages>
</system.web.webPages.razor>
I'm using windows authentication with no roles setup, I simply have some admin names stored in a table that I want to check against in combination with the authorize attribute. I don't have much experience using this, but the only examples I see are hard coded values like below so I'm not sure if this functionality is available or if I'd need to add it.
[Authorize(Users = #"domain\user1, domain\user2")]
Any suggestions will be appreciated.
I ended up adding this myself, very easy to do.
public class AuthorizeUser : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
string[] admins =
//get user names
if (admins.Contains(httpContext.User.Identity.Name))
return true;
return false;
}
}
Then to use just
[AuthorizeUser]
Being rather new to MVC 3 and EF, I'm trying to understand the best architectural approach to developing an application for my company. The application will be a large-scale application that potentially handles hundreds of users at the same time, so I want to make sure I understand and am following proper procedures. So far, I've determined that a simple repository pattern (such as Controller -> Repository -> EF) approach is the best and easiest to implement, but I'm not sure if that is definitely the best way to do things. The application will basically return data that is shown to a user in a devexpress grid and they can modify this data/add to it etc.
I found this article and it is rather confusing for me at this time, so I'm wondering if there is any reason to attempt to work with a disconnected EF and why you would even want to do so: http://www.codeproject.com/Articles/81543/Finally-Entity-Framework-working-in-fully-disconne?msg=3717432#xx3717432xx
So to summarize my question(s):
Is the code below acceptable?
Should it work fine for a large-scale MVC application?
Is there a better way?
Will unnecessary connections to SQL remain open from EF? (SQL Profiler makes it look like it stays open a while even after the using statement has exited)
Is the disconnected framework idea a better one and why would you even want to do that? I don't believe we'll need to track data across tiers ...
Note: The repository implements IDisposable and has the dispose method listed below. It creates a new instance of the entity context in the repository constructor.
Example Usage:
Controller (LogOn using Custom Membership Provider):
if (MembershipService.ValidateUser(model.UserName, model.Password))
{
User newUser = new User();
using (AccountRepository repo = new AccountRepository())
{
newUser = repo.GetUser(model.UserName);
...
}
}
Membership Provider ValidateUser:
public override bool ValidateUser(string username, string password)
{
using (AccountRepository repo = new AccountRepository())
{
try
{
if (string.IsNullOrEmpty(password.Trim()) || string.IsNullOrEmpty(username.Trim()))
return false;
string hash = FormsAuthentication.HashPasswordForStoringInConfigFile(password.Trim(), "md5");
bool exists = false;
exists = repo.UserExists(username, hash);
return exists;
}catch{
return false;
}
}
}
Account Repository Methods for GetUser & UserExists:
Get User:
public User GetUser(string userName)
{
try
{
return entities.Users.SingleOrDefault(user => user.UserName == userName);
}
catch (Exception Ex)
{
throw new Exception("An error occurred: " + Ex.Message);
}
}
User Exists:
public bool UserExists(string userName, string userPassword)
{
if (userName == "" || userPassword == "")
throw new ArgumentException(InvalidUsernamePassword);
try
{
bool exists = (entities.Users.SingleOrDefault(u => u.UserName == userName && u.Password == userPassword) != null);
return exists;
}
catch (Exception Ex)
{
throw new Exception("An error occurred: " + Ex.Message);
}
}
Repository Snippets (Constructor, Dispose etc):
public class AccountRepository : IDisposable
{
private DbContext entities;
public AccountRepository()
{
entities = new DbContext();
}
...
public void Dispose()
{
entities.Dispose();
}
}
What's acceptable is pretty subjective, but if you want to do proper data access I suggest you do NOT use the repository pattern, as it breaks down as your application gets more complex.
The biggest reason is minimizing database access. So for example look at your repository and notice the GetUser() method. Now take a step back from the code and think about how your application is going to be used. Now think about how often you are going to request data from the user table without any additional data. The answer is almost always going to be "rarely" unless you are creating a basic data entry application.
You say it your application will show a lot of grids. What data is in that Grid? I'm assuming (without knowing your application domain) that the grids will combine user data with other information that's relevant for that user. If that's the case, how do you do it with your repositories?
One way is to call on each repository's method individually, like so:
var user = userRepository.GetUser("KallDrexx");
var companies = companyRepository.GetCompaniesForUser(user.Id);
This now means you have 2 database calls for what really should be just one. As your screens get more and more complex, this will cause the number of database hits to increase and increase, and if your application gets significant traffic this will cause performance issues. The only real way to do this in the repository pattern is to add special methods to your repositories to do that specific query, like:
public class UserRepository
{
public User GetUser(string userName)
{
// GetUser code
}
public User GetUserWithCompanies(string userName)
{
// query code here
}
}
So now what happens if you need users and say their contact data in one query. Now you have to add another method to your user repository. Now say you need to do another query that also returns the number of clients each company has, so you need to add yet another method (or add an optional parameter). Now say you want to add a query that returns all companies and what users they contain. Now you need a new query method but then comes the question of do you put that in the User repository or the Company repository? How do you keep track of which one it's in and make it simple to choose between GetUserWithCompany and GetCompanyWithUsers when you need it later?
Everything gets very complex from that point on, and it's those situations that have made me drop the repository pattern. What I do now for data access is I create individual query and command classes, each class represents 1 (and only 1) query or data update command to the database. Each query class returns a view model that only contains the data I need for one specific user usage scenario. There are other data access patterns that will work too (specification pattern, some good devs even say you should just do your data access in your controllers since EF is your data access layer).
The key to doing data access successfully is good planning. Do you know what your screens are going to look like? Do you know how users are going to use your system? Do you know all the data that is actually going to be on each screen? If the answer to any of these is no, then you need to take a step back and forget about the data layer, because the data layer is (or should be for a good application) determined based on how the application is actually going to be used, the UI and the screens should not be dependent on how the data layer was designed. If you don't take your UI needs and user usage scenarios into account when developing the data access, your application will not scale well and will not be performant. Sometimes that's not an issue if you don't plan on your site being big, but it never hurts to keep those things in mind.
No matter what you do, you may consider moving instantiation and disposing of your context to your controller like this:
public class MyController : Controller
{
private Entities context = new Entities();
...
public override void Dispose()
{
context.Dispose();
}
}
You can then pass that context into any method that needs it without duplicating the overhead of creating it.
I disagree that the repository pattern is necessarily bad for the same reason. You create multiple classes to break up your code to make it manageable and still reuse the same context. That could look something like this:
repository.Users.GetUser(userName);
In this case "Users" is a lazy loaded instance of your user repository class which reuses the context from your repository. So the code for that Users property in your repository would look something like this:
private UserRepository users;
public UserRepository Users
{
get
{
If (users == null)
{
users = new UserRepository(this);
}
return users;
}
}
You can then expose your context to these other lazy loaded classes via a property.
I don't think this necessarily conflicts with KallDrexx's pattern. His method simply flips this so instead of
repository.Users.GetUser(userName);
You would have something like
UserQuery query = new UserQuery(repository.Users);
This then becomes an issue of syntax. Do you want this:
repository.Area.Query(value1, value2, ...);
Or this:
AreaQuery query = new AreaQuery { Property1 = value1, ... };
The latter actually works nicer with model binding but obviously is more verbose when you actually have to code it.
Best advice KallDrexx gave is to just put your code I your actions and then figure it out. If you are doing simple CRUD, then let MVC instantiate and populate your model, then all you have to do is attach and save. If you find you can reuse code, move it to where it can be reused. If your application starts getting too complicated, try some of these recommendations until you find what works for you.
I need to consume a value passed by the default sharepoint filter webpart. I don't see how a custom sharepoint webpart can establish a connect and get data. Is this even possible?
Updated
The provider WebPart is a default SharePoint List Filter WebPart.
The consumer WebPart is a custom WebPart
This is the code I came up with, but the "connections" option is still greyed out on the SharePoint page. On the page, I have a SharePoint List Filter WebPart and my CustomPageViewer WebPart.
namespace PageViewerWithConnections.CustomPageViewer
{
[ToolboxItemAttribute(false)]
public class CustomPageViewer : System.Web.UI.WebControls.WebParts.WebPart
{
IFilterValues _filterVals;
[ConnectionConsumer("Consumer connection", "Consumer param")]
public void ConsumeFilter(IFilterValues filterValues)
{
_filterVals = filterValues;
}
Microsoft.SharePoint.WebPartPages.PageViewerWebPart objPageViewer;
protected override void CreateChildControls()
{
}
}
}
Reason's for this approach
My goal is to set a different URL to the page viewer Web Part based on the value I get from a SharePoint List Filter Web Part. It seems that the SharePoint List Filter WebPart cannot send data to a Page Viewer WebPart.
You'll need to create a consumer method on your custom webpart that takes an instance of IFilterValues as an argument and uses the ConnectionConsumerAttribute Attribute.
private IFilterValues _filterVals;
[ConnectionConsumer("Filter Consumer", "FilterConsumer")]
public void ConsumeFilter(IFilterValues filterValues)
{
_filterVals = filterValues;
}
Note that the consumption of the filter values occurs during the OnPreRender stage of the page lifecycle, so you'll need to override the OnRender method to act on any values consumed from the connection, or include the logic in the consumer method.
For more information, check out these links:
http://msdn.microsoft.com/en-us/library/ms494838(v=office.12).aspx
http://msdn.microsoft.com/en-us/library/ms469765.aspx
In the CreateChildControls you should call base.CreateChildControls();
Here is some working code:
List<IFilterValues> providers = new List<IFilterValues>();
protected override void CreateChildControls()
{
if (providers.Count > 0 && providers[0].ParameterValues != null)
{
this.FilterValue1 = providers[0].ParameterValues[0];
}
base.CreateChildControls();
}
[ConnectionConsumer("Provider WebPart", "IFilterValues", AllowsMultipleConnections = false)]
public void SetConnectionInterface(IFilterValues provider)
{
if (provider != null)
{
this.providers.Add(provider);
List<ConsumerParameter> parameters = new List<ConsumerParameter>();
parameters.Add(new ConsumerParameter("param1",
ConsumerParameterCapabilities.SupportsSingleValue | ConsumerParameterCapabilities.SupportsEmptyValue | ConsumerParameterCapabilities.SupportsAllValue));
provider.SetConsumerParameters(new ReadOnlyCollection<ConsumerParameter>(parameters));
}
}