google-cloud-bigquery 1.24.0 → 1.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 32f4b672ccecbbd45bfb9790fc4110a22244a6459c459aa40bb1c1b4b77c1094
4
- data.tar.gz: 2ac69379fefad5547aac47be509a50945af647d7fe7b61e1653fc4dd9c9ca865
3
+ metadata.gz: ae4b6e4d7c37a945f027fe56425698b19b430f5e75451ce82f2156f2dae96719
4
+ data.tar.gz: d50284a47bf96d5221b574590a60b3af0f77b3a8943d201fd473d78f41a46670
5
5
  SHA512:
6
- metadata.gz: 9362e5d687de750ecdbdcbb131739116d3571c58db900fdf4748b6ce480f120670fce8901a8a9cfe83ddd53bbfd5e7866e396caff66e2151dc36a22dbabc3e7f
7
- data.tar.gz: f08197d575142c5c5c16ba4c09e9d96af5ccb5e92fd9b0c11b87fe8431a0cac72bc2462e57ec6080a8c2d8c45b2f92b25a908cdbab35c3116e33f41b46b82559
6
+ metadata.gz: 56c0b2d3214b385efb3866e502faa0b0c28c2fffc0c3d2ca3c5e1ad05f4d69e0638e68d59d8310c68d01c26c3db55b42225944379bfb8f6ad7a57eec558ced3a
7
+ data.tar.gz: 0562b98e65e880a40d634f541285241823354a966f4d05a559463a23eacca54addf8843f89b32731d313a56d91dfe2984f6098630c4126d7dbc97033d3dc6a81
@@ -1,5 +1,17 @@
1
1
  # Release History
2
2
 
3
+ ### 1.25.0 / 2020-11-16
4
+
5
+ #### Features
6
+
7
+ * Add routine (UDF) to Dataset::Access
8
+ * Add support for Table ACLS (IAM Policy)
9
+ * feat(bigquery): Add support for Table ACLS
10
+ * Add Bigquery::Policy
11
+ * Add Table#policy
12
+ * Add Table#test_iam_permissions
13
+ * Add Table#update_policy
14
+
3
15
  ### 1.24.0 / 2020-10-29
4
16
 
5
17
  #### Features
@@ -54,6 +54,7 @@ module Google
54
54
  "groupByEmail" => :group_by_email,
55
55
  "iam_member" => :iam_member,
56
56
  "iamMember" => :iam_member,
57
+ "routine" => :routine,
57
58
  "special" => :special_group,
58
59
  "special_group" => :special_group,
59
60
  "specialGroup" => :special_group,
@@ -212,6 +213,33 @@ module Google
212
213
  add_access_role_scope_value :reader, :special, group
213
214
  end
214
215
 
216
+ ##
217
+ # Add access to a routine from a different dataset. Queries executed
218
+ # against that routine will have read access to views/tables/routines
219
+ # in this dataset. Only UDF is supported for now. The role field is
220
+ # not required when this field is set. If that routine is updated by
221
+ # any user, access to the routine needs to be granted again via an
222
+ # update operation.
223
+ #
224
+ # @param [Google::Cloud::Bigquery::Routine] routine A routine object.
225
+ #
226
+ # @example
227
+ # require "google/cloud/bigquery"
228
+ #
229
+ # bigquery = Google::Cloud::Bigquery.new
230
+ # dataset = bigquery.dataset "my_dataset"
231
+ # other_dataset = bigquery.dataset "my_other_dataset", skip_lookup: true
232
+ #
233
+ # routine = other_dataset.routine "my_routine"
234
+ #
235
+ # dataset.access do |access|
236
+ # access.add_reader_routine routine
237
+ # end
238
+ #
239
+ def add_reader_routine routine
240
+ add_access_routine routine
241
+ end
242
+
215
243
  ##
216
244
  # Add reader access to a view.
217
245
  #
@@ -227,9 +255,9 @@ module Google
227
255
  #
228
256
  # bigquery = Google::Cloud::Bigquery.new
229
257
  # dataset = bigquery.dataset "my_dataset"
230
- # other_dataset = bigquery.dataset "my_other_dataset"
258
+ # other_dataset = bigquery.dataset "my_other_dataset", skip_lookup: true
231
259
  #
232
- # view = other_dataset.table "my_view"
260
+ # view = other_dataset.table "my_view", skip_lookup: true
233
261
  #
234
262
  # dataset.access do |access|
235
263
  # access.add_reader_view view
@@ -533,6 +561,28 @@ module Google
533
561
  remove_access_role_scope_value :reader, :special, group
534
562
  end
535
563
 
564
+ ##
565
+ # Remove reader access from a routine from a different dataset.
566
+ #
567
+ # @param [Google::Cloud::Bigquery::Routine] routine A routine object.
568
+ #
569
+ # @example
570
+ # require "google/cloud/bigquery"
571
+ #
572
+ # bigquery = Google::Cloud::Bigquery.new
573
+ # dataset = bigquery.dataset "my_dataset"
574
+ # other_dataset = bigquery.dataset "my_other_dataset", skip_lookup: true
575
+ #
576
+ # routine = other_dataset.routine "my_routine", skip_lookup: true
577
+ #
578
+ # dataset.access do |access|
579
+ # access.remove_reader_routine routine
580
+ # end
581
+ #
582
+ def remove_reader_routine routine
583
+ remove_access_routine routine
584
+ end
585
+
536
586
  ##
