• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
17import json
18import os
19
20from oauth2client import client
21
22
23DEVSHELL_ENV = 'DEVSHELL_CLIENT_PORT'
24
25
26class Error(Exception):
27  """Errors for this module."""
28  pass
29
30
31class CommunicationError(Error):
32  """Errors for communication with the Developer Shell server."""
33
34
35class 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.
41CREDENTIAL_INFO_REQUEST_JSON = '[]'
42
43
44class 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
65def _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
92class 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.')
136
137