예제 #1
0
    def test_storage_delete(self):
        # An initial request to an oauth_required decorated path should be a
        # redirect to start the OAuth dance.
        response = self.app.get("/foo_path")
        self.assertTrue(response.status.startswith("302"))

        m = mox.Mox()
        m.StubOutWithMock(appengine, "_parse_state_value")
        appengine._parse_state_value("foo_path:xsrfkey123", mox.IgnoreArg()).AndReturn("foo_path")
        m.ReplayAll()

        # Now simulate the callback to /oauth2callback.
        response = self.app.get("/oauth2callback", {"code": "foo_access_code", "state": "foo_path:xsrfkey123"})
        self.assertEqual("http://localhost/foo_path", response.headers["Location"])
        self.assertEqual(None, self.decorator.credentials)

        # Now requesting the decorated path should work.
        response = self.app.get("/foo_path")

        self.assertTrue(self.had_credentials)

        # Credentials should be cleared after each call.
        self.assertEqual(None, self.decorator.credentials)

        # Invalidate the stored Credentials.
        self.found_credentials.store.delete()

        # Invalid Credentials should start the OAuth dance again.
        response = self.app.get("/foo_path")
        self.assertTrue(response.status.startswith("302"))

        m.UnsetStubs()
        m.VerifyAll()
  def test_storage_delete(self):
    # An initial request to an oauth_required decorated path should be a
    # redirect to start the OAuth dance.
    response = self.app.get('/foo_path')
    self.assertTrue(response.status.startswith('302'))

    m = mox.Mox()
    m.StubOutWithMock(appengine, '_parse_state_value')
    appengine._parse_state_value('foo_path:xsrfkey123',
                       mox.IgnoreArg()).AndReturn('foo_path')
    m.ReplayAll()

    # Now simulate the callback to /oauth2callback.
    response = self.app.get('/oauth2callback', {
        'code': 'foo_access_code',
        'state': 'foo_path:xsrfkey123',
        })
    self.assertEqual('http://localhost/foo_path', response.headers['Location'])
    self.assertEqual(None, self.decorator.credentials)

    # Now requesting the decorated path should work.
    response = self.app.get('/foo_path')

    # Invalidate the stored Credentials.
    self.decorator.credentials.store.delete()

    # Invalid Credentials should start the OAuth dance again.
    response = self.app.get('/foo_path')
    self.assertTrue(response.status.startswith('302'))

    m.UnsetStubs()
    m.VerifyAll()
