def _authenticateApiKey(self, username, password): if not Setting().get(SettingKey.API_KEYS): logger.warn('API key functionality is disabled') return False token_user, token = ApiKeyModel().createToken(password[4:], days=7) user = self._getUser(username) return token_user.get('_id', 'no_token') == user['_id']
def migrate(user, apiUrl): print("\nMigrating data for user %s" % user['login']) # Get or create API key for migration apiKey = ApiKey().createApiKey(user, 'migration') # Look up the "Home" directory homeDir = lookUpPath("/user/%s/Home" % user['login'], user=user, test=True)["document"] print("Found homeDir %s" % homeDir) # Remove old Home, create new (needed for wt_home_dirs) print("Removing homeDir") Folder().remove(homeDir) print("Creating new homeDir") newHomeDir = Folder().createFolder(parent=user, name="Home", creator=user, parentType="user") # Mount home dir via webdav tmpDir = "/tmp/migrate/%s/" % user['login'] os.makedirs(tmpDir, exist_ok=True) print("Created tmpDir %s" % tmpDir) mount(apiKey['key'], tmpDir, newHomeDir['_id'], apiUrl) # Move Data directory, if present print("Moving files") try: if os.path.exists('/user/%s/Data' % user['login']): shutil.move('/user/%s/Data' % user['login'], tmpDir) if os.path.exists('/user/%s/Workspace' % user['login']): shutil.move('/user/%s/Workspace' % user['login'], tmpDir) if os.path.exists('/user/%s/Home' % user['login']): for file in glob.glob('/user/%s/Home/*' % user['login']): print(file) shutil.move(file, tmpDir) os.rmdir('/user/%s/Home' % user['login']) except Exception as e: print("Error moving files: %s" % str(e)) time.sleep(60) # Unmount unmount(tmpDir) # Remove tmp folder shutil.rmtree(tmpDir) # Remove the API key ApiKey().remove(apiKey)
def testScopeValidation(self): # Make sure normal user cannot request admin scopes requestedScopes = [TokenScope.DATA_OWN, TokenScope.SETTINGS_READ] msg = 'Invalid scopes: %s.$' % TokenScope.SETTINGS_READ with six.assertRaisesRegex(self, ValidationException, msg): ApiKey().createApiKey(user=self.user, name='', scope=requestedScopes) # Make sure an unregistered scope cannot be set on an API key requestedScopes = [TokenScope.DATA_OWN, TokenScope.SETTINGS_READ, 'nonsense'] msg = 'Invalid scopes: nonsense.$' with six.assertRaisesRegex(self, ValidationException, msg): ApiKey().createApiKey(user=self.admin, name='', scope=requestedScopes)
def testReactivatedKeyCanCreateTokens(server, user, apiKey): newScopes = [TokenScope.DATA_READ, TokenScope.DATA_WRITE] resp = server.request('/api_key/%s' % apiKey['_id'], params={ 'active': False, 'tokenDuration': 10, 'scope': json.dumps(newScopes) }, method='PUT', user=user) assertStatusOk(resp) resp = server.request('/api_key/%s' % apiKey['_id'], params={'active': True}, method='PUT', user=user) assertStatusOk(resp) assert resp.json['key'] == apiKey['key'] apiKey = ApiKey().load(resp.json['_id'], force=True) # Should now be able to make tokens with 10 day duration resp = server.request('/api_key/token', method='POST', params={'key': apiKey['key']}) assertStatusOk(resp) token = Token().load(resp.json['authToken']['token'], force=True, objectId=False) duration = token['expires'] - token['created'] assert duration == datetime.timedelta(days=10) assert set(token['scope']) == set(newScopes)
def testScopeValidation(db, admin, user): # Make sure normal user cannot request admin scopes requestedScopes = [TokenScope.DATA_OWN, TokenScope.SETTINGS_READ] with pytest.raises(ValidationException, match='Invalid scopes: %s.$' % TokenScope.SETTINGS_READ): ApiKey().createApiKey(user=user, name='', scope=requestedScopes) # Make sure an unregistered scope cannot be set on an API key requestedScopes = [ TokenScope.DATA_OWN, TokenScope.SETTINGS_READ, 'nonsense' ] with pytest.raises(ValidationException, match='Invalid scopes: nonsense.$'): ApiKey().createApiKey(user=admin, name='', scope=requestedScopes)
def apiKey(server, user): # Create a new API key with full access resp = server.request('/api_key', method='POST', params={'name': 'test key'}, user=user) assertStatusOk(resp) apiKey = ApiKey().load(resp.json['_id'], force=True) yield apiKey
def setUp(self): base.TestCase.setUp(self) self.user = User().createUser( firstName='First', lastName='Last', login='******', password='******', email='*****@*****.**') self.publicFolder = six.next(Folder().childFolders( parentType='user', parent=self.user, user=None, limit=1)) self.apiKey = ApiKey().createApiKey(self.user, name='') self.downloadDir = os.path.join( os.path.dirname(__file__), '_testDownload') shutil.rmtree(self.downloadDir, ignore_errors=True)
def testDisableApiKeysSetting(server, user): errMsg = 'API key functionality is disabled on this instance.' resp = server.request('/api_key', method='POST', user=user, params={'name': 'test key'}) assertStatusOk(resp) # Disable API keys Setting().set(SettingKey.API_KEYS, False) # Key should still exist key = ApiKey().load(resp.json['_id'], force=True, exc=True) # No longer possible to authenticate with existing key resp = server.request('/api_key/token', method='POST', params={'key': key['key']}) assertStatus(resp, 400) assert resp.json['message'] == errMsg # No longer possible to create new keys resp = server.request('/api_key', method='POST', user=user, params={'name': 'should not work'}) assertStatus(resp, 400) assert resp.json['message'] == errMsg # Still possible to delete key resp = server.request('/api_key/%s' % key['_id'], method='DELETE', user=user) assertStatusOk(resp) assert ApiKey().load(key['_id'], force=True) is None
def testInactiveKeyStructure(server, user, apiKey): newScopes = [TokenScope.DATA_READ, TokenScope.DATA_WRITE] resp = server.request('/api_key/%s' % apiKey['_id'], params={ 'active': False, 'tokenDuration': 10, 'scope': json.dumps(newScopes) }, method='PUT', user=user) assertStatusOk(resp) # Make sure key itself didn't change assert resp.json['key'] == apiKey['key'] apiKey = ApiKey().load(resp.json['_id'], force=True) assert not apiKey['active'] assert apiKey['tokenDuration'] == 10 assert set(apiKey['scope']) == set(newScopes)
def setUp(self): super().setUp(self) from girder.plugins.wt_home_dir import HOME_DIRS_APPS self.homeDirsApps = HOME_DIRS_APPS # nopep8 from girder.plugins.wt_home_dir.constants import WORKSPACE_NAME global WORKSPACE_NAME # We need to recreate DirectFS assetstore, which was dropped in # base.TestCase.setUp... # girder.plugins is not available until setUp is running self.rootPaths = {} for e in self.homeDirsApps.entries(): provider = e.app.providerMap['/']['provider'] self.rootPaths[e.realm] = provider.rootFolderPath if e.realm == 'homes': self.homesRoot = provider.rootFolderPath self.homesPathMapper = e.pathMapper users = ({ 'email': '*****@*****.**', 'login': '******', 'firstName': 'Root', 'lastName': 'van Klompf', 'password': '******' }, { 'email': '*****@*****.**', 'login': '******', 'firstName': 'Joe', 'lastName': 'Regular', 'password': '******' }) self.admin, self.user = [ self.model('user').createUser(**user) for user in users ] self.token = Token().createToken(self.user) self.api_key = ApiKey().createApiKey(user=self.user, name="webdav", scope=[TokenScope.DATA_OWN]) from girder.plugins.wt_versioning.constants import PluginSettings as \ VerPluginSettings Setting().set(VerPluginSettings.RUNS_DIRS_ROOT, self.rootPaths["runs"]) # Needs to be on the same level as runs and needs to be called versions... version_root = pathlib.Path(os.path.dirname( self.rootPaths["runs"])) / "versions" if not version_root.is_dir(): version_root.mkdir() Setting().set(VerPluginSettings.VERSIONS_DIRS_ROOT, version_root.as_posix()) from girder.plugins.wholetale.models.image import Image from girder.plugins.wholetale.models.tale import Tale image = Image().createImage(name='test image', creator=self.admin, public=True) self.privateTale = Tale().createTale(image, [], creator=self.user, public=False) # TODO: add tests checking that other users only have read access to public tales self.publicTale = Tale().createTale(image, [], creator=self.user, public=True) self.clearDAVAuthCache()
def testKeyPolicies(self): # Create a new API key with full access resp = self.request('/api_key', method='POST', params={ 'name': 'test key' }, user=self.user) self.assertStatusOk(resp) apiKey = ApiKey().load(resp.json['_id'], force=True) self.assertEqual(apiKey['scope'], None) self.assertEqual(apiKey['name'], 'test key') self.assertEqual(apiKey['lastUse'], None) self.assertEqual(apiKey['tokenDuration'], None) self.assertEqual(apiKey['active'], True) # Create a token using the key resp = self.request('/api_key/token', method='POST', params={ 'key': apiKey['key'], 'duration': self.defaultDuration + 1000 }) self.assertStatusOk(resp) token = Token().load(resp.json['authToken']['token'], force=True, objectId=False) # Make sure token has full user auth access self.assertEqual(token['userId'], self.user['_id']) self.assertEqual(token['scope'], [TokenScope.USER_AUTH]) # Make sure the token references the API key used to create it self.assertEqual(token['apiKeyId'], apiKey['_id']) # Make sure the token duration is not longer than the default duration = token['expires'] - token['created'] self.assertEqual(duration, datetime.timedelta(days=self.defaultDuration)) # We should be able to request a duration shorter than default resp = self.request('/api_key/token', method='POST', params={ 'key': apiKey['key'], 'duration': self.defaultDuration - 1 }) self.assertStatusOk(resp) token = Token().load(resp.json['authToken']['token'], force=True, objectId=False) duration = token['expires'] - token['created'] self.assertEqual(duration, datetime.timedelta(days=self.defaultDuration - 1)) # We should have two tokens for this key q = { 'userId': self.user['_id'], 'apiKeyId': apiKey['_id'] } count = Token().find(q).count() self.assertEqual(count, 2) # Deactivate the key and change the token duration and scope newScopes = [TokenScope.DATA_READ, TokenScope.DATA_WRITE] resp = self.request('/api_key/%s' % apiKey['_id'], params={ 'active': False, 'tokenDuration': 10, 'scope': json.dumps(newScopes) }, method='PUT', user=self.user) self.assertStatusOk(resp) # Make sure key itself didn't change self.assertEqual(resp.json['key'], apiKey['key']) apiKey = ApiKey().load(resp.json['_id'], force=True) self.assertEqual(apiKey['active'], False) self.assertEqual(apiKey['tokenDuration'], 10) self.assertEqual(set(apiKey['scope']), set(newScopes)) # Should now have a last used timestamp self.assertIsInstance(apiKey['lastUse'], datetime.datetime) # This should have deleted all corresponding tokens q = { 'userId': self.user['_id'], 'apiKeyId': apiKey['_id'] } count = Token().find(q).count() self.assertEqual(count, 0) # We should not be able to create tokens for this key anymore resp = self.request('/api_key/token', method='POST', params={ 'key': apiKey['key'] }) self.assertStatus(resp, 400) self.assertEqual(resp.json['message'], 'Invalid API key.') # Reactivate key resp = self.request('/api_key/%s' % apiKey['_id'], params={ 'active': True }, method='PUT', user=self.user) self.assertStatusOk(resp) self.assertEqual(resp.json['key'], apiKey['key']) apiKey = ApiKey().load(resp.json['_id'], force=True) # Should now be able to make tokens with 10 day duration resp = self.request('/api_key/token', method='POST', params={ 'key': apiKey['key'] }) self.assertStatusOk(resp) token = Token().load(resp.json['authToken']['token'], force=True, objectId=False) duration = token['expires'] - token['created'] self.assertEqual(duration, datetime.timedelta(days=10)) self.assertEqual(set(token['scope']), set(newScopes)) # Deleting the API key should delete the tokens made with it count = Token().find(q).count() self.assertEqual(count, 1) resp = self.request('/api_key/%s' % apiKey['_id'], method='DELETE', user=self.user) self.assertStatusOk(resp) count = Token().find(q).count() self.assertEqual(count, 0)