537
587
  # Remove reader access from a view.
538
588
  #
@@ -548,9 +598,9 @@ module Google
548
598
  #
549
599
  # bigquery = Google::Cloud::Bigquery.new
550
600
  # dataset = bigquery.dataset "my_dataset"
551
- # other_dataset = bigquery.dataset "my_other_dataset"
601
+ # other_dataset = bigquery.dataset "my_other_dataset", skip_lookup: true
552
602
  #
553
- # view = other_dataset.table "my_view"
603
+ # view = other_dataset.table "my_view", skip_lookup: true
554
604
  #
555
605
  # dataset.access do |access|
556
606
  # access.remove_reader_view view
@@ -849,6 +899,32 @@ module Google
849
899
  lookup_access_role_scope_value :reader, :special, group
850
900
  end
851
901
 
902
+ ##
903
+ # Checks access for a routine from a different dataset. Queries executed
904
+ # against that routine will have read access to views/tables/routines
905
+ # in this dataset. Only UDF is supported for now. The role field is
906
+ # not required when this field is set. If that routine is updated by
907
+ # any user, access to the routine needs to be granted again via an
908
+ # update operation.
909
+ #
910
+ # @param [Google::Cloud::Bigquery::Routine] routine A routine object.
911
+ #
912
+ # @example
913
+ # require "google/cloud/bigquery"
914
+ #
915
+ # bigquery = Google::Cloud::Bigquery.new
916
+ # dataset = bigquery.dataset "my_dataset"
917
+ # other_dataset = bigquery.dataset "my_other_dataset", skip_lookup: true
918
+ #
919
+ # routine = other_dataset.routine "my_routine", skip_lookup: true
920
+ #
921
+ # access = dataset.access
922
+ # access.reader_routine? routine #=> false
923
+ #
924
+ def reader_routine? routine
925
+ lookup_access_routine routine
926
+ end
927
+
852
928
  ##
853
929
  # Checks reader access for a view.
854
930
  #
@@ -864,9 +940,9 @@ module Google
864
940
  #
865
941
  # bigquery = Google::Cloud::Bigquery.new
866
942
  # dataset = bigquery.dataset "my_dataset"
867
- # other_dataset = bigquery.dataset "my_other_dataset"
943
+ # other_dataset = bigquery.dataset "my_other_dataset", skip_lookup: true
868
944
  #
869
- # view = other_dataset.table "my_view"
945
+ # view = other_dataset.table "my_view", skip_lookup: true
870
946
  #
871
947
  # access = dataset.access
872
948
  # access.reader_view? view #=> false
@@ -1121,12 +1197,22 @@ module Google
1121
1197
  @rules << Google::Apis::BigqueryV2::Dataset::Access.new(opts)
1122
1198
  end
1123
1199
 
1200
+ # @private
1201
+ def add_access_routine routine
1202
+ value = routine.routine_ref
1203
+ # Remove existing routine rule, if any
1204
+ @rules.reject!(&find_by_scope_and_resource_ref(:routine, value))
1205
+ # Add new rule for this role, scope, and value
1206
+ opts = { routine: value }
1207
+ @rules << Google::Apis::BigqueryV2::Dataset::Access.new(opts)
1208
+ end
1209
+
1124
1210
  # @private
1125
1211
  def add_access_view value
1126
1212
  # scope is view, make sure value is in the right format
1127
1213
  value = validate_view value
1128
1214
  # Remove existing view rule, if any
1129
- @rules.reject!(&find_view(value))
1215
+ @rules.reject!(&find_by_scope_and_resource_ref(:view, value))
1130
1216
  # Add new rule for this role, scope, and value
1131
1217
  opts = { view: value }
1132
1218
  @rules << Google::Apis::BigqueryV2::Dataset::Access.new(opts)
@@ -1144,12 +1230,18 @@ module Google
1144
1230
  )
1145
1231
  end
1146
1232
 
1233
+ # @private
1234
+ def remove_access_routine routine
1235
+ # Remove existing routine rule, if any
1236
+ @rules.reject!(&find_by_scope_and_resource_ref(:routine, routine.routine_ref))
1237
+ end
1238
+
1147
1239
  # @private
1148
1240
  def remove_access_view value
1149
1241
  # scope is view, make sure value is in the right format
1150
1242
  value = validate_view value
1151
1243
  # Remove existing view rule, if any
1152
- @rules.reject!(&find_view(value))
1244
+ @rules.reject!(&find_by_scope_and_resource_ref(:view, value))
1153
1245
  end
1154
1246
 
1155
1247
  # @private
@@ -1162,12 +1254,18 @@ module Google
1162
1254
  !(!@rules.detect(&find_by_role_and_scope_and_value(role, scope, value)))
