コード例 #1
0
    def _MakeUsageDict():
        """Lookup the user's accounting entries and build a USAGE_METADATA dict."""
        # Make sure that the accounting model is up-to-date.
        validator.ValidateAccounting()

        user_hash = '%s:%d' % (Accounting.USER_SIZE, user_id)

        def _AccountingAsDict(sort_key):
            act = validator.GetModelObject(Accounting,
                                           DBKey(user_hash, sort_key),
                                           must_exist=False)
            if act is None:
                return None
            act_dict = act._asdict()
            # GetModelObject just returns everything, with no possibility of selecting the columns.
            act_dict.pop('hash_key', None)
            act_dict.pop('sort_key', None)
            act_dict.pop('op_ids', None)
            return act_dict

        usage_dict = {}
        util.SetIfNotNone(usage_dict, 'owned_by',
                          _AccountingAsDict(Accounting.OWNED_BY))
        util.SetIfNotNone(usage_dict, 'shared_by',
                          _AccountingAsDict(Accounting.SHARED_BY))
        util.SetIfNotNone(usage_dict, 'visible_to',
                          _AccountingAsDict(Accounting.VISIBLE_TO))
        if len(usage_dict.keys()) > 0:
            return usage_dict
        return None
コード例 #2
0
    def _StartAuthViewfinder(self, action):
        """Validates the request and prepares to authenticate by creating user, identity, and device
    dicts. Returns a tuple of (user_dict, ident_dict, device_dict).
    """
        # Validate the request.
        if action == 'register':
            schema = json_schema.REGISTER_VIEWFINDER_REQUEST
        elif action == 'login':
            schema = json_schema.LOGIN_VIEWFINDER_REQUEST
        else:
            schema = json_schema.AUTH_VIEWFINDER_REQUEST

        # Strip out names in all but register case for older clients.
        if action == 'register':
            migrators = _REQUEST_MIGRATORS
        else:
            migrators = _REQUEST_MIGRATORS + [message.SUPPRESS_AUTH_NAME]

        yield gen.Task(self._StartJSONRequest,
                       action,
                       self.request,
                       schema,
                       migrators=migrators)

        auth_info_dict = self._request_message.dict['auth_info']

        # Validate the identity key.
        identity_key = auth_info_dict['identity']
        identity_type, identity_value = AuthViewfinderHandler._ValidateIdentityKey(
            identity_key)

        # Create identity dict.
        ident_dict = {'key': identity_key, 'authority': 'Viewfinder'}

        # Create user dict.
        user_dict = {identity_type.lower(): identity_value}
        if action == 'register':
            user_dict['name'] = auth_info_dict['name']
            util.SetIfNotNone(user_dict, 'given_name',
                              auth_info_dict.get('given_name', None))
            util.SetIfNotNone(user_dict, 'family_name',
                              auth_info_dict.get('family_name', None))

            # If password is specified, compute hash and generate salt (if not already generated).
            password = self._request_message.dict['auth_info'].get(
                'password', None)
            if password is not None:
                # Generate password hash and salt.
                pwd_hash, salt = password_util.GeneratePasswordHash(password)
                user_dict['pwd_hash'] = pwd_hash
                user_dict['salt'] = salt

        # Create device_dict.
        device_dict = self._request_message.dict.get('device', None)

        # Validate input and fill out the dicts.
        yield self._PrepareAuthUser(user_dict, ident_dict, device_dict)

        raise gen.Return((user_dict, ident_dict, device_dict))
コード例 #3
0
    def _handle_request_exception(self, e):
        """Handles presentation of an exception condition to the user, either as an HTML error page,
    or as a JSON error response.
    """
        try:
            status, message = www_util.HTTPInfoFromException(e)
            self.set_status(status)

            # Write JSON error response if this was a user-level interactive request, otherwise
            # write an HTML error response.
            if self._IsInteractiveRequest():
                title = 'Unknown Error'
                if status == 500:
                    logging.error('failure processing %s' %
                                  getattr(self, 'api_name', None),
                                  exc_info=sys.exc_info())
                    message = 'We\'re sorry but an unforeseen error occurred; please try again later.'
                else:
                    logging.warning('[%s] %s' % (type(e).__name__, message))
                    if status in _ERROR_MAP:
                        title = _ERROR_MAP[status]

                self.render('info.html',
                            title=title,
                            message=message,
                            button_url='/',
                            button_text='home')
            else:
                if status == 500:
                    logging.error('failure processing %s:\n%s',
                                  escape.utf8(getattr(self, 'api_name',
                                                      'N/A')),
                                  escape.utf8(self.request.body),
                                  exc_info=sys.exc_info())
                else:
                    logging.warning('[%s] %s' % (type(e).__name__, message))

                error_dict = {
                    'error': {
                        'message': message if message else 'Unknown error.'
                    }
                }
                util.SetIfNotNone(error_dict['error'], 'method',
                                  getattr(self, 'api_name', None))
                util.SetIfNotNone(error_dict['error'], 'id',
                                  getattr(e, 'id', None))
                validictory.validate(error_dict, json_schema.ERROR_RESPONSE)
                self.write(error_dict)
                self.finish()
        except Exception:
            # This is the exception handler of last resort - if we don't finish the request here, nothing will,
            # and it will just leak and time out on the client.
            logging.exception(
                'exception in BaseHandler._handle_request_exception')
            self.set_status(500)
            self.finish()

        return True
