Azure Analysis Services (AAS) Cube Roles: How to grant 2 levels of access, without having overlapping users, who thus get the lower level of access? - azure-active-directory

Situation is:
We have an AAS cube. We want some users to have access to everything, and some to have limited access. Am currently doing this via roles:
[Role- All Access]: Has access to everything. Very limited number of users; hand curated by the business
[Role- Limited Access]: Has a few tables restricted. Many users; business has ~10 existing security groups whose members should have this access.
This issue is security is reductive.
If someone is a member of both [Role- All Access] and one of the security groups that rolls up to [Role- Limited Access], that user ends up with Limited Access. At least the way I know to write the roles.
My first thought was to do add an single security group to each role:
[Role- All Access]: Gets [Security Group, All Access], which itself is hand curated
[Role- Limited Access]: Gets [Security Group, Limited Access]. This ones gets those 10 security group nested in it. But do this as a Dynamic User AAD Security Group, and exclude any one who is a member of [Security Group, All Access], via the new memberof property - but from what I read:
https://identity-man.eu/2022/06/07/using-the-new-azure-ad-dynamic-groups-memberof-property/
You can’t use other operators with memberOf (i.e. you cannot create a rule which states member Of group A can’t be in Dynamic group B).
So that doesn't work.
I may be thinking about this the entirely wrong way as well (neither AAS nor dynamic security groups are things I know much about). Thoughts?
EDIT: Maybe the way I wrote the role, is causing security to be reductive? If security were not reductive, that would make it easy.
{
"create": {
"parentObject": {
"database": "redacted"
},
"role": {
"name": "Role - Limited Access",
"modelPermission": "read",
"members": [
{
"memberName": "obj:blablaSecurityGroupGUID",
"identityProvider": "AzureAD"
}
],
"tablePermissions": [
{
"name": "FACTGeneralLedger",
"metadataPermission": "none"
}
]
}
}
}

Somehow having the [Role- All Access] as read and process was screwing something up. Switching it to just read, and the security switched back to additive.

Related

Limit size and/or frequency of user queries in SQL Server

Is it possible to put a cap on the "size" and frequency of user queries in SQL Server (or perhaps another engine)? For example:
Let's say there are a few tables with millions of rows. Maybe there's a handful of admins and analysts working on the tables, and they'd know their way around enough to not run any unnecessary heavy queries that may run for several minutes/hours.
However, a sales/marketers/admin staff less familiar with SQL is more likely to run a heavy query e.g. with loads of joins, whether accidently or just for the fun of it. Multiply this by dozens of them, and the server can be severely hammered at.
Ideally, I'd want restrictions like the following:
If the engine anticipates there'll be above a million row scans, cancel the query (and tell the user why it was cancelled).
Limit queries being run by a single user to 20 queries within a 10-minute window.
User/role-level "caps"
You have pretty radical requirements for code execution. There is nothing out of the box that will work for you. However, you can implement certain things to achieve what you are trying to achieve here:
Do not give users access to tables directly, create procs and only give access to procs.
Inside the procs you can get all fancy with limiting the maximum number of rows a user i can return by adding top clause.
Create an Audit table and inside the proc every time a user makes a call to a proc add a row to that audit table, also the very first step could be to check how many rows are already in the audit table for that proc for the caller (how many times user has already executed the proc) and raise an error if the user has already exceeded the limit etc. you get the idea.
I would suggest not to limit the cost of query, this would come back to haunt you, for many reasons, write the queries/Procs yourself or someone you trust to write efficient code.
Something like this....
CREATE PROC dbo.usp_Test
AS
BEGIN
SET NOCOUNT ON;
Declare #UserCalls INT;
SELECT #UserCalls = Count(*)
FROM dbo.AuditTable
WHERE UserName = SUSER_SNAME()
AND ProcName = 'usp_Test'
AND Logged >= DATEADD(MINUTE , -1 , GETDATE());
IF (#UserCalls >= 10)
BEGIN
RAISERROR ('Come back in 1 minute, you have exceeded 10 execution/min limit' , 16 , 1);
RETURN;
END
ELSE
BEGIN
INSERT INTO dbo.Audit (ProcName , UserName , Logged)
VALUES ('usp_Test' , SUSER_SNAME() , GETDATE());
END
/* Rest of the code */
SELECT TOP (1000) *
FROM ...........;
END
The feature you're looking for is called Resource Governor.
You can classify incoming connections and assign them to a Workload Group, which specifies
CREATE WORKLOAD GROUP group_name
[ WITH
( [ IMPORTANCE = { LOW | MEDIUM | HIGH } ]
[ [ , ] REQUEST_MAX_MEMORY_GRANT_PERCENT = value ]
[ [ , ] REQUEST_MAX_CPU_TIME_SEC = value ]
[ [ , ] REQUEST_MEMORY_GRANT_TIMEOUT_SEC = value ]
[ [ , ] MAX_DOP = value ]
[ [ , ] GROUP_MAX_REQUESTS = value ] )
]
[ USING {
[ pool_name | "default" ]
[ [ , ] EXTERNAL external_pool_name | "default" ] ]
} ]
[ ; ]
And maps to a Resource Pool which has limited access to server resources.
In the SQL Server Resource Governor, a resource pool represents a
subset of the physical resources of an instance of the Database
Engine. Resource Governor enables you to specify limits on the amount
of CPU, physical IO, and memory that incoming application requests can
use within the resource pool. Each resource pool can contain one or
more workload groups.
It's important to combine Resource Governor with snapshot-based reads for the reporting users, either using SNAPSHOT isolation, or by setting the database to READ_COMMITTED_SNAPSHOT. Otherwise a reporting user with limited access to resources can acquire locks that interfere with other workloads.

