Skip to content
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

feat(iam): support iam conditions #2416

Merged
merged 38 commits into from
Feb 5, 2020
Merged
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
492594d
support IAM condition
jkwlui Sep 25, 2019
f4c139f
update Storage JSON definition, rev,. 20190913
jkwlui Sep 25, 2019
c4e43e3
throw InvalidOperationException if version is greater than 1
jkwlui Sep 25, 2019
27347f1
nit: doc
jkwlui Sep 26, 2019
5336355
do not cache Iam instance on bucket; make $version arg a key in $opti…
jkwlui Oct 7, 2019
793739d
Merge branch 'iam-condition' into iam-condition-2
jkwlui Oct 15, 2019
cc4e81a
revert version argument from IAM constructor
jkwlui Oct 15, 2019
ee27c52
docs: document optionsRequestedPolicyVersion
jkwlui Oct 15, 2019
399313f
revert stored $iam instance on bucket
jkwlui Oct 15, 2019
150467e
docs: add example of a policy in PolicyBuilder
jkwlui Oct 15, 2019
93dad3c
more docs
jkwlui Oct 15, 2019
b1216ca
fix link
jkwlui Oct 15, 2019
8b8d0d4
map requestedPolicyVersion arg to optionsRequestedPolicyVersion in St…
jkwlui Oct 22, 2019
a979b0e
fix(docs): optionsRequestedPolicyVersion => requestedPolicyVersion
jkwlui Oct 22, 2019
d0cb628
fix
jkwlui Dec 23, 2019
8826b01
test: validate policy version and conditions
jkwlui Dec 23, 2019
84b664f
test: assert requestedPolicyVersion arg is mapped to optionsRequested…
jkwlui Dec 23, 2019
4b3c17e
merge Storage definition from master
jkwlui Dec 26, 2019
18c9ff8
Merge branch 'master' into iam-condition-2
jkwlui Dec 26, 2019
34da3e6
fix: lint
jkwlui Dec 26, 2019
2e4a4f0
lint
jkwlui Dec 26, 2019
4929052
fix
jkwlui Dec 26, 2019
8721058
docs: update inline sample to use prefix condition
jkwlui Jan 22, 2020
9091e89
add IAM get/set system tests
jkwlui Jan 31, 2020
2d928d3
add conditional policy system test
jkwlui Jan 31, 2020
bd93d60
fix docs
jkwlui Jan 31, 2020
19c59eb
Merge branch 'iam-condition-2' of github.com:jkwlui/google-cloud-php …
jkwlui Jan 31, 2020
1d3ae27
fix @see markdown links
jkwlui Jan 31, 2020
672d5dc
fix ;
jkwlui Jan 31, 2020
51f8a6f
add @deprecated tag
jkwlui Jan 31, 2020
2a0f181
use BadMethodCallException
jkwlui Jan 31, 2020
c9bf231
fix style
jkwlui Jan 31, 2020
357aa5e
fix style
jkwlui Jan 31, 2020
da89f15
add snippet coverage
jkwlui Jan 31, 2020
85078ef
update bucket->iam snippet tests
jkwlui Feb 1, 2020
28c3da7
fix snippet parsing issue
jkwlui Feb 4, 2020
59445e6
Update Storage/tests/System/IamTest.php
jkwlui Feb 5, 2020
e38265b
Update Core/src/Iam/PolicyBuilder.php
jkwlui Feb 5, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .rnd
Binary file not shown.
4 changes: 4 additions & 0 deletions Core/src/Iam/Iam.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ public function __construct(IamConnectionInterface $connection, $resource, array
* ```
*
* @param array $options Configuration Options
* @param int $options['requestedPolicyVersion'] Specify the policy version to
* request from the server. Please see
* [policy versioning](https://cloud.google.com/iam/docs/policies#versions)
* for more information.
* @return array An array of policy data
*/
public function policy(array $options = [])
Expand Down
88 changes: 83 additions & 5 deletions Core/src/Iam/PolicyBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
namespace Google\Cloud\Core\Iam;

use InvalidArgumentException;
use BadMethodCallException;

/**
* Helper class for creating valid IAM policies
Expand Down Expand Up @@ -51,6 +52,38 @@ class PolicyBuilder
/**
* Create a PolicyBuilder.
*
* To use conditions in the bindings, the version of the policy must be set
* to 3.
*
* @see https://cloud.google.com/iam/docs/policies#versions Policy versioning
* @see https://cloud-dot-devsite.googleplex.com/storage/docs/access-control/using-iam-permissions#conditions-iam
* Using Cloud IAM Conditions on buckets
*
* Example:
* ```
* $policy = [
* 'etag' => 'AgIc==',
* 'version' => 3,
* 'bindings' => [
* [
* 'role' => 'roles/admin',
* 'members' => [
* 'user:[email protected]',
* 'user2:[email protected]'
* ],
* 'condition' => [
* 'title' => 'match-prefix',
* 'description' => 'Applies to objects matching a prefix',
* 'expression' =>
* 'resource.name.startsWith("projects/_/buckets/bucket-name/objects/prefix-a-")'
* ]
* ]
* ],
* ];
*
* $builder = new PolicyBuilder($policy);
* ```
*
* @param array $policy A policy array
* @throws InvalidArgumentException
*/
Expand Down Expand Up @@ -81,6 +114,10 @@ public function __construct(array $policy = [])
* 'role' => 'roles/admin',
* 'members' => [
* 'user:[email protected]'
* ],
* 'condition' => [
* 'expression' =>
* 'request.time < timestamp("2020-07-01T00:00:00.000Z")'
* ]
* ]
* ]);
Expand All @@ -92,17 +129,20 @@ public function __construct(array $policy = [])
*/
public function setBindings(array $bindings = [])
{
$this->bindings = [];
foreach ($bindings as $binding) {
$this->addBinding($binding['role'], $binding['members']);
}

$this->bindings = $bindings;
return $this;
}

/**
* Add a new binding to the policy.
*
* This method will fail with an InvalidOpereationException if it is
* called on a Policy with a version greater than 1 as that indicates
* a more complicated policy than this method is prepared to handle.
* Changes to such policies must be made manually by the setBindings()
* method.
*
*
* Example:
* ```
* $builder->addBinding('roles/admin', [ 'user:[email protected]' ]);
Expand All @@ -112,9 +152,13 @@ public function setBindings(array $bindings = [])
* @param array $members An array of members to assign to the binding
* @return PolicyBuilder
* @throws InvalidArgumentException
* @throws BadMethodCallException if the policy's version is greater than 1.
* @deprecated
*/
public function addBinding($role, array $members)
{
$this->validatePolicyVersion();

$this->bindings[] = [
'role' => $role,
'members' => $members
Expand All @@ -126,6 +170,12 @@ public function addBinding($role, array $members)
/**
* Remove a binding from the policy.
*
* This method will fail with an InvalidOpereationException if it is
jkwlui marked this conversation as resolved.
Show resolved Hide resolved
* called on a Policy with a version greater than 1 as that indicates
* a more complicated policy than this method is prepared to handle.
* Changes to such policies must be made manually by the setBindings()
* method.
*
* Example:
* ```
* $builder->setBindings([
Expand All @@ -144,9 +194,13 @@ public function addBinding($role, array $members)
* @param array $members An array of members to remove from the role
* @return PolicyBuilder
* @throws InvalidArgumentException
* @throws BadMethodCallException if the policy's version is greater than 1.
* @deprecated
*/
public function removeBinding($role, array $members)
{
$this->validatePolicyVersion();

$bindings = $this->bindings;
foreach ((array) $bindings as $i => $binding) {
if ($binding['role'] == $role) {
Expand Down Expand Up @@ -226,4 +280,28 @@ public function result()
'version' => $this->version
]);
}

private function validatePolicyVersion()
{
if (isset($this->version) && $this->version > 1) {
throw new BadMethodCallException("Helper methods cannot be " .
"invoked on policies with version {$this->version}.");
}

$this->validateConditions();
}

private function validateConditions()
{
if (!$this->bindings) {
return;
}

foreach ($this->bindings as $binding) {
if (isset($binding['condition'])) {
throw new BadMethodCallException("Helper methods cannot " .
"be invoked on policies containing conditions.");
}
}
}
}
88 changes: 86 additions & 2 deletions Core/tests/Unit/Iam/PolicyBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public function testBuilder()

$builder = new PolicyBuilder;
$builder->setEtag($etag);
$builder->setVersion(2);
$builder->setVersion(1);
$builder->addBinding($role, $members);

$result = $builder->result();
Expand All @@ -55,7 +55,7 @@ public function testBuilder()
]
],
'etag' => $etag,
'version' => 2
'version' => 1
];

