示例#1
0
 def testGetCurrent_AfterGetForEmailWithNonexistentGoogleAccount(self):
   self.mox.stubs.Set(users, '_EmailToGaeUserId', {}.get)
   # Introduce a previously unseen e-mail address.
   self.assertEquals('1', users.GetForEmail('*****@*****.**').id)
   # A sign-in with that e-mail address should associate the Google Account.
   with test_utils.EnvContext(USER_ID='123456789', USER_EMAIL='*****@*****.**'):
     self.assertEquals('1', users.GetCurrent().id)
   # Subsequent sign-ins with the same Google Account ID should also hook up.
   with test_utils.EnvContext(USER_ID='123456789',
                              USER_EMAIL='*****@*****.**'):
     self.assertEquals('1', users.GetCurrent().id)
示例#2
0
 def testGetCurrent_GoogleAccountMapping(self):
     with test_utils.EnvContext(USER_ID='123456789',
                                USER_EMAIL='*****@*****.**'):
         user = users.GetCurrent()  # should allocate the first uid, '1'
         self.assertEquals('1', user.id)
     with test_utils.EnvContext(USER_ID='666666',
                                USER_EMAIL='*****@*****.**'):
         user = users.GetCurrent()  # should allocate the next uid, '2'
         self.assertEquals('2', user.id)
     with test_utils.EnvContext(USER_ID='123456789',
                                USER_EMAIL='*****@*****.**'):
         user = users.GetCurrent()  # should match by USER_ID
         self.assertEquals('1', user.id)
示例#3
0
 def testGetCurrent_UpdateEmailAndDomain(self):
     with test_utils.EnvContext(USER_ID='123456789',
                                USER_EMAIL='*****@*****.**',
                                USER_ORGANIZATION='alpha.test'):
         users.GetCurrent()  # should allocate the first uid, '1'
         user = users.Get('1')
         self.assertEquals('alpha.test', user.ga_domain)
         self.assertEquals('*****@*****.**', user.email)
     with test_utils.EnvContext(USER_ID='123456789',
                                USER_EMAIL='*****@*****.**',
                                USER_ORGANIZATION='beta.test'):
         users.GetCurrent()  # should update the existing UserModel
         user = users.Get('1')
         self.assertEquals('beta.test', user.ga_domain)
         self.assertEquals('*****@*****.**', user.email)
示例#4
0
    def RenderTemplate(self, template_name, context):
        """Renders a template from the templates/ directory.

    Args:
      template_name: A string, the filename of the template to render.
      context: An optional dictionary of template variables.  A few variables
          are automatically added to this context:
            - {{root}} is the root_path of the app
            - {{user}} is the signed-in user
            - {{login_url}} is a URL to a sign-in page
            - {{logout_url}} is a URL that signs the user out
            - {{navbar}} contains variables used by the navigation sidebar
    Returns:
      A string, the rendered template.
    """
        path = os.path.join(os.path.dirname(__file__), 'templates',
                            template_name)
        root = config.Get('root_path') or ''
        user = users.GetCurrent()
        context = dict(context,
                       root=root,
                       user=user,
                       xsrf_tag=self.xsrf_tag,
                       login_url=users.GetLoginUrl(self.request.url),
                       logout_url=users.GetLogoutUrl(root + '/.maps'),
                       navbar=self._GetNavbarContext(user))
        return template.render(path, context)
示例#5
0
 def CheckAccess(self):
     """If login_access_list is set, accept only the specified logins."""
     login_access_list = config.Get('login_access_list')
     if login_access_list is not None:
         user = users.GetCurrent()
         if not user:
             raise RedirectToUrl(users.GetLoginUrl(self.request.url))
         if user.email not in login_access_list:
             raise perms.AuthorizationError(user, None, None)
示例#6
0
 def testGetCurrent_NormalLogin(self):
     # Try an effective user determined by the Google Account login.
     with test_utils.EnvContext(USER_ID='123456789',
                                USER_EMAIL='*****@*****.**',
                                USER_ORGANIZATION='alpha.test'):
         user = users.GetCurrent()  # should allocate the first uid, '1'
         self.assertEquals('1', user.id)
         user = users.Get('1')
         self.assertEquals('alpha.test', user.ga_domain)
         self.assertEquals('*****@*****.**', user.email)