コード例 #4
0
ファイル: follower.py プロジェクト: zorro0799/viewfinder
 def MakeMetadataDict(self):
   """Projects all follower attributes that the follower himself can see."""
   foll_dict = {'follower_id': self.user_id}
   util.SetIfNotNone(foll_dict, 'adding_user_id', self.adding_user_id)
   util.SetIfNotNone(foll_dict, 'viewed_seq', self.viewed_seq)
   if self.labels is not None:
     # Normalize labels property for easier testing.
     foll_dict['labels'] = sorted(self.labels)
   return foll_dict
コード例 #5
0
ファイル: follower.py プロジェクト: zorro0799/viewfinder
 def MakeFriendMetadataDict(self):
   """Projects a subset of the follower attributes that should be provided to another user
   that is on the same viewpoint as this follower.
   """
   foll_dict = {'follower_id': self.user_id}
   util.SetIfNotNone(foll_dict, 'adding_user_id', self.adding_user_id)
   util.SetIfNotNone(foll_dict, 'follower_timestamp', self.timestamp)
   if self.IsUnrevivable():
     # Only project labels if the follower has left the viewpoint entirely.
     foll_dict['labels'] = [Follower.REMOVED, Follower.UNREVIVABLE]
   return foll_dict
コード例 #6
0
ファイル: auth_test.py プロジェクト: zorro0799/viewfinder
def _CreateRegisterRequest(device_dict=None, auth_info_dict=None, synchronous=True,
                           version=message.MAX_SUPPORTED_MESSAGE_VERSION):
  """Returns a new AUTH_REQUEST dict that has been populated with information from the
  specified dicts.
  """
  request_dict = {'headers': {'version': version}}
  util.SetIfNotNone(request_dict, 'device', device_dict)
  util.SetIfNotNone(request_dict, 'auth_info', auth_info_dict)
  if synchronous:
    request_dict['headers']['synchronous'] = True
  return request_dict
コード例 #7
0
  def _Update(self):
    """Updates the database:
       1. Creates episode if it did not exist, or sets episode's location/placemark.
       2. Creates posts that did not previously exist.
       3. Creates photos that did not previously exist.
       4. Updates photo MD5 values if they were given in a re-upload.
    """
    # Set episode location/placemark.
    if self._set_location or self._set_placemark:
      for ph_dict in self._ph_dicts:
        if 'location' not in self._ep_dict and 'location' in ph_dict:
          self._ep_dict['location'] = ph_dict['location']
        if 'placemark' not in self._ep_dict and 'placemark' in ph_dict:
          self._ep_dict['placemark'] = ph_dict['placemark']

    # Create new episode if it did not exist at the beginning of the operation.
    if self._episode_id in self._new_ids:
      yield gen.Task(Episode.CreateNew, self._client, **self._ep_dict)
    # Update existing episode's location/placemark.
    elif self._set_location or self._set_placemark:
      yield gen.Task(self._episode.UpdateExisting,
                     self._client,
                     location=self._ep_dict.get('location', None),
                     placemark=self._ep_dict.get('placemark', None))

    # Create posts and photos that did not exist at the beginning of the operation.
    tasks = []
    for ph_dict in self._ph_dicts:
      # Only create post, user_photo and photo if photo did not exist at the beginning of the operation.
      if ph_dict['photo_id'] in self._new_ids:
        # Create user photo record if asset keys were specified.
        asset_keys = ph_dict.pop('asset_keys', None)
        if asset_keys is not None:
          tasks.append(UserPhoto.CreateNew(self._client,
                                           user_id=self._user.user_id,
                                           photo_id=ph_dict['photo_id'],
                                           asset_keys=asset_keys))

        tasks.append(Photo.CreateNew(self._client, **ph_dict))
        tasks.append(Post.CreateNew(self._client, episode_id=self._episode_id, photo_id=ph_dict['photo_id']))
      else:
        # Update the photo if any MD5 attributes need to be overwritten. This is allowed if the photo image
        # has not yet been uploaded. This can happen if the MD5 value has changed on the client due to an IOS
        # upgrade.
        md5_dict = {'photo_id': ph_dict['photo_id']}
        util.SetIfNotNone(md5_dict, 'tn_md5', ph_dict['tn_md5'])
        util.SetIfNotNone(md5_dict, 'med_md5', ph_dict['med_md5'])
        util.SetIfNotNone(md5_dict, 'full_md5', ph_dict['full_md5'])
        util.SetIfNotNone(md5_dict, 'orig_md5', ph_dict['orig_md5'])
        if md5_dict:
          yield Photo.UpdateExisting(self._client, **md5_dict)

    yield tasks
