示例#1
0
def login_with_jwt():
  token = request.args.get('token')

  if not token:
    return abort(400)

  try:
    user_info = jwt.decode(token, get_config()['sessions_secret'], algorithms=['HS256'])

    if 'id' in user_info:
      authentication.login(user_info['method'], user_id=user_info['id'])
    else:
      if get_organization_id_for_email(user_info['email']) != user_info['organization']:
        logging.warning('Attempt to use JWT with mismatched org: %s', token)

        return abort(400)

      authentication.login(user_info['method'], user_email=user_info['email'])
  except jwt.DecodeError:
    logging.warning('Attempt to use invalid JWT: %s', token)

    return abort(400)
  except jwt.ExpiredSignatureError:
    logging.warning('Attempt to use expired JWT: %s', token)

  return redirect('/')
示例#2
0
    def test_get_organization_id_for_email__corp_domain(
            self, mock_get_organization_config):
        self.assertEqual(
            'optimizely.com',
            utils.get_organization_id_for_email('*****@*****.**'))

        mock_get_organization_config.assert_called_once_with('optimizely.com')
示例#3
0
    def get(self):
        if self.session.get('pickled_oauth_flow'):
            flow = pickle.loads(self.session['pickled_oauth_flow'])
        else:
            flow = flow_from_clientsecrets(
                get_path_to_oauth_secrets(),
                scope='https://www.googleapis.com/auth/userinfo.email',
                redirect_uri='https://trot.to/_/auth/oauth2_callback')

        if not self.session.get('oauth_state') or self.session.get(
                'oauth_state') != self.request.get('state'):
            self.redirect('/_/auth/login')
            return

        try:
            credentials = flow.step2_exchange(self.request.get('code'))
        except (FlowExchangeError, ValueError):
            # user declined to auth; move on
            self.redirect(self.session.get('redirect_to_after_oauth', '/'))
            return

        self.session['credentials'] = pickle.dumps(credentials)

        self.session['user_email'] = authentication.get_user_email(credentials)

        user = get_or_create_user(
            self.session['user_email'],
            get_organization_id_for_email(self.session['user_email']))
        if not user.accepted_terms_at:
            # all login methods now have UI for consenting to terms
            user.accepted_terms_at = datetime.datetime.utcnow()
            user.put()

        self.redirect(self.session.get('redirect_to_after_oauth', '/'))
示例#4
0
def login(authentication_method, user_id=None, user_email=None):
    if user_id:
        user = get_user_by_id(user_id)

        if not user:
            logging.warning('Attempt to sign in nonexistent user %s', user_id)

            abort(400)
    else:
        user = get_or_create_user(user_email,
                                  get_organization_id_for_email(user_email))

    allowed_authentication_methods = get_allowed_authentication_methods(
        user.organization)
    if allowed_authentication_methods is not None and authentication_method not in allowed_authentication_methods:
        logging.warning(
            "User %s attempted to authenticate with method '%s'. Allowed methods are %s.",
            user.id, authentication_method, allowed_authentication_methods)

        abort(
            redirect(
                f'/_/auth/login?e=auth_not_allowed-{authentication_method}'))

    if not user.accepted_terms_at:
        # all login methods now have UI for consenting to terms
        user.accepted_terms_at = datetime.datetime.utcnow()
        user.put()

    login_user(user)
示例#5
0
def login_test_user():
    if os.getenv('ENVIRONMENT') == 'test_env' and request.headers.get(
            'TROTTO_USER_UNDER_TEST'):
        login_user(
            get_or_create_user(
                request.headers.get('TROTTO_USER_UNDER_TEST'),
                get_organization_id_for_email(
                    request.headers.get('TROTTO_USER_UNDER_TEST'))))
示例#6
0
    def attempt_auth_by_user_header(self):
        # only for testing
        if not env.current_env_is_local():
            return

        if self.request.headers.get('TROTTO_USER_UNDER_TEST'):
            self.user_email = self.request.headers.get(
                'TROTTO_USER_UNDER_TEST')
            self.user_org = get_organization_id_for_email(self.user_email)
示例#7
0
def login_email(user_email):
    user = get_or_create_user(user_email,
                              get_organization_id_for_email(user_email))

    if not user.accepted_terms_at:
        # all login methods now have UI for consenting to terms
        user.accepted_terms_at = datetime.datetime.utcnow()
        user.put()

    login_user(user)
