-
Notifications
You must be signed in to change notification settings - Fork 4k
force input parameter in NewAzureRoleAssignmentCommand to follow vali… #4019
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@wisexp, |
Can one of the admins verify this patch? |
namespace Microsoft.WindowsAzure.Commands.Common | ||
{ | ||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] | ||
public class ValidateScope : ValidateArgumentsAttribute |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
test case to be added. #Resolved
if (arguments != null) | ||
{ | ||
string scope = (string)arguments; | ||
var pairs = scope.Split('/'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should be a more robust check for this, but I would think you would want to match the scope to an existing scope (like doing a Head or Get) #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if I got your comment, can you explain a little bit more (after iteration#2)?
In reply to: 118140390 [](ancestors = 118140390)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sync'ed with Surabhi, so far we only want to check scopes that start with subscriptions.
In reply to: 118322183 [](ancestors = 118322183,118140390)
{ | ||
protected override void Validate(object arguments, EngineIntrinsics engineIntrinsics) | ||
{ | ||
if (arguments != null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As you note, you must add sometest cases, both a scenario test case and some unit tests of this validator #Resolved
|
||
// Only append parentAndType and ResoruceName when provider is not empty. | ||
// Otherwise the url will be invalid. | ||
if (!string.IsNullOrEmpty(provider)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (!string.IsNullOrEmpty(provider)) [](start = 9, length = 39)
shouldn't we be doing not null or empty check for everything? #ByDesign
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes we did. one problem that this change is going to address is that when provider is null but resourceName is not null, we still append the ResourceName (without "/providers/"). so for parentAndType and ResourceName, we only append them when they are not null, and when provider is not null.
In reply to: 118149831 [](ancestors = 118149831)
{ | ||
protected override void Validate(object arguments, EngineIntrinsics engineIntrinsics) | ||
{ | ||
if (arguments != null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (arguments != null) [](start = 10, length = 24)
we can probably check that the scope contains even no. of parts #ByDesign
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to do so in first iteration but later i realized that for "providers" section, we could have more than 1 corresponding value.
also that won't help for this case: we have two name parts, but neither has a value, this will cause the number of parts being even but still invalid. like: "/subscriptions/providers/"
so in iteration#2 i made more strict check.
In reply to: 118150608 [](ancestors = 118150608)
this part is really confusing. when i looked at the constructor i understood this, but i am not sure if end users will understand all the corresponding parameters in the cmdlet. Refers to: src/ResourceManager/Resources/Commands.Resources/Models.ResourceGroups/ResourceIdentifier.cs:136 in 413ca5c. [](commit_id = 413ca5cc1a835fd5115f8b6a62bdc278d757e7d9, deletion_comment = False) |
{ | ||
if (string.Compare(parts[0], "subscriptions", true) != 0) | ||
{ | ||
throw new ValidationMetadataException("Scope should begin with \"/subscriptions/\"."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Scope should begin with "/subscriptions/ [](start = 63, length = 41)
please let me know if this is not always true (i.e. is subscriptions optional?) #Resolved
this is the only document about ParentResource i can find, as an end user i definitely don't understand how to build this parameter:( will follow up with Rashid. -ParentResourceThe parent resource in the hierarchy(of the resource specified using ResourceName parameter). In reply to: 303602729 [](ancestors = 303602729) Refers to: src/ResourceManager/Resources/Commands.Resources/Models.ResourceGroups/ResourceIdentifier.cs:136 in 413ca5c. [](commit_id = 413ca5cc1a835fd5115f8b6a62bdc278d757e7d9, deletion_comment = False) |
|
||
[Fact] | ||
[Trait(Category.AcceptanceType, Category.CheckIn)] | ||
public void RaValidateInputParameters() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is necessary, but not sufficient. Please include some unit tests of just the validation method. This will allow you to easily cover all that code without requiring a powershell invocation. Make sure to cover each of the negative and positive outcomes [/, tenant resources, subscription, resource group, top-level resource, child resource, grandchild resource] #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unit test added, but basically it is doing same thing as the powershell scripts:)
In reply to: 118623381 [](ancestors = 118623381)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@markcowl #Resolved
@azuresdkci add to whitelist |
resourceId.AppendFormat("/{0}", parentAndType); | ||
AppendIfNotNull(ref resourceId, "/{0}", ResourceName); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this change might end up silently absorbing invalid inputs. i liked the option of not changing anything here and validating the final scope better #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, but doing the validation later might not be able to give user the correct error message. e.g. for this issue, we tell user that the composed scope doesn't have even number of parts, but actually the root cause is user didn't have a type in the ResourceType.
The ultimate goal should be validation on each input parameter (like this change we do on Scope), like i mentioned in other comment, it should be done in another work item.
In reply to: 118643432 [](ancestors = 118643432)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (!scope.StartsWith("/subscriptions", StringComparison.InvariantCultureIgnoreCase)) | ||
{ | ||
return; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should validate that scope either starts with subscriptions or providers. and for the providers scenario, as we discussed we should check that the no. of parts is even. #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we do the scope validation in this method, after getting scope from resourceidentifier parameter? #Resolved Refers to: src/ResourceManager/Resources/Commands.Resources/RoleAssignments/NewAzureRoleAssignmentCommand.cs:170 in 2b3879b. [](commit_id = 2b3879b7d968f406f62fd460994788fc93f4e871, deletion_comment = False) |
I can apply the Scope validation logic on the scope that we get from resource identifier parameter, but the error messages should be totally different, also the logic are slightly different, e.g. we’ll ensure that the number of parts in scope from the resource identifier will be always even. The other choice will be validate each input parameter, at some extent I think we should do this check for all the cmdlets, but I don’t think it is worth to do so for this work item. In reply to: 304206812 [](ancestors = 304206812) Refers to: src/ResourceManager/Resources/Commands.Resources/RoleAssignments/NewAzureRoleAssignmentCommand.cs:170 in 2b3879b. [](commit_id = 2b3879b7d968f406f62fd460994788fc93f4e871, deletion_comment = False) |
|
||
if (parts.Contains(string.Empty)) | ||
{ | ||
throw new ValidationMetadataException("Name or value in Scope must be not empty."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Name or value in Scope must be not empty. [](start = 59, length = 41)
scope should not have empty parts #Resolved
|
||
if (count >= 4 && string.Compare(parts[2], "resourceGroups", true) != 0) | ||
{ | ||
throw new ValidationMetadataException("Scope should begin with \"/subscriptions/<subscription id>/resourceGroups/\"."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
throw new ValidationMetadataException("Scope should begin with "/subscriptions//resourceGroups/"."); [](start = 20, length = 119)
Requested '/subscriptions/41c396c8-531c-4bbb-b5f1-60c16aa5606a/providers/Microsoft.Authorization/roleAssignments/bdf56bf8-1005-49d7-9b11-1bba23c3e033' resource, Success
this fails for this call
this is the kusto query
PerRequestTable
|where Call contains "RoleAssignments:PutRoleAssignment"
|where CorrelationId == "80045c63-d4c4-48bb-b525-7c2875bca9c7"
#ByDesign
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
need to figure out the call for
New-AzureRmRoleAssignment -ObjectId $id -RoleDefinitionId "8e3af657-a8ff-443c-a75c-2fe8c4bcb635" -Scope "/subscriptions/4004a9fd-d58e-48dc-aeb2-4a4aec58606f/providers/Microsoft.Authorization"
In reply to: 118789162 [](ancestors = 118789162)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
assignment is created at subscription scope, 'providers/microsoft.authorization' is not a part of the input scope.
In reply to: 118796516 [](ancestors = 118796516,118789162)
@@ -0,0 +1,74 @@ | |||
using System; | |||
using System.Linq; | |||
using System.Management.Automation; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this file will be removed. #Resolved
@wisexp Please clean up your cmdlets. There should not be a successive set of commits with the commit descripotion 'Update'. Please refactor these changes around a logical set of commits with short descriptive comments #Resolved |
@@ -498,5 +500,65 @@ private static void ValidateRoleDefinition(PSRoleDefinition roleDefinition) | |||
throw new ArgumentException(ProjectResources.InvalidActions); | |||
} | |||
} | |||
public static void ValidateScope(string scope) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ValidateScope [](start = 27, length = 13)
one problem here is that if service changes, those changes won't reflect here. Ideally all such error messages maybe solved at service side. #WontFix
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes it will be better if service side can return the correct error message. however this is not going to happen in a short time.
here we want do some very basic validations which is not going to be changed by service side for a while so end user will not get confused (for most of below cases, ARM the service side only returns an error message like "unsupported API version".
we are involving ARM folks to review this as well.
In reply to: 119177756 [](ancestors = 119177756)
i've squashed them into one commit, and forcedly pushed to remote, the code flow is still showing 7 commits but web page is showing 1: https://github.com/Azure/azure-powershell/pull/4019/commits. it looks good now:) In reply to: 304962442 [](ancestors = 304962442) |
|
||
var parts = scope.Substring(1).Split('/'); // Skip the leading '/' | ||
|
||
if (parts.Contains(string.Empty)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
string.Empty [](start = 31, length = 12)
Aren't the double slashes "//" ignored by the service. I thought that should be the case (It'll be if the scope is derived from scope.) #ByDesign
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ARM does ignore the extra slash, but it is not clear to end user, and i don't think there is no benefit for end user to type double slash in the scope.
In reply to: 119178261 [](ancestors = 119178261)
public static void ValidateScope(string scope) | ||
{ | ||
|
||
if (scope.Length == 0 || !scope.StartsWith("/")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
scope [](start = 16, length = 5)
Can the scope be null ? #WontFix
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
} | ||
else if (string.Compare(parts[0], "subscriptions", true) != 0) | ||
{ | ||
throw new ArgumentException(string.Format(ProjectResources.ScopeShouldBeginWithSubscriptionsOrPrivoders, scope)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ivo [](start = 112, length = 3)
ivo [](start = 112, length = 3)
typo #Resolved
<value>Scope '{0}' must begin with '/'.</value> | ||
</data> | ||
<data name="ScopeShouldBeginWithSubscriptionsAndResourceGroups" xml:space="preserve"> | ||
<value>Scope '{0}' should begin with '/subscriptions/<subid>/resourceGroups'.</value> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'/subscriptions/<subid>/resourceGroups [](start = 41, length = 44)
'/subscriptions/<subid>/resourceGroups [](start = 41, length = 44)
isnt the error message misleading where in at the first error we say
scope should be like "/subscriptions/subid/resourcegroups"
and when they correct that to the above we say
scope should be like "/subscriptions/subid/resourcegroups/rgname/providers"
and so on #ByDesign
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO, it is better to point out the error step by step, user can easily learn which part is wrong.
In reply to: 119960272 [](ancestors = 119960272)
@@ -157,9 +157,11 @@ public PSRoleAssignment CreateRoleAssignment(FilterRoleAssignmentsOptions parame | |||
{ | |||
Guid principalId = ActiveDirectoryClient.GetObjectId(parameters.ADObjectFilter); | |||
Guid roleAssignmentId = RoleAssignmentNames.Count == 0 ? Guid.NewGuid() : RoleAssignmentNames.Dequeue(); | |||
string scope = parameters.Scope; | |||
ValidateScope(scope); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ValidateScope(scope); [](start = 11, length = 22)
just a afterthought,we can use the same function for other operations also correct to validate the scope(maybe a change as part of a different CR)?
As i see other operations like get and remove also have a set of parameters which takes in scope #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
|
|
||
namespace Commands.Resources.UnitTests | ||
{ | ||
[TestClass] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MSTest tests are not allowed. Please make these xunit tests, and mark them as check-in tests
#Resolved
public void VerifyInvalidScopes() | ||
{ | ||
Dictionary<string, string> scopeAndErrors = new Dictionary<string, string>(); | ||
scopeAndErrors.Add("/subscriptions/e9ee799d-6ab2-4084-b952-e7c86344bbab/Should be 'ResourceGroups'/any group name", "Scope '/subscriptions/e9ee799d-6ab2-4084-b952-e7c86344bbab/Should be 'ResourceGroups'/any group name' should begin with '/subscriptions/<subid>/resourceGroups'."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. The entire idea is to cover a much larger range of inputs and outputs, which is much easier in a unit test.
Please cover positive and negative cases, null and empty values, and other important input equivalence classes. #Resolved
3f1f9c6
to
14b70b0
Compare
…d pattern update update update update update update
} | ||
|
||
// "/" is a valid scope. | ||
if (string.Compare(scope, "/") == 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is "/" really a valid scope .. for tenant admins I suppose ? Would a /providers/Microsoft.Authorization/roleAssignments create an assignment ? #ByDesign
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes "/" is a valid scope. everything starts from "providers/Microsoft.Authorization" is not part of the scope.
In reply to: 120757288 [](ancestors = 120757288)
|
||
// Now check the case that scope begins with '/subscriptions' | ||
|
||
if (count >= 4 && string.Compare(parts[2], "resourceGroups", true) != 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All these rules make sense to me. Is there a way to make sure that are there any current role assignments which are at scope which don't adhere to these rules.
At the end of the day you guys are the master of role assignments and the best judge for what the valid scope is and what is not.
One way would be to add the same validation in the RP as well for the scope and make sure that both the validations in service and at client side matches. #ByDesign
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You may also do a dry run with these validations at the service side #ByDesign
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
on RP side we do have similar check. i've checked all the role assignments (~1.6M) in microsoft tenant that all scopes in these assignments passed this check.
In reply to: 120758308 [](ancestors = 120758308)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -498,5 +500,66 @@ private static void ValidateRoleDefinition(PSRoleDefinition roleDefinition) | |||
throw new ArgumentException(ProjectResources.InvalidActions); | |||
} | |||
} | |||
|
|||
public static void ValidateScope(string scope) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I am assuming that this is just the scope and you'll append /providers/Microsoft.Authorization/roleAssignments after this #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@markcowl any additional comments? |
…d pattern
Description
This checklist is used to make sure that common guidelines for a pull request are followed. You can find a more complete discussion of PowerShell cmdlet best practices here.
General Guidelines
Testing Guidelines
Cmdlet Signature Guidelines
ShouldProcess
and haveSupportShouldProcess=true
specified in the cmdlet attribute. You can find more information onShouldProcess
here.OutputType
attribute if any output is produced - if the cmdlet produces no output, it should implement aPassThru
parameter.Cmdlet Parameter Guidelines