Using ASP.net, is there a recommended best way to access a particular field of the profile in code. I was looking in my old Problem-Design-Solution 2.0 book, and it does it by pulling all members in the DB and then iterating through each one's profile (see code below). Is there a better way?
for each (MembershipUser user in Membership.GetAllUsers())
{
ProfileCommon userProfile = profile.GetProfile(user.UserName);
if (userProfile.mysetting == desiredValue)
{
//do something
}
}
Edit 1
I found that it can be done a little more efficiently than pulling members and then pulling profiles. It is possible that not all members have a profile, so if you use the following code, you'll pull all the profiles (which may be fewer in number than members, and then can iterate across it:
for each (ProfileInfo theProfile in ProfileManager.GetAllProfiles (ProfileAuthenticationOption.All)
{
ProfileCommon pc = ProfileBase.Create(theProfile.UserName)
if (pc.mysetting == desiredValue)
{
//do something
}
}
It still round trips the DB for each profile, but it may not do it as many as if we used the members...
With built-in Profiles, no there isn't a better way. One option as provided by Tim is Table Profile provider or writing your own profile provider.
Or you can go completely other route i.e. storing profile information in your own custom table.
You could use the Table Profile Provider and build custom queries to get your desired settings.
You could probably do better with linq, I don't have VS with me right now, but pseudo code would look something like this:
var users = from MembershipUser user in Membership.GetAllUsers()
where user.mysetting == desiredValue
select user
then iterate over the users,
foreach(MembershipUser u in users) {
// do something
}
that should only contain the ones of interest. Linq should handle executing the SQL for you correctly, but you can check to see what it's doing with profiler.
EDIT
Actually that probably won't get you anything from a performance perspective, the GetAllUsers is going to bring back everything. You might want to create a linq2sql dbml map for the users table and use that instead of Membership class for querying against a custom property.
EDIT 2
ASP.NET Roles and Profiles: best way to query for collection of users who match custom profile property?
If you're using the table profile provider you may be able to use the linq query against that table:
http://weblogs.asp.net/kencox/archive/2010/09/05/using-the-sql-table-profile-provider-in-asp-net-4-web-applications-c-amp-vb.aspx
Related
I've a project with a huge amount of data. I need to make query based on user role/group/permission. This means that a query like
$fooRepository = $this->getDoctrine()
->getManager()
->getRepository(Foo::class)
->findAll();
should return different records if done by a ROLE_SUPER_ADMIN or by ROLE_USER. Also, I need to filter record based on relations and so on.
I've different solution in mind:
inject user role inside the repository's query
create a role based repository
create a query for each role
Inject user role in repository's query
In this case each repository should be responsible to provide right data. This is a solution similar to this. In that solution record are filtered by tenant.
Create role based repository
In this case I'll need to create different repositories and instantiate them differently. But I don think this can be easy in doctrine? while I am writing, ... I am thinking this is an exaggerated solution.
Create queries for each roles
At the moment I think this is the more natural way to do queries. I just imagine that a repository should contain
+ findAllStuffForGuestRole()
+ findAllStuffForAdminRole()
Each time I need to add a query, I MUST create different queries.
I think Doctrine filters are the cleanest and the simplest way to solve your problem. See the documentation
This is actually very straight forward with Symfony/Doctrine. Here's a great page in the official docs that explains it better than I could: http://symfony.com/doc/current/doctrine/repository.html
We created special form to creating purchase prices for vendors.
New form has almost the same fields as original (so we used PriceDiscTable), but the record/datasoruce was set as temporary table. After user filled mandatory fields will click button, (extra logic behind) and record will inster to database (real priceDiscTable).
The idea was to grand access to trade prices for users that not necessarily has access to purchase prices. In theory everything was ok, but when user with no access to PriceDiscTable open new form, error was shown "Not enougt right to use table 'Price agreements'".
We try set the AllowCheck to false in formDatasource but this only allow us to open the form, but user still cannot add or modify records.
Is there any way to force system to allow user to write data in the temporary table?
Disabling security key or grand access to real table is not an option.
Duplicate table and create with same fields is nuisance (if we use same table we can use data() method to assign fields)
I think that creating a new temporary table with [almost] the same fields would be the best solution.
If the only reason you oppose to this approach is that you wouldn't be able to use data() to copy data from one table to another you can use buf2BufByName() as described here: http://mybhat.blogspot.co.uk/2012/07/dynamics-ax-buf2buf-and-buf2bufbyname.html
You can use RunAs to impersonate another user...perhaps a system user. I don't entirely follow what you are trying to do, but it sounds like this solution would work for you if you know exactly what your custom code is doing and is capable of.
See Classes\AifOutboundProcessingService\runAsWrapper to see an example.
You will not be able to display the PriceDiscTable without giving the user at least "view" access or editing Classes\FormRun to somehow bypass the security key, which is kernel level so it's also not possible.
I agree with 10p where you should create a temp table and then create a custom method handler combined with buf2bufbyname() or buf2buf().
Another option you can screw around with, if you REALLY want to use .data() is using a Common as the datasource. You could add the fields you want on the grid with the common, then you can pass a common back/forth. This has a good amount of form setup to get this working, but it could produce what you want eventually I think.
static void Job8(Args _args)
{
Common common;
salesTable salesTable;
;
common = new DictTable(366).makeRecord();
select firstonly common where common.RecId == 5637145357;
salesTable.data(common);
info(strfmt("%1 - %2", salesTable.SalesId, salesTable.SalesName));
}
I have created a content management system (CMS) for my company’s product databases. The CMS is based on asp.net scaffolding with many custom pages and actions mixed in. We have 7 products currently, all of which share the same database schema (Entity Framework model-first) and all run perfectly in the CMS. The issue is that every time we get a new product we must clone the CMS and change the connection string in the app.config to point to the correct database in order to work with the new database. While this works, it’s becoming bothersome to maintain and will fail us completely as we acquire more products.
What I would like to do is have a centralized landing page where a user is directed to log in, then given the option to connect to and edit a specific product based on their selection. The idea is that we would have one CMS site which would be able to switch between the databases depending on the user. It is not an option to combine all of the product database in to a single master product database.
I am not sure where to start to achieve this goal, or if this is even the correct plan to achieve my goal of having a single CMS to maintain, and am looking for some guidance in this.
Assuming that your database structures are identical, you could use a factory method anywhere you get an instance of your entity context and put logic in there to grab the correct connection string (or calculate it if there's a naming convention that you could use). Something like this might work for example:
public static MyDatabaseEntities CreateEntityContext(string productName)
{
string connectionString = null;
switch (productName.Trim().ToLower())
{
case "apples":
connectionString = ConfigurationManager.ConnectionStrings["MyDatabase_Apples"].ConnectionString;
break;
case "pears":
connectionString = ConfigurationManager.ConnectionStrings["MyDatabase_Pears"].ConnectionString;
break;
default:
connectionString = ConfigurationManager.ConnectionStrings["MyDatabase"].ConnectionString;
break;
}
return new MyDatabaseEntities(connectionString);
}
Then use this method anywhere you need an instance of your CRM data context passing in the product name that you've calculated on your landing page.
Create another database for user to database mapping. The structure would be like so:
database UserMap
table Users
username (composite primary key)
dbID (composite primary key, foreign key to "Databases" table)
table Databases
dbID (primary key)
connectionString
Then,
populate the list of database in table "Databases"
Do your SQL work to copy the users from the other websites into this "UserMap" database
Write a trigger in each CMS database to add or remove a user as they are created or removed in their respective CMS so it updates the "UserMap" database
Modify your code on the CMS(s) to use this single database for lookup to what connection string should be used.
This would allow you to rely on the single database for the lookup and switch between them in a managed fashion going forward. It requires some up front work but after the triggers are there, you don't have to do anything more.
I have a simple ASP.NET MVC 3 application. I have the database I designed for the application, which corresponds to a domain model (in an App.Domain assembly).
I auto-generated the Application Services Membership tables and added them to the application database. Membership creation is working fine.
I created a 'Permission' table which has a composite PK made up of UserId (from the auto-generated aspnet_Users table) and ContentId (from the Content table holding the application content).
The idea is to allow users to allocate permissions to other users for the content they create.
My plan is to then place logic in the Controllers that goes something like:
Collapse | Copy Code
Guid currentUser = (Guid)Membership.GetUser().ProviderUserKey;
int[] accessible = (from p in context.Permissions
where p.UserId == currentUser
select p.ContentId).toArray();
Then to get the content for the current user, something like this:
Collapse | Copy Code
IEnumerable<content> content = context.Content
.Where(x => x.PublicAccess < 3
|| accessible.Contains(x.ContentId));</content>
If I have made any sense, can anyone tell me if this is a normal way to handle user defined permissions.
Also, this code doesn't work because it won't cast the linq to an int[]. Any help with that?
Typically permissions are handled by the role subystem. You do something like this:
if (User.IsInRole("RoleName")) {
DoWhateverTheUserIsAllowedToDo();
}
You can combine this with dynamically assigned content permissions by looking up what roles or users are assigned to the content, and checking if the user is in that role, or if the users is specifically allowed.
Role based permission scales better than assigning users to specific pages. As the number of pages grow, assigning users to pages becomes a nightmare. So you typically assign roles to pages, then assign users to roles.
Your code seems to want to do a lot of work, returning lots of different content items. Typically, you know what item you want to control access to. So you might do something like:
var roles = Roles.GetRolesForUser()
var content = from p in context.Permissions where p.ContentID == contentID
&& roles.Any(x => p.Roles.Contains(x)) select p;
But, there are so many ways to do this, you will have decide what works best for you.
I don't understand your last bit about not casting to an int[]. I assume .toArray() is a typo, it should be .ToArray(). And if ContentID is an int, .ToArray should create an int[].
In my master page I have:
MembershipUser thisUser = Membership.GetUser();
loggedInUserID = thisUser.ProviderUserKey.ToString();
thisUser gives me access to all the fields in aspnet_Membership.
I want a new field, isSubscribed for each user. I can use an SQL query to fetch the value fine, but I want to know if there is someway to modify the membershipuser object so it retrieves this value as well, so it is accessible from:
thisUser.isSubscribed.ToString();
Thanks for any help!
you will need to add the field to the Profile Provider
A description of the Profile provider can be found here.
http://msdn.microsoft.com/en-us/library/2y3fs9xs.aspx
here is an excerpt from the article
"The ASP.NET profile feature associates information with an individual user and stores the information in a persistent format. Profiles allow you to manage user information without requiring you to create and maintain your own database. In addition, the ASP.NET profile feature makes the user information available using a strongly typed API that you can access from anywhere in your application."
Membership is for identification and authentication. It is not good practice to hack your security for the sake of a meta property.
As mentioned, Profile is the proper place to store meta data and this would obviate the need for a custom MembershipUser.
If you need sql query access to the data use the SqlTableProvider
Si Robinson gave a good answer for storing additional meta data against users without having to change the underlying schema but if you already have data stored about this user in your custom database schema, that won't quite work out.
The solution I have used is to implement my own membership provider:
http://msdn.microsoft.com/en-us/library/f1kyba5e.aspx
And then you can implement your own MembershipUser which exposes the IsSubscribed property.
This works fine with the Membership process within ASP.NET such as the login components. All you need to do is cast the object returned by GetUser() to your custom implementation and you are set!
You could use roles for this and assign users to a Subscriber role. Such as:
Roles.AddUserToRole("Bob", "Subscriber");
You're gonna have a real un-fun time querying by profile fields. With a role you will be able to enumerate users with:
Roles.GetUsersInRoles("Subscriber");
And you'll be able to add these roles to Web.Config files to control which parts of the site only Subscribers can see. Possibly better than wrapping content with a conditional based on a profile field.