 belongs.
      path: Canonical path of the location.
      uuid: Externally visible UUID of the location, allows to identify a REST
          resource representing the location.

      open_access: can be:
        disabled ('n') - only explicitly allowed users can access a location;
        enabled ('y') - everyone can access a location, no login is required;
        (the attribute is a char not a bool for historical
         reasons. 'a' mode used to be also supported that allowed
         everyone access but required authentication).

    """
    class Meta:
        app_label = 'wwwhisper_auth'
        unique_together = ('site', 'path')

    OPEN_ACCESS_CHOICES = (
        ('n', 'no open access'),
        ('y', 'open access'),
        )
    site = models.ForeignKey(Site, related_name='+')
    path = models.TextField(db_index=True)
    uuid = models.CharField(max_length=36, db_index=True,
                            editable=False, unique=True)
    open_access = models.CharField(max_length=2, choices=OPEN_ACCESS_CHOICES,
                                   default='n')

    def __init__(self, *args, **kwargs):
        super(Location, self).__init__(*args, **kwargs)

    def permissions(self):
        # Does not run a query to get permissions if not needed.
        return self.site.locations.get_permissions(self.id)

    def __unicode__(self):
        return "%s" % (self.path)

    @models.permalink
    def get_absolute_url(self):
        """Constructs URL of the location resource."""
        return ('wwwhisper_location', (), {'uuid' : self.uuid})

    @modify_site
    def grant_open_access(self):
        """Allows to access the location without authentication."""
        self.open_access = 'y'
        self.save()

    def open_access_granted(self):
        return self.open_access == 'y'

    @modify_site
    def revoke_open_access(self):
        self.open_access = 'n'
        self.save()

    def can_access(self, user):
        """Determines if a user can access the location.

        Returns:
            True if the user is granted permission to access the
            location or it the location is open.
        """
        # Sanity check (this should normally be ensured by the caller).
        if user.site_id!= self.site_id:
            return False
        return (self.open_access_granted()
                or self.permissions().get(user.id)!= None)

    @modify_site
    def grant_access(self, user_uuid):
        """Grants access to the location to a given user.

        Args:
            user_uuid: string UUID of a user.

        Returns:
            (new Permission object, True) if access to the location was
                successfully granted.
            (existing Permission object, False) if user already had
                granted access to the location.

        Raises:
            LookupError: A site to which location belongs has no user
                with a given UUID.
        """
        user = self.site.users.find_item(uuid=user_uuid)
        if user is None:
            raise LookupError('User not found')
        permission = self.permissions().get(user.id)
        created = False
        if permission is None:
            created = True
            permission = Permission.objects.create(
                http_location_id=self.id, user_id=user.id, site_id=self.site_id)
        return (permission, created)

    @modify_site
    def revoke_access(self, user_uuid):
        """Revokes access to the location from a given user.

        Args:
            user_uuid: string UUID of a user.

        Raises:
            LookupError: Site has no user with a given UUID or the
                user can not access the location.
        """
        permission = self.get_permission(user_uuid)
        permission.delete()

    def get_permission(self, user_uuid):
        """Gets Permission object for a given user.

        Args:
            user_uuid: string UUID of a user.

        Raises:
            LookupError: No user with a given UUID or the user can not
                access the location.
        """
        user = self.site.users.find_item(uuid=user_uuid)
        if user is None:
            raise LookupError('User not found.')
        permission = self.permissions().get(user.id)
        if permission is None:
            raise LookupError('User can not access location.')
        return permission

    def allowed_users(self):
        """"Returns a list of users that can access the location."""
        # The code could access permission.user like this:
        # [perm.user for perm in self.permissions().itervalues()]
        # but this involves a single DB query per allowed user, going
        # through cached site.users involves no queries.
        return [self.site.users.find_item_by_pk(user_id)
                for user_id in self.permissions().iterkeys()]

    def attributes_dict(self, site_url):
        """Returns externally visible attributes of the location resource."""
        result = {
            'path': self.path,
            'allowedUsers': [
                user.attributes_dict(site_url) for user in self.allowed_users()
                ],
            }
        if self.open_access_granted():
            result['openAccess'] = True
        return _add_common_attributes(self, site_url, result)

class Permission(ValidatedModel):
    """Connects a location with a user that can access the location.

    Attributes:
        http_location: The location to which the Permission object gives access.
        user: The user that is given access to the location.
    """

    http_location = models.ForeignKey(Location, related_name='+')
    site = models.ForeignKey(Site, related_name='+')
    user = models.ForeignKey(User, related_name='+')

    def __unicode__(self):
        return "%s, %s" % (self.http_location, self.user.email)

    @models.permalink
    def get_absolute_url(self):
        """Constructs URL of the permission resource."""
        return ('wwwhisper_allowed_user', (),
                {'location_uuid' : self.http_location.uuid,
                 'user_uuid': self.user.uuid})

    def attributes_dict(self, site_url):
        """Returns externally visible attributes of the permission resource."""
        return _add_common_attributes(
            self, site_url, {'user': self.user.attributes_dict(site_url)})

class Alias(ValidatedModel):
    """One of urls that can be used to access the site.

    Attributes:
      site: Site to which the alias belongs.
      url: Has form http(s)://domain[:port], default ports (80 for http,
         443 for https) are always stripped.
      uuid: Externally visible UUID of the alias.
    """
    class Meta:
        app_label = 'wwwhisper_auth'
        unique_together = ('site', 'url')

    site = models.ForeignKey(Site, related_name='+')
    url = models.TextField(db_index=True)
    uuid = models.CharField(max_length=36, db_index=True,
                            editable=False, unique=True)
    force_ssl = models.BooleanField(default=False)

    @models.permalink
    def get_absolute_url(self):
        return ('wwwhisper_alias', (), {'uuid' : self.uuid})

    def attributes_dict(self, site_url):
        return _add_common_attributes(self, site_url, {'url': self.url})


class Collection(object):
    """A common base class for managing a collection of resources.

    All resources in a collection belong to a common site and only
    this site can manipulate the resouces.

    Resources in the collection are of the same type and need to be
    identified by an UUID.

    Attributes (Need to be defined in subclasses):
        item_name: Name of a resource stored in the collection.
        model_class: Class that manages storage of resources.
