diff --git a/gce_rescue/tasks/pre_validations.py b/gce_rescue/tasks/pre_validations.py index 5c1a981..b8f36a4 100644 --- a/gce_rescue/tasks/pre_validations.py +++ b/gce_rescue/tasks/pre_validations.py @@ -18,6 +18,7 @@ from dataclasses import dataclass, field import googleapiclient.discovery +from gce_rescue.tasks.validations.authorization import authorize_check from gce_rescue.tasks.validations.authentication import ( authenticate_check, project_name @@ -42,6 +43,10 @@ def _authentication(self): test_mode = self.test_mode, ) + def __post_init__(self): + if not self.test_mode: + authorize_check(project = self.project) + @property def compute(self) -> googleapiclient.discovery.Resource: return self._authentication() diff --git a/gce_rescue/tasks/validations/api.py b/gce_rescue/tasks/validations/api.py new file mode 100644 index 0000000..2c47adf --- /dev/null +++ b/gce_rescue/tasks/validations/api.py @@ -0,0 +1,42 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" Common API objects """ +import googleapiclient +import google_auth_httplib2 +import httplib2 + +from google.oauth2.credentials import Credentials +from googleapiclient.discovery import Resource + +def api_service( + service: str, + version: str, + credentials: Credentials) -> Resource: + + def _builder(http, *args, **kwargs): + # google api client is not thread safe + # https://github.com/googleapis/google-api-python-client/blob/main/docs/thread_safety.md + del http + headers = kwargs.setdefault('headers',{}) + headers['user-agent'] = 'gce_rescue_header' + auth_http = google_auth_httplib2.AuthorizedHttp(credentials, + http=httplib2.Http()) + return googleapiclient.http.HttpRequest(auth_http, *args, **kwargs) + + service_ = googleapiclient.discovery.build(service, version, + cache_discovery=False, + credentials=credentials, + requestBuilder=_builder) + return service_ diff --git a/gce_rescue/tasks/validations/authentication.py b/gce_rescue/tasks/validations/authentication.py index b8a6c57..db75b55 100644 --- a/gce_rescue/tasks/validations/authentication.py +++ b/gce_rescue/tasks/validations/authentication.py @@ -13,20 +13,14 @@ # limitations under the License. """ Authentication validation to be called from ../pre_validations.py """ -import googleapiclient -import googleapiclient.discovery import google.auth -import google_auth_httplib2 - -import httplib2 import sys +from googleapiclient.discovery import Resource +from gce_rescue.tasks.validations.api import api_service from gce_rescue.test.mocks import mock_api_object - PROJECT = '' -GCE_RESCUE_HEADER = 'gce_rescue_header' - def _get_auth(): global PROJECT @@ -49,7 +43,7 @@ def authenticate_check( instance_name: str, project: str = None, test_mode: bool = False -) -> googleapiclient.discovery.Resource: +) -> Resource: global PROJECT PROJECT = project if test_mode: @@ -63,7 +57,7 @@ def authenticate_check( # 'v1', # credentials = credentials # ) - service = gce_service(credentials) + service = api_service('compute', 'v1', credentials) request = service.instances().get( project = PROJECT, zone = zone, @@ -76,26 +70,6 @@ def authenticate_check( print(msg, file=sys.stderr) sys.exit(1) - -def gce_service(credentials): - - def _builder(http, *args, **kwargs): - # google api client is not thread safe - # https://github.com/googleapis/google-api-python-client/blob/main/docs/thread_safety.md - del http - headers = kwargs.setdefault('headers',{}) - headers['user-agent'] = GCE_RESCUE_HEADER - auth_http = google_auth_httplib2.AuthorizedHttp(credentials, - http=httplib2.Http()) - return googleapiclient.http.HttpRequest(auth_http, *args, **kwargs) - - service_ = googleapiclient.discovery.build('compute', 'v1', - cache_discovery=False, - credentials=credentials, - requestBuilder=_builder) - return service_ - - def project_name() -> str: return PROJECT diff --git a/gce_rescue/tasks/validations/authorization.py b/gce_rescue/tasks/validations/authorization.py new file mode 100644 index 0000000..a46dece --- /dev/null +++ b/gce_rescue/tasks/validations/authorization.py @@ -0,0 +1,51 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Authorization validation to be called from ../pre_validations.py +Permissions: + compute.instances.stop + compute.instances.start + compute.instances.attachDisk + compute.instances.detachDisk + compute.images.useReadOnly + compute.disks.use + compute.disks.setLabels + compute.snapshots.create + compute.disks.createSnapshot + compute.instances.setMetadata + compute.instances.setLabels +""" +import google.auth +from gce_rescue.tasks.validations.api import api_service + +def authorize_check(project: str = None) -> bool: + + permissions_list = ['compute.snapshots.create'] + body_data = {'permissions': permissions_list} + credentials, project_id = google.auth.default() + + if not project: + project = project_id + + service = api_service('cloudresourcemanager', 'v1', credentials) + result = service.projects().testIamPermissions( + resource = project, + body = body_data + ).execute() + + if permissions_list != result['permissions']: + raise PermissionError() + + return True