Get Display Name for License SKU in Microsoft Graph

I am trying to use Microsoft Graph to capture the products which we have licenses for.
While I can get the skupartname, that name is not exactly display-friendly.
I have come across DisplayName as a datapoint in almost all the API calls that give out an object with an id.
I was wondering if there was a DisplayName for the skus, and where I could go to get them via the graph.
For reference, the call I made was on the https://graph.microsoft.com/v1.0/subscribedSkus endpoint following the doc https://learn.microsoft.com/en-us/graph/api/subscribedsku-list?view=graph-rest-1.0
The following is what's returned (after filtering out things I don't need), and as mentioned before, while I have a unique identifier which I can use via the skuPartNumber, that is not exactly PRESENTABLE.
You might notice for some of the skus, it difficult to figure out what it is referring to based on the names in the image of the Licenses page posted after the output
[
{
"capabilityStatus": "Enabled",
"consumedUnits": 0,
"id": "aca06701-ea7e-42b5-81e7-6ecaee2811ad_2b9c8e7c-319c-43a2-a2a0-48c5c6161de7",
"skuId": "2b9c8e7c-319c-43a2-a2a0-48c5c6161de7",
"skuPartNumber": "AAD_BASIC"
},
{
"capabilityStatus": "Enabled",
"consumedUnits": 0,
"id": "aca06701-ea7e-42b5-81e7-6ecaee2811ad_df845ce7-05f9-4894-b5f2-11bbfbcfd2b6",
"skuId": "df845ce7-05f9-4894-b5f2-11bbfbcfd2b6",
"skuPartNumber": "ADALLOM_STANDALONE"
},
{
"capabilityStatus": "Enabled",
"consumedUnits": 96,
"id": "aca06701-ea7e-42b5-81e7-6ecaee2811ad_0c266dff-15dd-4b49-8397-2bb16070ed52",
"skuId": "0c266dff-15dd-4b49-8397-2bb16070ed52",
"skuPartNumber": "MCOMEETADV"
}
]
Edit:
I am aware that I can get "friendly names" of SKUs in the following link
https://learn.microsoft.com/en-us/azure/active-directory/users-groups-roles/licensing-service-plan-reference
The problem is that it contains ONLY the 70ish most COMMON SKUs (in the last financial quarter), NOT ALL.
My organization alone has 5 SKUs not present on that page, and some of our clients for who we are an MSP for, also have a few. In that context, the link really does not solve the problem, since it is not reliable, nor updated fast enough for new SKUs
You can see a match list from Product names and service plan identifiers for licensing.
Please note that:
the table lists the most commonly used Microsoft online service
products and provides their various ID values. These tables are for
reference purposes and are accurate only as of the date when this
article was last updated. Microsoft does not plan to update them for
newly added services periodically.
Here is an extra list which may be helpful.
There is a CSV download available of the data on the "Product names and service plan identifiers for licensing" page now.
For example, the current CSV (as of the time of posting this answer) is located at https://download.microsoft.com/download/e/3/e/e3e9faf2-f28b-490a-9ada-c6089a1fc5b0/Product%20names%20and%20service%20plan%20identifiers%20for%20licensing%20v9_22_2021.csv. This can be downloaded, cached and parsed in your application to resolve the product display name.
This is just a CSV format of the same table that is displayed on the webpage, which is not comprehensive, but it should have many of the products listed. If you find one that is missing, you can use the "Submit feedback for this page" button on the bottom of the page to create a GitHub issue. The documentation team usually responds in a few weeks.
Microsoft may provide an API for this data in the future, but it's only in their backlog. (source)

