files.new_contents: """ Implements the App Identity API. """
  
  import json
  import logging
  import time
  import urllib
  import urllib2
  
  from kazoo.exceptions import KazooException
  from kazoo.exceptions import NoNodeError
  from kazoo.exceptions import NodeExistsError
  from tornado.ioloop import IOLoop
  
  from appscale.api_server import app_identity_service_pb2 as service_pb
  from appscale.api_server import crypto
  from appscale.api_server.base_service import BaseService
  from appscale.api_server.constants import ApplicationError
  from appscale.api_server.constants import CallNotFound
  from appscale.api_server.crypto import (
      AccessToken, PrivateKey, PublicCertificate)
  from appscale.common.async_retrying import retry_children_watch_coroutine
  
  logger = logging.getLogger(__name__)
  
  
  class UnknownError(Exception):
      """ Indicates that the request cannot be completed at this time. """
      pass
  
  
  class AppIdentityService(BaseService):
      """ Implements the App Identity API. """
      SERVICE_NAME = 'app_identity_service'
  
      # A dummy bucket name for satisfying calls.
      DEFAULT_GCS_BUCKET_NAME = 'app_default_bucket'
  
      # The appropriate messages for each API call.
      METHODS = {'SignForApp': (service_pb.SignForAppRequest,
                                service_pb.SignForAppResponse),
                 'GetPublicCertificatesForApp': (
                     service_pb.GetPublicCertificateForAppRequest,
                     service_pb.GetPublicCertificateForAppResponse),
                 'GetServiceAccountName': (
                     service_pb.GetServiceAccountNameRequest,
                     service_pb.GetServiceAccountNameResponse),
                 'GetAccessToken': (service_pb.GetAccessTokenRequest,
                                    service_pb.GetAccessTokenResponse),
                 'GetDefaultGcsBucketName': (
                     service_pb.GetDefaultGcsBucketNameRequest,
                     service_pb.GetDefaultGcsBucketNameResponse)}
  
      def __init__(self, project_id, zk_client):
          """ Creates a new AppIdentityService.
  
          Args:
              project_id: A string specifying the project ID.
              zk_client: A KazooClient.
          """
          super(AppIdentityService, self).__init__(self.SERVICE_NAME)
  
          self.project_id = project_id
          project_node = '/appscale/projects/{}'.format(self.project_id)
  
          self._zk_client = zk_client
          self._key_node = '{}/private_key'.format(project_node)
          self._key = None
          self._ensure_private_key()
          self._zk_client.DataWatch(self._key_node, self._update_key)
  
          self._certs_node = '{}/certificates'.format(project_node)
          self._zk_client.ensure_path(self._certs_node)
          self._certs = []
          self._zk_client.ChildrenWatch(self._certs_node, self._update_certs)
  
          self._service_accounts_node = '{}/service_accounts'.format(
              project_node)
  
      def get_public_certificates(self):
          """ Retrieves a list of valid public certificates for the project.
  
          Returns:
              A list of PublicCertificate objects.
          Raises:
              UnknownError if unable to retrieve any certificates.
          """
          valid_certs = []
          for cert in self._certs:
              if cert.expired:
                  try:
                      self._remove_cert(cert)
                  except (KazooException, UnknownError):
                      pass
              else:
                  valid_certs.append(cert)
  
          if valid_certs:
              return valid_certs
  
          # If there are no valid certificates, try to generate a a new one.
          if self._key is None:
              raise UnknownError('A private key is not configured')
  
          new_cert = PublicCertificate.from_key(self._key, self.project_id)
          try:
              self._zk_client.create('{}/cert'.format(self._certs_node),
                                     new_cert.to_json(), sequence=True)
          except KazooException:
              raise UnknownError('Unable to create new certificate')
  
          return [new_cert]
  
      def get_service_account_name(self):
          """ Retrieves the default service account name.
  
          Returns:
              A string specifying the service account name.
          Raises:
              UnknownError if no service account is configured.
          """
          if self._key is None:
              raise UnknownError('A private key is not configured')
  
          return self._key.key_name
  
      def get_access_token(self, scopes, service_account_id=None,
                           service_account_name=None):
          """ Generates an access token from a service account.
  
          Args:
              scopes: A list of strings specifying scopes.
              service_account_id: An integer specifying a service account ID.
             
  
  [... the rest is omitted ...]
files.new_path: APIServer/appscale/api_server/app_identity.py
files.old_contents: """ Implements the App Identity API. """
  
  import logging
  
  from kazoo.exceptions import KazooException
  from kazoo.exceptions import NoNodeError
  from kazoo.exceptions import NodeExistsError
  from tornado.ioloop import IOLoop
  
  from appscale.api_server import app_identity_service_pb2 as service_pb
  from appscale.api_server import crypto
  from appscale.api_server.base_service import BaseService
  from appscale.api_server.constants import ApplicationError
  from appscale.api_server.constants import CallNotFound
  from appscale.api_server.crypto import PrivateKey
  from appscale.api_server.crypto import PublicCertificate
  from appscale.common.async_retrying import retry_children_watch_coroutine
  
  logger = logging.getLogger(__name__)
  
  
  class UnknownError(Exception):
      """ Indicates that the request cannot be completed at this time. """
      pass
  
  
  class AppIdentityService(BaseService):
      """ Implements the App Identity API. """
      SERVICE_NAME = 'app_identity_service'
  
      # A dummy bucket name for satisfying calls.
      DEFAULT_GCS_BUCKET_NAME = 'app_default_bucket'
  
      # The appropriate messages for each API call.
      METHODS = {'SignForApp': (service_pb.SignForAppRequest,
                                service_pb.SignForAppResponse),
                 'GetPublicCertificatesForApp': (
                     service_pb.GetPublicCertificateForAppRequest,
                     service_pb.GetPublicCertificateForAppResponse),
                 'GetServiceAccountName': (
                     service_pb.GetServiceAccountNameRequest,
                     service_pb.GetServiceAccountNameResponse),
                 'GetAccessToken': (service_pb.GetAccessTokenRequest,
                                    service_pb.GetAccessTokenResponse),
                 'GetDefaultGcsBucketName': (
                     service_pb.GetDefaultGcsBucketNameRequest,
                     service_pb.GetDefaultGcsBucketNameResponse)}
  
      def __init__(self, project_id, zk_client):
          """ Creates a new AppIdentityService.
  
          Args:
              project_id: A string specifying the project ID.
              zk_client: A KazooClient.
          """
          super(AppIdentityService, self).__init__(self.SERVICE_NAME)
  
          self.project_id = project_id
  
          self._zk_client = zk_client
          self._key_node = '/appscale/projects/{}/private_key'.format(
              self.project_id)
          self._key = None
          self._ensure_private_key()
          self._zk_client.DataWatch(self._key_node, self._update_key)
  
          self._certs_node = '/appscale/projects/{}/certificates'.format(
              self.project_id)
          self._zk_client.ensure_path(self._certs_node)
          self._certs = []
          self._zk_client.ChildrenWatch(self._certs_node, self._update_certs)
  
      def get_public_certificates(self):
          """ Retrieves a list of valid public certificates for the project.
  
          Returns:
              A list of PublicCertificate objects.
          Raises:
              UnknownError if unable to retrieve any certificates.
          """
          valid_certs = []
          for cert in self._certs:
              if cert.expired:
                  try:
                      self._remove_cert(cert)
                  except (KazooException, UnknownError):
                      pass
              else:
                  valid_certs.append(cert)
  
          if valid_certs:
              return valid_certs
  
          # If there are no valid certificates, try to generate a a new one.
          if self._key is None:
              raise UnknownError('A private key is not configured')
  
          new_cert = PublicCertificate.from_key(self._key, self.project_id)
          try:
              self._zk_client.create('{}/cert'.format(self._certs_node),
                                     new_cert.to_json(), sequence=True)
          except KazooException:
              raise UnknownError('Unable to create new certificate')
  
          return [new_cert]
  
      def get_service_account_name(self):
          """ Retrieves the default service account name.
  
          Returns:
              A string specifying the service account name.
          Raises:
              UnknownError if no service account is configured.
          """
          if self._key is None:
              raise UnknownError('A private key is not configured')
  
          return self._key.key_name
  
      def get_access_token(self, scopes, service_account_id=None,
                           service_account_name=None):
          """ Generates an access token from a service account.
  
          Args:
              scopes: A list of strings specifying scopes.
              service_account_id: An integer specifying a service account ID.
              service_account_name: A string specifying a service account name.
          Returns:
              An AccessToken.
          Raises:
              UnknownError if the service account is not configured.
  
  
  [... the rest is omitted ...]
