Skip to content

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

Merged
merged 1 commit into from
Jun 13, 2017

Conversation

wisexp
Copy link
Contributor

@wisexp wisexp commented May 23, 2017

…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

  • Title of the pull request is clear and informative.
  • There are a small number of commits, each of which have an informative message. This means that previously merged commits do not appear in the history of the PR. For more information on cleaning up the commits in your PR, see this page.
  • The pull request does not introduce breaking changes (unless a major version change occurs in the assembly and module).

Testing Guidelines

  • Pull request includes test coverage for the included changes.
  • PowerShell scripts used in tests should do any necessary setup as part of the test or suite setup, and should not use hard-coded values for locations or existing resources.

Cmdlet Signature Guidelines

  • New cmdlets that make changes or have side effects should implement ShouldProcess and have SupportShouldProcess=true specified in the cmdlet attribute. You can find more information on ShouldProcess here.
  • Cmdlet specifies OutputType attribute if any output is produced - if the cmdlet produces no output, it should implement a PassThru parameter.

Cmdlet Parameter Guidelines

  • Parameter types should not expose types from the management library - complex parameter types should be defined in the module.
  • Complex parameter types are discouraged - a parameter type should be simple types as often as possible. If complex types are used, they should be shallow and easily creatable from a constructor or another cmdlet.
  • Cmdlet parameter sets should be mutually exclusive - each parameter set must have at least one mandatory parameter not in other parameter sets.

@msftclas
Copy link

@wisexp,
Thanks for your contribution as a Microsoft full-time employee or intern. You do not need to sign a CLA.
Thanks,
Microsoft Pull Request Bot

@azuresdkci
Copy link

Can one of the admins verify this patch?

namespace Microsoft.WindowsAzure.Commands.Common
{
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class ValidateScope : ValidateArgumentsAttribute
Copy link
Contributor Author

@wisexp wisexp May 24, 2017

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('/');
Copy link
Member

@markcowl markcowl May 24, 2017

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

Copy link
Contributor Author

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)

Copy link
Contributor Author

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)
Copy link
Member

@markcowl markcowl May 24, 2017

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))
Copy link
Contributor

@surabhi-pandey surabhi-pandey May 24, 2017

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

Copy link
Contributor Author

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)
Copy link
Contributor

@surabhi-pandey surabhi-pandey May 24, 2017

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

Copy link
Contributor Author

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)

@surabhi-pandey
Copy link
Contributor

surabhi-pandey commented May 24, 2017

        string parentAndType = string.IsNullOrEmpty(ParentResource) ? type : ParentResource + "/" + type;

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.
Can you check the documentation to see if these are explained clearly?
We can follow up with our PM Rashid as well if some of these should be simplified #WontFix


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/\".");
Copy link
Contributor Author

@wisexp wisexp May 24, 2017

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

@wisexp
Copy link
Contributor Author

wisexp commented May 24, 2017

        string parentAndType = string.IsNullOrEmpty(ParentResource) ? type : ParentResource + "/" + type;

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.

-ParentResource

The parent resource in the hierarchy(of the resource specified using ResourceName parameter).
Should only be used in conjunction with ResourceGroupName, ResourceType and ResourceName parameters to construct a hierarchical scope in the form of a relative URI that identifies a resource.


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()
Copy link
Member

@markcowl markcowl May 26, 2017

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

Copy link
Contributor Author

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)

Copy link
Contributor Author

@wisexp wisexp May 31, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@markcowl #Resolved

@markcowl
Copy link
Member

@azuresdkci add to whitelist

resourceId.AppendFormat("/{0}", parentAndType);
AppendIfNotNull(ref resourceId, "/{0}", ResourceName);
}
}
Copy link
Contributor

@surabhi-pandey surabhi-pandey May 26, 2017

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

Copy link
Contributor Author

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)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reverted the change in this file.


In reply to: 118760265 [](ancestors = 118760265,118643432)

if (!scope.StartsWith("/subscriptions", StringComparison.InvariantCultureIgnoreCase))
{
return;
}
Copy link
Contributor

@surabhi-pandey surabhi-pandey May 26, 2017

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

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also no empty parts


In reply to: 118643589 [](ancestors = 118643589)

@surabhi-pandey
Copy link
Contributor

surabhi-pandey commented May 26, 2017

        WriteObject(PoliciesClient.CreateRoleAssignment(parameters));

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)

@wisexp
Copy link
Contributor Author

wisexp commented May 26, 2017

        WriteObject(PoliciesClient.CreateRoleAssignment(parameters));

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.");
Copy link
Contributor

@surabhi-pandey surabhi-pandey May 26, 2017

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/\".");
Copy link
Contributor

@darshanhs90 darshanhs90 May 26, 2017

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

Copy link
Contributor

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)

Copy link
Contributor Author

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;
Copy link
Contributor Author

@wisexp wisexp May 26, 2017

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

@markcowl
Copy link
Member

markcowl commented May 30, 2017

@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)
Copy link

@perseusCode perseusCode May 30, 2017

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

Copy link
Contributor Author

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)

@wisexp
Copy link
Contributor Author

wisexp commented May 30, 2017

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))
Copy link

@perseusCode perseusCode May 30, 2017

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

Copy link
Contributor Author

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("/"))
Copy link

@perseusCode perseusCode May 30, 2017

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no.


In reply to: 119192092 [](ancestors = 119192092)

@perseusCode
Copy link

:shipit:

}
else if (string.Compare(parts[0], "subscriptions", true) != 0)
{
throw new ArgumentException(string.Format(ProjectResources.ScopeShouldBeginWithSubscriptionsOrPrivoders, scope));
Copy link
Contributor

@darshanhs90 darshanhs90 Jun 2, 2017

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/&lt;subid&gt;/resourceGroups'.</value>
Copy link
Contributor

@darshanhs90 darshanhs90 Jun 2, 2017

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

Copy link
Contributor Author

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);
Copy link
Contributor

@darshanhs90 darshanhs90 Jun 2, 2017

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will consider this in a separate change.


In reply to: 119961507 [](ancestors = 119961507)

@surabhi-pandey
Copy link
Contributor

:shipit:

@darshanhs90
Copy link
Contributor

:shipit:


namespace Commands.Resources.UnitTests
{
[TestClass]
Copy link
Member

@markcowl markcowl Jun 4, 2017

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'.");
Copy link
Member

@markcowl markcowl Jun 4, 2017

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

@markcowl markcowl assigned wisexp and unassigned markcowl Jun 4, 2017
@wisexp wisexp force-pushed the preview branch 2 times, most recently from 3f1f9c6 to 14b70b0 Compare June 5, 2017 19:20
…d pattern

update

update

update

update

update

update
}

// "/" is a valid scope.
if (string.Compare(scope, "/") == 0)
Copy link

@perseusCode perseusCode Jun 7, 2017

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

Copy link
Contributor Author

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)
Copy link

@perseusCode perseusCode Jun 7, 2017

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

Copy link

@perseusCode perseusCode Jun 7, 2017

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

Copy link
Contributor Author

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)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we have similar check on RP side.


In reply to: 120759174 [](ancestors = 120759174)

@@ -498,5 +500,66 @@ private static void ValidateRoleDefinition(PSRoleDefinition roleDefinition)
throw new ArgumentException(ProjectResources.InvalidActions);
}
}

public static void ValidateScope(string scope)
Copy link

@perseusCode perseusCode Jun 7, 2017

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes.


In reply to: 120759096 [](ancestors = 120759096)

@cormacpayne
Copy link
Member

@markcowl any additional comments?

@markcowl markcowl merged commit 387f72f into Azure:preview Jun 13, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants