Ejemplo n.º 1
0
 def remove_identifier(self, identifier):
     if identifier not in self.identifiers:
         raise errors.ForbiddenAction('That identifier belongs to another account')
     if len(self.identifiers) < 2:
         raise errors.ForbiddenAction('Can not remove last identifier')
     account = models.Identity.release(identifier, assert_account_key=self.account.key)
     # Update in-memory instance to reflect reality.
     if account:
         self.account.populate(**account.to_dict())
     # Disconnect service if the identifier is a service identifier.
     identifier, identifier_type = identifiers.parse(identifier)
     if identifier_type in (identifiers.EMAIL, identifiers.SERVICE_ID):
         service, team, resource = identifiers.parse_service(identifier)
         self.disconnect_service(service, team, resource)
     self._notify_account_change()
Ejemplo n.º 2
0
 def change_identifier(self, old, new, notify_connect=True, primary=False):
     new, identifier_type = identifiers.parse(new)
     if not new:
         logging.warning('%r is invalid', new)
         raise errors.InvalidArgument('That identifier is not valid')
     if old not in self.identifiers:
         raise errors.ForbiddenAction('That identifier belongs to another account')
     if old == new:
         return
     # Get the service, team, resource from the new identifier.
     try:
         service, team, resource = identifiers.parse_service(new)
         new_team = not self.is_on_team(service, team)
     except:
         service, team, resource = (None, None, None)
         new_team = True
     identity, account = models.Identity.change(
             old, new, assert_account_key=self.account.key, primary=primary)
     if not identity:
         raise errors.AlreadyExists('That identifier is already in use')
     # Update in-memory instance to reflect reality.
     if account:
         self.account.populate(**account.to_dict())
     if self.account.is_activated and service == 'email' and new_team:
         # Connect the email "service" (if the user is not already on this domain).
         self.connect_service(service, team, resource, notify=notify_connect)
     # TODO: We should also disconnect service if the old identifier was a service.
     self._notify_account_change()
Ejemplo n.º 3
0
 def eject(self, participants, **kwargs):
     if not isinstance(participants, (list, set)):
         participants = [participants]
     account_keys = models.Account.resolve_keys(participants)
     if self.account.key in account_keys:
         raise errors.ForbiddenAction(
             'Leave instead of removing from participants')
     self._set_participants(remove=account_keys, **kwargs)
Ejemplo n.º 4
0
 def create_session(self, skip_activation=False, **kwargs):
     # Activate the user when they log in.
     if not (self.is_activated or skip_activation) or self.is_inactive:
         if not self.can_activate:
             raise errors.ForbiddenAction('Account may not be activated at this time')
         self.change_status('active', status_reason='logged_in')
     scopes = {auth.SCOPE_REFRESH_TOKEN}
     return auth.Session(self.account, scopes=scopes, **kwargs)
Ejemplo n.º 5
0
 def wrap(self, *args, **kwargs):
     if not self.participant:
         raise errors.ForbiddenAction(
             'Account is not a participant in stream')
     if len(self._stream.participants) == 2:
         for p in self._stream.participants:
             if p.account in self.account.blocked_by:
                 logging.info('Blocked Stream.%s from %s to %s',
                              func.__name__, self.account.key, p.account)
                 return
     return func(self, *args, **kwargs)
Ejemplo n.º 6
0
 def show(self):
     t = self._thread.key.get()
     if self.account_key in t.visible_by:
         return
     for a in t.accounts:
         if a.account == self.account_key:
             break
     else:
         raise errors.ForbiddenAction('Cannot update that thread')
     t.visible_by.insert(0, self.account_key)
     t.put()
     self._thread = t
Ejemplo n.º 7
0
def _validate_status_transition(old_status, new_status):
    """Validates that a status may change from a certain value to another."""
    can_change = False if old_status is not None else True
    for tier in config.VALID_STATUS_TRANSITIONS:
        if old_status in tier:
            can_change = True
        if new_status in tier:
            if not can_change:
                raise errors.ForbiddenAction('Cannot change status from "%s" to "%s"' % (
                    old_status, new_status))
            break
    else:
        raise errors.InvalidArgument('Invalid status')
