def authenticateCredentials(self, credentials): """Authenticate a request that contains an OAuth2 bearer access token. Implementation of IAuthenticationPlugin that authenticates requests that contain a valid OAuth2 bearer access token. """ # Ignore credentials that are not from our extractor extractor = credentials.get('extractor') if extractor != self.getId(): # While RFC 6750 says that requests without authentication MUST # be answered with a WWW-Authenticate challenge, we can't do that # here, since OAuth2 isn't the only authentication mechanism we # need to support. # See: https://tools.ietf.org/html/rfc6750#section-3 return None received_token = credentials['access_token'] storage = CredentialStorage(self) # Reject unknown or revoked tokens if not storage.contains_access_token(received_token): # TODO: Should we send an 'invalid_token' error response here? return None stored_access_token = storage.get_access_token(received_token) # Reject expired tokens if self.is_expired(stored_access_token): # Token expired, send error response according to # https://tools.ietf.org/html/rfc6750#section-3.1 response = getRequest().response body = json.dumps({ 'error': 'invalid_token', 'error_description': 'Access token expired' }) response.setBody(body, lock=True) response.setStatus(401, lock=True) # TODO: According to RFC 6750, we should also send a # WWW-Authenticate: Bearer realm="example" # here. Check whether we really want to send this challenge return None # Fetch service key that the token was tied to (by us) service_key = storage.get_service_key(stored_access_token['key_id'], unrestricted=True) ip_range = service_key['ip_range'] if ip_range is not None: client_ip = self.REQUEST.getClientAddr() if not client_ip: # IP range limitations in place - require that we get a # client IP (trusted proxies need to be set up correctly) log.warn('Authentication attempt for key with IP range ' 'restrictions, but failed to get client IP from ' 'getClientAddr() - check trusted-proxy in zope.conf') return None if not permitted_ip(client_ip, ip_range): log.warn('Authentication attempt from ' 'disallowed IP %s' % client_ip) return None # Fetch and verify the user associated with the stored access token user_id = stored_access_token['user_id'] pas = self._getPAS() # This only works for users in Plone site, not Zope application root info = pas._verifyUser(pas.plugins, user_id=user_id) if info is None: return None mtool = getToolByName(getSite(), 'portal_membership') member = mtool.getMemberById(user_id) if member is None: return None return user_id, user_id
def test_disallowed_ip_in_multiple_ipv4_address_ranges(self): self.assertFalse(permitted_ip('192.168.0.7', '10.0.0.5, 192.168.0.1'))
def test_allowed_ip_in_multiple_ipv4_cidr_network_ranges(self): self.assertTrue( permitted_ip('192.168.5.5', '10.0.0.0/8, 192.168.0.0/16')) self.assertTrue(permitted_ip('10.1.5.20', '10.0.0.0/8, 192.168.0.0/16'))
def test_invalid_ip_range_is_rejected(self): self.assertFalse(permitted_ip('10.0.0.1', '500.500.0.0/33'))
def test_allowed_ip_in_multiple_ipv4_address_ranges(self): self.assertTrue(permitted_ip('10.0.0.5', '10.0.0.5, 192.168.0.1')) self.assertTrue(permitted_ip('192.168.0.1', '10.0.0.5, 192.168.0.1'))
def test_invalid_ip_address_is_rejected(self): with self.assertRaises(ValueError): permitted_ip('500.500.0.0', '192.168.0.0/16')
def test_disallowed_ip_in_single_ipv4_cidr_network_range(self): self.assertFalse(permitted_ip('10.0.0.0', '192.168.0.0/16'))
def test_allowed_ip_in_single_ipv4_cidr_network_range(self): self.assertTrue(permitted_ip('192.168.0.1', '192.168.0.0/16'))
def test_disallowed_ip_in_single_ipv4_address_range(self): self.assertFalse(permitted_ip('10.0.0.0', '192.168.0.1'))
def test_allowed_ip_in_single_ipv4_address_range(self): self.assertTrue(permitted_ip('192.168.0.1', '192.168.0.1'))