コード例 #8
0
    def _MakeMetadataDict(friend_user, forward_friend, reverse_friend):
        user_dict = {'user_id': friend_user.user_id}

        # Certain labels are visible even to non-friends.
        labels = list(
            friend_user.labels.intersection([User.REGISTERED,
                                             User.TERMINATED]))

        # User profile attributes should only be visible to those who consider caller a friend.
        if reverse_friend is not None:
            util.SetIfNotNone(user_dict, 'name', friend_user.name)
            util.SetIfNotNone(user_dict, 'given_name', friend_user.given_name)
            util.SetIfNotNone(user_dict, 'family_name',
                              friend_user.family_name)
            util.SetIfNotNone(user_dict, 'email', friend_user.email)
            util.SetIfNotNone(user_dict, 'picture', friend_user.picture)
            util.SetIfNotNone(user_dict, 'merged_with',
                              friend_user.merged_with)
            labels.append('friend')

        user_dict['labels'] = labels

        if friend_user.user_id == user_id:
            # Subscriptions don't currently use the model so we can't access them here,
            # but since most tests don't have subscriptions we just turn off validation
            # in the ones that do.
            user_dict['private'] = {'subscriptions': [], 'user_identities': []}
            if friend_user.pwd_hash is None:
                user_dict['private']['no_password'] = True

            db_key = DBKey('us:%d' % user_id, AccountSettings.GROUP_NAME)
            settings = validator.GetModelObject(AccountSettings,
                                                db_key,
                                                must_exist=False)
            if settings is not None:
                settings_dict = user_dict['private'].setdefault(
                    'account_settings', {})
                util.SetIfNotNone(settings_dict, 'email_alerts',
                                  settings.email_alerts)
                util.SetIfNotNone(settings_dict, 'sms_alerts',
                                  settings.sms_alerts)
                util.SetIfNotEmpty(settings_dict, 'storage_options',
                                   list(settings.storage_options))

            predicate = lambda ident: ident.user_id == user_id
            for expected_ident in validator.QueryModelObjects(
                    Identity, predicate=predicate):
                ident_dict = {'identity': expected_ident.key}
                if expected_ident.authority is not None:
                    ident_dict['authority'] = expected_ident.authority

                user_dict['private']['user_identities'].append(ident_dict)

        # Add attributes assigned to the friend by the user himself (such as nickname).
        if forward_friend is not None:
            util.SetIfNotNone(user_dict, 'nickname', forward_friend.nickname)

        return user_dict
コード例 #9
0
ファイル: user.py プロジェクト: zorro0799/viewfinder
  def MakeUserMetadataDict(self, client, viewer_user_id, forward_friend, reverse_friend, callback):
    """Projects a subset of the user attributes that can be provided to the viewing user (using
    the same schema as the query_users service method). The 'forward_friend' is viewer_user_id =>
    friend_user_id, and the 'reverse_friend' is the reverse. This user's profile information
    will only be provided to the viewer if the viewer is a reverse friend (i.e. user considers
    the viewer a friend).

    The 'private' block will be returned only if viewer_user_id == self.user_id.
    """
    user_dict = {'user_id': self.user_id}

    # First, populate basic user data, but only if the user considers the viewer a friend.
    if reverse_friend is not None:
      for attr_name in User._USER_FRIEND_ATTRIBUTES:
        util.SetIfNotNone(user_dict, attr_name, getattr(self, attr_name, None))
      user_dict['labels'] = self.MakeLabelList(True)
    else:
      # Set labels which are visible to non-friends.
      user_dict['labels'] = self.MakeLabelList(False)

    # Now project friend attributes.
    for attr_name in Friend.FRIEND_ATTRIBUTES:
      util.SetIfNotNone(user_dict, attr_name, getattr(forward_friend, attr_name, None))

    # Now fill out private attributes if this user is also the viewing user.
    if viewer_user_id == self.user_id:
      user_dict['private'] = {}
      if self.pwd_hash is None:
        user_dict['private']['no_password'] = True

      subs, settings = yield [gen.Task(Subscription.QueryByUser, client, user_id=self.user_id),
                              gen.Task(AccountSettings.QueryByUser, client, self.user_id, None, must_exist=False)]

      sub_dicts = [sub.MakeMetadataDict() for sub in subs]
      user_dict['private']['subscriptions'] = sub_dicts

      if settings is not None:
        user_dict['private']['account_settings'] = settings.MakeMetadataDict()

      def _MakeIdentityDict(ident):
        i_dict = {'identity': ident.key}
        if ident.authority is not None:
          i_dict['authority'] = ident.authority
        return i_dict

      query_expr = 'identity.user_id=%d' % self.user_id
      identities = yield gen.Task(Identity.IndexQuery, client, query_expr, ['key', 'authority'])
      user_dict['private']['user_identities'] = [_MakeIdentityDict(ident) for ident in identities]

    callback(user_dict)