$this->assertEquals($policy, $result);
Expand Down Expand Up @@ -139,6 +139,43 @@ public function testConstructWithExistingPolicy()
$this->assertEquals($policy, $result);
}

/**
* @expectedException BadMethodCallException
* @expectedExceptionMessage Helper methods cannot be invoked on policies with version 3.
*/
public function testAddBindingVersionThrowsException()
{
$builder = new PolicyBuilder();
$builder->setVersion(3);

$builder->addBinding('test', ['user:[email protected]']);
}

/**
* @expectedException BadMethodCallException
* @expectedExceptionMessage Helper methods cannot be invoked on policies containing conditions.
*/
public function testAddBindingWithConditionsThrowsException()
{
$policy = [
'bindings' => [
[
'role' => 'test',
'members' => [
'user:[email protected]',
],
'condition' => [
'expression' => 'true',
]
],
],
];
$builder = new PolicyBuilder($policy);
$builder->setVersion(1);

$builder->addBinding('test2', ['user:[email protected]']);
}

public function testRemoveBinding()
{
$policy = [
Expand Down Expand Up @@ -225,4 +262,51 @@ public function testRemoveBindingInvalidRoleThrowsException()
$builder = new PolicyBuilder($policy);
$builder->removeBinding('test2', ['user:[email protected]']);
}

/**
* @expectedException BadMethodCallException
* @expectedExceptionMessage Helper methods cannot be invoked on policies with version 3.
*/
public function testRemoveBindingVersionThrowsException()
{
$policy = [
'version' => 3,
'bindings' => [
[
'role' => 'test',
'members' => [
'user:[email protected]',
]
],
]
];

$builder = new PolicyBuilder($policy);
$builder->removeBinding('test', ['user:[email protected]']);
}

/**
* @expectedException BadMethodCallException
* @expectedExceptionMessage Helper methods cannot be invoked on policies containing conditions.
*/
public function testRemoveBindingWithConditionsThrowsException()
{
$policy = [
'bindings' => [
[
'role' => 'test',
'members' => [
'user:[email protected]',
],
'condition' => [
'expression' => 'true',
]
],
],
];

$builder = new PolicyBuilder($policy);
$builder->setVersion(1);
$builder->removeBinding('test', ['user:[email protected]']);
}
}
11 changes: 10 additions & 1 deletion Storage/src/Bucket.php
Original file line number Diff line number Diff line change
Expand Up @@ -1165,18 +1165,27 @@ public function isWritable($file = null)
/**
* Manage the IAM policy for the current Bucket.
*
* Please note that this method may not yet be available in your project.
* To request a policy with conditions, pass an array with
* '[requestedPolicyVersion => 3]' as argument to the policy() and
* reload() methods.
*
* Example:
* ```
* $iam = $bucket->iam();
*
* // Returns the stored policy, or fetches the policy if none exists.
* $policy = $iam->policy(['requestedPolicyVersion' => 3]);
*
* // Fetches a policy from the server.
* $policy = $iam->reload(['requestedPolicyVersion' => 3]);
* ```
*
* @codingStandardsIgnoreStart
* @see https://cloud.google.com/storage/docs/access-control/iam-with-json-and-xml Storage Access Control Documentation
* @see https://cloud.google.com/storage/docs/json_api/v1/buckets/getIamPolicy Get Bucket IAM Policy
* @see https://cloud.google.com/storage/docs/json_api/v1/buckets/setIamPolicy Set Bucket IAM Policy
* @see https://cloud.google.com/storage/docs/json_api/v1/buckets/testIamPermissions Test Bucket Permissions
* @see https://cloud.google.com/iam/docs/policies#versions policy versioning.
* @codingStandardsIgnoreEnd
*
* @return Iam
Expand Down
5 changes: 5 additions & 0 deletions Storage/src/Connection/IamBucket.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ public function __construct(ConnectionInterface $connection)
*/
public function getPolicy(array $args)
{
if (isset($args['requestedPolicyVersion'])) {
$args['optionsRequestedPolicyVersion'] = $args['requestedPolicyVersion'];
unset($args['requestedPolicyVersion']);
}

return $this->connection->getBucketIamPolicy($args);
}

Expand Down
3 changes: 3 additions & 0 deletions Storage/tests/Snippet/BucketTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,10 @@ public function testIam()
{
$snippet = $this->snippetFromMethod(Bucket::class, 'iam');
$snippet->addLocal('bucket', $this->bucket);
$this->connection->getBucketIamPolicy(Argument::withEntry('optionsRequestedPolicyVersion', 3))
->shouldBeCalled();

$this->bucket->___setProperty('connection', $this->connection->reveal());
$res = $snippet->invoke('iam');
$this->assertInstanceOf(Iam::class, $res->returnVal());
}
Expand Down
Loading