예제 #3
0
    def test_aware(self):
        # An initial request to an oauth_aware decorated path should not redirect.
        response = self.app.get('http://localhost/bar_path/2012/01')
        self.assertEqual('Hello World!', response.body)
        self.assertEqual('200 OK', response.status)
        self.assertEqual(False, self.decorator.has_credentials())
        url = self.decorator.authorize_url()
        q = urlparse.parse_qs(url.split('?', 1)[1])
        self.assertEqual('http://localhost/oauth2callback',
                         q['redirect_uri'][0])
        self.assertEqual('foo_client_id', q['client_id'][0])
        self.assertEqual('foo_scope bar_scope', q['scope'][0])
        self.assertEqual('http://localhost/bar_path/2012/01',
                         q['state'][0].rsplit(':', 1)[0])
        self.assertEqual('code', q['response_type'][0])

        m = mox.Mox()
        m.StubOutWithMock(appengine, '_parse_state_value')
        appengine._parse_state_value('bar_path:xsrfkey456',
                                     mox.IgnoreArg()).AndReturn('bar_path')
        m.ReplayAll()

        # Now simulate the callback to /oauth2callback.
        url = self.decorator.authorize_url()
        response = self.app.get('/oauth2callback', {
            'code': 'foo_access_code',
            'state': 'bar_path:xsrfkey456',
        })
        self.assertEqual('http://localhost/bar_path',
                         response.headers['Location'])
        self.assertEqual(False, self.decorator.has_credentials())

        m.UnsetStubs()
        m.VerifyAll()

        # Now requesting the decorated path will have credentials.
        response = self.app.get('/bar_path/2012/01')
        self.assertEqual('200 OK', response.status)
        self.assertEqual('Hello World!', response.body)
        self.assertEqual(True, self.had_credentials)
        self.assertEqual('foo_refresh_token',
                         self.found_credentials.refresh_token)
        self.assertEqual('foo_access_token',
                         self.found_credentials.access_token)

        # Credentials should be cleared after each call.
        self.assertEqual(None, self.decorator.credentials)

        # Raising an exception still clears the Credentials.
        self.should_raise = True
        try:
            response = self.app.get('/bar_path/2012/01')
            self.fail('Should have raised an exception.')
        except Exception:
            pass
        self.assertEqual(None, self.decorator.credentials)
        self.should_raise = False
  def test_aware(self):
    # An initial request to an oauth_aware decorated path should not redirect.
    response = self.app.get('http://localhost/bar_path/2012/01')
    self.assertEqual('Hello World!', response.body)
    self.assertEqual('200 OK', response.status)
    self.assertEqual(False, self.decorator.has_credentials())
    url = self.decorator.authorize_url()
    q = parse_qs(url.split('?', 1)[1])
    self.assertEqual('http://localhost/oauth2callback', q['redirect_uri'][0])
    self.assertEqual('foo_client_id', q['client_id'][0])
    self.assertEqual('foo_scope bar_scope', q['scope'][0])
    self.assertEqual('http://localhost/bar_path/2012/01',
                     q['state'][0].rsplit(':', 1)[0])
    self.assertEqual('code', q['response_type'][0])

    m = mox.Mox()
    m.StubOutWithMock(appengine, '_parse_state_value')
    appengine._parse_state_value('bar_path:xsrfkey456',
                       mox.IgnoreArg()).AndReturn('bar_path')
    m.ReplayAll()

    # Now simulate the callback to /oauth2callback.
    url = self.decorator.authorize_url()
    response = self.app.get('/oauth2callback', {
        'code': 'foo_access_code',
        'state': 'bar_path:xsrfkey456',
        })
    self.assertEqual('http://localhost/bar_path', response.headers['Location'])
    self.assertEqual(False, self.decorator.has_credentials())

    m.UnsetStubs()
    m.VerifyAll()

    # Now requesting the decorated path will have credentials.
    response = self.app.get('/bar_path/2012/01')
    self.assertEqual('200 OK', response.status)
    self.assertEqual('Hello World!', response.body)
    self.assertEqual(True, self.had_credentials)
    self.assertEqual('foo_refresh_token',
                     self.found_credentials.refresh_token)
    self.assertEqual('foo_access_token',
                     self.found_credentials.access_token)

    # Credentials should be cleared after each call.
    self.assertEqual(None, self.decorator.credentials)

    # Raising an exception still clears the Credentials.
    self.should_raise = True
    try:
      response = self.app.get('/bar_path/2012/01')
      self.fail('Should have raised an exception.')
    except Exception:
      pass
    self.assertEqual(None, self.decorator.credentials)
    self.should_raise = False
  def test_required(self):
    # An initial request to an oauth_required decorated path should be a
    # redirect to start the OAuth dance.
    response = self.app.get('http://localhost/foo_path')
    self.assertTrue(response.status.startswith('302'))
    q = parse_qs(response.headers['Location'].split('?', 1)[1])
    self.assertEqual('http://localhost/oauth2callback', q['redirect_uri'][0])
    self.assertEqual('foo_client_id', q['client_id'][0])
    self.assertEqual('foo_scope bar_scope', q['scope'][0])
    self.assertEqual('http://localhost/foo_path',
                     q['state'][0].rsplit(':', 1)[0])
    self.assertEqual('code', q['response_type'][0])
    self.assertEqual(False, self.decorator.has_credentials())

    m = mox.Mox()
    m.StubOutWithMock(appengine, '_parse_state_value')
    appengine._parse_state_value('foo_path:xsrfkey123',
                       mox.IgnoreArg()).AndReturn('foo_path')
    m.ReplayAll()

    # Now simulate the callback to /oauth2callback.
    response = self.app.get('/oauth2callback', {
        'code': 'foo_access_code',
        'state': 'foo_path:xsrfkey123',
        })
    parts = response.headers['Location'].split('?', 1)
    self.assertEqual('http://localhost/foo_path', parts[0])
    self.assertEqual(None, self.decorator.credentials)
    if self.decorator._token_response_param:
        response = parse_qs(parts[1])[self.decorator._token_response_param][0]
        self.assertEqual(Http2Mock.content,
                         simplejson.loads(urllib.unquote(response)))

    m.UnsetStubs()
    m.VerifyAll()

    # Now requesting the decorated path should work.
    response = self.app.get('/foo_path')
    self.assertEqual('200 OK', response.status)
    self.assertEqual(True, self.decorator.has_credentials())
    self.assertEqual('foo_refresh_token',
                     self.decorator.credentials.refresh_token)
    self.assertEqual('foo_access_token',
                     self.decorator.credentials.access_token)

    # Invalidate the stored Credentials.
    self.decorator.credentials.invalid = True
    self.decorator.credentials.store.put(self.decorator.credentials)

    # Invalid Credentials should start the OAuth dance again.
    response = self.app.get('/foo_path')
    self.assertTrue(response.status.startswith('302'))
    q = parse_qs(response.headers['Location'].split('?', 1)[1])
    self.assertEqual('http://localhost/oauth2callback', q['redirect_uri'][0])