示例#7
0
 def testGetCurrent_ImpersonationNotAllowed(self):
   # Verify that the crisismap_login cookie doesn't work for ordinary users.
   with test_utils.EnvContext(
       SERVER_SOFTWARE='Google App Engine/1.7.6', USER_ID='123456789',
       USER_EMAIL='*****@*****.**', USER_ORGANIZATION='alpha.test',
       HTTP_COOKIE='crisismap_login=t1000:sky.net:[email protected]'):
     user = users.GetCurrent()
     self.assertEquals('1', user.id)  # cookie should be ignored
     self.assertEquals('alpha.test', user.ga_domain)
     self.assertEquals('*****@*****.**', user.email)
示例#8
0
 def testGetCurrent_ImpersonationInDev(self):
   # Verify that the crisismap_login cookie works in development.
   with test_utils.EnvContext(
       SERVER_SOFTWARE='Development/1.0',
       USER_ID='123456789', USER_EMAIL='*****@*****.**',
       USER_ORGANIZATION='alpha.test',
       HTTP_COOKIE='crisismap_login=t1000:sky.net:[email protected]'):
     user = users.GetCurrent()
     self.assertEquals('t1000', user.id)  # cookie should be used
     self.assertEquals('sky.net', user.ga_domain)
     self.assertEquals('*****@*****.**', user.email)
示例#9
0
 def testGetCurrent_ImpersonationInProd(self):
   # Verify that the crisismap_login cookie works for admins in prod.
   with test_utils.EnvContext(
       SERVER_SOFTWARE='Google App Engine/1.7.6',
       USER_ID='123456789', USER_EMAIL='*****@*****.**',
       USER_ORGANIZATION='alpha.test', USER_IS_ADMIN='1',
       HTTP_COOKIE='crisismap_login=t1000:sky.net:[email protected]'):
     user = users.GetCurrent()
     self.assertEquals('t1000', user.id)  # cookie should be used
     self.assertEquals('sky.net', user.ga_domain)
     self.assertEquals('*****@*****.**', user.email)
示例#10
0
def CheckAccess(role, target=None, user=None, policy=None):
    """Checks whether the given user has the specified access role.

  Args:
    role: A Role constant identifying the desired access role.
    target: The object to which access is desired.  If 'role' is in MAP_ROLES,
        this should be a Map object; if 'role' is in DOMAIN_ROLES, this should
        be a domain name (a string); otherwise, this argument is unused.
    user: (optional) A users.User object.  If not specified, access permissions
        are checked for the currently signed-in user.
    policy: The access policy to apply.

  Returns:
    True if the user has the specified access permission.

  Raises:
    ValueError: The specified role is not a valid member of Role.
    TypeError: The target has the wrong type for the given role.
  """
    policy = policy or AccessPolicy()
    user = user or users.GetCurrent()

    # Roles that are unrelated to a target.
    if role == Role.ADMIN:
        return policy.HasRoleAdmin(user)

    # Roles with a domain as the target.
    if role in DOMAIN_ROLES and not isinstance(target, basestring):
        raise TypeError('For role %r, target should be a string' % role)
    if role == Role.CATALOG_EDITOR:
        return policy.HasRoleCatalogEditor(user, target)
    if role == Role.MAP_CREATOR:
        return policy.HasRoleMapCreator(user, target)
    if role == Role.DOMAIN_REVIEWER:
        return policy.HasRoleDomainReviewer(user, target)
    if role == Role.DOMAIN_ADMIN:
        return policy.HasRoleDomainAdmin(user, target)

    # Roles with a Map as the target
    if role in MAP_ROLES and target.__class__.__name__ not in [
            'Map', 'EmptyMap'
    ]:
        raise TypeError('For role %r, target should be a Map' % role)
    if role == Role.MAP_OWNER:
        return policy.HasRoleMapOwner(user, target)
    if role == Role.MAP_REVIEWER:
        return policy.HasRoleMapReviewer(user, target)
    if role == Role.MAP_EDITOR:
        return policy.HasRoleMapEditor(user, target)
    if role == Role.MAP_VIEWER:
        return policy.HasRoleMapViewer(user, target)

    raise ValueError('Invalid role %r' % role)