コード例 #10
0
  def ValidateFollower(self, user_id, viewpoint_id, labels, last_updated,
                       timestamp=None, adding_user_id=None, viewed_seq=None):
    """Validates that Follower and Followed records have been created or updated in the database
    for user "user_id" and viewpoint "viewpoint_id".

    Returns the follower.
    """
    follower_dict = {'user_id': user_id,
                     'viewpoint_id': viewpoint_id,
                     'labels': labels}
    util.SetIfNotNone(follower_dict, 'timestamp', timestamp)
    util.SetIfNotNone(follower_dict, 'adding_user_id', adding_user_id)
    util.SetIfNotNone(follower_dict, 'viewed_seq', viewed_seq)
    follower = self.ValidateUpdateDBObject(Follower, **follower_dict)

    self._ValidateUpdateFollowed(user_id, viewpoint_id, None, last_updated)

    return follower
コード例 #11
0
ファイル: subscription.py プロジェクト: zorro0799/viewfinder
 def MakeMetadataDict(self):
     """Project a subset of subscription attributes that can be provided to the user."""
     sub_dict = {}
     for attr_name in Subscription._JSON_ATTRIBUTES:
         util.SetIfNotNone(sub_dict, attr_name,
                           getattr(self, attr_name, None))
     if self.extra_info:
         sub_dict['extra_info'] = deepcopy(self.extra_info)
     return sub_dict
コード例 #12
0
 def CreateUserCookieDict(self,
                          user_id,
                          device_id,
                          user_name=None,
                          viewpoint_id=None,
                          confirm_time=None,
                          is_session_cookie=None):
     """Creates a user cookie dict from the given arguments."""
     user_dict = {
         'user_id': user_id,
         'device_id': device_id,
         'server_version': self.settings['server_version']
     }
     util.SetIfNotNone(user_dict, 'name', user_name)
     util.SetIfNotNone(user_dict, 'viewpoint_id', viewpoint_id)
     util.SetIfNotNone(user_dict, 'confirm_time', confirm_time)
     util.SetIfNotNone(user_dict, 'is_session_cookie', is_session_cookie)
     return user_dict
コード例 #13
0
ファイル: settings.py プロジェクト: zorro0799/viewfinder
    def MakeMetadataDict(self):
        """Project a subset of account settings attributes that can be provided to the user."""
        settings_dict = {}
        for attr_name in AccountSettings._JSON_ATTRIBUTES:
            value = getattr(self, attr_name, None)
            if isinstance(value, frozenset):
                util.SetIfNotEmpty(settings_dict, attr_name, list(value))
            else:
                util.SetIfNotNone(settings_dict, attr_name, value)

        return settings_dict
コード例 #14
0
    def GenerateUploadUrl(self,
                          key,
                          content_type=None,
                          content_md5=None,
                          expires_in=constants.SECONDS_PER_DAY,
                          max_bytes=5 << 20):
        """Generates a URL for a PUT request to allow a client to store the specified key directly
    to S3 from a browser or mobile client. 'max_bytes' limits the upload file size to prevent
    D.O.S. attacks.
    TODO(andy) max_bytes is not currently enforced, need to fix this.
    """
        headers = {}
        util.SetIfNotNone(headers, 'Content-Type', content_type)
        util.SetIfNotNone(headers, 'Content-MD5', content_md5)

        return self._s3_conn.generate_url(expires_in,
                                          'PUT',
                                          self._bucket_name,
                                          key,
                                          headers=headers or None)