예제 #6
0
    def test_aware(self):
        # An initial request to an oauth_aware decorated path should not redirect.
        response = self.app.get("http://localhost/bar_path/2012/01")
        self.assertEqual("Hello World!", response.body)
        self.assertEqual("200 OK", response.status)
        self.assertEqual(False, self.decorator.has_credentials())
        url = self.decorator.authorize_url()
        q = parse_qs(url.split("?", 1)[1])
        self.assertEqual("http://localhost/oauth2callback", q["redirect_uri"][0])
        self.assertEqual("foo_client_id", q["client_id"][0])
        self.assertEqual("foo_scope bar_scope", q["scope"][0])
        self.assertEqual("http://localhost/bar_path/2012/01", q["state"][0].rsplit(":", 1)[0])
        self.assertEqual("code", q["response_type"][0])

        m = mox.Mox()
        m.StubOutWithMock(appengine, "_parse_state_value")
        appengine._parse_state_value("bar_path:xsrfkey456", mox.IgnoreArg()).AndReturn("bar_path")
        m.ReplayAll()

        # Now simulate the callback to /oauth2callback.
        url = self.decorator.authorize_url()
        response = self.app.get("/oauth2callback", {"code": "foo_access_code", "state": "bar_path:xsrfkey456"})
        self.assertEqual("http://localhost/bar_path", response.headers["Location"])
        self.assertEqual(False, self.decorator.has_credentials())

        m.UnsetStubs()
        m.VerifyAll()

        # Now requesting the decorated path will have credentials.
        response = self.app.get("/bar_path/2012/01")
        self.assertEqual("200 OK", response.status)
        self.assertEqual("Hello World!", response.body)
        self.assertEqual(True, self.had_credentials)
        self.assertEqual("foo_refresh_token", self.found_credentials.refresh_token)
        self.assertEqual("foo_access_token", self.found_credentials.access_token)

        # Credentials should be cleared after each call.
        self.assertEqual(None, self.decorator.credentials)

        # Raising an exception still clears the Credentials.
        self.should_raise = True
        try:
            response = self.app.get("/bar_path/2012/01")
            self.fail("Should have raised an exception.")
        except Exception:
            pass
        self.assertEqual(None, self.decorator.credentials)
        self.should_raise = False
 def test_build_and_parse_state(self):
   state = appengine._build_state_value(MockRequestHandler(), UserMock())
   self.assertEqual(
       'https://example.org',
       appengine._parse_state_value(state, UserMock()))
   self.assertRaises(appengine.InvalidXsrfTokenError,
                     appengine._parse_state_value, state[1:], UserMock())