示例#11
0
    def GetCurrentUserUrl(self):
        """Gets a URL identifying the current user.

    If the user is logged in, the URL will contain the user ID.  Otherwise,
    we'll use a randomly generated cookie to make a semi-stable user URL.

    Returns:
      A URL (under this app's root URL) that identifies the current user.
    """
        user = users.GetCurrent()
        if user:
            return self.GetUrlForUser(user)
        return self.GetUrlForAnonymousUser()
示例#12
0
 def testGetCurrent_AfterGetForEmailWithExistingGoogleAccount(self):
   self.mox.stubs.Set(users, '_EmailToGaeUserId', {
       '*****@*****.**': '123456789',
       '*****@*****.**': '123456789'
   }.get)
   # Introduce an e-mail address that's new to us but has a Google Account.
   # GetForEmail should associate the address with the Google Account.
   self.assertEquals('1', users.GetForEmail('*****@*****.**').id)
   # A sign-in with that Google Account ID should get the same UserModel,
   # even if the e-mail address is different.
   with test_utils.EnvContext(USER_ID='123456789',
                              USER_EMAIL='*****@*****.**'):
     self.assertEquals('1', users.GetCurrent().id)
   # Any other e-mail address associated with the same Google Account should
   # yield the same UserModel.
   self.assertEquals('1', users.GetForEmail('*****@*****.**').id)
示例#13
0
    def SendPermissionChangeEmail(self, recipient_email, map_object, role,
                                  message):
        """Sends recipient_email an email with info of map and permission level."""
        email = users.GetCurrent().email
        subject = map_object.title
        url = (self.request.host_url + self.request.root_path + '/.maps/' +
               map_object.id)
        body = """
I've invited you to collaborate on the map "%s".
You can access the map at:

    %s

You have %s access; please use this invitation within 30 days.

%s""" % (map_object.title, url, SHARING_ROLES[role], message)
        mail.send_mail(email, recipient_email, subject, body)
示例#14
0
def AssertAccess(role, target=None, user=None, policy=None):
    """Requires that the given user has the specified access role.

  Args:
    role: A Role constant identifying the desired access role.
    target: The object to which access is desired.  If 'role' is in MAP_ROLES,
        this should be a Map object; if 'role' is in DOMAIN_ROLES, this should
        be a domain name (a string); otherwise, this argument is unused.
    user: (optional) A users.User object.  If not specified, access permissions
        are checked for the currently signed-in user.
    policy: The access policy to apply.

  Raises:
    AuthorizationError: If the user lacks the given access permission.
  """
    user = user or users.GetCurrent()  # ensure user is set in error message
    if not CheckAccess(role, target=target, user=user, policy=policy):
        raise AuthorizationError(user, role, target)
示例#15
0
def RecordEvent(event, domain_name=None, map_id=None, map_version_key=None,
                catalog_entry_key=None, acceptable_purpose=None,
                acceptable_org=None, org_name=None, uid=None):
  """Stores an event log entry."""
  if not uid:
    user = users.GetCurrent()
    uid = user and user.id or None
  try:
    EventLog(time=datetime.datetime.utcnow(),
             uid=uid,
             event=event,
             domain_name=domain_name,
             map_id=map_id,
             map_version_key=map_version_key,
             catalog_entry_key=catalog_entry_key,
             acceptable_purpose=acceptable_purpose,
             acceptable_org=acceptable_org,
             org_name=org_name).put()
  except Exception, e:  # pylint: disable=broad-except
    logging.exception(e)
示例#16
0
def AssertCatalogEntryOwner(entry, user=None):
    user = user or users.GetCurrent()
    if user.id != entry.creator_uid:
        raise NotCatalogEntryOwnerError(user, entry)