Ejemplo n.º 8
0
 def hide_for_all(self):
     # Note: Intended for admin only.
     t = self._thread.key.get()
     if not t.visible_by:
         return
     for a in t.accounts:
         if a.account == self.account_key:
             break
     else:
         raise errors.ForbiddenAction('Cannot update that thread')
     t.visible_by = []
     t.put()
     self._thread = t
Ejemplo n.º 9
0
 def wrap(*args, **kwargs):
     time_1 = time.clock()
     session = get_session()
     if not session or not session.account:
         raise errors.InvalidAccessToken()
     # Ensure that the account is active.
     if session.account.can_make_requests:
         if update_last_active:
             # Update last active date if they haven't been active earlier today.
             today = date.today()
             if session.account.last_active < today:
                 session.account.last_active = today
                 agent = request.headers.get('User-Agent')
                 if agent:
                     session.account.last_active_client = agent
                 session.account.put()
     elif not allow_nonactive:
         logging.warning('Account %d is %s', session.account_id,
                         session.account.status)
         if session.account.is_activated:
             # The account has been activated but can't make requests so it's disabled.
             raise errors.AccountDisabled()
         else:
             raise errors.ForbiddenAction('Account is not active')
     # Update the user's location coordinates if provided.
     client_id, _ = get_client_details()
     if session.account.share_location and config.LOCATION_HEADER in request.headers:
         try:
             latlng = request.headers[config.LOCATION_HEADER]
             point = ndb.GeoPt(latlng)
             session.account.set_location(point)
         except:
             pass
     elif client_id in (
             'fika', 'reactioncam'
     ) and 'X-AppEngine-CityLatLong' in request.headers:
         latlng = request.headers['X-AppEngine-CityLatLong']
         session.account.set_location(ndb.GeoPt(latlng),
                                      timezone_only=True)
     if set_view_account:
         g.public_options['view_account'] = session.account
     time_2 = time.clock()
     try:
         result = func(session, *args, **kwargs)
     finally:
         time_3 = time.clock()
         logging.debug('Account %d', session.account_id)
     return result
Ejemplo n.º 10
0
 def public(self, num_chunks=5, version=None, **kwargs):
     if not self.participant:
         raise errors.ForbiddenAction(
             'Account is not a participant in stream')
     # Convert the stream to a dict, including the set account's metadata.
     data = {
         'chunks': self.get_relevant_chunks(num_chunks),
         'created': self.created,
         'id': self.key.id(),
         'joined': self.joined,
         'played_until': self.played_until,
         'title': self.title,
         'total_duration': self.total_duration,
         'visible': self.visible,
     }
     # Note: Versions before 6 included the current account in the participants list.
     participants = self.get_others(
     ) if version >= 6 else self.get_accounts()
     if version < 11:
         data['color'] = None
     if version >= 6:
         data['last_interaction'] = self.last_interaction
         data['last_played_from'] = self.last_played_from
         data['others'] = participants
     else:
         data['last_chunk_end'] = self.last_chunk_end
         data['participants'] = participants
     if version >= 7:
         data['image_url'] = self.image_url
     if 17 <= version < 22:
         data['service'] = None
     if version >= 18:
         data['attachments'] = {a.id: a for a in self.attachments}
         data['invite_token'] = self.invite_token
     elif version >= 16:
         data['attachments'] = self.attachments
     if version < 23:
         # This data is available with more info from Participant in v23+.
         data['others_listened'] = self.others_played
     if version >= 28:
         data['service_content_id'] = self.service_content_id
     if version >= 29:
         data['service_member_count'] = len(self.service_members) or None
     return data
Ejemplo n.º 11
0
 def set_seen_until(self, seen_until):
     t = self._thread.key.get()
     for m in t.messages:
         if m.message_id == seen_until:
             break
     else:
         # TODO: Have another look at this logic.
         raise errors.InvalidArgument(
             'Message id must be one of 10 most recent')
     for a in t.accounts:
         if a.account == self.account_key:
             break
     else:
         raise errors.ForbiddenAction('Cannot update that thread')
     if a.seen_until and a.seen_until.id() >= seen_until:
         return
     a.seen_until = m.key_with_parent(self.key)
     t.put()
     self._thread = t