예제 #8
0
 def test_build_and_parse_state(self):
   state = appengine._build_state_value(MockRequestHandler(), UserMock())
   self.assertEqual(
       'https://example.org',
       appengine._parse_state_value(state, UserMock()))
   self.assertRaises(appengine.InvalidXsrfTokenError,
                     appengine._parse_state_value, state[1:], UserMock())
예제 #9
0
  def get(self):
    # In order to use our own User class and webapp2 sessions
    # for user management instead of the App Engine Users API (which requires
    # showing a very ugly sign in page and requires the user to authorize
    # Google twice, essentially), we've created our own version of oauth2client's
    # OAuth2CallbackHandler.
    error = self.request.get('error')
    if error:
      message = self.request.get('error_description', error)
      logging.error(message)
      self.response.out.write('Authorization request failed.')
      return

    # Resume the oauth flow.
    self.decorator._create_flow(self)
    credentials = self.decorator.flow.step2_exchange(self.request.params)

    # Get a Google Account ID for the user that just OAuthed in.
    http = credentials.authorize(httplib2.Http(memcache))
    service = discovery.build('oauth2', 'v2', http=httplib2.Http(memcache))

    # Keys are: name, email, given_name, family_name, link, locale, id,
    # gender, verified_email (which is a bool), picture (url).
    data = service.userinfo().v2().me().get().execute(http=http)
    auth_id = 'google:{}'.format(data['id'])

    # If the user is returning, try and find an existing User.
    # If the user is signing in for the first time, create a User.
    user = self.user_model.get_by_auth_id(auth_id)
    if user is None:
      nickname = data['email']
      data.pop('id', None)
      unique_properties = ['nickname', 'email']
      ok, user = self.user_model.create_user(
          auth_id, unique_properties=unique_properties, nickname=nickname,
          **data)
      if not ok:
        logging.exception('Invalid values: {}'.format(user))
        self.error(500, 'Error creating user.')
        return

    # Store the user in the session.
    self.auth.set_session({'user_id': auth_id}, remember=True)

    session_user = users.UserStub(self.session['sid'])
    redirect_uri = appengine._parse_state_value(
        str(self.request.get('state')), session_user)

    # Store the user's credentials for later possible use.
    storage = self.decorator._storage_class(
        model=self.decorator._credentials_class,
        key_name='user:{}'.format(user.user_id()),
        property_name=self.decorator._credentials_property_name)
    storage.put(credentials)

    # Adjust the redirect uri in case this callback occurred as part of an
    # authenticated request to get some data.
    if self.decorator._token_response_param and credentials.token_response:
      resp = json.dumps(credentials.token_response)
      redirect_uri = appengine.util._add_query_parameter(
          redirect_uri, self.decorator._token_response_param, resp)

    self.redirect(redirect_uri)
