| OLD | NEW |
| (Empty) |
| 1 # Copyright 2015 Google Inc. All Rights Reserved. | |
| 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 # http://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 """OAuth 2.0 utitilies for Google Developer Shell environment.""" | |
| 16 | |
| 17 import json | |
| 18 import os | |
| 19 | |
| 20 from . import client | |
| 21 | |
| 22 | |
| 23 DEVSHELL_ENV = 'DEVSHELL_CLIENT_PORT' | |
| 24 | |
| 25 | |
| 26 class Error(Exception): | |
| 27 """Errors for this module.""" | |
| 28 pass | |
| 29 | |
| 30 | |
| 31 class CommunicationError(Error): | |
| 32 """Errors for communication with the Developer Shell server.""" | |
| 33 | |
| 34 | |
| 35 class NoDevshellServer(Error): | |
| 36 """Error when no Developer Shell server can be contacted.""" | |
| 37 | |
| 38 | |
| 39 # The request for credential information to the Developer Shell client socket is | |
| 40 # always an empty PBLite-formatted JSON object, so just define it as a constant. | |
| 41 CREDENTIAL_INFO_REQUEST_JSON = '[]' | |
| 42 | |
| 43 | |
| 44 class CredentialInfoResponse(object): | |
| 45 """Credential information response from Developer Shell server. | |
| 46 | |
| 47 The credential information response from Developer Shell socket is a | |
| 48 PBLite-formatted JSON array with fields encoded by their index in the array: | |
| 49 * Index 0 - user email | |
| 50 * Index 1 - default project ID. None if the project context is not known. | |
| 51 * Index 2 - OAuth2 access token. None if there is no valid auth context. | |
| 52 """ | |
| 53 | |
| 54 def __init__(self, json_string): | |
| 55 """Initialize the response data from JSON PBLite array.""" | |
| 56 pbl = json.loads(json_string) | |
| 57 if not isinstance(pbl, list): | |
| 58 raise ValueError('Not a list: ' + str(pbl)) | |
| 59 pbl_len = len(pbl) | |
| 60 self.user_email = pbl[0] if pbl_len > 0 else None | |
| 61 self.project_id = pbl[1] if pbl_len > 1 else None | |
| 62 self.access_token = pbl[2] if pbl_len > 2 else None | |
| 63 | |
| 64 | |
| 65 def _SendRecv(): | |
| 66 """Communicate with the Developer Shell server socket.""" | |
| 67 | |
| 68 port = int(os.getenv(DEVSHELL_ENV, 0)) | |
| 69 if port == 0: | |
| 70 raise NoDevshellServer() | |
| 71 | |
| 72 import socket | |
| 73 | |
| 74 sock = socket.socket() | |
| 75 sock.connect(('localhost', port)) | |
| 76 | |
| 77 data = CREDENTIAL_INFO_REQUEST_JSON | |
| 78 msg = '%s\n%s' % (len(data), data) | |
| 79 sock.sendall(msg.encode()) | |
| 80 | |
| 81 header = sock.recv(6).decode() | |
| 82 if '\n' not in header: | |
| 83 raise CommunicationError('saw no newline in the first 6 bytes') | |
| 84 len_str, json_str = header.split('\n', 1) | |
| 85 to_read = int(len_str) - len(json_str) | |
| 86 if to_read > 0: | |
| 87 json_str += sock.recv(to_read, socket.MSG_WAITALL).decode() | |
| 88 | |
| 89 return CredentialInfoResponse(json_str) | |
| 90 | |
| 91 | |
| 92 class DevshellCredentials(client.GoogleCredentials): | |
| 93 """Credentials object for Google Developer Shell environment. | |
| 94 | |
| 95 This object will allow a Google Developer Shell session to identify its user | |
| 96 to Google and other OAuth 2.0 servers that can verify assertions. It can be | |
| 97 used for the purpose of accessing data stored under the user account. | |
| 98 | |
| 99 This credential does not require a flow to instantiate because it represents | |
| 100 a two legged flow, and therefore has all of the required information to | |
| 101 generate and refresh its own access tokens. | |
| 102 """ | |
| 103 | |
| 104 def __init__(self, user_agent=None): | |
| 105 super(DevshellCredentials, self).__init__( | |
| 106 None, # access_token, initialized below | |
| 107 None, # client_id | |
| 108 None, # client_secret | |
| 109 None, # refresh_token | |
| 110 None, # token_expiry | |
| 111 None, # token_uri | |
| 112 user_agent) | |
| 113 self._refresh(None) | |
| 114 | |
| 115 def _refresh(self, http_request): | |
| 116 self.devshell_response = _SendRecv() | |
| 117 self.access_token = self.devshell_response.access_token | |
| 118 | |
| 119 @property | |
| 120 def user_email(self): | |
| 121 return self.devshell_response.user_email | |
| 122 | |
| 123 @property | |
| 124 def project_id(self): | |
| 125 return self.devshell_response.project_id | |
| 126 | |
| 127 @classmethod | |
| 128 def from_json(cls, json_data): | |
| 129 raise NotImplementedError( | |
| 130 'Cannot load Developer Shell credentials from JSON.') | |
| 131 | |
| 132 @property | |
| 133 def serialization_data(self): | |
| 134 raise NotImplementedError( | |
| 135 'Cannot serialize Developer Shell credentials.') | |
| OLD | NEW |