Ejemplo n.º 12
0
def get_session():
    try:
        access_token = request.args.get('access_token')
        if not access_token:
            authorization = request.headers['Authorization']
            token_type, access_token = authorization.split(' ')
            assert token_type == 'Bearer'
        session = Session.from_access_token(access_token)
    except:
        return None
    # Allow admins to override the session account id.
    # TODO: This needs to be checked on the token, so that a token for an
    #       admin granted to a third-party app can't also do this.
    on_behalf_of = request.args.get('on_behalf_of')
    if on_behalf_of:
        if session.account.admin:
            session = Session(int(on_behalf_of))
        else:
            raise errors.ForbiddenAction('Forbidden use of on_behalf_of')
    return session
Ejemplo n.º 13
0
 def challenge(self):
     if self.client == 'fika':
         # TODO: Support more than email.
         try:
             service_key, team_key, _ = models.Service.parse_identifier(
                 self.identifier)
         except ValueError:
             raise ValueError('%r could not be used as email' %
                              (self.identifier, ))
         if team_key.id() in config.PUBLIC_EMAIL_DOMAINS:
             # Explicitly ignore public email domains.
             team = None
         else:
             # Ensure that the team exists and fetch it.
             team = models.ServiceTeam.create_or_update(team_key)
         login_enabled = (team and team.whitelisted)
         block_due_to_roger = False
         event = events.FikaLoginV3(blocked=False,
                                    identifier=self.identifier)
         if not login_enabled:
             # Check the account for another valid team which will let them in.
             # TODO: Improve this logic.
             account = models.Account.resolve(self.identifier)
             event.account_id = account.key.id() if account else None
             event.has_roger = account.has_roger if account else None
             if account and account.can_activate and g.api_version >= 34:
                 if not account.has_roger:
                     logging.debug(
                         'Allowing login for %s (id: %d, status: %r)',
                         self.identifier, account.key.id(), account.status)
                     login_enabled = True
                 else:
                     block_due_to_roger = True
                     logging.debug(
                         'Not allowing login for Roger account %s (%d)',
                         self.identifier, account.key.id())
             elif account:
                 # If we get here, it means the account status is "waiting".
                 logging.debug(
                     '%s (%d) is not whitelisted, checking other teams',
                     self.identifier, account.key.id())
                 for id_key in account.identifiers:
                     try:
                         s, t, _ = models.Service.parse_identifier(
                             id_key.id())
                         if s.id() in ('fika', 'slack'):
                             team = t.get()
                             login_enabled = True
                             logging.debug('Allowing login because of %s',
                                           id_key.id())
                             break
                         elif s.id() == 'email' and t != team_key:
                             # Only emails use whitelisting.
                             team = t.get()
                             login_enabled = t.get().whitelisted
                             if login_enabled:
                                 logging.debug(
                                     'Allowing login because of another whitelisted email'
                                 )
                                 break
                         logging.debug('%s is not whitelisted', id_key.id())
                     except:
                         logging.debug('Skipping %s', id_key.id())
                         continue
         # Enforce block list.
         if identifiers.email(self.identifier) in config.BLOCKED_EMAILS:
             login_enabled = False
         # Block any users who are not whitelisted somehow.
         if not login_enabled:
             logging.warning('Blocking %s (not on a valid team)',
                             self.identifier)
             if block_due_to_roger:
                 # We can assume the account variable is set here.
                 link = 'https://api.rogertalk.com/admin/accounts/%d/' % (
                     account.key.id(), )
                 template = 'Blocked Roger user (*{}*): {{}}'.format(
                     '<%s|%s>' % (link, account.display_name))
             else:
                 template = 'Blocked login: {}'
             if identifiers.email(
                     self.identifier) not in config.MUTED_EMAILS:
                 # TODO: Don't assume self.identifier is an email.
                 slack_api.message(channel='#review',
                                   hook_id='fika',
                                   text=template.format(
                                       identifiers.email(self.identifier)))
                 event.blocked = True
                 event.report()
             raise errors.ForbiddenAction(
                 'That identifier has not been enabled for login')
         event.report()
         self.team = team
     challenge = models.Challenge.get(self.client, self.identifier,
                                      self.code_length)
     self._deliver(challenge.code)
Ejemplo n.º 14
0
 def set_featured(self, featured):
     if featured and (not self.title or not self.image):
         raise errors.ForbiddenAction(
             'Stream needs an image and title to be featured')
     self._tx(models.Stream.set_featured, featured)