コード例 #15
0
    def _MakeNotificationDict(notification):
        """Create a viewpoint dict from the followed object plus its
    referenced viewpoint object.
    """
        notification_dict = {
            'notification_id': notification.notification_id,
            'name': notification.name,
            'sender_id': notification.sender_id,
            'timestamp': notification.timestamp
        }

        util.SetIfNotNone(notification_dict, 'op_id', notification.op_id)

        if notification.update_seq is not None or notification.viewed_seq is not None:
            vp_dict = notification_dict.setdefault('inline', {}).setdefault(
                'viewpoint', {})
            vp_dict['viewpoint_id'] = notification.viewpoint_id
            util.SetIfNotNone(vp_dict, 'update_seq', notification.update_seq)
            util.SetIfNotNone(vp_dict, 'viewed_seq', notification.viewed_seq)

        if notification.activity_id is not None:
            viewpoint_id = notification.viewpoint_id
            activity = validator.GetModelObject(
                Activity, DBKey(viewpoint_id, notification.activity_id))
            activity_dict = activity.MakeMetadataDict()
            notification_dict.setdefault('inline',
                                         {})['activity'] = activity_dict

            if activity.name == 'post_comment' and notification.invalidate is None:
                comment_id = activity_dict['post_comment']['comment_id']
                comment = validator.GetModelObject(
                    Comment, DBKey(viewpoint_id, comment_id))
                notification_dict['inline']['comment'] = comment._asdict()

        invalidate = notification.GetInvalidate()
        if invalidate is not None:
            invalidate.pop('headers')
            notification_dict['invalidate'] = invalidate

        return notification_dict
コード例 #16
0
 def _MakeContactDict(contact):
     """Create a contact dict from the contact object plus its referenced identity object.
 """
     identity_dict = dict()
     for identity_key in contact.identities:
         identity = validator.GetModelObject(Identity,
                                             identity_key,
                                             must_exist=False)
         identity_dict[identity_key] = identity
     contact_dict = {
         'contact_id': contact.contact_id,
         'contact_source': contact.contact_source
     }
     util.SetIfNotNone(contact_dict, 'name', contact.name)
     util.SetIfNotNone(contact_dict, 'given_name', contact.given_name)
     util.SetIfNotNone(contact_dict, 'family_name', contact.family_name)
     util.SetIfNotNone(contact_dict, 'rank', contact.rank)
     if contact.labels is not None and len(contact.labels) > 0:
         contact_dict['labels'] = list(contact.labels)
     identities_list = []
     if contact.identities_properties is not None:
         for identity_properties in contact.identities_properties:
             identity_key = identity_properties[0]
             properties = {'identity': identity_key}
             util.SetIfNotNone(properties, 'description',
                               identity_properties[1])
             if identity_dict[Identity.Canonicalize(identity_key)] is None:
                 user_id = None
             else:
                 user_id = identity_dict[Identity.Canonicalize(
                     identity_key)].user_id
             util.SetIfNotNone(properties, 'user_id', user_id)
             identities_list.append(properties)
         contact_dict['identities'] = identities_list
     return contact_dict
コード例 #17
0
    def _GetAuthEmail(cls, client, action, use_short_token, user_name,
                      identity, short_url):
        """Returns a dict of parameters that will be passed to EmailManager.SendEmail in order to
    email an access token to a user who is verifying his/her account.
    """
        action_info = VerifyIdBaseHandler.ACTION_MAP[action]
        identity_type, identity_value = Identity.SplitKey(identity.key)

        # Create arguments for the email.
        args = {
            'from': EmailManager.Instance().GetInfoAddress(),
            'fromname': 'Viewfinder',
            'to': identity_value
        }
        util.SetIfNotNone(args, 'toname', user_name)

        # Create arguments for the email template.
        fmt_args = {
            'user_name':
            user_name or identity_value,
            'user_email':
            identity_value,
            'url':
            'https://%s/%s%s' % (ServerEnvironment.GetHost(),
                                 short_url.group_id, short_url.random_key),
            'title':
            action_info.title,
            'use_short_token':
            use_short_token,
            'access_token':
            identity.access_token
        }

        # The email html format is designed to meet these requirements:
        #   1. It must be viewable on even the most primitive email html viewer. Avoid fancy CSS.
        #   2. It cannot contain any images. Some email systems (like Gmail) do not show images by default.
        #   3. It must be short and look good on an IPhone 4S screen. The action button should be visible
        #      without any scrolling necessary.
        resources_mgr = ResourcesManager.Instance()
        if use_short_token:
            args['subject'] = 'Viewfinder Code: %s' % identity.access_token
        else:
            args['subject'] = action_info.title
        args['html'] = resources_mgr.GenerateTemplate(
            action_info.email_template, is_html=True, **fmt_args)
        args['text'] = resources_mgr.GenerateTemplate(
            action_info.email_template, is_html=False, **fmt_args)

        # Remove extra whitespace in the HTML (seems to help it avoid Gmail spam filter).
        args['html'] = escape.squeeze(args['html'])

        return args