示例#17
0
def GetConfig(request, map_object=None, catalog_entry=None, xsrf_token=''):
  dev_mode = request.get('dev') and users.IsDeveloper()
  map_picker_items = GetMapPickerItems(
      catalog_entry and catalog_entry.domain or
      config.Get('primary_domain'), request.root_path)

  # Fill the cm_config dictionary.
  root = request.root_path
  xsrf_qs = '?xsrf_token=' + xsrf_token  # needed for all POST URLs
  result = {
      'dev_mode': dev_mode,
      'langs': base_handler.ALL_LANGUAGES,
      # Each endpoint that the JS client code uses gets an entry in config.
      'js_root': root + '/.js',
      'json_proxy_url': root + '/.jsonp',
      'kmlify_url': request.host_url + root + '/.kmlify',
      'login_url': users.GetLoginUrl(request.url),
      'logout_url': users.GetLogoutUrl(request.url),
      'map_picker_items': map_picker_items,
      'protect_url': root + '/.protect',
      'report_query_url': root + '/.api/reports',
      'report_post_url': root + '/.api/reports' + xsrf_qs,
      'vote_post_url': root + '/.api/votes' + xsrf_qs,
      'static_content_url': root + '/.static',
      'user_email': users.GetCurrent() and users.GetCurrent().email,
      'wms_configure_url': root + '/.wms/configure',
      'wms_tiles_url': root + '/.wms/tiles'
  }

  # Add settings from the selected client config, if any.
  result.update(GetClientConfig(request.get('client'),
                                request.headers.get('referer'), dev_mode))

  # Add the MapRoot data and other map-specific information.
  if catalog_entry:  # published map
    map_root = result['map_root'] = catalog_entry.map_root
    result['label'] = catalog_entry.label
    result['publisher_name'] = catalog_entry.publisher_name
    key = catalog_entry.map_version_key
  elif map_object:  # draft map
    map_root = result['map_root'] = map_object.map_root
    result['map_list_url'] = root + '/.maps'
    result['diff_url'] = root + '/.diff/' + map_object.id + xsrf_qs
    result['save_url'] = root + '/.api/maps/' + map_object.id + xsrf_qs
    result['share_url'] = root + '/.share/' + map_object.id + xsrf_qs
    result['api_maps_url'] = root + '/.api/maps'
    result['legend_url'] = root + '/.legend'
    result['wms_query_url'] = root + '/.wms/query'
    result['enable_editing'] = map_object.CheckAccess(perms.Role.MAP_EDITOR)
    result['draft_mode'] = True
    key = map_object.current_version_key

  # Parameters that depend on the MapRoot, for both published and draft maps.
  ui_region = request.get('gl')
  if map_object or catalog_entry:
    result['lang'] = base_handler.SelectLanguageForRequest(request, map_root)
    ui_region = map_root.get('region', ui_region)
    cache_key, sources = metadata.CacheSourceAddresses(key, result['map_root'])
    result['metadata'] = {s: METADATA_CACHE.Get(s) for s in sources}
    result['metadata_url'] = root + '/.metadata?ck=' + cache_key
    metadata.ActivateSources(sources)

  # Construct the URL for the Maps JavaScript API.
  api_url_params = {
      'sensor': 'false',
      'libraries': 'places,search,visualization,weather',
      'client': GetMapsApiClientId(request.host),
      'language': request.lang
  }
  if ui_region:
    api_url_params['region'] = ui_region
  result['maps_api_url'] = (MAPS_API_BASE_URL + '?' +
                            urllib.urlencode(api_url_params))

  maproot_url = request.get('maproot_url', '')
  if dev_mode or maproot_url.startswith(request.root_url + '/'):
    # It's always okay to fetch MapRoot JSON from a URL if it's from this app.
    # In developer mode only, allow MapRoot JSON from arbitrary URLs.
    result['maproot_url'] = maproot_url

  if dev_mode:
    # In developer mode only, allow query params to override the result.
    # Developers can also specify map_root directly as a query param.
    for name in (
        ClientConfig.properties().keys() + ['map_root', 'use_tab_panel']):
      value = request.get(name)
      if value:
        result[name] = json.loads(value)

  return result