files.old_path: APIServer/appscale/api_server/app_identity.py
files.new_contents: """ Manages cryptography-related data and operations. """
  
  import base64
  import datetime
  import json
  import time
  
  import six
  from cryptography import x509
  from cryptography.hazmat.backends import default_backend
  from cryptography.hazmat.primitives import hashes
  from cryptography.hazmat.primitives import serialization
  from cryptography.hazmat.primitives.asymmetric import padding
  from cryptography.hazmat.primitives.asymmetric import rsa
  from cryptography.x509.oid import NameOID
  
  # The length of time a certificate should remain valid for.
  CERTIFICATE_TTL = datetime.timedelta(days=10)
  
  # The generated key length in bits.
  KEY_SIZE = 2048
  
  # The public exponent to use when generating keys.
  PUBLIC_EXPONENT = 65537
  
  
  class InvalidKey(Exception):
      """ Indicates that a private key is invalid. """
      pass
  
  
  class InvalidCertificate(Exception):
      """ Indicates that a certificate is invalid. """
      pass
  
  
  class AccessToken(object):
      """ Holds an access token and its metadata. """
      def __init__(self, token, expiration_time):
          """ Creates a new AccessToken.
  
          Args:
              token: A string containing an access token.
              expiration_time: An integer specifying a unix timestamp.
          """
          self.token = token
          self.expiration_time = expiration_time
  
  
  class PrivateKey(object):
      """ Holds a private key and its metadata. """
      ENCODING = serialization.Encoding.PEM
      ENCRYPTION = serialization.NoEncryption()
      FORMAT = serialization.PrivateFormat.PKCS8
      PADDING = padding.PKCS1v15()
      TOKEN_LIFETIME = 3600
  
      def __init__(self, key_name, key):
          """ Creates a new PrivateKey.
  
          Args:
              key_name: A string specifying the key name.
              key: An RSAPrivateKey.
          """
          self.key_name = key_name
          self.key = key
  
      @property
      def pem(self):
          """ Serializes the private key.
  
          Returns:
              A binary type containing a PEM-formatted key.
          """
          return self.key.private_bytes(self.ENCODING, self.FORMAT,
                                        self.ENCRYPTION)
  
      def generate_assertion(self, audience, scopes):
          """ Creates an access token signed by the key.
  
          Args:
              project_id: A string specifying the project ID.
              scopes: A list of strings specifying scopes.
          Returns:
              An AccessToken.
          """
          def encode_part(part):
              if isinstance(part, dict):
                  part = json.dumps(part, separators=(',', ':')).encode('utf-8')
  
              return base64.urlsafe_b64encode(part).rstrip('=')
  
          header = encode_part({'typ': 'JWT', 'alg': 'RS256'})
          current_time = int(time.time())
          metadata = encode_part({'iss': self.key_name,
                                  'aud': audience,
                                  'scope': ' '.join(scopes),
                                  'iat': current_time,
                                  'exp': current_time + self.TOKEN_LIFETIME})
  
          signature = self.sign('.'.join([header, metadata]))
          return '.'.join([header, metadata, encode_part(signature)])
  
      def sign(self, blob):
          """ Signs a given payload.
  
          Args:
              blob: A binary type containing an arbitrary payload.
          Returns:
              A binary type containing the signed payload.
          """
          return self.key.sign(blob, self.PADDING, hashes.SHA256())
  
      def to_json(self):
          """ Serializes the key details as JSON.
  
          Returns:
              A string containing the JSON-encoded key details.
          """
          return json.dumps({'key_name': self.key_name, 'pem': self.pem})
  
      @classmethod
      def generate(cls, key_name):
          """ Creates a new key with the given name.
  
          Args:
              key_name: A string specifying the key name.
          Returns:
              A PrivateKey.
          """
          key = rsa.generate_private_key(PUBLIC_EXPONENT, KEY_SIZE,
                                         default_backend())
          return cls(key_name, key)
  
      @classmethod
      def from_json(cls, key_data):
          """ Creates a new key from a JSON-encoded string.
  
          Args:
              key_data: A JSON string containing key details.
          Returns:
              A PrivateKey.
          """
          try:
              key_details = json.loads(key_data)
          except (TypeError, ValueError):
              raise InvalidKey('Invalid JSON')
  
          try:
              key_name = key_details['
  
  [... the rest is omitted ...]
files.new_path: APIServer/appscale/api_server/crypto.py
files.old_contents: """ Manages cryptography-related data and operations. """
  
  import base64
  import datetime
  import json
  import time
  
  import six
  from cryptography import x509
  from cryptography.hazmat.backends import default_backend
  from cryptography.hazmat.primitives import hashes
  from cryptography.hazmat.primitives import serialization
  from cryptography.hazmat.primitives.asymmetric import padding
  from cryptography.hazmat.primitives.asymmetric import rsa
  from cryptography.x509.oid import NameOID
  
  # The length of time a certificate should remain valid for.
  CERTIFICATE_TTL = datetime.timedelta(days=10)
  
  # The generated key length in bits.
  KEY_SIZE = 2048
  
  # The public exponent to use when generating keys.
  PUBLIC_EXPONENT = 65537
  
  
  class InvalidKey(Exception):
      """ Indicates that a private key is invalid. """
      pass
  
  
  class InvalidCertificate(Exception):
      """ Indicates that a certificate is invalid. """
      pass
  
  
  class AccessToken(object):
      """ Holds an access token and its metadata. """
      def __init__(self, token, expiration_time):
          """ Creates a new AccessToken.
  
          Args:
              token: A string containing an access token.
              expiration_time: An integer specifying a unix timestamp.
          """
          self.token = token
          self.expiration_time = expiration_time
  
  
  class PrivateKey(object):
      """ Holds a private key and its metadata. """
      ENCODING = serialization.Encoding.PEM
      ENCRYPTION = serialization.NoEncryption()
      FORMAT = serialization.PrivateFormat.PKCS8
      PADDING = padding.PSS(mgf=padding.MGF1(hashes.SHA256()),
                            salt_length=padding.PSS.MAX_LENGTH)
      TOKEN_LIFETIME = 1800
  
      def __init__(self, key_name, key):
          """ Creates a new PrivateKey.
  
          Args:
              key_name: A string specifying the key name.
              key: An RSAPrivateKey.
          """
          self.key_name = key_name
          self.key = key
  
      @property
      def pem(self):
          """ Serializes the private key.
  
          Returns:
              A binary type containing a PEM-formatted key.
          """
          return self.key.private_bytes(self.ENCODING, self.FORMAT,
                                        self.ENCRYPTION)
  
      def generate_access_token(self, project_id, scopes):
          """ Creates an access token signed by the key.
  
          Args:
              project_id: A string specifying the project ID.
              scopes: A list of strings specifying scopes.
          Returns:
              An AccessToken.
          """
          def encode_part(part):
              serialized = json.dumps(part, separators=(',', ':'))
              return base64.urlsafe_b64encode(serialized).rstrip('=')
  
          header = encode_part({'typ': 'JWT', 'alg': 'RS256'})
          expiration_time = int(time.time() + self.TOKEN_LIFETIME)
          metadata = encode_part({'project_id': project_id,
                                  'exp': expiration_time,
                                  'scopes': scopes})
  
          signature = self.sign('.'.join([header, metadata]))
          encoded_sig = base64.urlsafe_b64encode(signature).rstrip('=')
          token = '.'.join([header, metadata, encoded_sig])
          return AccessToken(token, expiration_time)
  
      def sign(self, blob):
          """ Signs a given payload.
  
          Args:
              blob: A binary type containing an arbitrary payload.
          Returns:
              A binary type containing the signed payload.
          """
          return self.key.sign(blob, self.PADDING, hashes.SHA256())
  
      def to_json(self):
          """ Serializes the key details as JSON.
  
          Returns:
              A string containing the JSON-encoded key details.
          """
          return json.dumps({'key_name': self.key_name, 'pem': self.pem})
  
      @classmethod
      def generate(cls, key_name):
          """ Creates a new key with the given name.
  
          Args:
              key_name: A string specifying the key name.
          Returns:
              A PrivateKey.
          """
          key = rsa.generate_private_key(PUBLIC_EXPONENT, KEY_SIZE,
                                         default_backend())
          return cls(key_name, key)
  
      @classmethod
      def from_json(cls, key_data):
          """ Creates a new key from a JSON-encoded string.
  
          Args:
              key_data: A JSON string containing key details.
          Returns:
              A PrivateKey.
          """
          try:
              key_details = json.loads(key_data)
          except (
  
  [... the rest is omitted ...]
files.old_path: APIServer/appscale/api_server/crypto.py