コード例 #18
0
 def GenerateUrl(self,
                 key,
                 method='GET',
                 cache_control=None,
                 expires_in=constants.SECONDS_PER_DAY,
                 content_type=None):
     """Generates a URL that can be used retrieve the specified key. If 'cache_control' is
 given, it will result in a Cache-Control header being added to any S3 responses. The
 expires_in parameter specifies how long (in seconds) the URL is valid for.
 content-type forces the content-type of the downloaded file. eg: use text/plain for logs.
 """
     response_headers = {}
     util.SetIfNotNone(response_headers, 'response-cache-control',
                       cache_control)
     util.SetIfNotNone(response_headers, 'response-content-type',
                       content_type)
     return self._s3_conn.generate_url(expires_in,
                                       method,
                                       self._bucket_name,
                                       key,
                                       response_headers=response_headers
                                       or None)
コード例 #19
0
def SendEmailToUser(template, user):
    assert user.email is not None, user

    unsubscribe_cookie = User.CreateUnsubscribeCookie(
        user.user_id, AccountSettings.MARKETING)
    unsubscribe_url = 'https://%s/unsubscribe?%s' % (
        options.domain, urlencode(dict(cookie=unsubscribe_cookie)))

    # Create arguments for the email template.
    fmt_args = {
        'first_name': user.given_name,
        'unsubscribe_url': unsubscribe_url
    }

    # Create arguments for the email.
    args = {
        'from': EmailManager.Instance().GetInfoAddress(),
        'fromname': 'Viewfinder',
        'to': user.email,
        'subject': options.email_subject
    }
    util.SetIfNotNone(args, 'toname', user.name)

    args['html'] = template.generate(is_html=True, **fmt_args)
    args['text'] = template.generate(is_html=False, **fmt_args)

    print 'Sending marketing email to %s (%s) (#%d)' % (user.email, user.name,
                                                        user.user_id)

    if options.test_mode:
        global _is_first_email
        if _is_first_email:
            print args['html']
            _is_first_email = False
    else:
        # Remove extra whitespace in the HTML (seems to help it avoid Gmail spam filter).
        args['html'] = escape.squeeze(args['html'])

        yield gen.Task(EmailManager.Instance().SendEmail,
                       description='marketing email',
                       **args)
コード例 #20
0
    def _TestGenerateMergeToken(self,
                                identity_key,
                                user_cookie,
                                error_if_linked=None):
        """Invokes the merge_token auth API that triggers the email of a Viewfinder access token.
    Validates that an identity was created. Returns a source_identity_dict that can be passed
    directly to merge_accounts.
    """
        url = self._tester.GetUrl('/merge_token/viewfinder')
        request_dict = {
            'headers': {
                'version': message.MAX_SUPPORTED_MESSAGE_VERSION,
                'synchronous': True
            },
            'identity': identity_key
        }
        util.SetIfNotNone(request_dict, 'error_if_linked', error_if_linked)

        auth_test._SendAuthRequest(self._tester,
                                   url,
                                   'POST',
                                   user_cookie=user_cookie,
                                   request_dict=request_dict)
        identity = self._RunAsync(Identity.Query, self._client, identity_key,
                                  None)

        # Validate the identity.
        self._validator.ValidateUpdateDBObject(
            Identity,
            key=identity_key,
            authority='Viewfinder',
            user_id=identity.user_id,
            access_token=identity.access_token,
            expires=identity.expires)
        return {
            'identity': identity.key,
            'access_token': identity.access_token
        }