示例#8
0
    def dispatch(self):
        if self.request.host == 'dev.trot.to':
            if not users.get_current_user():
                self.redirect(users.create_login_url('/'))
                return
            elif not users.is_current_user_admin():
                self.abort(403)

        self.is_local_env = not os.getenv('SERVER_SOFTWARE',
                                          '').startswith('Google App Engine/')

        # Get a session store for this request.
        self.session_store = sessions.get_store(request=self.request)

        self.login_error = None

        self.user_email = None
        self.user = None

        if not self.user_email:
            self.user_email = self.session.get('user_email')
            self.user_org = get_organization_id_for_email(
                self.user_email) if self.user_email else None

        if not self.user_email:
            self.attempt_auth_by_emailed_link()

        if not self.user_email and env.current_env_is_local():
            self.attempt_auth_by_user_header()

        self.session['already_accepted_terms'] = False
        if self.user_email:
            self.user = get_or_create_user(self.user_email, self.user_org)
            self.session[
                'already_accepted_terms'] = not not self.user.accepted_terms_at

            if not self.session.get('csrf_token'):
                self.session['csrf_token'] = utils.generate_secret(32)

        try:
            self.redirect_to = None
            self.check_authorization()

            if self.redirect_to:
                self.response.write(
                    json.dumps({'redirect_to': self.redirect_to}))
                return

            # Dispatch the request.
            webapp2.RequestHandler.dispatch(self)
        finally:
            # Save all sessions, IFF secure connection
            if self.request.scheme == 'https' or self.is_local_env:
                self.session_store.save_sessions(self.response)
示例#9
0
  def get(self):
    if self.request.get('r'):  # outgoing request
      self.session['request_data_to_replay'] = str(self.request.get('r'))

      self.render_login_selector_page(self.request.path_url)
      return

    try:
      original_request_data = json.loads(base64.urlsafe_b64decode(self.session['request_data_to_replay']))
    except (KeyError, ValueError):
      self.redirect('/')
      return

    if self.request.get('code'):
      flow = pickle.loads(self.session['pickled_oauth_flow'])

      try:
        credentials = flow.step2_exchange(self.request.get('code'))
      except (FlowExchangeError, ValueError):
        # user declined to auth; move on
        self.redirect(original_request_data['origin'])
        return

      self.session['credentials'] = pickle.dumps(credentials)

      self.session['user_email'] = authentication.get_user_email(credentials)
      self.user_email = self.session['user_email']
      self.user_org = get_organization_id_for_email(self.user_email)
    else:
      # assume this was a login via email, which would have been processed in the base handler
      pass

    object_data = json.loads(original_request_data['body'])

    new_link = None

    try:
      new_link = helpers.create_short_link(self.user_org,
                                           self.user_email,
                                           object_data['shortpath'],
                                           object_data['destination'])
      response_data = {
        'shortpath': new_link.shortpath,
        'destination': new_link.destination_url
      }
    except helpers.LinkCreationException as e:
      response_data = {
        'error': str(e)
      }

    self.redirect(str(original_request_data['origin'] + '?r=' + base64.urlsafe_b64encode(json.dumps(response_data))))
示例#10
0
    def attempt_auth_by_emailed_link(self):
        email = self.request.get('e')
        secret = self.request.get('s')

        if not email or not secret:
            return

        logging.info('Attempting auth by link: (%s,%s)' % (email, secret))

        try:
            validate_login_link(email, secret)

            self.user_email = email
            self.user_org = get_organization_id_for_email(
                self.user_email) if self.user_email else None

            self.session['user_email'] = self.user_email
        except LoginLinkValidationError as e:
            self.login_error = str(e)
            return
示例#11
0
def login_test_user():
  if os.getenv('ENVIRONMENT') == 'test_env' and request.headers.get('TROTTO_USER_UNDER_TEST'):
    login_user(get_or_create_user(request.headers.get('TROTTO_USER_UNDER_TEST'),
                                  get_organization_id_for_email(request.headers.get('TROTTO_USER_UNDER_TEST'))))
    session['last_signin'] = datetime.datetime.utcnow()
示例#12
0
    def test_get_organization_id_for_email__aliased_domain(
            self, mock_get_organization_config):
        self.assertEqual('itso.io',
                         utils.get_organization_id_for_email('*****@*****.**'))

        mock_get_organization_config.assert_called_once_with('itso.co')
