text: 
  import os
  import uuid
  import urllib
  import logging
  
  import predix.config
  import predix.service
  
  
  class AccessControl(object):
      """
      Use the Access Control service to provide a more powerful authorization
      framework than basic User Account and Authorization (UAA) service.
  
      Access Control service provides app-specific policies without adding
      overhead to a UAA server that may become the entry point for several apps
      over time.
  
      :param uri: The URI endpoint for accessing the ACS service instance.
  
      :param zone_id: The Predix-Zone-Id assigned to the tenant for the ACS
      service instance.
  
      """
      def __init__(self, uri=None, zone_id=None, *args, **kwargs):
          super(AccessControl, self).__init__(*args, **kwargs)
  
          self.zone_id = zone_id or self._get_zone_id()
          self.uri = uri or self._get_uri()
  
          self.service = predix.service.Service(self.zone_id)
  
      def _get_zone_id(self):
          """
          Returns the Predix Zone Id for the service that is a required
          header in service calls.
          """
          if 'VCAP_SERVICES' in os.environ:
              services = json.loads(os.getenv('VCAP_SERVICES'))
              predix_acs = services['predix-acs'][0]['credentials']
              return predix_acs['zone']['http-header-value']
          else:
              return predix.config.get_env_value(self, 'zone_id')
  
      def _get_uri(self):
          """
          Returns the URI endpoint for an instance of the ACS
          service from environment inspection.
          """
          if 'VCAP_SERVICES' in os.environ:
              services = json.loads(os.getenv('VCAP_SERVICES'))
              predix_acs = services['predix-acs'][0]['credentials']
              return predix_acs['uri']
          else:
              return predix.config.get_env_value(self, 'uri')
  
      def authenticate_as_client(self, client_id, client_secret):
          """
          Will authenticate for the given client / secret.
          """
          self.service.uaa.authenticate(client_id, client_secret)
  
      def _get_resource_uri(self, guid=None):
          """
          Returns the full path that uniquely identifies
          the resource endpoint.
          """
          uri = self.uri + '/v1/resource'
          if guid:
              uri += '/' + urllib.quote_plus(guid)
          return uri
  
      def get_resources(self):
          """
          Return all of the resources in the ACS service.
          """
          uri = self._get_resource_uri()
          return self.service._get(uri)
  
      def get_resource(self, resource_id):
          """
          Returns a specific resource by resource id.
          """
          # resource_id could be a path such as '/asset/123' so quote
          uri = self._get_resource_uri(guid=resource_id)
          return self.service._get(uri)
  
      def _post_resource(self, body):
          """
          Create new resources and associated attributes.
  
          Example:
  
              acs.post_resource([
                  {
                      "resourceIdentifier": "masaya",
                      "parents": [],
                      "attributes": [
                          {
                              "issuer": "default",
                              "name": "country",
                              "value": "Nicaragua"
                              }
                          ],
                  }
              ])
  
          The issuer is effectively a namespace, and in policy evaluations you
          identify an attribute by a specific namespace.  Many examples provide
          a URL but it could be any arbitrary string.
  
          The body is a list, so many resources can be added at the same time.
          """
          assert isinstance(body, (list)), "POST for requires body to be a list"
          uri = self._get_resource_uri()
          return self.service._post(uri, body)
  
      def delete_resource(self, resource_id):
          """
          Remove a specific resource by its identifier.
          """
          # resource_id could be a path such as '/asset/123' so quote
          uri = self._get_resource_uri(guid=resource_id)
          return self.service._delete(uri)
  
      def _put_resource(self, resource_id, body):
          """
          Update a resource for the given resource id.  The body is not
          a list but a dictionary of a single resource.
          """
          assert isinstance(body, (dict)), "PUT requires body to be a dict."
          # resource_id could be a path such as '/asset/123' so quote
          uri = self._get_resource_uri(guid=resource_id)
          return self.service._put(uri, body)
  
      def add_resource(self, resource_id, attributes, parents=[],
              issuer='default'):
          """
          Will add the given resource with a given identifier and attribute
          dictionary.
  
              example/
  
                  add_resource('/asset/12', {'id': 12, 'manufacturer': 'GE'})
          """
          # MAINT: consider test to avoid adding duplicate resource id
          assert isinstance(attributes, (dict)), "attributes expected to be dict"
  
          attrs = []
          for key in attributes.keys():
              attrs.append({
                  'issuer': issuer,
                  'name': key,
                  'value': attributes[key]
                  })
  
          body = {
              "resourceIdentifier": resource_id,
              "parents": parents,
              "attributes": attrs,
          }
  
          return self._put_resource(resource_id, body)
  
      def _get_subject_uri(self, guid=None):
          """
          Returns the full path that uniquely identifies
          the subject endpoint.
          """
          uri = self.uri + '/v1/subject'
          if guid:
              uri += '/' + urllib.quote_plus(guid)
          return uri
  
      def get_subjects(self):
          """
          Return all of the subjects in the ACS service.
          """
          uri = self._get_subject_uri()
          return self.service._get(uri)
  
      def get_subject(self, subject_id):
          """
          Returns a specific subject by subject id.
          """
          # subject_id could be a path such as '/user/j12y' so quote
          uri = self._get_subject_uri(guid=subject_id)
          return self.service._get(uri)
  
      def _post_subject(self, body):
          """
          Create new subjects and associated attributes.
  
          Example:
  
              acs.post_subject([
                  {
                      "subjectIdentifier": "/role/evangelist",
                      "parents": [],
                      "attributes": [
                          {
                              "issuer": "default",
                              "name": "role",
                              "value": "developer evangelist",
                          }
                      ]
                  }
              ])
  
          The issuer is effectively a namespace, and in policy evaluations
          you identify an attribute by a specific namespace.  Many examples
          provide a URL but it could be any arbitrary string.
  
          The body is a list, so many subjects can be added at the same time.
          """
          assert isinstance(body, (list)), "POST requires body to be a list"
  
          uri = self._get_subject_uri()
          return self.service._post(uri, body)
  
      def delete_subject(self, subject_id):
          """
          Remove a specific subject by its identifier.
          """
          # subject_id could be a path such as '/role/analyst' so quote
          uri = self._get_subject_uri(guid=subject_id)
          return self.service._delete(uri)
  
      def _put_subject(self, subject_id, body):
          """
          Update a subject for the given subject id.  The body is not
          a list but a dictionary of a single resource.
          """
          assert isinstance(body, (dict)), "PUT requires body to be dict."
  
          # subject_id could be a path such as '/asset/123' so quote
          uri = self._get_subject_uri(guid=subject_id)
          return self.service._put(uri, body)
  
      def add_subject(self, subject_id, attributes, parents=[],
              issuer='default'):
          """
          Will add the given subject with a given identifier and attribute
          dictionary.
  
              example/
  
                  add_subject('/user/j12y', {'username': 'j12y'})
          """
          # MAINT: consider test to avoid adding duplicate subject id
          assert isinstance(attributes, (dict)), "attributes expected to be dict"
  
          attrs = []
          for key in attributes.keys():
              attrs.append({
                  'issuer': issuer,
                  'name': key,
                  'value': attributes[key]
                  })
  
          body = {
              "subjectIdentifier": subject_id,
              "parents": parents,
              "attributes": attrs,
          }
  
          return self._put_subject(subject_id, body)
  
      def _get_monitoring_heartbeat(self):
          """
          Tests whether or not the ACS service being monitored is alive.
          """
          target = self.uri + '/monitoring/heartbeat'
          response = self.session.get(target)
          return response
  
      def is_alive(self):
          """
          Will test whether the ACS service is up and alive.
          """
          response = self.get_monitoring_heartbeat()
          if response.status_code == 200 and response.content == 'alive':
              return True
  
          return False
  
      def _get_policy_set_uri(self, guid=None):
          """
          Returns the full path that uniquely identifies
          the subject endpoint.
          """
          uri = self.uri + '/v1/policy-set'
          if guid:
              uri += '/' + urllib.quote_plus(guid)
          return uri
  
      def get_policy_sets(self):
          """
          Return all of the policy sets in the ACS service.
          """
          uri = self._get_policy_set_uri()
          return self.service._get(uri)
  
      def _put_policy_set(self, policy_set_id, body):
          """
          Will create or update a policy set for the given path.
          """
          assert isinstance(body, (dict)), "PUT requires body to be a dict."
          uri = self._get_policy_set_uri(guid=policy_set_id)
          return self.service._put(uri, body)
  
      def _get_policy_set(self, policy_set_id):
          """
          Get a specific policy set by id.
          """
          uri = self._get_policy_set_uri(guid=policy_set_id)
          return self.service._get(uri)
  
      def delete_policy_set(self, policy_set_id):
          """
          Delete a specific policy set by id.  Method is idempotent.
          """
          uri = self._get_policy_set_uri(guid=policy_set_id)
          return self.service._delete(uri)
  
      def add_policy(self, name, action, resource, subject, condition,
              policy_set_id=None, effect='PERMIT'):
          """
          Will create a new policy set to enforce the given policy details.
  
          The name is just a helpful descriptor for the policy.
  
          The action maps to a HTTP verb.
  
          Policies are evaluated against resources and subjects.  They are
          identified by matching a uriTemplate or attributes.
  
          Examples::
  
              resource = {
                  "uriTemplate": "/asset/{id}"
                  }
  
              subject: {
                  "attributes": [{
                      "issuer": "default",
                      "name": "role"
                      }]
                  }
  
          The condition is expected to be a string that defines a groovy
          operation that can be evaluated.
  
          Examples::
  
              condition = "match.single(subject.attributes('default', 'role'),
                  'admin')
  
          """
          # If not given a policy set id will generate one
          if not policy_set_id:
              policy_set_id = str(uuid.uuid4())
  
          # Only a few operations / actions are supported in policy definitions
          if action not in ['GET', 'PUT', 'POST', 'DELETE']:
              raise ValueError("Invalid action")
  
          # Defines a single policy to be part of the policy set.
          policy = {
              "name": name,
              "target": {
                  "resource": resource,
                  "subject": subject,
                  "action": action,
                  },
              "conditions": [{
                  "name": "",
                  "condition": condition,
                  }],
              "effect": effect,
          }
  
          # Body of the request is a list of policies
          body = {
              "name": policy_set_id,
              "policies": [policy],
          }
  
          result = self._put_policy_set(policy_set_id, body)
          return result
  
      def is_allowed(self, subject_id, action, resource_id, policy_sets=[]):
          """
          Evaluate a policy-set against a subject and resource.
  
          example/
  
              is_allowed('/user/j12y', 'GET', '/asset/12')
  
          """
          body = {
              "action": action,
              "subjectIdentifier": subject_id,
              "resourceIdentifier": resource_id,
          }
  
          if policy_sets:
              body['policySetsEvaluationOrder'] = policy_sets
  
          # Will return a 200 with decision
          uri = self.uri + '/v1/policy-evaluation'
  
          logging.debug("URI=" + str(uri))
          logging.debug("BODY=" + str(body))
  
          response = self.service._post(uri, body)
  
          if 'effect' in response:
              if response['effect'] in ['NOT_APPLICABLE', 'PERMIT']:
                  return True
  
          return False
  