コード例 #21
0
    def _Check(self):
        """Gathers pre-mutation information:
       1. Queries for existing followers and viewpoint.
       2. Checkpoints list of followers that need to be revived.

       Validates the following:
       1. Permission to update viewpoint metadata.
    """
        # Get the viewpoint to be modified, along with the follower that is adding the additional users.
        # This state will not be changed by add followers, and so doesn't need to be part of the checkpoint.
        self._viewpoint, self._follower = yield gen.Task(
            Viewpoint.QueryWithFollower, self._client, self._user_id,
            self._viewpoint_id)

        if self._viewpoint is None:
            raise InvalidRequestError(
                'Viewpoint "%s" does not exist and so cannot be updated.' %
                (self._viewpoint_id))

        if self._follower is None or not self._follower.CanContribute():
            raise PermissionError(
                'User %d does not have permission to update viewpoint "%s".' %
                (self._user_id, self._viewpoint_id))

        # Get all existing followers.
        self._followers, _ = yield gen.Task(Viewpoint.QueryFollowers,
                                            self._client,
                                            self._viewpoint_id,
                                            limit=Viewpoint.MAX_FOLLOWERS)

        # Check that cover photo exists in this viewpoint.
        cover_photo = self._vp_dict.get('cover_photo', None)
        if cover_photo is not None:
            if self._viewpoint.IsDefault():
                # cover_photo isn't supported creating default viewpoint.
                raise InvalidRequestError(
                    'A cover photo cannot be set on your library.')

            cover_photo_episode, cover_photo_post = yield [
                gen.Task(Episode.Query,
                         self._client,
                         cover_photo['episode_id'],
                         None,
                         must_exist=False),
                gen.Task(Post.Query,
                         self._client,
                         cover_photo['episode_id'],
                         cover_photo['photo_id'],
                         None,
                         must_exist=False)
            ]

            if cover_photo_post is None:
                raise InvalidRequestError(
                    'The requested cover photo does not exist.')

            if cover_photo_post.IsUnshared():
                raise PermissionError(
                    'The requested cover photo has been unshared.')

            if cover_photo_episode.viewpoint_id != self._viewpoint_id:
                raise InvalidRequestError(
                    'The requested cover photo is not in viewpoint "%s".' %
                    self._viewpoint_id)

        # Start populating the checkpoint if this the first time the operation has been run.
        if self._op.checkpoint is None:
            # Get list of followers which have removed themselves from the viewpoint and will need to be revived.
            self._revive_follower_ids = self._GetRevivableFollowers(
                self._followers)

            # Get previous values of title and cover_photo, if they are being updated.
            self._prev_values = {}
            if 'title' in self._vp_dict:
                util.SetIfNotNone(self._prev_values, 'prev_title',
                                  self._viewpoint.title)
            if 'cover_photo' in self._vp_dict:
                util.SetIfNotNone(self._prev_values, 'prev_cover_photo',
                                  self._viewpoint.cover_photo)

            # Set checkpoint.
            # Followers to revive need to be check-pointed because they are changed in the UPDATE phase.
            # If we fail after UPDATE, but before NOTIFY, we would not send correct notifications on retry.
            checkpoint = {'revive': self._revive_follower_ids}
            util.SetIfNotEmpty(checkpoint, 'prev', self._prev_values)
            yield self._op.SetCheckpoint(self._client, checkpoint)
        else:
            # Restore state from checkpoint.
            self._revive_follower_ids = self._op.checkpoint['revive']
            self._prev_values = self._op.checkpoint.get('prev', {})
コード例 #22
0
ファイル: auth.py プロジェクト: zorro0799/viewfinder
    def _AuthUser(self, user_dict, ident_dict, device_dict, confirmed=False):
        """Called when a requester has been authenticated as a Viewfinder user by a trusted authority
    that provides additional information about the user, such as name, email, gender, etc. At
    this point, we can trust that the identity key provided in "ident_dict" is controlled by the
    calling user.

    Completes the authentication action in two steps: first, makes sure the user id and device
    id are retrieved or allocated as necessary; second, starts a user registration operation and
    returns a login cookie. If "confirmed" is True, then the "confirm_time" field in the user
    cookie is set, indicating the time at which the user confirmed their control of the identity
    via email or SMS. This type of cookie is necessary to perform certain high-privilege
    operations, such as updating the password.

    Registration is synchronous, meaning that the caller will wait until it is complete. This
    ensures that when the caller tries to login immediately following this call, the new user
    will be created and ready.
    """
        before_user = yield gen.Task(self._PrepareAuthUser, user_dict,
                                     ident_dict, device_dict)

        # Map auth attribute names to those used by User schema and exclude any attributes that are not yet stored
        # in the User table.
        scratch_user_dict = {'user_id': user_dict['user_id']}
        for k, v in user_dict.items():
            user_key = AuthHandler._AUTH_ATTRIBUTE_MAP.get(k, None)
            if user_key is not None:
                if getattr(before_user, user_key) is None:
                    scratch_user_dict[user_key] = v

                # Set facebook email if it has not yet been set.
                if user_key == 'email' and ident_dict[
                        'authority'] == 'Facebook':
                    if getattr(before_user, 'facebook_email') is None:
                        scratch_user_dict['facebook_email'] = v

        # If the device id is not present, then allocate it now.
        if device_dict is not None and 'device_id' not in device_dict:
            device_dict['device_id'] = yield gen.Task(Device._allocator.NextId,
                                                      self._client)

        # Make synchronous request to ensure user is fully created before returning.
        request = {
            'headers': {
                'synchronous': True
            },
            'user_dict': scratch_user_dict,
            'ident_dict': ident_dict,
            'device_dict': device_dict
        }
        op = yield gen.Task(
            Operation.CreateAndExecute, self._client, user_dict['user_id'],
            device_dict['device_id']
            if device_dict is not None else before_user.webapp_dev_id,
            'RegisterUserOperation.Execute', request)

        if self._action == 'link':
            # Now make asynchronous request (or synchronous if requested by client) to fetch contacts.
            # Fetching contacts can take a long time, so best to do this in the background by default.
            request = {
                'key': ident_dict['key'],
                'user_id': user_dict['user_id']
            }
            if self._IsInteractiveRequest(
            ) or self._request_message.dict['headers'].get(
                    'synchronous', False):
                request['headers'] = {'synchronous': True}

            op = yield gen.Task(
                Operation.CreateAndExecute, self._client, user_dict['user_id'],
                device_dict['device_id']
                if device_dict is not None else before_user.webapp_dev_id,
                'FetchContactsOperation.Execute', request)

        # Get the user that was registered by the operation.
        after_user = yield gen.Task(User.Query, self._client,
                                    user_dict['user_id'], None)

        # If the identity was confirmed via email/SMS, set the cookie "confirm_time", which allows
        # the cookie to authorize higher privilege operations, such as setting the user password.
        confirm_time = util.GetCurrentTimestamp() if confirmed else None

        # Create the user cookie dict that will be returned to the caller.
        device_id = after_user.webapp_dev_id if device_dict is None else device_dict[
            'device_id']
        user_cookie_dict = self.CreateUserCookieDict(after_user.user_id,
                                                     device_id,
                                                     after_user.name,
                                                     confirm_time=confirm_time)

        # Sets the user cookie and finishes the request.
        if self._IsInteractiveRequest():
            self.SetUserCookie(user_cookie_dict)
            self._FinishInteractiveRequest()
        else:
            response_dict = {'user_id': user_dict['user_id']}
            if device_dict is not None:
                response_dict['device_id'] = device_dict['device_id']

            use_session_cookie = self._request_message.dict.get(
                'use_session_cookie', None)
            util.SetIfNotNone(user_cookie_dict, 'is_session_cookie',
                              use_session_cookie)

            self.SetUserCookie(user_cookie_dict)
            self._FinishJSONRequest(op, response_dict,
                                    json_schema.AUTH_RESPONSE)