1163
1255
  end
1164
1256
 
1257
+ # @private
1258
+ def lookup_access_routine routine
1259
+ # Detect routine rule, if any
1260
+ !(!@rules.detect(&find_by_scope_and_resource_ref(:routine, routine.routine_ref)))
1261
+ end
1262
+
1165
1263
  # @private
1166
1264
  def lookup_access_view value
1167
1265
  # scope is view, make sure value is in the right format
1168
1266
  value = validate_view value
1169
1267
  # Detect view rule, if any
1170
- !(!@rules.detect(&find_view(value)))
1268
+ !(!@rules.detect(&find_by_scope_and_resource_ref(:view, value)))
1171
1269
  end
1172
1270
 
1173
1271
  # @private
@@ -1186,11 +1284,11 @@ module Google
1186
1284
  end
1187
1285
  end
1188
1286
 
1189
- # @private
1190
- def find_view value
1287
+ # @private Compare hash representations to find table_ref, routine_ref.
1288
+ def find_by_scope_and_resource_ref scope, value
1191
1289
  lambda do |a|
1192
1290
  h = a.to_h
1193
- h[:view].to_h == value.to_h
1291
+ h[scope].to_h == value.to_h
1194
1292
  end
1195
1293
  end
1196
1294
  end
@@ -0,0 +1,431 @@
1
+ # Copyright 2020 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ require "google/apis/bigquery_v2"
17
+
18
+ module Google
19
+ module Cloud
20
+ module Bigquery
21
+ ##
22
+ # # Policy
23
+ #
24
+ # Represents a Cloud IAM Policy for BigQuery resources.
25
+ #
26
+ # A Policy is a collection of bindings. A {Policy::Binding} binds one or more members to a single role. Member
27
+ # strings can describe user accounts, service accounts, Google groups, and domains. A role string represents a
28
+ # named list of permissions; each role can be an IAM predefined role or a user-created custom role.
29
+ #
30
+ # @see https://cloud.google.com/iam/docs/managing-policies Managing Policies
31
+ # @see https://cloud.google.com/bigquery/docs/table-access-controls-intro Controlling access to tables
32
+ #
33
+ # @attr [String] etag Used to check if the policy has changed since the last request. When you make a request with
34
+ # an `etag` value, Cloud IAM compares the `etag` value in the request with the existing `etag` value associated
35
+ # with the policy. It writes the policy only if the `etag` values match.
36
+ # @attr [Array<Binding>] bindings The bindings in the policy, which may be mutable or frozen depending on the
37
+ # context. See [Understanding Roles](https://cloud.google.com/iam/docs/understanding-roles) for a list of
38
+ # primitive and curated roles. See [BigQuery Table ACL
39
+ # permissions](https://cloud.google.com/bigquery/docs/table-access-controls-intro#permissions) for a list of
40
+ # values and patterns for members.
41
+ #
42
+ # @example
43
+ # require "google/cloud/bigquery"
44
+ #
45
+ # bigquery = Google::Cloud::Bigquery.new
46
+ # dataset = bigquery.dataset "my_dataset"
47
+ # table = dataset.table "my_table"
48
+ # policy = table.policy
49
+ #
50
+ # policy.frozen? #=> true
51
+ # binding_owner = policy.bindings.find { |b| b.role == "roles/owner" }
52
+ #
53
+ # binding_owner.role #=> "roles/owner"
54
+ # binding_owner.members #=> ["user:owner@example.com"]
55
+ # binding_owner.frozen? #=> true
56
+ # binding_owner.members.frozen? #=> true
57
+ #
58
+ # @example Update mutable bindings in the policy.
59
+ # require "google/cloud/bigquery"
60
+ #
61
+ # bigquery = Google::Cloud::Bigquery.new
62
+ # dataset = bigquery.dataset "my_dataset"
63
+ # table = dataset.table "my_table"
64
+ #
65
+ # table.update_policy do |p|
66
+ # p.grant role: "roles/viewer", members: "user:viewer@example.com"
67
+ # p.revoke role: "roles/editor", members: "user:editor@example.com"
68
+ # p.revoke role: "roles/owner"
69
+ # end
70
+ #
71
+ # @example Iterate over frozen bindings.
72
+ # require "google/cloud/bigquery"
73
+ #
74
+ # bigquery = Google::Cloud::Bigquery.new
75
+ # dataset = bigquery.dataset "my_dataset"
76
+ # table = dataset.table "my_table"
77
+ # policy = table.policy
78
+ #
79
+ # policy.frozen? #=> true
80
+ # policy.bindings.each do |b|
81
+ # puts b.role
82
+ # puts b.members
83
+ # end
84
+ #
85
+ # @example Update mutable bindings.
86
+ # require "google/cloud/bigquery"
87
+ #
88
+ # bigquery = Google::Cloud::Bigquery.new
89
+ # dataset = bigquery.dataset "my_dataset"
90
+ # table = dataset.table "my_table"
91
+ #
92
+ # table.update_policy do |p|
93
+ # p.bindings.each do |b|
94
+ # b.members.delete_if { |m| m.include? "@example.com" }
95
+ # end
96
+ # end
97
+ #
98
+ class Policy
99
+ attr_reader :etag, :bindings
100
+
101
+ # @private
102
+ def initialize etag, bindings
103
+ @etag = etag.freeze
104
+ @bindings = bindings
105
+ end
106
+
107
+ ##
108
+ # Convenience method adding or updating a binding in the policy. See [Understanding
109
+ # Roles](https://cloud.google.com/iam/docs/understanding-roles) for a list of primitive and curated roles. See
110
+ # [BigQuery Table ACL
111
+ # permissions](https://cloud.google.com/bigquery/docs/table-access-controls-intro#permissions) for a list of
112
+ # values and patterns for members.
113
+ #
114
+ # @param [String] role The role that is bound to members in the binding. For example, `roles/viewer`,
115
+ # `roles/editor`, or `roles/owner`. Required.
116
+ # @param [String, Array<String>] members Specifies the identities requesting access for a Cloud Platform
117
+ # resource. `members` can have the following values. Required.
118
+ #
119
+ # * `allUsers`: A special identifier that represents anyone who is on the internet; with or without a Google
120
+ # account.
121
+ # * `allAuthenticatedUsers`: A special identifier that represents anyone who is authenticated with a Google
122
+ # account or a service account.
123
+ # * `user:<emailid>`: An email address that represents a specific Google account. For example,
124
+ # `alice@example.com`.
125
+ # * `serviceAccount:<emailid>`: An email address that represents a service account. For example,
126
+ # `my-other-app@appspot.gserviceaccount.com`.
127
+ # * `group:<emailid>`: An email address that represents a Google group. For example, `admins@example.com`.
128
+ # * `deleted:user:<emailid>?uid=<uniqueid>`: An email address (plus unique identifier) representing a user
129
+ # that has been recently deleted. For example, `alice@example.com?uid=123456789012345678901`. If the user
130
+ # is recovered, this value reverts to `user:<emailid>` and the recovered user retains the role in the
131
+ # binding.
132
+ # * `deleted: serviceAccount:<emailid>?uid=<uniqueid>`: An email address (plus unique identifier) representing
133
+ # a service account that has been recently deleted. For example,
134
+ # `my-other-app@appspot.gserviceaccount.com?uid=123456789012345678901`. If the service account is undeleted,
135
+ # this value reverts to `serviceAccount:<emailid>` and the undeleted service account retains the role in
136
+ # the binding.
137
+ # * `deleted:group:<emailid>?uid=<uniqueid>`: An email address (plus unique identifier) representing a Google
138
+ # group that has been recently deleted. For example, `admins@example.com?uid=123456789012345678901`. If the
139
+ # group is recovered, this value reverts to `group:<emailid>` and the recovered group retains the role in
140
+ # the binding.
141
+ # * `domain:<domain>`: The G Suite domain (primary) that represents all the users of that domain. For example,
142
+ # `google.com` or `example.com`.
143
+ #
144
+ # @return [nil]
145
+ #
146
+ # @example Grant a role to a member.
147
+ # require "google/cloud/bigquery"
148
+ #
149
+ # bigquery = Google::Cloud::Bigquery.new
150
+ # dataset = bigquery.dataset "my_dataset"
151
+ # table = dataset.table "my_table"
152
+ #
153
+ # table.update_policy do |p|
154
+ # p.grant role: "roles/viewer", members: "user:viewer@example.com"
155
+ # end
156
+ #
157
+ def grant role:, members:
158
+ existing_binding = bindings.find { |b| b.role == role }
159
+ if existing_binding
160
+ existing_binding.members.concat Array(members)
161
+ existing_binding.members.uniq!
162
+ else
163
+ bindings << Binding.new(role, members)
164
+ end
165
+ nil
166
+ end
167
+
168
+ ##
169
+ # Convenience method for removing a binding or bindings from the policy. See
170
+ # [Understanding Roles](https://cloud.google.com/iam/docs/understanding-roles) for a list of primitive and
171
+ # curated roles. See [BigQuery Table ACL
172
+ # permissions](https://cloud.google.com/bigquery/docs/table-access-controls-intro#permissions) for a list of
173
+ # values and patterns for members.
174
+ #
175
+ # @param [String] role A role that is bound to members in the policy. For example, `roles/viewer`,
176
+ # `roles/editor`, or `roles/owner`. Optional.
177
+ # @param [String, Array<String>] members Specifies the identities receiving access for a Cloud Platform
178
+ # resource. `members` can have the following values. Optional.
179
+ #
180
+ # * `allUsers`: A special identifier that represents anyone who is on the internet; with or without a Google
181
+ # account.
182
+ # * `allAuthenticatedUsers`: A special identifier that represents anyone who is authenticated with a Google
183
+ # account or a service account.
184
+ # * `user:<emailid>`: An email address that represents a specific Google account. For example,
185
+ # `alice@example.com`.
186
+ # * `serviceAccount:<emailid>`: An email address that represents a service account. For example,
187
+ # `my-other-app@appspot.gserviceaccount.com`.
188
+ # * `group:<emailid>`: An email address that represents a Google group. For example, `admins@example.com`.
189
+ # * `deleted:user:<emailid>?uid=<uniqueid>`: An email address (plus unique identifier) representing a user
190
+ # that has been recently deleted. For example, `alice@example.com?uid=123456789012345678901`. If the user
191
+ # is recovered, this value reverts to `user:<emailid>` and the recovered user retains the role in the
192
+ # binding.
193
+ # * `deleted: serviceAccount:<emailid>?uid=<uniqueid>`: An email address (plus unique identifier) representing
194
+ # a service account that has been recently deleted. For example,
195
+ # `my-other-app@appspot.gserviceaccount.com?uid=123456789012345678901`. If the service account is undeleted,
196
+ # this value reverts to `serviceAccount:<emailid>` and the undeleted service account retains the role in
197
+ # the binding.
198
+ # * `deleted:group:<emailid>?uid=<uniqueid>`: An email address (plus unique identifier) representing a Google
199
+ # group that has been recently deleted. For example, `admins@example.com?uid=123456789012345678901`. If the
200
+ # group is recovered, this value reverts to `group:<emailid>` and the recovered group retains the role in
201
+ # the binding.
202
+ # * `domain:<domain>`: The G Suite domain (primary) that represents all the users of that domain. For example,
203
+ # `google.com` or `example.com`.
204
+ #
205
+ # @return [nil]
206
+ #
207
+ # @example Revoke a role for a member or members.
208
+ # require "google/cloud/bigquery"
209
+ #
210
+ # bigquery = Google::Cloud::Bigquery.new
211
+ # dataset = bigquery.dataset "my_dataset"
212
+ # table = dataset.table "my_table"
213
+ #
214
+ # table.update_policy do |p|
215
+ # p.revoke role: "roles/viewer", members: "user:viewer@example.com"
216
+ # end
217
+ #
218
+ # @example Revoke a role for all members.
219
+ # require "google/cloud/bigquery"
220
+ #
221
+ # bigquery = Google::Cloud::Bigquery.new
222
+ # dataset = bigquery.dataset "my_dataset"
223
+ # table = dataset.table "my_table"
224
+ #
225
+ # table.update_policy do |p|
226
+ # p.revoke role: "roles/viewer"
227
+ # end
228
+ #
229
+ # @example Revoke all roles for a member or members.
230
+ # require "google/cloud/bigquery"
231
+ #
232
+ # bigquery = Google::Cloud::Bigquery.new
233
+ # dataset = bigquery.dataset "my_dataset"
234
+ # table = dataset.table "my_table"
235
+ #
236
+ # table.update_policy do |p|
237
+ # p.revoke members: ["user:viewer@example.com", "user:editor@example.com"]
238
+ # end
239
+ #
240
+ def revoke role: nil, members: nil
241
+ bindings_for_role = role ? bindings.select { |b| b.role == role } : bindings
242
+ bindings_for_role.each do |b|
243
+ if members
244
+ b.members -= Array(members)
245
+ bindings.delete b if b.members.empty?
246
+ else
247
+ bindings.delete b
248
+ end
249
+ end
250
+ nil
251
+ end
252
+
253
+ ##
254
+ # @private Convert the Policy to a Google::Apis::BigqueryV2::Policy.
255
+ def to_gapi
256
+ Google::Apis::BigqueryV2::Policy.new(
257
+ bindings: bindings_to_gapi,
258
+ etag: etag,
259
+ version: 1
260
+ )
261
+ end
262
+
263
+ ##
264
+ # @private Deep freeze the policy including its bindings.
265
+ def freeze
266
+ super
267
+ @bindings.each(&:freeze)
268
+ @bindings.freeze
269
+ self
270
+ end
271
+
272
+ ##
273
+ # @private New Policy from a Google::Apis::BigqueryV2::Policy object.
274
+ def self.from_gapi gapi
275
+ bindings = Array(gapi.bindings).map do |binding|
276
+ Binding.new binding.role, binding.members.to_a
277
+ end
278
+ new gapi.etag, bindings
279
+ end
280
+
281
+ ##
282
+ # # Policy::Binding
283
+ #
284
+ # Represents a Cloud IAM Binding for BigQuery resources within the context of a {Policy}.
285
+ #
286
+ # A binding binds one or more members to a single role. Member strings can describe user accounts, service
287
+ # accounts, Google groups, and domains. A role is a named list of permissions; each role can be an IAM
288
+ # predefined role or a user-created custom role.
289
+ #
290
+ # @see https://cloud.google.com/bigquery/docs/table-access-controls-intro Controlling access to tables
291
+ #
292
+ # @attr [String] role The role that is assigned to `members`. For example, `roles/viewer`, `roles/editor`, or
293
+ # `roles/owner`. Required.
294
+ # @attr [Array<String>] members Specifies the identities requesting access for a Cloud Platform resource.
295
+ # `members` can have the following values. Required.
296
+ #
297
+ # * `allUsers`: A special identifier that represents anyone who is on the internet; with or without a Google
298
+ # account.
299
+ # * `allAuthenticatedUsers`: A special identifier that represents anyone who is authenticated with a Google
300
+ # account or a service account.
301
+ # * `user:<emailid>`: An email address that represents a specific Google account. For example,
302
+ # `alice@example.com`.
303
+ # * `serviceAccount:<emailid>`: An email address that represents a service account. For example,
304
+ # `my-other-app@appspot.gserviceaccount.com`.
305
+ # * `group:<emailid>`: An email address that represents a Google group. For example, `admins@example.com`.
306
+ # * `deleted:user:<emailid>?uid=<uniqueid>`: An email address (plus unique identifier) representing a user
307
+ # that has been recently deleted. For example, `alice@example.com?uid=123456789012345678901`. If the user
308
+ # is recovered, this value reverts to `user:<emailid>` and the recovered user retains the role in the
309
+ # binding.
310
+ # * `deleted: serviceAccount:<emailid>?uid=<uniqueid>`: An email address (plus unique identifier) representing
311
+ # a service account that has been recently deleted. For example,
312
+ # `my-other-app@appspot.gserviceaccount.com?uid=123456789012345678901`. If the service account is undeleted,
313
+ # this value reverts to `serviceAccount:<emailid>` and the undeleted service account retains the role in
314
+ # the binding.
315
+ # * `deleted:group:<emailid>?uid=<uniqueid>`: An email address (plus unique identifier) representing a Google
316
+ # group that has been recently deleted. For example, `admins@example.com?uid=123456789012345678901`. If the
317
+ # group is recovered, this value reverts to `group:<emailid>` and the recovered group retains the role in
318
+ # the binding.
319
+ # * `domain:<domain>`: The G Suite domain (primary) that represents all the users of that domain. For example,
320
+ # `google.com` or `example.com`.
321
+ #
322
+ # @example
323
+ # require "google/cloud/bigquery"
324
+ #
325
+ # bigquery = Google::Cloud::Bigquery.new
326
+ # dataset = bigquery.dataset "my_dataset"
327
+ # table = dataset.table "my_table"
328
+ #
329
+ # policy = table.policy
330
+ # binding_owner = policy.bindings.find { |b| b.role == "roles/owner" }
331
+ #
332
+ # binding_owner.role #=> "roles/owner"
333
+ # binding_owner.members #=> ["user:owner@example.com"]
334
+ #
335
+ # binding_owner.frozen? #=> true
336
+ # binding_owner.members.frozen? #=> true
337
+ #
338
+ # @example Update mutable bindings.
339
+ # require "google/cloud/bigquery"
340
+ #
341
+ # bigquery = Google::Cloud::Bigquery.new
342
+ # dataset = bigquery.dataset "my_dataset"
343
+ # table = dataset.table "my_table"
344
+ #
345
+ # table.update_policy do |p|
346
+ # binding_owner = p.bindings.find { |b| b.role == "roles/owner" }
347
+ # binding_owner.members.delete_if { |m| m.include? "@example.com" }
348
+ # end
349
+ #
350
+ class Binding
351
+ attr_accessor :role
352
+ attr_reader :members
353
+
354
+ # @private
355
+ def initialize role, members
356
+ members = Array(members).uniq
357
+ raise ArgumentError, "members cannot be empty" if members.empty?
358
+ @role = role
359
+ @members = members
360
+ end
361
+
362
+ ##
363
+ # Sets the binding members.
364
+ #
365
+ # @param [Array<String>] new_members Specifies the identities requesting access for a Cloud Platform resource.
366
+ # `new_members` can have the following values. Required.
367
+ #
368
+ # * `allUsers`: A special identifier that represents anyone who is on the internet; with or without a Google
369
+ # account.
370
+ # * `allAuthenticatedUsers`: A special identifier that represents anyone who is authenticated with a Google
371
+ # account or a service account.
372
+ # * `user:<emailid>`: An email address that represents a specific Google account. For example,
373
+ # `alice@example.com`.
374
+ # * `serviceAccount:<emailid>`: An email address that represents a service account. For example,
375
+ # `my-other-app@appspot.gserviceaccount.com`.
376
+ # * `group:<emailid>`: An email address that represents a Google group. For example, `admins@example.com`.
377
+ # * `deleted:user:<emailid>?uid=<uniqueid>`: An email address (plus unique identifier) representing a user
378
+ # that has been recently deleted. For example, `alice@example.com?uid=123456789012345678901`. If the user
379
+ # is recovered, this value reverts to `user:<emailid>` and the recovered user retains the role in the
380
+ # binding.
381
+ # * `deleted: serviceAccount:<emailid>?uid=<uniqueid>`: An email address (plus unique identifier)
382
+ # representing a service account that has been recently deleted. For example,
383
+ # `my-other-app@appspot.gserviceaccount.com?uid=123456789012345678901`. If the service account is
384
+ # undeleted, this value reverts to `serviceAccount:<emailid>` and the undeleted service account retains
385
+ # the role in the binding.
386
+ # * `deleted:group:<emailid>?uid=<uniqueid>`: An email address (plus unique identifier) representing a
387
+ # Google group that has been recently deleted. For example,
388
+ # `admins@example.com?uid=123456789012345678901`. If the group is recovered, this value reverts to
389
+ # `group:<emailid>` and the recovered group retains the role in the binding.
390
+ # * `domain:<domain>`: The G Suite domain (primary) that represents all the users of that domain. For
391
+ # example, `google.com` or `example.com`.
392
+ #
393
+ def members= new_members
394
+ @members = Array(new_members).uniq
395
+ end
396
+
397
+ ##
398
+ # @private Convert the Binding to a Google::Apis::BigqueryV2::Binding.
399
+ def to_gapi
400
+ Google::Apis::BigqueryV2::Binding.new role: role, members: members
401
+ end
402
+
403
+ ##
404
+ # @private Deep freeze the policy including its members.
405
+ def freeze
406
+ super
407
+ role.freeze
408
+ members.each(&:freeze)
409
+ members.freeze
410
+ self
411
+ end
412
+
413
+ ##
414
+ # @private New Binding from a Google::Apis::BigqueryV2::Binding object.
415
+ def self.from_gapi gapi
416
+ new gapi.etag, gapi.members.to_a
417
+ end
418
+ end
419
+
420
+ protected
421
+
422
+ def bindings_to_gapi
423
+ @bindings.compact.uniq.map do |b|
424
+ next if b.members.empty?
425
+ b.to_gapi
426
+ end
427
+ end
428
+ end
429
+ end
430
+ end
431
+ end
@@ -182,6 +182,34 @@ module Google
182
182
  end