Solr request: SQL-like JOIN, GROUP BY, SUM(), WHERE SUM()

I'm new to Solr and I have the following problem:
I have those documents:
category:contract:
{
"contract_id_s": "contract-ENG-00001",
"title_s": "contract title",
"ref_easy_s": "REFAAA",
"commitment_id_s": "ENG-00001",
},
category:commitment:
{
"commitment_id_s": "ENG-00001",
"title_s": "commitment title",
"status_s": "Validated",
"date_changed_status_s": "2015-09-30",
"date_status_initiated_s": "2015-09-27",
"date_status_confirmed_s": "2015-09-28",
"date_status_validated_s": "2015-09-30",
},
category:commitment AND sub_category_s:commitment_project:
{
"id": "ENG-00001_AAA",
"commitment_id_s": "ENG-00001",
"project_id_s": "AAA",
"project_name_s": "project name",
"project_amount_asked_s": "2000",
"project_amount_validated_s": "2100"
},
{
"id": "ENG-00001_AAA2",
"commitment_id_s": "ENG-00001",
"project_id_s": "AAA",
"project_name_s": "project name",
"project_amount_asked_s": "1000",
"project_amount_validated_s": "1200"
},
For each commitment, there could be a contract.
For each commitment, there could be some payments.
Here is what I want to do:
- by default, only select commitment that have at least :
. one sub_category_s:commitment_project with a project_amount_validated_s value.
. one contract.
- if filtered on amounts, only select in this list, commitments with the SUM of project_amount_validated_s > amount_min AND < amount_max.
I don't know what is the best practice in terms of performance?
- Requesting the ids of the commitments then requesting the details for them?
- Is there a way to JOIN the contract informations in this request?
- Or the best practice is to request each document one by one?
The problem is that I don't want to request useless data (performance, bandwidth).
There are some tools available to you in the form of:
Solr's Block Join Query Parser (which allows for simple parent/child
queries).
Solr Facets (which allow for aggregrations (e.g. sum of payments) ... with recent support for faceting on parent/child fields).
The Solr Expand Component (which recently allows parent information to be expanded from a child block join query).
However, I'm not certain you can do everything you're hoping in one query (using with these pieces). And even if you can, stitching them together doesn't even come close the the simplicity of the SELECT...JOIN...GROUP BY...HAVING SQL query you're hoping to replicate. (Unless you want to try out the Solr 6 developer snapshot with parallel SQL support)
BUT If this is your only use-case, AND Solr is not your primary datastore, I'd strongly recommend modeling your Solr data to fit your use-case.
E.g. Start simple, denormalize, and only include the fields in your datamodel needed for search:
Only one type of record: commitment
Fields
commitment_id_s
title_s
status_s
date_changed_status_s
date_status_initiated_s
date_status_confirmed_s
date_status_validated_s
total_payments_asked (numeric sum of project_amount_asked from DB)
total_payments_validated (numeric sum of project_amount_validated from DB)
project_names (multiValued list of searchable project names)
contract_names (multiValued list of searchable contract names)
Then your query just needs a filter:
total_payments_validated:[<amount_min>TO<amount_max>]
to enforce your default criteria.
Once your search has identified the commitment IDs matching the Solr query, then go back and query the source database for any additional information needed for display (project details, contract details, dates, etc...)
Ok, I've found a solution by using !join.
For instance, in PHP:
[
'q' => "{!join from=id to=service_id score=none}uri:\\$serviceUri* AND -deleted:true",
'fq' => "{!cache=false}category:monthly_volume AND type:\"$type\" AND timestamp:[$strDateStart TO $strDateEnd]",
'alt' => 'json',
'max-results' => 1000,
'sort' => 'timestamp ASC',
'statsFields' => 'stats.field=value&stats.facet=timestamp',
]
Or with URL request:
http://localhost:8983/solr/fluks-admin/select?q={!join+from=id+to=sector_id+score=none}{!join+from=uri+to=service+score=none}uri:/test-en/service-en*+AND+-deleted:true&fq={!cache=false}category:indicator+AND+timestamp:[201608+TO+201610]+AND+type:("-3"+OR+2+OR+3)+AND+-deleted:true&wt=json&indent=true&json.facet={sum_timestamp:{terms:{limit:-1, field:timestamp, facet:{sum_type:{terms:{limit:-1, field:type, facet:{sum_vol_value:"sum(vol_value)"}}}}}}}

