How do I get the account structure for a particular Main Account in AX 2012? - axapta

I want to get the account structure (particularly the dimension names) for an AX Main Account that you can create in General Ledger --> Setup --> Account structures, for the purpose of dynamically creating input boxes in a webpage.
The account structures form is pretty complex, and so is the data dictionary schema that powers the account structure system. Is there a simple way to find the account structure for a given main account?

If you already know the Main Account that you want to get the account structure for, you can use the DimensionHierarchy::getAccountStructure() method, which returns a single account structure ID for that Main Account. From there, you can visit each level in the DimensionHierarchyLevel table for that account structure ID, and extract whatever data you require about the account structure from the DimensionAttribute table.
DimensionHierarchy hierarchy;
DimensionHierarchyLevel hierarchyLevel;
DimensionHierarchyId hId;
int i = 1; // Set to 0 if you also want to get the root MainAccount level
hId = DimensionHierarchy::getAccountStructure(MainAccount::findByMainAccountId("1234").RecId);
do
{
i++;
hierarchyLevel = DimensionHierarchyLevel::findByDimensionHierarchyAndLevel(hId, i);
if (hierarchyLevel)
{
info(DimensionAttribute::find(hierarchyLevel.DimensionAttribute).Name);
}
}
while (hierarchyLevel);

Related

MongoDB Realm - Block data leaks

I am in the stage of setting up MongoDB Realm sync with Schemas but I have a question regarding data breaches.
My schemas looks like this:
User {
id
name
groups[] (ids)
}
Group {
id
name
members[] (users - Inverse Relationship)
}
I would like the user to login and receive their user data which includes the groups they are members of. How can I define the sync rules (or another way?) to stop anyone reverse engineering my app code and change the partition (group ID) and get access to other groups?

Preventing access to Dynamic Data tables programmatically

I have an admin section for many databases set up using Dynamic Data. When the user logs in, they are presented with a list of databases that they have access to modify (this list is kept in a separate database that is tied to the person's login information and also lists the tables they are allowed to see). Upon clicking on the database, a list of tables is then shown in a second gridview on the same default.aspx page for the Dynamic Data site.
I have found a way to limit the tables shown to the ones corresponding to the database entries of allowed tables, however, upon viewing the table entries, any foreign key associations to tables they do not have access to show up with the dynamic data hyperlinks that take you over to those tables directly.
Soooo, if you are still with me, thank you. My thought was to do a check in the global.asax file where the metamodels are bound and just not scaffold the tables that are supposed to be inaccessible. Can this be done programmatically? I would post some code but I'm not really sure what would be needed. Here is where I'm thinking I can make an edit:
foreach (var table in metaModel.Value.Tables)
{
var tablePermission = authorizedTables.Any(p => p == table.Name);
if (tablePermission) continue;
//Next lines are possible ways in?
var canThisBeSetSomehow = table.Attributes.OfType<ScaffoldTableAttribute>().FirstOrDefault();
table.Scaffold = false;
}
Assume that "authorizedTables" is a string[] that holds my table names in the above scenario. Is there a way to take the metamodel and edit it so that the remaining tables are inaccessible on a per user basis?

How to add/save temporary table on form

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));
}

Single website multiple databases, database switching

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.

How to use ASP.NET Membership for User Defined Permissions

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[].

Resources