예제 #10
0
    def test_required(self):
        # An initial request to an oauth_required decorated path should be a
        # redirect to start the OAuth dance.
        self.assertEqual(self.decorator.flow, None)
        self.assertEqual(self.decorator.credentials, None)
        response = self.app.get("http://localhost/foo_path")
        self.assertTrue(response.status.startswith("302"))
        q = parse_qs(response.headers["Location"].split("?", 1)[1])
        self.assertEqual("http://localhost/oauth2callback", q["redirect_uri"][0])
        self.assertEqual("foo_client_id", q["client_id"][0])
        self.assertEqual("foo_scope bar_scope", q["scope"][0])
        self.assertEqual("http://localhost/foo_path", q["state"][0].rsplit(":", 1)[0])
        self.assertEqual("code", q["response_type"][0])
        self.assertEqual(False, self.decorator.has_credentials())

        m = mox.Mox()
        m.StubOutWithMock(appengine, "_parse_state_value")
        appengine._parse_state_value("foo_path:xsrfkey123", mox.IgnoreArg()).AndReturn("foo_path")
        m.ReplayAll()

        # Now simulate the callback to /oauth2callback.
        response = self.app.get("/oauth2callback", {"code": "foo_access_code", "state": "foo_path:xsrfkey123"})
        parts = response.headers["Location"].split("?", 1)
        self.assertEqual("http://localhost/foo_path", parts[0])
        self.assertEqual(None, self.decorator.credentials)
        if self.decorator._token_response_param:
            response = parse_qs(parts[1])[self.decorator._token_response_param][0]
            self.assertEqual(Http2Mock.content, simplejson.loads(urllib.unquote(response)))
        self.assertEqual(self.decorator.flow, self.decorator._tls.flow)
        self.assertEqual(self.decorator.credentials, self.decorator._tls.credentials)

        m.UnsetStubs()
        m.VerifyAll()

        # Now requesting the decorated path should work.
        response = self.app.get("/foo_path")
        self.assertEqual("200 OK", response.status)
        self.assertEqual(True, self.had_credentials)
        self.assertEqual("foo_refresh_token", self.found_credentials.refresh_token)
        self.assertEqual("foo_access_token", self.found_credentials.access_token)
        self.assertEqual(None, self.decorator.credentials)

        # Raising an exception still clears the Credentials.
        self.should_raise = True
        try:
            response = self.app.get("/foo_path")
            self.fail("Should have raised an exception.")
        except Exception:
            pass
        self.assertEqual(None, self.decorator.credentials)
        self.should_raise = False

        # Invalidate the stored Credentials.
        self.found_credentials.invalid = True
        self.found_credentials.store.put(self.found_credentials)

        # Invalid Credentials should start the OAuth dance again.
        response = self.app.get("/foo_path")
        self.assertTrue(response.status.startswith("302"))
        q = parse_qs(response.headers["Location"].split("?", 1)[1])
        self.assertEqual("http://localhost/oauth2callback", q["redirect_uri"][0])
예제 #11
0
    def get(self):
        # In order to use our own User class and webapp2 sessions
        # for user management instead of the App Engine Users API (which requires
        # showing a very ugly sign in page and requires the user to authorize
        # Google twice, essentially), we've created our own version of oauth2client's
        # OAuth2CallbackHandler.
        error = self.request.get('error')
        if error:
            message = self.request.get('error_description', error)
            logging.error(message)
            self.response.out.write('Authorization request failed.')
            return

        # Resume the oauth flow.
        self.decorator._create_flow(self)
        credentials = self.decorator.flow.step2_exchange(self.request.params)

        # Get a Google Account ID for the user that just OAuthed in.
        http = credentials.authorize(httplib2.Http(memcache))
        service = discovery.build('oauth2', 'v2', http=httplib2.Http(memcache))

        # Keys are: name, email, given_name, family_name, link, locale, id,
        # gender, verified_email (which is a bool), picture (url).
        data = service.userinfo().v2().me().get().execute(http=http)
        auth_id = 'google:{}'.format(data['id'])

        # If the user is returning, try and find an existing User.
        # If the user is signing in for the first time, create a User.
        user = self.user_model.get_by_auth_id(auth_id)
        if user is None:
            nickname = data['email']
            data.pop('id', None)
            unique_properties = ['nickname', 'email']
            ok, user = self.user_model.create_user(
                auth_id,
                unique_properties=unique_properties,
                nickname=nickname,
                **data)
            if not ok:
                logging.exception('Invalid values: {}'.format(user))
                self.error(500, 'Error creating user.')
                return

        # Store the user in the session.
        self.auth.set_session({'user_id': auth_id}, remember=True)

        session_user = users.UserStub(self.session['sid'])
        redirect_uri = appengine._parse_state_value(
            str(self.request.get('state')), session_user)

        # Store the user's credentials for later possible use.
        storage = self.decorator._storage_class(
            model=self.decorator._credentials_class,
            key_name='user:{}'.format(user.user_id()),
            property_name=self.decorator._credentials_property_name)
        storage.put(credentials)

        # Adjust the redirect uri in case this callback occurred as part of an
        # authenticated request to get some data.
        if self.decorator._token_response_param and credentials.token_response:
            resp = json.dumps(credentials.token_response)
            redirect_uri = appengine.util._add_query_parameter(
                redirect_uri, self.decorator._token_response_param, resp)

        self.redirect(redirect_uri)