示例#18
0
def SetupUser(context):
    """Ensures that the User for a login context exists in the datastore."""
    with context:
        return users.GetCurrent()  # implicitly updates the datastore
示例#19
0
    def HandleRequest(self, **kwargs):
        """A wrapper around the Get or Post method defined in the handler class."""
        try:
            method = getattr(self, self.request.method.capitalize(), None)
            root_path = config.Get('root_path') or ''
            user = users.GetCurrent()

            if not method:
                raise Error(405,
                            '%s method not allowed.' % self.request.method)

            # Enforce login restrictions.
            self.CheckAccess()

            # Set self.auth according to the API key in the request, if specified.
            self.auth = GetAuthForRequest(self.request)

            # Require/allow domain name and user sign-in based on whether the method
            # takes arguments named 'domain' and 'user'.
            args, _, _, defaults = inspect.getargspec(method)
            required_args = args[:len(args) - len(defaults or [])]
            if 'domain' in kwargs and 'domain' not in args:
                raise Error(404, 'Not found.')
            if 'domain' in required_args and 'domain' not in kwargs:
                raise Error(400, 'Domain not specified.')
            if 'user' in args:
                kwargs['user'] = user
            if 'user' in required_args and not user:
                return self.redirect(users.GetLoginUrl(self.request.url))

            # Prepare an XSRF token if the user is signed in.
            if user:
                self.xsrf_token = GenerateXsrfToken(user.id)
                self.xsrf_tag = (
                    '<input type="hidden" name="xsrf_token" value="%s">' %
                    self.xsrf_token)

            # Require a valid XSRF token for all authenticated POST requests.
            if user and self.request.method == 'POST':
                xsrf_token = self.request.get('xsrf_token', '')
                if not ValidateXsrfToken(user.id, xsrf_token):
                    logging.warn('Bad xsrf_token %r for uid %r', xsrf_token,
                                 user.id)
                    # The window might have been idle for a day; go somewhere reasonable.
                    return self.redirect(root_path + '/.maps')

            # Fill in some useful request variables.
            self.request.lang = SelectLanguage(
                self.request.get('hl'),
                self.request.headers.get('accept-language'))
            self.request.root_path = root_path
            self.request.root_url = self.request.host_url + root_path

            # To prevent clickjacking attacks, disable framing by default.
            if not self.embeddable:
                self.response.headers['X-Frame-Options'] = 'DENY'

            # Call the handler, making nice pages for errors derived from Error.
            method(**kwargs)

        except RedirectToUrl as exception:
            return self.redirect(exception.url)
        except perms.AuthorizationError as exception:
            self.response.set_status(403, message=exception.message)
            self.response.out.write(
                self.RenderTemplate(
                    'unauthorized.html', {
                        'exception': exception,
                        'login_url': users.GetLoginUrl(self.request.url)
                    }))
        except perms.NotPublishableError as exception:
            self.response.set_status(403, message=exception.message)
            self.response.out.write(
                self.RenderTemplate(self.error_template,
                                    {'exception': exception}))
        except perms.NotCatalogEntryOwnerError as exception:
            # TODO(kpy): Either add a template for this type of error, or use an
            # error representation that can be handled by one common error template.
            self.response.set_status(403, message=exception.message)
            self.response.out.write(
                self.RenderTemplate(
                    self.error_template, {
                        'exception':
                        utils.Struct(
                            message='That publication label is owned '
                            'by someone else; you can\'t replace or delete it.'
                        )
                    }))
        except ApiError as exception:
            self.response.set_status(exception.status,
                                     message=exception.message)
            self.response.headers['Content-Type'] = 'text/plain'
            self.response.out.write(exception.message + '\n')
        except Error as exception:
            self.response.set_status(exception.status,
                                     message=exception.message)
            self.response.out.write(
                self.RenderTemplate(self.error_template,
                                    {'exception': exception}))