183
183
  end
184
184
 
185
+ ##
186
+ # Returns Google::Apis::BigqueryV2::Policy
187
+ def get_table_policy dataset_id, table_id
188
+ policy_options = API::GetPolicyOptions.new requested_policy_version: 1
189
+ execute do
190
+ service.get_table_iam_policy table_path(dataset_id, table_id),
191
+ API::GetIamPolicyRequest.new(options: policy_options)
192
+ end
193
+ end
194
+
195
+ ##
196
+ # @param [Google::Apis::BigqueryV2::Policy] new_policy
197
+ def set_table_policy dataset_id, table_id, new_policy
198
+ execute do
199
+ service.set_table_iam_policy table_path(dataset_id, table_id),
200
+ API::SetIamPolicyRequest.new(policy: new_policy)
201
+ end
202
+ end
203
+
204
+ ##
205
+ # Returns Google::Apis::BigqueryV2::TestIamPermissionsResponse
206
+ def test_table_permissions dataset_id, table_id, permissions
207
+ execute do
208
+ service.test_table_iam_permissions table_path(dataset_id, table_id),
209
+ API::TestIamPermissionsRequest.new(permissions: permissions)
210
+ end
211
+ end
212
+
185
213
  ##
186
214
  # Deletes the table specified by tableId from the dataset.