restricting aro scope in cakephp

Ok, I'll preface this by saying i'm a noob to cake and ACL, and MVC architecture. I'm following a cakephp tutorial from its site, http://book.cakephp.org/2.0/en/tutorials-and-examples/simple-acl-controlled-application/part-two.html. I understand the content of the tutorial, but wonder about the easiest way to take this simple group based ACL to the next level. I've searched SO to no avail. So, on with the question.
Lets take a slightly modified version of the function in the above tutorial, for example. It simply sets up ARO permissions for ACOs.
There are many districts, and many departments, all with many users, in each.
relationships are as follows:
A district hasMany Departments -- one to many.
A district has many users, and users can potentially have many districts(so long as they belong to a dept in that district).
A department hasAnBelongsToMany Users -- many to many.
public function initDB() {
$group = $this->User->Group;
//Allow admins to everything
$group->id = 1;
$this->Acl->allow($group, 'controllers');
// issue 1) allow district managers to districts, departments, users
$group->id = 2;
$this->Acl->deny($group, 'controllers');
$this->Acl->allow($group, 'controllers/Districts');
$this->Acl->allow($group, 'controllers/Departments');
$this->Acl->allow($group, 'controllers/Users');
//issue 2) allow department managers to edit departments and users
$group->id = 3;
$this->Acl->deny($group, 'controllers');
$this->Acl->allow($group, 'controllers/Departments');
$this->Acl->allow($group, 'controllers/Users');
//we add an exit to avoid an ugly "missing views" error message
echo "all done";
exit;
}
Ok, so these permissions being set-up, although it's definitely convenient, really solve a small part of the problem. As it stands, I've really only prevented the department admins from performing district level functions. Both district admins and dept admins can perform user CRUD without restrictions
What i'd like to do, is limit the scope of each of the admin types USER CRUD to only those users residing within their department or district. for example, Department Admin Foo can't delete another Dept Admin's Users, and Dist Admin Bar can't change all of another Dist Admin's Users first names to nancy.
lol. I'm seriously at a loss with how to accomplish this.
I guess one approach would be get an administrators rank, dist admin for example, then figure out in which district he is a user. lastly, return all of the users in this district into a variable $userScope. Again, I am new to cake php and would be unsure of how to carry out the this proposal, even if it were a good idea and the best way to handle this. Any suggestions?? Thanks in advance!!!
edit: helpful answer, #nicolae.
however, it still seems that i could write a function that returns all users that any given admin(of any given aco) is permitted to edit. is seems this way to me because i'm assuming the admin, him/herself to be a part of the returned user base. for example, dist admin of dist 9, resides in dist 9; likewise, dept admin of dept 1, resides in dept 1.
could i write something like:
public getUserScope(){
$id = $this->Auth->user('id');
$dept = $this...
$dist = $this...
$access_level = $this->Session->read('Auth.User.group_id');
if($access_level == 3){
//get all users in this user's dept
} elseif($access_level == 2) {
//find all users in this user's dist
} elseif($access_level == 1) {
//find all users
}
}
ouch. granted all of the join tables involved in bringing this info together, this is bound to look like medusas hair when actually typed up. can you see my line of logic. this is a bit difficult for me to articulate. let me know if i should clarify anything further.
So you're trying to set up an ACL system that manages restrictions based on more than group ID and controller/action - you need department- and district-based restrictions.
As far as I know, the embedded ACL system in CakePHP can not manage this level of restrictions.
As a solution, I recommend you allow the users to the places they need to access (e.g. dept admins have ACL access to the user CRUD interface) and insert specific restrictions in the controller.
function editUserFromDepartment($userId = NULL) {
// Custom ACL check
if (! $this->__checkDepartmentByUserId($userId)) {
// Show the user a custom error message and redirect him to index page
}
// User editing code ...
}
function __checkDepartmentByUserId($userId) {
$dept = $this->Department->getByUserId($userId);
// Check if the current user belongs to this department
$currentUserDepartmentId = $this->Session->read('Auth.User.department_id');
return ($dept['Department']['id'] != $currentUserDepartmentId);
}
Good luck and let me know if my explanation was clear enough.
Nicolae
Edit (trying to answer to #Todd's edit):
I get your point.
Your code aims at obtaining all users of that the current user has access of.
Rather than that, I would have an event-based approach: when the user X tries to be edited, perform a query that finds if Auth.User is allowed to perform this action on user X.
E.g. You (Todd) are trying to edit my profile (NicolaeS) to change my employee ID, as my current one is wrong.
The function $this->getUserScope('user-ID-of-NicolaeS') will check if your department ID or district ID is the same as main and allow/disallow you to continue in your actions according to this check.
Does this look clear/straightforward enough?
Greets,
Nicolae

CakePHP Access Allocation on Role Based specific Data Access

My project requirement is something like this:
On Top, there will be Administrator, who will have all d access, first level
Under Administrator, there will be Department Heads, who will have all d access, apart from Creating Department Heads
Under Department Head, there will Other Members, who will be managing their allocated department wise data.
Now, all different department heads will have their own information and members, and all department heads / Members will have access to their own specific records, which they are entering / managing.
Now, with CakePHP's ACL Component, I can divide the roles and their access level, but all department heads can see the other department head's information, as they will have same level of access, and All Other Members can see the other members information on diff departments, as of they will have same level of access.
My project complexity is that - they should be visible only their assigned or created information / data, though they have same level / role assignments as of others.
Can anyone suggest me best suitable option, to manage all these things with already available plug-ins with CakePHP.
I can work by customizing the default ACL Component, but that will take some more amount of time, than what is expected.
Any better ideas / suggestions would be appreciated !
the way i see it, ACL is not that magical. For exemple: ACL could manage the permissions to tell who has access to add/edit/remove a product.. but it wont be able to change a query to filter the products accordingly to the defined permissions (like "users from department A can only see products from department A").. well actually that's a lie, ACL could manage that but it might not be practical, because every time you add a product you'd have to create an ACO, and set the permission in the AROS_ACOS table and since the AROS is a tree structure, so it could easily become a nigthmare, if your planning to query your data
I'd use a group-only ACL to control the access to certain pages/actions and make rules like:
"Department Head can access the page
'products list' and add/delete/modify
products"
"Administrators can access
all pages"
"Other users can access
'products list' and they can add
products but not delete them"
and i'd adjust my queries accordingly to the connected user, so in the controller of 'products list' page, i'd do something like:
If connected user blongs to Department Head then select all products where product.department_id=connected_user.department_id
If connected user is Admin then select all products
if you have too much queries and you dont want to do thousands of if's sentences, you could create a component, a behavior or maybe extend the find() method in the app_model. The idea is to catch all queries and check if one of the models used on the query have field called "department_id", if they do then add the model.department_id=connected_user.department_id condition to the query.
I did that for one website that can be seen in multiple languages and each language has it's own users, data, logs, etc., and there's one Admin that can see all the info.. and it's working great for me =)
Good Luck!
EDITED:
the behavior i use is:
<?php
class LocalizableBehavior extends ModelBehavior {
/**
* Filter query conditions with the correct `type' field condition.
*/
function beforeFind(&$model, $query)
{
/**
* Condition for the paginators that uses joins
*/
if(isset($query['joins']) && !empty($query['joins'])){
foreach($query['joins'] as $key => $joinTable){
if(ClassRegistry::init($joinTable['alias'])->hasField('lang')){
$query['joins'][$key]['conditions'][] = $joinTable['alias'].".lang = '".$_SESSION['lang']."'";
}
}
}
/**
* condition for the normal find queries
*/
if($model->hasField('lang') && $model->name != "User"){
$query['conditions'][$model->name.'.lang'] = $_SESSION['lang'];
}
return $query;
}
}
?>
it's quite simple really, i change the query to add a condition to match to the current language ($_SESSION['lang']). In the controller all i need to do is to attach the LocalizableBehavior and use find method as usual:
$this->Products->find('all');

Resources