示例#13
0
 def test_get_organization_id_for_email__special_test_org(self):
     self.assertEqual(
         'test_org',
         utils.get_organization_id_for_email('*****@*****.**'))
示例#14
0
 def test_get_organization_id_for_email__generic_domain(self):
     self.assertEqual('*****@*****.**',
                      utils.get_organization_id_for_email('*****@*****.**'))
示例#15
0
 def extract_organization(self):
     return get_organization_id_for_email(
         self.email) if self.email else None
示例#16
0
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        self.organization = get_organization_id_for_email(kwargs['email'])
示例#17
0
 def get_organization_name(self):
     return get_organization_id_for_email(
         self.email) if self.email else None
示例#18
0
def _extract_domain_type(email):
    return 'generic' if '@' in get_organization_id_for_email(
        email) else 'corporate'
示例#19
0
def upsert_short_link(organization, owner, namespace, shortpath, destination, updated_link_object):
  shortpath = shortpath.strip().lower().strip('/')
  destination = _encode_ascii_incompatible_chars(destination.strip())

  if not shortpath:
    raise LinkCreationException('You must provide a path')

  PATH_RESTRICTIONS_ERROR = 'Keywords can include only letters, numbers, hyphens, "/", and "%s" placeholders'

  if shortpath != re.sub('[^0-9a-zA-Z\-\/%]', '', shortpath):
    raise LinkCreationException(PATH_RESTRICTIONS_ERROR)

  check_namespaces(organization, namespace, shortpath)

  if organization != get_organization_id_for_email(owner):
    raise LinkCreationException("The go link's owner must be in the go link's organization")

  shortpath_parts = shortpath.split('/')
  if len(shortpath_parts) > 1:
    placeholder_found = False

    for part in shortpath_parts[1:]:
      if part == '%s':
        placeholder_found = True
      elif placeholder_found:
        raise LinkCreationException('After the first "%s" placeholder, you can only have additional placeholders')

  if '%' in shortpath:
    if '%' in shortpath and shortpath.count('%') != shortpath.count('%s'):
      raise LinkCreationException(PATH_RESTRICTIONS_ERROR)

    if '%s' in shortpath.split('/')[0]:
      raise LinkCreationException('"%s" placeholders must come after a "/". Example: "jira/%s"')

    if shortpath.count('%s') != destination.count('%s'):
      raise LinkCreationException('The keyword and the destination must have the same number of "%s" placeholders')

  if not updated_link_object:
    existing_link, _ = get_shortlink(organization, namespace, shortpath)

    if existing_link:
      error_message = 'That go link already exists. %s/%s points to %s' % (namespace,
                                                                           shortpath,
                                                                           existing_link.destination_url)

      if existing_link.shortpath != shortpath:
        error_message = 'A conflicting go link already exists. %s/%s points to %s' % (namespace,
                                                                                      existing_link.shortpath,
                                                                                      existing_link.destination_url)

      raise LinkCreationException(error_message)

    if '%s' in shortpath:
      conflicting_link = find_conflicting_link(organization, namespace, shortpath)

      if conflicting_link:
        raise LinkCreationException('A conflicting go link already exists. %s/%s points to %s'
                                    % (namespace, conflicting_link.shortpath, conflicting_link.destination_url))

  # Note: urlparse('128.90.0.1:8080/start').scheme returns '128.90.0.1'. Hence the additional checking.
  destination_url_scheme = urlparse(destination).scheme
  if (not destination_url_scheme
      or not destination_url_scheme.strip()
      or not destination.startswith(destination_url_scheme + '://')):
    # default to HTTP (see Slack discussion)
    destination = 'http://' + destination

  if type(validators.url(destination)) is ValidationFailure:
    raise LinkCreationException('You must provide a valid destination URL')

  if updated_link_object:
    link = updated_link_object
  else:
    link_kwargs = {'organization': organization,
                   'owner': owner,
                   'namespace': namespace,
                   'shortpath': shortpath,
                   'destination_url': destination,
                   'shortpath_prefix': shortpath.split('/')[0]}
    link = models.ShortLink(**link_kwargs)

  link.put()

  link_dict = convert_entity_to_dict(link, ['owner', 'shortpath', 'destination_url', 'organization'])
  link_dict['id'] = link.get_id()

  enqueue_event('link.%s' % ('updated' if updated_link_object else 'created'),
                'link',
                link_dict)
  return link