187
215
  # If the table contains data, all the data will be deleted.
@@ -508,6 +536,11 @@ module Google
508
536
 
509
537
  protected
510
538
 
539
+ # Creates a formatted table path.
540
+ def table_path dataset_id, table_id
541
+ "projects/#{@project}/datasets/#{dataset_id}/tables/#{table_id}"
542
+ end
543
+
511
544
  # Generate a random string similar to the BigQuery service job IDs.
512
545
  def generate_id
513
546
  SecureRandom.urlsafe_base64 21
@@ -23,6 +23,7 @@ require "google/cloud/bigquery/external"
23
23
  require "google/cloud/bigquery/insert_response"
24
24
  require "google/cloud/bigquery/table/async_inserter"
25
25
  require "google/cloud/bigquery/convert"
26
+ require "google/cloud/bigquery/policy"
26
27
  require "google/apis/bigquery_v2"
27
28
 
28
29
  module Google
@@ -1274,6 +1275,107 @@ module Google
1274
1275
  Array(udfs_gapi).map { |udf| udf.inline_code || udf.resource_uri }
1275
1276
  end
1276
1277
 
1278
+ ##
1279
+ # Gets the Cloud IAM access control policy for the table. The latest policy will be read from the service. See
1280
+ # also {#update_policy}.
1281
+ #
1282
+ # @see https://cloud.google.com/iam/docs/managing-policies Managing Policies
1283
+ # @see https://cloud.google.com/bigquery/docs/table-access-controls-intro Controlling access to tables
1284
+ #
1285
+ # @return [Policy] The frozen policy for the table.
1286
+ #
1287
+ # @example
1288
+ # require "google/cloud/bigquery"
1289
+ #
1290
+ # bigquery = Google::Cloud::Bigquery.new
1291
+ # dataset = bigquery.dataset "my_dataset"
1292
+ # table = dataset.table "my_table"
1293
+ #
1294
+ # policy = table.policy
1295
+ #
1296
+ # policy.frozen? #=> true
1297
+ # binding_owner = policy.bindings.find { |b| b.role == "roles/owner" }
1298
+ # binding_owner.role #=> "roles/owner"
1299
+ # binding_owner.members #=> ["user:owner@example.com"]
1300
+ # binding_owner.frozen? #=> true
1301
+ # binding_owner.members.frozen? #=> true
1302
+ #
1303
+ def policy
1304
+ raise ArgumentError, "Block argument not supported: Use #update_policy instead." if block_given?
1305
+ ensure_service!
1306
+ gapi = service.get_table_policy dataset_id, table_id
1307
+ Policy.from_gapi(gapi).freeze
1308
+ end
1309
+
1310
+ ##
1311
+ # Updates the Cloud IAM access control policy for the table. The latest policy will be read from the service.
1312
+ # See also {#policy}.
1313
+ #
1314
+ # @see https://cloud.google.com/iam/docs/managing-policies Managing Policies
1315
+ # @see https://cloud.google.com/bigquery/docs/table-access-controls-intro Controlling access to tables
1316
+ #
1317
+ # @yield [policy] A block for updating the policy. The latest policy will be read from the service and passed to
1318
+ # the block. After the block completes, the modified policy will be written to the service.
1319
+ # @yieldparam [Policy] policy The mutable Policy for the table.
1320
+ #
1321
+ # @return [Policy] The updated and frozen policy for the table.
1322
+ #
1323
+ # @example Update the policy by passing a block.
1324
+ # require "google/cloud/bigquery"
1325
+ #
1326
+ # bigquery = Google::Cloud::Bigquery.new
1327
+ # dataset = bigquery.dataset "my_dataset"
1328
+ # table = dataset.table "my_table"
1329
+ #
1330
+ # table.update_policy do |p|
1331
+ # p.grant role: "roles/viewer", members: "user:viewer@example.com"
1332
+ # p.revoke role: "roles/editor", members: "user:editor@example.com"
1333
+ # p.revoke role: "roles/owner"
1334
+ # end # 2 API calls
1335
+ #
1336
+ def update_policy
1337
+ raise ArgumentError, "A block updating the policy must be provided" unless block_given?
1338
+ ensure_service!
1339
+ gapi = service.get_table_policy dataset_id, table_id
1340
+ policy = Policy.from_gapi gapi
1341
+ yield policy
1342
+ # TODO: Check for changes before calling RPC
1343
+ gapi = service.set_table_policy dataset_id, table_id, policy.to_gapi
1344
+ Policy.from_gapi(gapi).freeze
1345
+ end
1346
+
1347
+ ##
1348
+ # Tests the specified permissions against the [Cloud
1349
+ # IAM](https://cloud.google.com/iam/) access control policy.
1350
+ #
1351
+ # @see https://cloud.google.com/iam/docs/managing-policies Managing Policies
1352
+ #
1353
+ # @param [String, Array<String>] permissions The set of permissions
1354
+ # against which to check access. Permissions must be of the format
1355
+ # `bigquery.resource.capability`.
1356
+ # See https://cloud.google.com/bigquery/docs/access-control#bigquery.
1357
+ #
1358
+ # @return [Array<String>] The frozen array of permissions held by the caller.
1359
+ #
1360
+ # @example
1361
+ # require "google/cloud/bigquery"
1362
+ #
1363
+ # bigquery = Google::Cloud::Bigquery.new
1364
+ # dataset = bigquery.dataset "my_dataset"
1365
+ # table = dataset.table "my_table"
1366
+ #
1367
+ # permissions = table.test_iam_permissions "bigquery.tables.get",
1368
+ # "bigquery.tables.delete"
1369
+ # permissions.include? "bigquery.tables.get" #=> true
1370
+ # permissions.include? "bigquery.tables.delete" #=> false
1371
+ #
1372
+ def test_iam_permissions *permissions
1373
+ permissions = Array(permissions).flatten
1374
+ ensure_service!
1375
+ gapi = service.test_table_permissions dataset_id, table_id, permissions
1376
+ gapi.permissions.freeze
1377
+ end
1378
+
1277
1379
  ##
1278
1380
  # Retrieves data from the table.
1279
1381
  #
@@ -16,7 +16,7 @@
16
16
  module Google
17
17
  module Cloud
18
18
  module Bigquery
19
- VERSION = "1.24.0".freeze
19
+ VERSION = "1.25.0".freeze
20
20
  end
21
21
  end
22
22
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: google-cloud-bigquery
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.24.0
4
+ version: 1.25.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Moore
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-10-29 00:00:00.000000000 Z
12
+ date: 2020-11-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: concurrent-ruby
@@ -257,6 +257,7 @@ files:
257
257
  - lib/google/cloud/bigquery/load_job.rb
258
258
  - lib/google/cloud/bigquery/model.rb
259
259
  - lib/google/cloud/bigquery/model/list.rb
260
+ - lib/google/cloud/bigquery/policy.rb
260
261
  - lib/google/cloud/bigquery/project.rb
261
262
  - lib/google/cloud/bigquery/project/list.rb
262
263
  - lib/google/cloud/bigquery/query_job.rb