コード例 #23
0
def _TestUpdateViewpoint(tester, user_cookie, request_dict):
    """Called by the ServiceTester in order to test update_viewpoint
  service API call.
  """
    from viewfinder.backend.www.test.update_follower_test import _ValidateUpdateFollower
    validator = tester.validator
    user_id, device_id = tester.GetIdsFromCookie(user_cookie)
    request_dict = deepcopy(request_dict)
    viewpoint_id = request_dict['viewpoint_id']

    # Send update_viewpoint request.
    actual_dict = tester.SendRequest('update_viewpoint', user_cookie,
                                     request_dict)
    op_dict = tester._DeriveNotificationOpDict(user_id, device_id,
                                               request_dict)

    # Get previous values for title and cover_photo.
    viewpoint = validator.GetModelObject(Viewpoint, viewpoint_id)
    prev_title = viewpoint.title
    prev_cover_photo = viewpoint.cover_photo

    # Validate case where only follower attributes are specified (uses update_follower code path).
    follower_columns = set([
        'user_id', 'viewpoint_id', 'labels', 'viewed_seq', 'activity',
        'headers'
    ])
    if all(attr in follower_columns for attr in request_dict.keys()):
        # Validate Follower object.
        foll_dict = {'viewpoint_id': request_dict['viewpoint_id']}
        util.SetIfNotNone(foll_dict, 'labels',
                          request_dict.pop('labels', None))
        util.SetIfNotNone(foll_dict, 'viewed_seq',
                          request_dict.pop('viewed_seq', None))
        _ValidateUpdateFollower(tester, user_cookie, op_dict, foll_dict)
    else:
        # Validate Viewpoint object.
        vp_dict = deepcopy(request_dict)
        vp_dict.pop('headers', None)
        vp_dict.pop('labels', None)
        vp_dict.pop('viewed_seq', None)
        vp_dict.pop('activity', None)
        viewpoint = validator.ValidateUpdateDBObject(Viewpoint, **vp_dict)

        # Need to revive before validating the follower, below.  This also happens in
        # validator.ValidateFollowerNotifications(), below.
        validator.ValidateReviveRemovedFollowers(viewpoint_id, op_dict)

        # Validate activity and notifications for the update.
        invalidate = {
            'viewpoints': [{
                'viewpoint_id': viewpoint_id,
                'get_attributes': True
            }]
        }

        # Validate notifications to followers.
        activity_dict = {
            'name': 'update_viewpoint',
            'activity_id': request_dict['activity']['activity_id'],
            'timestamp': request_dict['activity']['timestamp'],
            'viewpoint_id': viewpoint_id
        }

        if 'title' in vp_dict and prev_title is not None:
            util.SetIfNotNone(activity_dict, 'prev_title', prev_title)
        if 'cover_photo' in vp_dict and prev_cover_photo is not None:
            util.SetIfNotNone(activity_dict, 'prev_cover_photo',
                              prev_cover_photo)

        validator.ValidateFollowerNotifications(viewpoint_id, activity_dict,
                                                op_dict, invalidate)

    tester._CompareResponseDicts('update_viewpoint', user_id, request_dict, {},
                                 actual_dict)
    return actual_dict