mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-04-12 03:18:08 -04:00
Merge branch 'master' into add_deployment_docs
This commit is contained in:
@@ -7,8 +7,11 @@ exclude_paths:
|
||||
- 'docs/**'
|
||||
- '**/docs/**'
|
||||
- '**/pkg/proto/**'
|
||||
- 'konnectd/assets/identifier/**'
|
||||
- 'konnectd/ui_config/**'
|
||||
- 'konnectd/scripts/**'
|
||||
- 'settings/rollup.config.js'
|
||||
- 'accounts/rollup.config.js'
|
||||
- 'ocis/docker/eos-ocis/Dockerfile'
|
||||
- 'ocis/docker/eos-ocis-dev/Dockerfile'
|
||||
|
||||
...
|
||||
...
|
||||
|
||||
274
.drone.star
274
.drone.star
@@ -15,12 +15,12 @@ config = {
|
||||
},
|
||||
'apiTests': {
|
||||
'coreBranch': 'master',
|
||||
'coreCommit': '35c53c096f40dd15ae15940c589965379e921857',
|
||||
'coreCommit': '6806e327048fa107678536f9eded5166eb781ba3',
|
||||
'numberOfParts': 6
|
||||
},
|
||||
'uiTests': {
|
||||
'phoenixBranch': 'master',
|
||||
'phoenixCommit': '693fa6d275243fc860b4b82e5146d25f62d927fb',
|
||||
'phoenixCommit': '2fabcb8bf376dbbdff9bb7e787dbee5c334b4a7b',
|
||||
'suites': {
|
||||
'phoenixWebUI1': [
|
||||
'webUICreateFilesFolders',
|
||||
@@ -38,12 +38,12 @@ config = {
|
||||
'webUIRenameFiles',
|
||||
'webUIRenameFolders',
|
||||
],
|
||||
'phoenixWebUI4': [
|
||||
'phoenixWebUI4': [
|
||||
'webUITrashbin',
|
||||
'webUIUpload',
|
||||
# All tests in the following suites are skipped currently
|
||||
# so they won't run now but when they are enabled they will run
|
||||
'webUIRestrictSharing',
|
||||
],
|
||||
'phoenixWebUI5': [
|
||||
'webUISharingAutocompletion',
|
||||
'webUISharingInternalGroups',
|
||||
'webUISharingInternalUsers',
|
||||
@@ -51,14 +51,20 @@ config = {
|
||||
'webUISharingFilePermissionsGroups',
|
||||
'webUISharingFolderPermissionsGroups',
|
||||
'webUISharingFolderAdvancedPermissionsGroups',
|
||||
],
|
||||
'phoenixWebUI6': [
|
||||
'webUIResharing',
|
||||
'webUISharingPublic',
|
||||
'webUISharingPublicDifferentRoles',
|
||||
'webUISharingAcceptShares',
|
||||
'webUISharingNotifications',
|
||||
],
|
||||
'phoenixWebUI7': [
|
||||
'webUISharingFilePermissionMultipleUsers',
|
||||
],
|
||||
'phoenixWebUI8': [
|
||||
'webUISharingFolderPermissionMultipleUsers',
|
||||
'webUISharingFolderAdvancedPermissionMultipleUsers',
|
||||
'webUISharingNotifications',
|
||||
],
|
||||
}
|
||||
}
|
||||
@@ -102,6 +108,7 @@ def main(ctx):
|
||||
docker(ctx, 'amd64'),
|
||||
docker(ctx, 'arm64'),
|
||||
docker(ctx, 'arm'),
|
||||
dockerEos(ctx),
|
||||
binary(ctx, 'linux'),
|
||||
binary(ctx, 'darwin'),
|
||||
binary(ctx, 'windows'),
|
||||
@@ -117,7 +124,7 @@ def main(ctx):
|
||||
updateDeployment(ctx)
|
||||
]
|
||||
|
||||
if '[docs-only]' in ctx.build.title:
|
||||
if '[docs-only]' in (ctx.build.title + ctx.build.message):
|
||||
pipelines = docs(ctx)
|
||||
pipelines['depends_on'] = []
|
||||
else:
|
||||
@@ -142,14 +149,14 @@ def testPipelines(ctx):
|
||||
pipelines.append(coreApiTests(ctx, config['apiTests']['coreBranch'], config['apiTests']['coreCommit'], runPart, config['apiTests']['numberOfParts'], 'ocis'))
|
||||
|
||||
pipelines += uiTests(ctx, config['uiTests']['phoenixBranch'], config['uiTests']['phoenixCommit'])
|
||||
pipelines.append(accountsUITests(ctx, config['uiTests']['phoenixBranch'], config['uiTests']['phoenixCommit'], 'owncloud'))
|
||||
pipelines.append(accountsUITests(ctx, config['uiTests']['phoenixBranch'], config['uiTests']['phoenixCommit']))
|
||||
return pipelines
|
||||
|
||||
def testing(ctx, module):
|
||||
steps = generate(module) + [
|
||||
{
|
||||
'name': 'vet',
|
||||
'image': 'webhippie/golang:1.13',
|
||||
'image': 'webhippie/golang:1.14',
|
||||
'pull': 'always',
|
||||
'commands': [
|
||||
'cd %s' % (module),
|
||||
@@ -164,7 +171,7 @@ def testing(ctx, module):
|
||||
},
|
||||
{
|
||||
'name': 'staticcheck',
|
||||
'image': 'webhippie/golang:1.13',
|
||||
'image': 'webhippie/golang:1.14',
|
||||
'pull': 'always',
|
||||
'commands': [
|
||||
'cd %s' % (module),
|
||||
@@ -179,7 +186,7 @@ def testing(ctx, module):
|
||||
},
|
||||
{
|
||||
'name': 'lint',
|
||||
'image': 'webhippie/golang:1.13',
|
||||
'image': 'webhippie/golang:1.14',
|
||||
'pull': 'always',
|
||||
'commands': [
|
||||
'cd %s' % (module),
|
||||
@@ -194,7 +201,7 @@ def testing(ctx, module):
|
||||
},
|
||||
{
|
||||
'name': 'test',
|
||||
'image': 'webhippie/golang:1.13',
|
||||
'image': 'webhippie/golang:1.14',
|
||||
'pull': 'always',
|
||||
'commands': [
|
||||
'cd %s' % (module),
|
||||
@@ -293,9 +300,9 @@ def uploadCoverage(ctx):
|
||||
'SONAR_TOKEN': {
|
||||
'from_secret': 'sonar_token',
|
||||
},
|
||||
'SONAR_PULL_REQUEST_BASE': 'master' if ctx.build.event == 'pull_request' else None,
|
||||
'SONAR_PULL_REQUEST_BRANCH': ctx.build.source if ctx.build.event == 'pull_request' else None,
|
||||
'SONAR_PULL_REQUEST_KEY': ctx.build.ref.replace("refs/pull/", "").split("/")[0] if ctx.build.event == 'pull_request' else None,
|
||||
'SONAR_PULL_REQUEST_BASE': '%s' % ('master' if ctx.build.event == 'pull_request' else None),
|
||||
'SONAR_PULL_REQUEST_BRANCH': '%s' % (ctx.build.source if ctx.build.event == 'pull_request' else None),
|
||||
'SONAR_PULL_REQUEST_KEY': '%s' % (ctx.build.ref.replace("refs/pull/", "").split("/")[0] if ctx.build.event == 'pull_request' else None),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -341,9 +348,10 @@ def localApiTests(ctx, coreBranch = 'master', coreCommit = '', storage = 'ownclo
|
||||
'pull': 'always',
|
||||
'environment' : {
|
||||
'TEST_SERVER_URL': 'https://ocis-server:9200',
|
||||
'OCIS_REVA_DATA_ROOT': '%s' % ('/srv/app/tmp/ocis/owncloud/' if storage == 'owncloud' else ''),
|
||||
'DELETE_USER_DATA_CMD': '%s' % ('rm -rf /srv/app/tmp/ocis/owncloud/data/*' if storage == 'owncloud' else 'rm -rf /srv/app/tmp/ocis/storage/users/nodes/root/*'),
|
||||
'OCIS_REVA_DATA_ROOT': '%s' % ('/srv/app/tmp/ocis/owncloud/data/' if storage == 'owncloud' else ''),
|
||||
'DELETE_USER_DATA_CMD': '%s' % ('' if storage == 'owncloud' else 'rm -rf /srv/app/tmp/ocis/storage/users/nodes/root/* /srv/app/tmp/ocis/storage/users/nodes/*-*-*-*'),
|
||||
'SKELETON_DIR': '/srv/app/tmp/testing/data/apiSkeleton',
|
||||
'OCIS_SKELETON_STRATEGY': '%s' % ('copy' if storage == 'owncloud' else 'upload'),
|
||||
'TEST_OCIS':'true',
|
||||
'BEHAT_FILTER_TAGS': '~@skipOnOcis-%s-Storage' % ('OC' if storage == 'owncloud' else 'OCIS'),
|
||||
'PATH_TO_CORE': '/srv/app/testrunner'
|
||||
@@ -394,9 +402,10 @@ def coreApiTests(ctx, coreBranch = 'master', coreCommit = '', part_number = 1, n
|
||||
'pull': 'always',
|
||||
'environment' : {
|
||||
'TEST_SERVER_URL': 'https://ocis-server:9200',
|
||||
'OCIS_REVA_DATA_ROOT': '%s' % ('/srv/app/tmp/ocis/owncloud/' if storage == 'owncloud' else ''),
|
||||
'DELETE_USER_DATA_CMD': '%s' % ('rm -rf /srv/app/tmp/ocis/owncloud/*' if storage == 'owncloud' else 'rm -rf /srv/app/tmp/ocis/storage/users/nodes/root/*'),
|
||||
'OCIS_REVA_DATA_ROOT': '%s' % ('/srv/app/tmp/ocis/owncloud/data/' if storage == 'owncloud' else ''),
|
||||
'DELETE_USER_DATA_CMD': '%s' % ('' if storage == 'owncloud' else 'rm -rf /srv/app/tmp/ocis/storage/users/nodes/root/* /srv/app/tmp/ocis/storage/users/nodes/*-*-*-*'),
|
||||
'SKELETON_DIR': '/srv/app/tmp/testing/data/apiSkeleton',
|
||||
'OCIS_SKELETON_STRATEGY': '%s' % ('copy' if storage == 'owncloud' else 'upload'),
|
||||
'TEST_OCIS':'true',
|
||||
'BEHAT_FILTER_TAGS': '~@notToImplementOnOCIS&&~@toImplementOnOCIS&&~comments-app-required&&~@federation-app-required&&~@notifications-app-required&&~systemtags-app-required&&~@local_storage&&~@skipOnOcis-%s-Storage' % ('OC' if storage == 'owncloud' else 'OCIS'),
|
||||
'DIVIDE_INTO_NUM_PARTS': number_of_parts,
|
||||
@@ -459,7 +468,7 @@ def uiTestPipeline(suiteName, phoenixBranch = 'master', phoenixCommit = '', stor
|
||||
'SERVER_HOST': 'https://ocis-server:9200',
|
||||
'BACKEND_HOST': 'https://ocis-server:9200',
|
||||
'RUN_ON_OCIS': 'true',
|
||||
'OCIS_REVA_DATA_ROOT': '/srv/app/tmp/ocis/owncloud',
|
||||
'OCIS_REVA_DATA_ROOT': '/srv/app/tmp/ocis/owncloud/data',
|
||||
'OCIS_SKELETON_DIR': '/srv/app/testing/data/webUISkeleton',
|
||||
'PHOENIX_CONFIG': '/drone/src/ocis/tests/config/drone/ocis-config.json',
|
||||
'TEST_TAGS': 'not @skipOnOCIS and not @skip',
|
||||
@@ -509,7 +518,7 @@ def uiTestPipeline(suiteName, phoenixBranch = 'master', phoenixCommit = '', stor
|
||||
},
|
||||
}
|
||||
|
||||
def accountsUITests(ctx, phoenixBranch, phoenixCommitId, storage):
|
||||
def accountsUITests(ctx, phoenixBranch, phoenixCommit, storage = 'owncloud'):
|
||||
return {
|
||||
'kind': 'pipeline',
|
||||
'type': 'docker',
|
||||
@@ -518,114 +527,48 @@ def accountsUITests(ctx, phoenixBranch, phoenixCommitId, storage):
|
||||
'os': 'linux',
|
||||
'arch': 'amd64',
|
||||
},
|
||||
'steps': [
|
||||
{
|
||||
'name': 'build-ocis',
|
||||
'image': 'webhippie/golang:1.13',
|
||||
'pull': 'always',
|
||||
'commands': [
|
||||
'cd ocis',
|
||||
'make build',
|
||||
'mkdir -p /srv/app/ocis/bin',
|
||||
'cp bin/ocis /srv/app/ocis/bin',
|
||||
],
|
||||
'volumes': [
|
||||
{
|
||||
'name': 'gopath',
|
||||
'path': '/srv/app'
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
'name': 'ocis-server',
|
||||
'image': 'webhippie/golang:1.13',
|
||||
'pull': 'always',
|
||||
'detach': True,
|
||||
'environment' : {
|
||||
#'OCIS_LOG_LEVEL': 'debug',
|
||||
'STORAGE_STORAGE_HOME_DRIVER': '%s' % (storage),
|
||||
'STORAGE_STORAGE_HOME_DATA_DRIVER': '%s' % (storage),
|
||||
'STORAGE_STORAGE_OC_DRIVER': '%s' % (storage),
|
||||
'STORAGE_STORAGE_OC_DATA_DRIVER': '%s' % (storage),
|
||||
'STORAGE_STORAGE_HOME_DATA_TEMP_FOLDER': '/srv/app/tmp/',
|
||||
'STORAGE_STORAGE_OCIS_ROOT': '/srv/app/tmp/ocis/storage/users',
|
||||
'STORAGE_STORAGE_LOCAL_ROOT': '/srv/app/tmp/ocis/reva/root',
|
||||
'STORAGE_STORAGE_OWNCLOUD_DATADIR': '/srv/app/tmp/ocis/owncloud/data',
|
||||
'STORAGE_STORAGE_OC_DATA_TEMP_FOLDER': '/srv/app/tmp/',
|
||||
'STORAGE_STORAGE_OWNCLOUD_REDIS_ADDR': 'redis:6379',
|
||||
'STORAGE_OIDC_ISSUER': 'https://ocis-server:9200',
|
||||
'STORAGE_LDAP_IDP': 'https://ocis-server:9200',
|
||||
'PROXY_OIDC_ISSUER': 'https://ocis-server:9200',
|
||||
'STORAGE_STORAGE_OC_DATA_SERVER_URL': 'http://ocis-server:9164/data',
|
||||
'STORAGE_DATAGATEWAY_URL': 'https://ocis-server:9200/data',
|
||||
'STORAGE_FRONTEND_URL': 'https://ocis-server:9200',
|
||||
'PHOENIX_WEB_CONFIG': '/drone/src/accounts/ui/tests/config/drone/ocis-config.json',
|
||||
'KONNECTD_IDENTIFIER_REGISTRATION_CONF': '/drone/src/accounts/ui/tests/config/drone/identifier-registration.yml',
|
||||
'KONNECTD_ISS': 'https://ocis-server:9200',
|
||||
'ACCOUNTS_STORAGE_DISK_PATH': '/srv/app/tmp/ocis-accounts', # Temporary workaround, don't use metadata storage
|
||||
},
|
||||
'commands': [
|
||||
'mkdir -p /srv/app/tmp/reva',
|
||||
# First run settings service because accounts need it to register the settings bundles
|
||||
'/srv/app/ocis/bin/ocis settings &',
|
||||
|
||||
# Wait for the settings service to start
|
||||
"while [[ \"$(curl -s -o /dev/null -w ''%{http_code}'' localhost:9190)\" != \"404\" ]]; do sleep 2; done",
|
||||
|
||||
# Now start the accounts service
|
||||
'/srv/app/ocis/bin/ocis accounts &',
|
||||
|
||||
# Wait for the accounts service to start
|
||||
"while [[ \"$(curl -s -o /dev/null -w ''%{http_code}'' localhost:9181)\" != \"404\" ]]; do sleep 2; done",
|
||||
|
||||
# Now run all the ocis services except the accounts and settings because they are already running
|
||||
'/srv/app/ocis/bin/ocis server',
|
||||
],
|
||||
'volumes': [
|
||||
{
|
||||
'name': 'gopath',
|
||||
'path': '/srv/app'
|
||||
},
|
||||
]
|
||||
},
|
||||
'steps':
|
||||
generate('ocis') +
|
||||
build() +
|
||||
ocisServer(storage) + [
|
||||
{
|
||||
'name': 'WebUIAcceptanceTests',
|
||||
'image': 'owncloudci/nodejs:10',
|
||||
'image': 'owncloudci/nodejs:11',
|
||||
'pull': 'always',
|
||||
'environment': {
|
||||
'SERVER_HOST': 'https://ocis-server:9200',
|
||||
'BACKEND_HOST': 'https://ocis-server:9200',
|
||||
'RUN_ON_OCIS': 'true',
|
||||
'OCIS_REVA_DATA_ROOT': '/srv/app/tmp/ocis/owncloud',
|
||||
'OCIS_REVA_DATA_ROOT': '/srv/app/tmp/ocis/owncloud/data',
|
||||
'OCIS_SKELETON_DIR': '/srv/app/testing/data/webUISkeleton',
|
||||
'PHOENIX_CONFIG': '/drone/src/accounts/ui/tests/config/drone/ocis-config.json',
|
||||
'PHOENIX_CONFIG': '/drone/src/ocis/tests/config/drone/ocis-config.json',
|
||||
'TEST_TAGS': 'not @skipOnOCIS and not @skip',
|
||||
'LOCAL_UPLOAD_DIR': '/uploads',
|
||||
'NODE_TLS_REJECT_UNAUTHORIZED': 0,
|
||||
'PHOENIX_PATH': '/srv/app/phoenix',
|
||||
'FEATURE_PATH': '/drone/src/accounts/ui/tests/acceptance/features',
|
||||
'NODE_TLS_REJECT_UNAUTHORIZED': '0'
|
||||
},
|
||||
'commands': [
|
||||
'git clone --depth=1 https://github.com/owncloud/testing.git /srv/app/testing',
|
||||
'git clone -b %s --single-branch https://github.com/owncloud/phoenix /srv/app/phoenix' % (phoenixBranch),
|
||||
'cd /srv/app/phoenix',
|
||||
'git checkout %s' % (phoenixCommitId),
|
||||
'git clone -b master --depth=1 https://github.com/owncloud/testing.git /srv/app/testing',
|
||||
'git clone -b %s --single-branch --no-tags https://github.com/owncloud/phoenix.git /srv/app/phoenix' % (phoenixBranch),
|
||||
'cp -r /srv/app/phoenix/tests/acceptance/filesForUpload/* /uploads',
|
||||
'cd /srv/app/phoenix',
|
||||
] + ([
|
||||
'git checkout %s' % (phoenixCommit)
|
||||
] if phoenixCommit != '' else []) + [
|
||||
'yarn install-all',
|
||||
'cd /drone/src/accounts',
|
||||
'yarn install --all',
|
||||
'make test-acceptance-webui'
|
||||
],
|
||||
'volumes': [
|
||||
{
|
||||
'name': 'gopath',
|
||||
'path': '/srv/app',
|
||||
},
|
||||
{
|
||||
'name': 'uploads',
|
||||
'path': '/uploads'
|
||||
}
|
||||
],
|
||||
'volumes': [{
|
||||
'name': 'gopath',
|
||||
'path': '/srv/app',
|
||||
},
|
||||
{
|
||||
'name': 'uploads',
|
||||
'path': '/uploads'
|
||||
}],
|
||||
},
|
||||
],
|
||||
'services': [
|
||||
@@ -740,6 +683,78 @@ def docker(ctx, arch):
|
||||
},
|
||||
}
|
||||
|
||||
def dockerEos(ctx):
|
||||
return {
|
||||
'kind': 'pipeline',
|
||||
'type': 'docker',
|
||||
'name': 'docker-eos-ocis',
|
||||
'platform': {
|
||||
'os': 'linux',
|
||||
'arch': 'amd64',
|
||||
},
|
||||
'steps':
|
||||
generate('ocis') +
|
||||
build() + [
|
||||
{
|
||||
'name': 'dryrun-eos-ocis',
|
||||
'image': 'plugins/docker:18.09',
|
||||
'pull': 'always',
|
||||
'settings': {
|
||||
'dry_run': True,
|
||||
'context': 'ocis/docker/eos-ocis',
|
||||
'tags': 'linux-eos-ocis',
|
||||
'dockerfile': 'ocis/docker/eos-ocis/Dockerfile',
|
||||
'repo': 'owncloud/eos-ocis',
|
||||
},
|
||||
'when': {
|
||||
'ref': {
|
||||
'include': [
|
||||
'refs/pull/**',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
'name': 'docker-eos-ocis',
|
||||
'image': 'plugins/docker:18.09',
|
||||
'pull': 'always',
|
||||
'settings': {
|
||||
'username': {
|
||||
'from_secret': 'docker_username',
|
||||
},
|
||||
'password': {
|
||||
'from_secret': 'docker_password',
|
||||
},
|
||||
'auto_tag': True,
|
||||
'context': 'ocis/docker/eos-ocis',
|
||||
'dockerfile': 'ocis/docker/eos-ocis/Dockerfile',
|
||||
'repo': 'owncloud/eos-ocis',
|
||||
},
|
||||
'when': {
|
||||
'ref': {
|
||||
'exclude': [
|
||||
'refs/pull/**',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
'volumes': [
|
||||
{
|
||||
'name': 'gopath',
|
||||
'temp': {},
|
||||
},
|
||||
],
|
||||
'depends_on': getDependsOnAllTestPipelines(ctx),
|
||||
'trigger': {
|
||||
'ref': [
|
||||
'refs/heads/master',
|
||||
'refs/tags/v*',
|
||||
'refs/pull/**',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
def binary(ctx, name):
|
||||
if ctx.build.event == "tag":
|
||||
settings = {
|
||||
@@ -792,7 +807,7 @@ def binary(ctx, name):
|
||||
generate('ocis') + [
|
||||
{
|
||||
'name': 'build',
|
||||
'image': 'webhippie/golang:1.13',
|
||||
'image': 'webhippie/golang:1.14',
|
||||
'pull': 'always',
|
||||
'commands': [
|
||||
'cd ocis',
|
||||
@@ -807,7 +822,7 @@ def binary(ctx, name):
|
||||
},
|
||||
{
|
||||
'name': 'finish',
|
||||
'image': 'webhippie/golang:1.13',
|
||||
'image': 'webhippie/golang:1.14',
|
||||
'pull': 'always',
|
||||
'commands': [
|
||||
'cd ocis',
|
||||
@@ -973,6 +988,7 @@ def manifest(ctx):
|
||||
'docker-amd64',
|
||||
'docker-arm64',
|
||||
'docker-arm',
|
||||
'docker-eos-ocis',
|
||||
'binaries-linux',
|
||||
'binaries-darwin',
|
||||
'binaries-windows',
|
||||
@@ -997,7 +1013,7 @@ def changelog(ctx):
|
||||
'steps': [
|
||||
{
|
||||
'name': 'generate',
|
||||
'image': 'webhippie/golang:1.13',
|
||||
'image': 'webhippie/golang:1.14',
|
||||
'pull': 'always',
|
||||
'commands': [
|
||||
'cd ocis',
|
||||
@@ -1127,6 +1143,7 @@ def badges(ctx):
|
||||
'docker-amd64',
|
||||
'docker-arm64',
|
||||
'docker-arm',
|
||||
'docker-eos-ocis',
|
||||
],
|
||||
'trigger': {
|
||||
'ref': [
|
||||
@@ -1160,7 +1177,7 @@ def docs(ctx):
|
||||
},
|
||||
{
|
||||
'name': 'generate-config-docs',
|
||||
'image': 'webhippie/golang:1.13',
|
||||
'image': 'webhippie/golang:1.14',
|
||||
'commands': generateConfigDocs,
|
||||
'volumes': [
|
||||
{
|
||||
@@ -1248,7 +1265,7 @@ def generate(module):
|
||||
return [
|
||||
{
|
||||
'name': 'generate',
|
||||
'image': 'webhippie/golang:1.13',
|
||||
'image': 'webhippie/golang:1.14',
|
||||
'pull': 'always',
|
||||
'commands': [
|
||||
'cd %s' % (module),
|
||||
@@ -1323,32 +1340,29 @@ def ocisServer(storage):
|
||||
return [
|
||||
{
|
||||
'name': 'ocis-server',
|
||||
'image': 'webhippie/golang:1.13',
|
||||
'image': 'webhippie/golang:1.14',
|
||||
'pull': 'always',
|
||||
'detach': True,
|
||||
'environment' : {
|
||||
#'OCIS_LOG_LEVEL': 'debug',
|
||||
'STORAGE_STORAGE_HOME_DRIVER': '%s' % (storage),
|
||||
'STORAGE_STORAGE_HOME_DATA_DRIVER': '%s' % (storage),
|
||||
'STORAGE_STORAGE_OC_DRIVER': '%s' % (storage),
|
||||
'STORAGE_STORAGE_OC_DATA_DRIVER': '%s' % (storage),
|
||||
'STORAGE_STORAGE_HOME_DATA_TEMP_FOLDER': '/srv/app/tmp/',
|
||||
'STORAGE_STORAGE_OCIS_ROOT': '/srv/app/tmp/ocis/storage/users',
|
||||
'STORAGE_STORAGE_LOCAL_ROOT': '/srv/app/tmp/ocis/local/root',
|
||||
'STORAGE_STORAGE_OWNCLOUD_DATADIR': '/srv/app/tmp/ocis/owncloud/data',
|
||||
'STORAGE_STORAGE_OC_DATA_TEMP_FOLDER': '/srv/app/tmp/',
|
||||
'STORAGE_STORAGE_OWNCLOUD_REDIS_ADDR': 'redis:6379',
|
||||
'STORAGE_HOME_DRIVER': '%s' % (storage),
|
||||
'STORAGE_USERS_DRIVER': '%s' % (storage),
|
||||
'STORAGE_DRIVER_OCIS_ROOT': '/srv/app/tmp/ocis/storage/users',
|
||||
'STORAGE_DRIVER_LOCAL_ROOT': '/srv/app/tmp/ocis/local/root',
|
||||
'STORAGE_METADATA_ROOT': '/srv/app/tmp/ocis/metadata',
|
||||
'STORAGE_DRIVER_OWNCLOUD_DATADIR': '/srv/app/tmp/ocis/owncloud/data',
|
||||
'STORAGE_DRIVER_OWNCLOUD_REDIS_ADDR': 'redis:6379',
|
||||
'STORAGE_LDAP_IDP': 'https://ocis-server:9200',
|
||||
'STORAGE_OIDC_ISSUER': 'https://ocis-server:9200',
|
||||
'PROXY_OIDC_ISSUER': 'https://ocis-server:9200',
|
||||
'STORAGE_STORAGE_OC_DATA_SERVER_URL': 'http://ocis-server:9164/data',
|
||||
'STORAGE_DATAGATEWAY_URL': 'https://ocis-server:9200/data',
|
||||
'STORAGE_FRONTEND_URL': 'https://ocis-server:9200',
|
||||
'STORAGE_HOME_DATA_SERVER_URL': 'http://ocis-server:9155/data',
|
||||
'STORAGE_DATAGATEWAY_PUBLIC_URL': 'https://ocis-server:9200/data',
|
||||
'STORAGE_USERS_DATA_SERVER_URL': 'http://ocis-server:9158/data',
|
||||
'STORAGE_FRONTEND_PUBLIC_URL': 'https://ocis-server:9200',
|
||||
'PHOENIX_WEB_CONFIG': '/drone/src/ocis/tests/config/drone/ocis-config.json',
|
||||
'KONNECTD_IDENTIFIER_REGISTRATION_CONF': '/drone/src/ocis/tests/config/drone/identifier-registration.yml',
|
||||
'KONNECTD_ISS': 'https://ocis-server:9200',
|
||||
'KONNECTD_TLS': 'true',
|
||||
'ACCOUNTS_DATA_PATH': '/srv/app/tmp/ocis-accounts/',
|
||||
},
|
||||
'commands': [
|
||||
'apk add mailcap', # install /etc/mime.types
|
||||
@@ -1414,7 +1428,7 @@ def build():
|
||||
return [
|
||||
{
|
||||
'name': 'build',
|
||||
'image': 'webhippie/golang:1.13',
|
||||
'image': 'webhippie/golang:1.14',
|
||||
'pull': 'always',
|
||||
'commands': [
|
||||
'cd ocis',
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -10,3 +10,8 @@ node_modules/
|
||||
*/assets
|
||||
|
||||
.idea
|
||||
|
||||
*/yarn-error.log
|
||||
|
||||
# Konnectd
|
||||
konnectd/assets/identifier
|
||||
172
CHANGELOG.md
172
CHANGELOG.md
@@ -4,10 +4,17 @@
|
||||
|
||||
* Bugfix - Add missing env vars to docker compose: [#392](https://github.com/owncloud/ocis/pull/392)
|
||||
* Bugfix - Don't enforce empty external apps slice: [#473](https://github.com/owncloud/ocis/pull/473)
|
||||
* Bugfix - Lower Bound was not working for the cs3 api index implementation: [#741](https://github.com/owncloud/ocis/pull/741)
|
||||
* Bugfix - Fix button layout after phoenix update: [#625](https://github.com/owncloud/ocis/pull/625)
|
||||
* Bugfix - Fix id or username query handling: [#745](https://github.com/owncloud/ocis/pull/745)
|
||||
* Bugfix - Use micro default client: [#718](https://github.com/owncloud/ocis/pull/718)
|
||||
* Bugfix - Mint token with uid and gid: [#737](https://github.com/owncloud/ocis/pull/737)
|
||||
* Bugfix - Don't create account if id/mail/username already taken: [#709](https://github.com/owncloud/ocis/pull/709)
|
||||
* Bugfix - Fix director selection in proxy: [#521](https://github.com/owncloud/ocis/pull/521)
|
||||
* Bugfix - Build docker images with alpine:latest instead of alpine:edge: [#416](https://github.com/owncloud/ocis/pull/416)
|
||||
* Change - Accounts UI shows message when no permissions: [#656](https://github.com/owncloud/ocis/pull/656)
|
||||
* Change - Filesystem based index: [#709](https://github.com/owncloud/ocis/pull/709)
|
||||
* Change - Rebuild index command for accounts: [#748](https://github.com/owncloud/ocis/pull/748)
|
||||
* Change - Add the thumbnails command: [#156](https://github.com/owncloud/ocis/issues/156)
|
||||
* Change - Choose disk or cs3 storage for accounts and groups: [#623](https://github.com/owncloud/ocis/pull/623)
|
||||
* Change - Integrate import command from ocis-migration: [#249](https://github.com/owncloud/ocis/pull/249)
|
||||
@@ -16,12 +23,17 @@
|
||||
* Change - Add cli-commands to manage accounts: [#115](https://github.com/owncloud/product/issues/115)
|
||||
* Change - Start ocis-accounts with the ocis server command: [#25](https://github.com/owncloud/product/issues/25)
|
||||
* Change - Switch over to a new custom-built runtime: [#287](https://github.com/owncloud/ocis/pull/287)
|
||||
* Change - Remove username field in OCS: [#709](https://github.com/owncloud/ocis/pull/709)
|
||||
* Change - Account management permissions for Admin role: [#124](https://github.com/owncloud/product/issues/124)
|
||||
* Change - Update phoenix to v0.18.0: [#651](https://github.com/owncloud/ocis/pull/651)
|
||||
* Change - Default apps in ownCloud Web: [#688](https://github.com/owncloud/ocis/pull/688)
|
||||
* Change - Make ocis-settings available: [#287](https://github.com/owncloud/ocis/pull/287)
|
||||
* Change - Start ocis-proxy with the ocis server command: [#119](https://github.com/owncloud/ocis/issues/119)
|
||||
* Change - Bring oC theme: [#698](https://github.com/owncloud/ocis/pull/698)
|
||||
* Change - Update phoenix to v0.20.0: [#674](https://github.com/owncloud/ocis/pull/674)
|
||||
* Change - Update phoenix to v0.21.0: [#728](https://github.com/owncloud/ocis/pull/728)
|
||||
* Change - Update reva config: [#336](https://github.com/owncloud/ocis/pull/336)
|
||||
* Change - Clarify storage driver env vars: [#729](https://github.com/owncloud/ocis/pull/729)
|
||||
* Change - Settings and accounts appear in the user menu: [#656](https://github.com/owncloud/ocis/pull/656)
|
||||
* Enhancement - Add the accounts service: [#244](https://github.com/owncloud/product/issues/244)
|
||||
* Enhancement - Document how to run OCIS on top of EOS: [#172](https://github.com/owncloud/ocis/pull/172)
|
||||
@@ -40,6 +52,9 @@
|
||||
* Enhancement - Add glauth fallback backend: [#649](https://github.com/owncloud/ocis/pull/649)
|
||||
* Enhancement - Launch a storage to store ocis-metadata: [#602](https://github.com/owncloud/ocis/pull/602)
|
||||
* Enhancement - Simplify tracing config: [#92](https://github.com/owncloud/product/issues/92)
|
||||
* Enhancement - Update konnectd to v0.33.8: [#744](https://github.com/owncloud/ocis/pull/744)
|
||||
* Enhancement - Update reva to cdb3d6688da5: [#748](https://github.com/owncloud/ocis/pull/748)
|
||||
* Enhancement - Update reva to dd3a8c0f38: [#725](https://github.com/owncloud/ocis/pull/725)
|
||||
|
||||
## Details
|
||||
|
||||
@@ -61,6 +76,14 @@
|
||||
|
||||
https://github.com/owncloud/ocis/pull/473
|
||||
|
||||
* Bugfix - Lower Bound was not working for the cs3 api index implementation: [#741](https://github.com/owncloud/ocis/pull/741)
|
||||
|
||||
Tags: accounts
|
||||
|
||||
Lower bound working on the cs3 index implementation
|
||||
|
||||
https://github.com/owncloud/ocis/pull/741
|
||||
|
||||
* Bugfix - Fix button layout after phoenix update: [#625](https://github.com/owncloud/ocis/pull/625)
|
||||
|
||||
Tags: accounts
|
||||
@@ -71,6 +94,42 @@
|
||||
|
||||
https://github.com/owncloud/ocis/pull/625
|
||||
|
||||
* Bugfix - Fix id or username query handling: [#745](https://github.com/owncloud/ocis/pull/745)
|
||||
|
||||
Tags: accounts
|
||||
|
||||
The code was stopping execution when encountering an error while loading an account by id. But
|
||||
for or queries we can continue execution.
|
||||
|
||||
https://github.com/owncloud/ocis/pull/745
|
||||
|
||||
* Bugfix - Use micro default client: [#718](https://github.com/owncloud/ocis/pull/718)
|
||||
|
||||
Tags: glauth
|
||||
|
||||
We found a file descriptor leak in the glauth connections to the accounts service. Fixed it by
|
||||
using the micro default client.
|
||||
|
||||
https://github.com/owncloud/ocis/pull/718
|
||||
|
||||
* Bugfix - Mint token with uid and gid: [#737](https://github.com/owncloud/ocis/pull/737)
|
||||
|
||||
Tags: accounts
|
||||
|
||||
The eos driver expects the uid and gid from the opaque map of a user. While the proxy does mint
|
||||
tokens correctly, the accounts service wasn't.
|
||||
|
||||
https://github.com/owncloud/ocis/pull/737
|
||||
|
||||
* Bugfix - Don't create account if id/mail/username already taken: [#709](https://github.com/owncloud/ocis/pull/709)
|
||||
|
||||
Tags: accounts
|
||||
|
||||
We don't allow anymore to create a new account if the provided id/mail/username is already
|
||||
taken.
|
||||
|
||||
https://github.com/owncloud/ocis/pull/709
|
||||
|
||||
* Bugfix - Fix director selection in proxy: [#521](https://github.com/owncloud/ocis/pull/521)
|
||||
|
||||
Tags: proxy
|
||||
@@ -97,6 +156,35 @@
|
||||
|
||||
https://github.com/owncloud/ocis/pull/656
|
||||
|
||||
* Change - Filesystem based index: [#709](https://github.com/owncloud/ocis/pull/709)
|
||||
|
||||
Tags: accounts, storage
|
||||
|
||||
We replaced `bleve` with a new filesystem based index implementation. There is an `indexer`
|
||||
which is capable of orchestrating different index types to build indices on documents by
|
||||
field. You can choose from the index types `unique`, `non-unique` or `autoincrement`.
|
||||
Indices can be utilized to run search queries (full matches or globbing) on document fields.
|
||||
The accounts service is using this index internally to run the search queries coming in via
|
||||
`ListAccounts` and `ListGroups` and to generate UIDs for new accounts as well as GIDs for new
|
||||
groups.
|
||||
|
||||
The accounts service can be configured to store the index on the local FS / a NFS (`disk`
|
||||
implementation of the index) or to use an arbitrary storage ( `cs3` implementation of the
|
||||
index). `cs3` is the new default, which is configured to use the `metadata` storage.
|
||||
|
||||
https://github.com/owncloud/ocis/pull/709
|
||||
|
||||
* Change - Rebuild index command for accounts: [#748](https://github.com/owncloud/ocis/pull/748)
|
||||
|
||||
Tags: accounts
|
||||
|
||||
The index for the accounts service can now be rebuilt by running the cli command `./bin/ocis
|
||||
accounts rebuild`. It deletes all configured indices and rebuilds them from the documents
|
||||
found on storage. For this we also introduced a `LoadAccounts` and `LoadGroups` function on
|
||||
storage for loading all existing documents.
|
||||
|
||||
https://github.com/owncloud/ocis/pull/748
|
||||
|
||||
* Change - Add the thumbnails command: [#156](https://github.com/owncloud/ocis/issues/156)
|
||||
|
||||
Tags: thumbnails
|
||||
@@ -172,6 +260,17 @@
|
||||
|
||||
https://github.com/owncloud/ocis/pull/287
|
||||
|
||||
* Change - Remove username field in OCS: [#709](https://github.com/owncloud/ocis/pull/709)
|
||||
|
||||
Tags: ocs
|
||||
|
||||
We use the incoming userid as both the `id` and the `on_premises_sam_account_name` for new
|
||||
accounts in the accounts service. The userid in OCS requests is in fact the username, not our
|
||||
internal account id. We need to enforce the userid as our internal account id though, because
|
||||
the account id is part of various `path` formats.
|
||||
|
||||
https://github.com/owncloud/ocis/pull/709
|
||||
|
||||
* Change - Account management permissions for Admin role: [#124](https://github.com/owncloud/product/issues/124)
|
||||
|
||||
Tags: accounts, settings
|
||||
@@ -210,6 +309,15 @@
|
||||
https://github.com/owncloud/phoenix/releases/tag/v0.18.0
|
||||
https://github.com/owncloud/owncloud-design-system/releases/tag/v1.12.1
|
||||
|
||||
* Change - Default apps in ownCloud Web: [#688](https://github.com/owncloud/ocis/pull/688)
|
||||
|
||||
Tags: web
|
||||
|
||||
We changed the default apps for ownCloud Web to be only files and media-viewer.
|
||||
Markdown-editor and draw-io have been removed as defaults.
|
||||
|
||||
https://github.com/owncloud/ocis/pull/688
|
||||
|
||||
* Change - Make ocis-settings available: [#287](https://github.com/owncloud/ocis/pull/287)
|
||||
|
||||
Tags: settings
|
||||
@@ -229,6 +337,14 @@
|
||||
https://github.com/owncloud/ocis/issues/119
|
||||
https://github.com/owncloud/ocis/issues/136
|
||||
|
||||
* Change - Bring oC theme: [#698](https://github.com/owncloud/ocis/pull/698)
|
||||
|
||||
Tags: konnectd
|
||||
|
||||
We've styled our konnectd login page to reflect ownCloud theme.
|
||||
|
||||
https://github.com/owncloud/ocis/pull/698
|
||||
|
||||
* Change - Update phoenix to v0.20.0: [#674](https://github.com/owncloud/ocis/pull/674)
|
||||
|
||||
Tags: web
|
||||
@@ -239,6 +355,16 @@
|
||||
https://github.com/owncloud/ocis/pull/674
|
||||
https://github.com/owncloud/phoenix/releases/tag/v0.20.0
|
||||
|
||||
* Change - Update phoenix to v0.21.0: [#728](https://github.com/owncloud/ocis/pull/728)
|
||||
|
||||
Tags: web
|
||||
|
||||
We updated phoenix to v0.21.0. Please refer to the changelog (linked) for details on the
|
||||
phoenix release.
|
||||
|
||||
https://github.com/owncloud/ocis/pull/728
|
||||
https://github.com/owncloud/phoenix/releases/tag/v0.21.0
|
||||
|
||||
* Change - Update reva config: [#336](https://github.com/owncloud/ocis/pull/336)
|
||||
|
||||
* EOS homes are not configured with an enable-flag anymore, but with a dedicated storage driver.
|
||||
@@ -249,6 +375,16 @@
|
||||
https://github.com/owncloud/ocis/pull/338
|
||||
https://github.com/owncloud/ocis-reva/pull/891
|
||||
|
||||
* Change - Clarify storage driver env vars: [#729](https://github.com/owncloud/ocis/pull/729)
|
||||
|
||||
After renaming ocsi-reva to storage and combining the storage and data providers some env vars
|
||||
were confusingly named `STORAGE_STORAGE_...`. We are changing the prefix for driver related
|
||||
env vars to `STORAGE_DRIVER_...`. This makes changing the storage driver using eg.:
|
||||
`STORAGE_HOME_DRIVER=eos` and setting driver options using
|
||||
`STORAGE_DRIVER_EOS_LAYOUT=...` less confusing.
|
||||
|
||||
https://github.com/owncloud/ocis/pull/729
|
||||
|
||||
* Change - Settings and accounts appear in the user menu: [#656](https://github.com/owncloud/ocis/pull/656)
|
||||
|
||||
We moved settings and accounts to the user menu.
|
||||
@@ -1250,3 +1386,39 @@
|
||||
https://github.com/owncloud/product/issues/92
|
||||
https://github.com/owncloud/ocis/pull/329
|
||||
https://github.com/owncloud/ocis/pull/409
|
||||
|
||||
* Enhancement - Update konnectd to v0.33.8: [#744](https://github.com/owncloud/ocis/pull/744)
|
||||
|
||||
This update adds options which allow the configuration of oidc-token expiration parameters:
|
||||
KONNECTD_ACCESS_TOKEN_EXPIRATION, KONNECTD_ID_TOKEN_EXPIRATION and
|
||||
KONNECTD_REFRESH_TOKEN_EXPIRATION.
|
||||
|
||||
Other changes from upstream:
|
||||
|
||||
- Generate random endsession state for external authority - Update dependencies in
|
||||
Dockerfile - Set prompt=None to avoid loops with external authority - Update Jenkins
|
||||
reporting plugin from checkstyle to recordIssues - Remove extra kty key from JWKS top level
|
||||
document - Fix regression which encodes URL fragments twice - Avoid generating fragmet/query
|
||||
URLs with wrong order - Return state for oidc endsession response redirects - Use server
|
||||
provided username to avoid case mismatch - Use signed-out-uri if set as fallback for goodbye
|
||||
redirect on saml slo - Add checks to ensure post_logout_redirect_uri is not empty - Fix SAML2
|
||||
logout request parsing - Cure panic when no state is found in saml esr - Use SAML IdP Issuer value
|
||||
from meta data entityID - Allow configuration of expiration of oidc access, id and refresh
|
||||
tokens - Implement trampolin for external OIDC authority end session - Update
|
||||
ca-certificates version
|
||||
|
||||
https://github.com/owncloud/ocis/pull/744
|
||||
|
||||
* Enhancement - Update reva to cdb3d6688da5: [#748](https://github.com/owncloud/ocis/pull/748)
|
||||
|
||||
* let the gateway filter invalid references
|
||||
|
||||
https://github.com/owncloud/ocis/pull/748
|
||||
https://github.com/cs3org/reva/pull/1274
|
||||
|
||||
* Enhancement - Update reva to dd3a8c0f38: [#725](https://github.com/owncloud/ocis/pull/725)
|
||||
|
||||
* fixes etag propagation in the ocis driver
|
||||
|
||||
https://github.com/owncloud/ocis/pull/725
|
||||
https://github.com/cs3org/reva/pull/1264
|
||||
|
||||
@@ -156,8 +156,8 @@ $(GOPATH)/bin/protoc-gen-micro:
|
||||
$(GOPATH)/bin/protoc-gen-microweb:
|
||||
GO111MODULE=off go get -v github.com/owncloud/protoc-gen-microweb
|
||||
|
||||
$(GOPATH)/bin/protoc-gen-swagger:
|
||||
GO111MODULE=off go get -v github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger
|
||||
$(GOPATH)/bin/protoc-gen-openapiv2:
|
||||
GO111MODULE=off go get -v github.com/grpc-ecosystem/grpc-gateway/protoc-gen-openapiv2
|
||||
|
||||
$(PROTO_SRC)/accounts.pb.go: $(PROTO_SRC)/accounts.proto
|
||||
protoc \
|
||||
@@ -186,5 +186,5 @@ $(PROTO_SRC)/accounts.swagger.json: $(PROTO_SRC)/accounts.proto
|
||||
--swagger_out=$(PROTO_SRC) accounts.proto
|
||||
|
||||
.PHONY: protobuf
|
||||
protobuf: $(GOPATH)/bin/protoc-gen-go $(GOPATH)/bin/protoc-gen-micro $(GOPATH)/bin/protoc-gen-microweb $(GOPATH)/bin/protoc-gen-swagger \
|
||||
protobuf: $(GOPATH)/bin/protoc-gen-go $(GOPATH)/bin/protoc-gen-micro $(GOPATH)/bin/protoc-gen-microweb $(GOPATH)/bin/protoc-gen-openapiv2 \
|
||||
$(PROTO_SRC)/accounts.pb.go $(PROTO_SRC)/accounts.pb.micro.go $(PROTO_SRC)/accounts.pb.web.go $(PROTO_SRC)/accounts.swagger.json
|
||||
|
||||
@@ -3,21 +3,14 @@ module github.com/owncloud/ocis/accounts
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/CiscoM31/godata v0.0.0-20191007193734-c2c4ebb1b415
|
||||
github.com/blevesearch/bleve v1.0.9
|
||||
github.com/cs3org/go-cs3apis v0.0.0-20200730121022-c4f3d4f7ddfd
|
||||
github.com/cs3org/reva v1.1.0
|
||||
github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d // indirect
|
||||
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 // indirect
|
||||
github.com/cznic/strutil v0.0.0-20181122101858-275e90344537 // indirect
|
||||
github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c // indirect
|
||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect
|
||||
github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 // indirect
|
||||
github.com/cs3org/go-cs3apis v0.0.0-20200810113633-b00aca449666
|
||||
github.com/cs3org/reva v1.2.2-0.20200924071957-e6676516e61e
|
||||
github.com/go-chi/chi v4.1.2+incompatible
|
||||
github.com/go-chi/render v1.0.1
|
||||
github.com/gofrs/uuid v3.3.0+incompatible
|
||||
github.com/golang/protobuf v1.4.2
|
||||
github.com/jmhodges/levigo v1.0.0 // indirect
|
||||
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 // indirect
|
||||
github.com/iancoleman/strcase v0.1.2
|
||||
github.com/mennanov/fieldmask-utils v0.3.2
|
||||
github.com/micro/cli/v2 v2.1.2
|
||||
github.com/micro/go-micro/v2 v2.9.1
|
||||
@@ -25,16 +18,16 @@ require (
|
||||
github.com/olekukonko/tablewriter v0.0.4
|
||||
github.com/owncloud/ocis/ocis-pkg v0.0.0-20200918114005-1a0ddd2190ee
|
||||
github.com/owncloud/ocis/settings v0.0.0-20200918114005-1a0ddd2190ee
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
||||
github.com/owncloud/ocis/storage v0.0.0-20201015120921-38358ba4d4df
|
||||
github.com/prometheus/client_golang v1.7.1
|
||||
github.com/restic/calens v0.2.0
|
||||
github.com/rs/zerolog v1.19.0
|
||||
github.com/rs/zerolog v1.20.0
|
||||
github.com/spf13/viper v1.7.0
|
||||
github.com/stretchr/testify v1.6.1
|
||||
github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect
|
||||
github.com/tredoe/osutil v1.0.5
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202
|
||||
google.golang.org/genproto v0.0.0-20200624020401-64a14ca9d1ad
|
||||
google.golang.org/grpc v1.31.0
|
||||
google.golang.org/grpc v1.32.0
|
||||
google.golang.org/protobuf v1.25.0
|
||||
)
|
||||
|
||||
|
||||
164
accounts/go.sum
164
accounts/go.sum
@@ -50,8 +50,6 @@ github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzS
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/CiscoM31/godata v0.0.0-20191007193734-c2c4ebb1b415 h1:rATYsVSP89BOyS9/o+cdGZ8qU7AHh4frtS59nFPoX8k=
|
||||
github.com/CiscoM31/godata v0.0.0-20191007193734-c2c4ebb1b415/go.mod h1:tjaihnMBH6p5DVnGBksDQQHpErbrLvb9ek6cEWuyc7E=
|
||||
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||
github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg=
|
||||
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
@@ -68,10 +66,9 @@ github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcy
|
||||
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
|
||||
github.com/Microsoft/hcsshim v0.8.7-0.20191101173118-65519b62243c/go.mod h1:7xhjOwRV2+0HXGmM0jxaEu+ZiXJFoVZOTfL/dmqbrD8=
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
|
||||
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks=
|
||||
github.com/RoaringBitmap/roaring v0.4.21 h1:WJ/zIlNX4wQZ9x8Ey33O1UaD9TCTakYsdLFSBcTwH+8=
|
||||
github.com/RoaringBitmap/roaring v0.4.21/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/UnnoTed/fileb0x v1.1.4 h1:IUgFzgBipF/ujNx9wZgkrKOF3oltUuXMSoaejrBws+A=
|
||||
@@ -103,12 +100,16 @@ github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:l
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496 h1:zV3ejI06GQ59hwDQAvmK1qxOQGB3WuVTRoY0okPTAv0=
|
||||
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
|
||||
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0S6Vi7/lbWECcX0j45yZReDZ56BQsrVBOEEY=
|
||||
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
|
||||
github.com/ascarter/requestid v0.0.0-20170313220838-5b76ab3d4aee h1:3T/l+vMotQ7cDSLWNAn2Vg1SAQ3mdyLgBWWBitSS3uU=
|
||||
github.com/ascarter/requestid v0.0.0-20170313220838-5b76ab3d4aee/go.mod h1:u7Wtt4WATGGgae9mURNGQQqxAudPKrxfsbSDSGOso+g=
|
||||
github.com/aws/aws-sdk-go v1.20.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.23.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.23.19/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.33.19/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
||||
github.com/aws/aws-sdk-go v1.34.12 h1:7UbBEYDUa4uW0YmRnOd806MS1yoJMcaodBWDzvBShAI=
|
||||
github.com/aws/aws-sdk-go v1.34.12/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
||||
github.com/aws/aws-xray-sdk-go v0.9.4/go.mod h1:XtMKdBQfpVut+tJEwI7+dJFRxxRdxHDyVNp2tHXRq04=
|
||||
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
@@ -120,26 +121,6 @@ github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkN
|
||||
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
|
||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||
github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
github.com/blevesearch/bleve v1.0.9 h1:kqw/Ank/61UV9/Bx9kCcnfH6qWPgmS8O5LNfpsgzASg=
|
||||
github.com/blevesearch/bleve v1.0.9/go.mod h1:tb04/rbU29clbtNgorgFd8XdJea4x3ybYaOjWKr+UBU=
|
||||
github.com/blevesearch/blevex v0.0.0-20190916190636-152f0fe5c040 h1:SjYVcfJVZoCfBlg+fkaq2eoZHTf5HaJfaTeTkOtyfHQ=
|
||||
github.com/blevesearch/blevex v0.0.0-20190916190636-152f0fe5c040/go.mod h1:WH+MU2F4T0VmSdaPX+Wu5GYoZBrYWdOZWSjzvYcDmqQ=
|
||||
github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo=
|
||||
github.com/blevesearch/go-porterstemmer v1.0.3/go.mod h1:angGc5Ht+k2xhJdZi511LtmxuEf0OVpvUUNrwmM1P7M=
|
||||
github.com/blevesearch/mmap-go v1.0.2 h1:JtMHb+FgQCTTYIhtMvimw15dJwu1Y5lrZDMOFXVWPk0=
|
||||
github.com/blevesearch/mmap-go v1.0.2/go.mod h1:ol2qBqYaOUsGdm7aRMRrYGgPvnwLe6Y+7LMvAB5IbSA=
|
||||
github.com/blevesearch/segment v0.9.0 h1:5lG7yBCx98or7gK2cHMKPukPZ/31Kag7nONpoBt22Ac=
|
||||
github.com/blevesearch/segment v0.9.0/go.mod h1:9PfHYUdQCgHktBgvtUOF4x+pc4/l8rdH0u5spnW85UQ=
|
||||
github.com/blevesearch/snowballstem v0.9.0 h1:lMQ189YspGP6sXvZQ4WZ+MLawfV8wOmPoD/iWeNXm8s=
|
||||
github.com/blevesearch/snowballstem v0.9.0/go.mod h1:PivSj3JMc8WuaFkTSRDW2SlrulNWPl4ABg1tC/hlgLs=
|
||||
github.com/blevesearch/zap/v11 v11.0.9 h1:wlSrDBeGN1G4M51NQHIXca23ttwUfQpWaK7uhO5lRSo=
|
||||
github.com/blevesearch/zap/v11 v11.0.9/go.mod h1:47hzinvmY2EvvJruzsSCJpro7so8L1neseaGjrtXHOY=
|
||||
github.com/blevesearch/zap/v12 v12.0.9 h1:PpatkY+BLVFZf0Ok3/fwgI/I4RU0z5blXFGuQANmqXk=
|
||||
github.com/blevesearch/zap/v12 v12.0.9/go.mod h1:paQuvxy7yXor+0Mx8p2KNmJgygQbQNN+W6HRfL5Hvwc=
|
||||
github.com/blevesearch/zap/v13 v13.0.1 h1:NSCM6uKu77Vn/x9nlPp4pE1o/bftqcOWZEHSyZVpGBQ=
|
||||
github.com/blevesearch/zap/v13 v13.0.1/go.mod h1:XmyNLMvMf8Z5FjLANXwUeDW3e1+o77TTGUWrth7T9WI=
|
||||
github.com/blevesearch/zap/v14 v14.0.0 h1:HF8Ysjm13qxB0jTGaKLlatNXmJbQD8bY+PrPxm5v4hE=
|
||||
github.com/blevesearch/zap/v14 v14.0.0/go.mod h1:sUc/gPGJlFbSQ2ZUh/wGRYwkKx+Dg/5p+dd+eq6QMXk=
|
||||
github.com/bmatcuk/doublestar v1.1.1 h1:YroD6BJCZBYx06yYFEWvUuKVWQn3vLLQAVmDmvTSaiQ=
|
||||
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
|
||||
@@ -147,6 +128,7 @@ github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dR
|
||||
github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40 h1:y4B3+GPxKlrigF1ha5FFErxK+sr6sWxQovRMzwMhejo=
|
||||
github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c=
|
||||
github.com/bwmarrin/discordgo v0.20.2/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q=
|
||||
github.com/c-bata/go-prompt v0.2.3/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34=
|
||||
github.com/caddyserver/certmagic v0.10.6 h1:sCya6FmfaN74oZE46kqfaFOVoROD/mF36rTQfjN7TZc=
|
||||
github.com/caddyserver/certmagic v0.10.6/go.mod h1:Y8jcUBctgk/IhpAzlHKfimZNyXCkfGgRTC0orl8gROQ=
|
||||
github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
@@ -162,6 +144,7 @@ github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
|
||||
github.com/cheggaaa/pb v1.0.28/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
|
||||
github.com/cheggaaa/pb v1.0.29/go.mod h1:W40334L7FMC5JKWldsTWbdGjLo0RxUKK73K+TuPxX30=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
@@ -200,10 +183,6 @@ github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+
|
||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/couchbase/ghistogram v0.1.0/go.mod h1:s1Jhy76zqfEecpNWJfWUiKZookAFaiGOEoyzgHt9i7k=
|
||||
github.com/couchbase/moss v0.1.0/go.mod h1:9MaHIaRuy9pvLPUJxB8sh8OrLfyDczECVL37grCIubs=
|
||||
github.com/couchbase/vellum v1.0.1 h1:qrj9ohvZedvc51S5KzPfJ6P6z0Vqzv7Lx7k3mVc2WOk=
|
||||
github.com/couchbase/vellum v1.0.1/go.mod h1:FcwrEivFpNi24R3jLOs3n+fs5RnuQnQqCLBJ1uAg1W4=
|
||||
github.com/cpu/goacmedns v0.0.1/go.mod h1:sesf/pNnCYwUevQEQfEwY0Y3DydlQWSGZbaMElOWxok=
|
||||
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
@@ -213,21 +192,26 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsr
|
||||
github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cs3org/cato v0.0.0-20200626150132-28a40e643719/go.mod h1:XJEZ3/EQuI3BXTp/6DUzFr850vlxq11I6satRtz0YQ4=
|
||||
github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e/go.mod h1:XJEZ3/EQuI3BXTp/6DUzFr850vlxq11I6satRtz0YQ4=
|
||||
github.com/cs3org/go-cs3apis v0.0.0-20200730121022-c4f3d4f7ddfd h1:uMaudkC7znaiIKT9rxIhoRYzrhTg1Nc78X7XEqhmjSk=
|
||||
github.com/cs3org/go-cs3apis v0.0.0-20200730121022-c4f3d4f7ddfd/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY=
|
||||
github.com/cs3org/go-cs3apis v0.0.0-20200810113633-b00aca449666 h1:E7VsSSN/2YZLSwrDMJJdAWU11lP7W1qkcXbrslb0PM0=
|
||||
github.com/cs3org/go-cs3apis v0.0.0-20200810113633-b00aca449666/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY=
|
||||
github.com/cs3org/reva v1.1.0 h1:Gih6ECHvMMGSx523SFluFlDmNMuhYelXYShdWvjvW38=
|
||||
github.com/cs3org/reva v1.1.0/go.mod h1:fBzTrNuAKdQ62ybjpdu8nyhBin90/3/3s6DGQDCdBp4=
|
||||
github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d h1:SwD98825d6bdB+pEuTxWOXiSjBrHdOl/UVp75eI7JT8=
|
||||
github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8=
|
||||
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 h1:iwZdTE0PVqJCos1vaoKsclOGD3ADKpshg3SRtYBbwso=
|
||||
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM=
|
||||
github.com/cznic/strutil v0.0.0-20181122101858-275e90344537 h1:MZRmHqDBd0vxNwenEbKSQqRVT24d3C05ft8kduSwlqM=
|
||||
github.com/cznic/strutil v0.0.0-20181122101858-275e90344537/go.mod h1:AHHPPPXTw0h6pVabbcbyGRK1DckRn7r/STdZEeIDzZc=
|
||||
github.com/cs3org/reva v1.2.2-0.20200924071957-e6676516e61e h1:khITGSnfDXtByQsLezoXgocUgGHJBBn0BPsUihGvk7w=
|
||||
github.com/cs3org/reva v1.2.2-0.20200924071957-e6676516e61e/go.mod h1:DOV5SjpOBKN+aWfOHLdA4KiLQkpyC786PQaXEdRAZ0M=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgraph-io/ristretto v0.0.1/go.mod h1:T40EBc7CJke8TkpiYfGGKAeFjSaxuFXhuXRyumBd6RE=
|
||||
github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
|
||||
github.com/dgraph-io/ristretto v0.0.3 h1:jh22xisGBjrEVnRZ1DVTpBVQm0Xndu8sMl0CWDzSIBI=
|
||||
github.com/dgraph-io/ristretto v0.0.3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
|
||||
github.com/dnaeon/go-vcr v0.0.0-20180814043457-aafff18a5cc2/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
|
||||
@@ -254,13 +238,8 @@ github.com/evanphx/json-patch/v5 v5.0.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2Vvl
|
||||
github.com/eventials/go-tus v0.0.0-20200718001131-45c7ec8f5d59 h1:t2+zxJPT/jq/YOx/JRsoByAZI/GHOxYJ7MKeillEX4U=
|
||||
github.com/eventials/go-tus v0.0.0-20200718001131-45c7ec8f5d59/go.mod h1:XYuK1S5+kS6FGhlIUFuZFPvWiSrOIoLk6+ro33Xce3Y=
|
||||
github.com/exoscale/egoscale v0.18.1/go.mod h1:Z7OOdzzTOz1Q1PjQXumlz9Wn/CddH0zSYdCF3rnBKXE=
|
||||
github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0=
|
||||
github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64=
|
||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A=
|
||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg=
|
||||
github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpmbhCOZJ293Lz68O7PYrF2EzeiFMwCLk=
|
||||
github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
github.com/fatih/structs v1.0.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
|
||||
@@ -268,6 +247,8 @@ github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI
|
||||
github.com/forestgiant/sliceutil v0.0.0-20160425183142-94783f95db6c/go.mod h1:pFdJbAhRf7rh6YYMUdIQGyzne6zYL1tCUW8QV2B3UfY=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsouza/go-dockerclient v1.6.0/go.mod h1:YWwtNPuL4XTX1SKJQk86cWPmmqwx+4np9qfPbb+znGc=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
@@ -275,10 +256,6 @@ github.com/gizak/termui/v3 v3.1.0 h1:ZZmVDgwHl7gR7elfKf1xc4IudXZ5qqfDh4wExk4Iajc
|
||||
github.com/gizak/termui/v3 v3.1.0/go.mod h1:bXQEBkJpzxUAKf0+xq9MSWAvWZlE7c+aidmyFlkYTrY=
|
||||
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
|
||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2 h1:Ujru1hufTHVb++eG6OuNDKMxZnGIvF6o/u8q/8h2+I4=
|
||||
github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
|
||||
github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31 h1:gclg6gY70GLy3PbkQ1AERPfmLMMagS60DKF78eWwLn8=
|
||||
github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
|
||||
github.com/go-acme/lego/v3 v3.4.0 h1:deB9NkelA+TfjGHVw8J7iKl/rMtffcGMWSMmptvMv0A=
|
||||
github.com/go-acme/lego/v3 v3.4.0/go.mod h1:xYbLDuxq3Hy4bMUT1t9JIuz6GWIWb3m5X+TeTHYaT7M=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8=
|
||||
@@ -517,6 +494,7 @@ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
@@ -534,8 +512,7 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
|
||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k=
|
||||
github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||
@@ -561,11 +538,14 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
|
||||
github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gophercloud/gophercloud v0.3.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181004151105-1babbf986f6f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
@@ -588,6 +568,8 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmg
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 h1:0IKlLyQ3Hs9nDaiK5cSHAGmcQEIC8l2Ts1u6x5Dfrqg=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.2.0/go.mod h1:mJzapYve32yjrKlk9GbyCZHuPgZsrbyIbyKhSzOpg6s=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 h1:FlFbCRLd5Jr4iYXZufAvgWN6Ao0JrI5chLINnUXDDr0=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
@@ -631,12 +613,15 @@ github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0
|
||||
github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
|
||||
github.com/huandu/xstrings v1.3.0 h1:gvV6jG9dTgFEncxo+AF7PH6MZXi/vZl25owA/8Dg8Wo=
|
||||
github.com/huandu/xstrings v1.3.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/iancoleman/strcase v0.1.2 h1:gnomlvw9tnV3ITTAxzKSgTF+8kFWcU/f+TgttpXGz1U=
|
||||
github.com/iancoleman/strcase v0.1.2/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4=
|
||||
github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
|
||||
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ=
|
||||
github.com/jackc/pgx v3.2.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
|
||||
@@ -647,8 +632,6 @@ github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJS
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc=
|
||||
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
|
||||
github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U=
|
||||
github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ=
|
||||
github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0/go.mod h1:IiEW3SEiiErVyFdH8NTuWjSifiEQKUoyK3LNqr2kCHU=
|
||||
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
|
||||
github.com/joho/godotenv v1.2.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
@@ -681,15 +664,17 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/cpuid v1.2.3 h1:CCtW0xUnWGVINKvE/WWOYKdsPV6mawAtvQuSl8guwQs=
|
||||
github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/kljensen/snowball v0.6.0/go.mod h1:27N7E8fVU5H68RlUmnWwZCfxgt4POBJfENGMvNRhldw=
|
||||
github.com/kolo/xmlrpc v0.0.0-20190717152603-07c4ee3fd181/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
@@ -733,9 +718,12 @@ github.com/marten-seemann/qpack v0.1.0/go.mod h1:LFt1NU/Ptjip0C2CPkhimBz5CGE3WGD
|
||||
github.com/marten-seemann/qtls v0.4.1/go.mod h1:pxVXcHHw1pNIt8Qo0pwSYQEoZ8yYOOPXTCZLQQunvRc=
|
||||
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
@@ -749,6 +737,8 @@ github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW
|
||||
github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
|
||||
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
|
||||
github.com/mattn/goveralls v0.0.5/go.mod h1:Xg2LHi51faXLyKXwsndxiW6uxEEQT9+3sjGzzwU4xy0=
|
||||
github.com/mattn/goveralls v0.0.6 h1:cr8Y0VMo/MnEZBjxNN/vh6G90SZ7IMb6lms1dzMoO+Y=
|
||||
github.com/mattn/goveralls v0.0.6/go.mod h1:h8b4ow6FxSPMQHF6o2ve3qsclnffZjYTNEKmLesRwqw=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mennanov/fieldmask-utils v0.3.2 h1:AkHXYBEOoyvocl8YhzoStATRnto5OH1PY4Rj78I5Cuc=
|
||||
@@ -788,6 +778,7 @@ github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.3.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8=
|
||||
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
|
||||
@@ -803,9 +794,6 @@ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwd
|
||||
github.com/monoculum/formam v0.0.0-20180901015400-4e68be1d79ba/go.mod h1:RKgILGEJq24YyJ2ban8EO0RUVSJlF1pGsEvoLEACr/Q=
|
||||
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/moul/http2curl v0.0.0-20170919181001-9ac6cf4d929b/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
|
||||
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
|
||||
github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM=
|
||||
github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8=
|
||||
github.com/nats-io/jwt v0.3.2 h1:+RB5hMpXUUA2dfxuhBTEkMOrYmM+gKIZYS1KjSostMI=
|
||||
@@ -834,6 +822,7 @@ github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQ
|
||||
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
|
||||
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/oleiade/reflections v1.0.0 h1:0ir4pc6v8/PJ0yw5AEtMddfXpWBXg9cnG7SgSoJuCgY=
|
||||
github.com/oleiade/reflections v1.0.0/go.mod h1:RbATFBbKYkVdqmSFtx13Bb/tVhR0lgOBXunWTZKeL4w=
|
||||
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
|
||||
@@ -862,16 +851,25 @@ github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnh
|
||||
github.com/ory/fosite v0.29.0/go.mod h1:0atSZmXO7CAcs6NPMI/Qtot8tmZYj04Nddoold4S2h0=
|
||||
github.com/ory/fosite v0.32.2 h1:iRV495P/9EyoYQ8qEHYxFQeeYCdDFawqjAML+qiMF9s=
|
||||
github.com/ory/fosite v0.32.2/go.mod h1:UeBhRgW6nAjTcd8S7kAo0IFsY/rTPyOXPq/t8N20Q8I=
|
||||
github.com/ory/fosite v0.33.0 h1:tK+3Luazv4vIBJY3uagOBryAQ3IG3cs6kfo8piGBhAY=
|
||||
github.com/ory/fosite v0.33.0/go.mod h1:h+ize9gk0GvRyGjabriqSEmTkMhny+O95cijb8DVqPE=
|
||||
github.com/ory/go-acc v0.0.0-20181118080137-ddc355013f90/go.mod h1:sxnvPCxChFuSmTJGj8FdMupeq1BezCiEpDjTUXQ4hf4=
|
||||
github.com/ory/go-acc v0.2.1/go.mod h1:0omgy2aa3nDBJ45VAKeLHH8ccPBudxLeic4xiDRtug0=
|
||||
github.com/ory/go-acc v0.2.5 h1:31irXHzG2vnKQSE4weJm7AdfrnpaVjVCq3nD7viXCJE=
|
||||
github.com/ory/go-acc v0.2.5/go.mod h1:4Kb/UnPcT8qRAk3IAxta+hvVapdxTLWtrr7bFLlEgpw=
|
||||
github.com/ory/go-convenience v0.1.0 h1:zouLKfF2GoSGnJwGq+PE/nJAE6dj2Zj5QlTgmMTsTS8=
|
||||
github.com/ory/go-convenience v0.1.0/go.mod h1:uEY/a60PL5c12nYz4V5cHY03IBmwIAEm8TWB0yn9KNs=
|
||||
github.com/ory/gojsonreference v0.0.0-20190720135523-6b606c2d8ee8/go.mod h1:wsH1C4nIeeQClDtD5AH7kF1uTS6zWyqfjVDTmB0Em7A=
|
||||
github.com/ory/gojsonschema v1.1.1-0.20190919112458-f254ca73d5e9/go.mod h1:BNZpdJgB74KOLSsWFvzw6roXg1I6O51WO8roMmW+T7Y=
|
||||
github.com/ory/herodot v0.6.2/go.mod h1:3BOneqcyBsVybCPAJoi92KN2BpJHcmDqAMcAAaJiJow=
|
||||
github.com/ory/viper v1.5.6/go.mod h1:TYmpFpKLxjQwvT4f0QPpkOn4sDXU1kDgAwJpgLYiQ28=
|
||||
github.com/ory/viper v1.7.5 h1:+xVdq7SU3e1vNaCsk/ixsfxE4zylk1TJUiJrY647jUE=
|
||||
github.com/ory/viper v1.7.5/go.mod h1:ypOuyJmEUb3oENywQZRgeAMwqgOyDqwboO1tj3DjTaM=
|
||||
github.com/ory/x v0.0.85/go.mod h1:s44V8t3xyjWZREcU+mWlp4h302rTuM4aLXcW+y5FbQ8=
|
||||
github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014/go.mod h1:joRatxRJaZBsY3JAOEMcoOp05CnZzsx4scTxi95DHyQ=
|
||||
github.com/owncloud/flaex v0.0.0-20200411150708-dce59891a203/go.mod h1:jip86t4OVURJTf8CM/0e2qcji/Y4NG3l2lR8kex4JWw=
|
||||
github.com/owncloud/ocis/storage v0.0.0-20201015120921-38358ba4d4df h1:PhRLD+WTGIfQ1T4MqBabp6/1Q8H/iwxjlygh6xzao0A=
|
||||
github.com/owncloud/ocis/storage v0.0.0-20201015120921-38358ba4d4df/go.mod h1:s9kJvxtBlHEi5qc1TuPAdz2bprk9yGFe+FSOeC76Pbs=
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
|
||||
github.com/parnurzeal/gorequest v0.2.15/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE=
|
||||
@@ -884,8 +882,8 @@ github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg=
|
||||
github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
|
||||
github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ=
|
||||
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
|
||||
github.com/pelletier/go-toml v1.8.0 h1:Keo9qb7iRJs2voHvunFtuuYFsbWeOBh8/P9v/kVMFtw=
|
||||
github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs=
|
||||
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@@ -893,6 +891,8 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
|
||||
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||
github.com/pkg/term v0.0.0-20200520122047-c3ffed290a03/go.mod h1:Z9+Ul5bCbBKnbCvdOWbLqTHhJiYV414CURZJba6L8qA=
|
||||
github.com/pkg/xattr v0.4.1 h1:dhclzL6EqOXNaPDWqoeb9tIxATfBSmjqL0b4DpSjwRw=
|
||||
github.com/pkg/xattr v0.4.1/go.mod h1:W2cGD0TBEus7MkUgv0tNZ9JutLtVO3cXu+IBRuHqnFs=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
@@ -936,9 +936,6 @@ github.com/prometheus/statsd_exporter v0.15.0/go.mod h1:Dv8HnkoLQkeEjkIE4/2ndAA7
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKcyumwBO6qip7RNQ5r77yrssm9bfCowcLEBcU5IA=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/restic/calens v0.2.0 h1:LVNAtmFc+Pb4ODX66qdX1T3Di1P0OTLyUsVyvM/xD7E=
|
||||
github.com/restic/calens v0.2.0/go.mod h1:UXwyAKS4wsgUZGEc7NrzzygJbLsQZIo3wl+62Q1wvmU=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
@@ -953,6 +950,8 @@ github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/zerolog v1.19.0 h1:hYz4ZVdUgjXTBUmrkrw55j1nHx68LfOKIQk5IYtyScg=
|
||||
github.com/rs/zerolog v1.19.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo=
|
||||
github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs=
|
||||
github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo=
|
||||
github.com/rubenv/sql-migrate v0.0.0-20190212093014-1007f53448d7/go.mod h1:WS0rl9eEliYI8DPnr3TOwz4439pay+qNgzJoVya/DmY=
|
||||
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
@@ -1001,17 +1000,24 @@ github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
|
||||
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.2.0/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/afero v1.3.2 h1:GDarE4TJQI52kYSbSAmLiId1Elfj+xgSDqrUZxFhxlU=
|
||||
github.com/spf13/afero v1.3.2/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
|
||||
github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
|
||||
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
|
||||
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||
@@ -1023,11 +1029,10 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
|
||||
github.com/spf13/viper v1.2.1/go.mod h1:P4AexN0a+C9tGAnUFNwDMYYZv3pjFuvmeiMyKRaNVlI=
|
||||
github.com/spf13/viper v1.3.1/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||
github.com/spf13/viper v1.6.3/go.mod h1:jUMtyi0/lB5yZH/FjyGAoH7IMNrIhlBf6pXZmbMDvzw=
|
||||
github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=
|
||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/steveyen/gtreap v0.1.0 h1:CjhzTa274PyJLJuMZwIzCO1PfC00oRa8d1Kc78bFXJM=
|
||||
github.com/steveyen/gtreap v0.1.0/go.mod h1:kl/5J7XbrOmlIbYIXdRHDDE5QxHqpk0cmkT7Z4dM9/Y=
|
||||
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@@ -1038,22 +1043,20 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/studio-b12/gowebdav v0.0.0-20200303150724-9380631c29a1 h1:TPyHV/OgChqNcnYqCoCvIFjR9TU60gFXXBKnhOBzVEI=
|
||||
github.com/studio-b12/gowebdav v0.0.0-20200303150724-9380631c29a1/go.mod h1:gCcfDlA1Y7GqOaeEKw5l9dOGx1VLdc/HuQSlQAaZ30s=
|
||||
github.com/subosito/gotenv v1.1.1/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||
github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzHWCjJB1zZfXPIAaDpzXIEJ0eS6B5Ok=
|
||||
github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8=
|
||||
github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog=
|
||||
github.com/tidwall/gjson v1.3.2/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls=
|
||||
github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E=
|
||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/tidwall/sjson v1.0.4/go.mod h1:bURseu1nuBkFpIES5cz6zBtjmYeOQmEESshn7VpF15Y=
|
||||
github.com/timewasted/linode v0.0.0-20160829202747-37e84520dcf7/go.mod h1:imsgLplxEC/etjIhdr3dNzV3JeT27LbVu5pYWm0JCBY=
|
||||
github.com/tinylib/msgp v1.1.0 h1:9fQd+ICuRIu/ue4vxJZu6/LzxN0HwMds2nq/0cFvxHU=
|
||||
github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20200122045848-3419fae592fc h1:yUaosFVTJwnltaHbSNC3i82I92quFs+OFPRl8kNMVwo=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20200122045848-3419fae592fc/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
@@ -1085,8 +1088,6 @@ github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4 h1:gKMu1Bf6QI
|
||||
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4/go.mod h1:50wTf68f99/Zt14pr046Tgt3Lp2vLyFZKzbFXTOabXw=
|
||||
github.com/vimeo/go-util v1.2.0/go.mod h1:s13SMDTSO7AjH1nbgp707mfN5JFIWUFDU5MDDuRRtKs=
|
||||
github.com/vultr/govultr v0.1.4/go.mod h1:9H008Uxr/C4vFNGLqKx232C206GL0PBHzOP0809bGNA=
|
||||
github.com/willf/bitset v1.1.10 h1:NotGKqX0KwQ72NUzqrjZq5ipPNDQex9lo3WpaS8L2sc=
|
||||
github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
||||
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
|
||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
@@ -1098,6 +1099,8 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63MEE3IF9l2/ebyx59GyGgPi+tICQdmM=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg=
|
||||
@@ -1150,16 +1153,20 @@ golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaE
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200320181102-891825fb96df/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 h1:DZhuSZLsGlFL4CmhA8BcRA0mnthyA/nZ00AqCUo7vHg=
|
||||
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -1170,6 +1177,7 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
@@ -1190,6 +1198,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180611182652-db08ff08e862/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180816102801-aaf60122140d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -1232,6 +1242,7 @@ golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
@@ -1278,12 +1289,12 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181206074257-70b957f3b65e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181221143128-b4a75ba826a6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190102155601-82a175fd1598/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190116161447-11f53e031339/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -1297,12 +1308,13 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -1310,21 +1322,28 @@ golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121 h1:rITEj+UZHYC927n8GT97eC3zrpzXdb/voyeOuVKS46o=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666 h1:gVCS+QOncANNPlmlO1AhlU3oxs4V9z+gTtPwIk3p2N8=
|
||||
golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -1392,6 +1411,9 @@ golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapK
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4 h1:kDtqNkeBrZb8B+atrj50B5XLHpzXXqcCdZPP/ApQ5NY=
|
||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200522201501-cb1345f3a375/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200721223218-6123e77877b2 h1:kxDWg8KNMtpGjI/XVKGgOtSljTnVg/PrjhS8+0pxjLE=
|
||||
golang.org/x/tools v0.0.0-20200721223218-6123e77877b2/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
@@ -1441,6 +1463,7 @@ google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece h1:1YM0uhfumvoDu9sx8+RyWwTI63zoCQvI23IYFRlvte0=
|
||||
@@ -1483,6 +1506,8 @@ gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.57.0 h1:9unxIsFcTt4I55uWluz+UmL95q4kdJ0buvQ1ZIqVQww=
|
||||
gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/mail.v2 v2.0.0-20180731213649-a0242b2233b4/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw=
|
||||
gopkg.in/ns1/ns1-go.v2 v2.0.0-20190730140822-b51389932cbc/go.mod h1:VV+3haRsgDiVLxyifmMBrBIuCWFBPYKbRssXB9z67Hw=
|
||||
gopkg.in/resty.v1 v1.9.1/go.mod h1:vo52Hzryw9PnPHcJfPsBiFW62XhNx5OczbV9y+IMpgc=
|
||||
@@ -1492,6 +1517,8 @@ gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76
|
||||
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/square/go-jose.v2 v2.5.0 h1:OZ4sdq+Y+SHfYB7vfthi1Ei8b0vkP8ZPQgUfUwdUSqo=
|
||||
gopkg.in/square/go-jose.v2 v2.5.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w=
|
||||
gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/telegram-bot-api.v4 v4.6.4/go.mod h1:5DpGO5dbumb40px+dXcwCpcjmeHNYLpk0bp3XRNvWDM=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
@@ -1505,6 +1532,9 @@ gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c h1:grhR+C34yXImVGp7EzNk+DTIk+323eIUWOmEevy6bDo=
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
"ldap": "^0.7.1",
|
||||
"nightwatch": "^1.3.6",
|
||||
"nightwatch-api": "^3.0.1",
|
||||
"node-fetch": "^2.6.0",
|
||||
"node-fetch": "^2.6.1",
|
||||
"qs": "^6.9.1",
|
||||
"rimraf": "^3.0.0",
|
||||
"rollup": "^1.28.0",
|
||||
|
||||
File diff suppressed because one or more lines are too long
34
accounts/pkg/command/rebuild_index.go
Normal file
34
accounts/pkg/command/rebuild_index.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/micro/go-micro/v2/client/grpc"
|
||||
merrors "github.com/micro/go-micro/v2/errors"
|
||||
"github.com/owncloud/ocis/accounts/pkg/config"
|
||||
index "github.com/owncloud/ocis/accounts/pkg/proto/v0"
|
||||
)
|
||||
|
||||
// RebuildIndex rebuilds the entire configured index.
|
||||
func RebuildIndex(cdf *config.Config) *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "rebuildIndex",
|
||||
Usage: "Rebuilds the service's index, i.e. deleting and then re-adding all existing documents",
|
||||
Aliases: []string{"rebuild", "ri"},
|
||||
Action: func(ctx *cli.Context) error {
|
||||
idxSvcID := "com.owncloud.api.accounts"
|
||||
idxSvc := index.NewIndexService(idxSvcID, grpc.NewClient())
|
||||
|
||||
_, err := idxSvc.RebuildIndex(context.Background(), &index.RebuildIndexRequest{})
|
||||
if err != nil {
|
||||
fmt.Println(merrors.FromError(err).Detail)
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("index rebuilt successfully")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,7 @@ func Execute() error {
|
||||
InspectAccount(cfg),
|
||||
RemoveAccount(cfg),
|
||||
PrintVersion(cfg),
|
||||
RebuildIndex(cfg),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -77,16 +77,27 @@ type CS3 struct {
|
||||
ProviderAddr string
|
||||
DataURL string
|
||||
DataPrefix string
|
||||
JWTSecret string
|
||||
}
|
||||
|
||||
// ServiceUser defines the user required for EOS
|
||||
// ServiceUser defines the user required for EOS.
|
||||
type ServiceUser struct {
|
||||
UUID string
|
||||
UUID string
|
||||
Username string
|
||||
UID int64
|
||||
GID int64
|
||||
}
|
||||
|
||||
// Index defines config for indexes.
|
||||
type Index struct {
|
||||
UID, GID Bound
|
||||
}
|
||||
|
||||
// Bound defines a lower and upper bound.
|
||||
type Bound struct {
|
||||
Lower, Upper int64
|
||||
}
|
||||
|
||||
// Config merges all Account config parameters.
|
||||
type Config struct {
|
||||
LDAP LDAP
|
||||
@@ -97,6 +108,7 @@ type Config struct {
|
||||
Log Log
|
||||
TokenManager TokenManager
|
||||
Repo Repo
|
||||
Index Index
|
||||
ServiceUser ServiceUser
|
||||
}
|
||||
|
||||
|
||||
@@ -127,6 +127,13 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag {
|
||||
EnvVars: []string{"ACCOUNTS_STORAGE_CS3_DATA_PREFIX"},
|
||||
Destination: &cfg.Repo.CS3.DataPrefix,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "storage-cs3-jwt-secret",
|
||||
Value: "Pive-Fumkiu4",
|
||||
Usage: "Used to create JWT to talk to reva, should equal reva's jwt-secret",
|
||||
EnvVars: []string{"ACCOUNTS_STORAGE_CS3_JWT_SECRET"},
|
||||
Destination: &cfg.Repo.CS3.JWTSecret,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "service-user-uuid",
|
||||
Value: "95cb8724-03b2-11eb-a0a6-c33ef8ef53ad",
|
||||
@@ -155,6 +162,34 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag {
|
||||
EnvVars: []string{"ACCOUNTS_SERVICE_USER_GID"},
|
||||
Destination: &cfg.ServiceUser.GID,
|
||||
},
|
||||
&cli.Int64Flag{
|
||||
Name: "uid-index-lower-bound",
|
||||
Value: 0,
|
||||
Usage: "define a starting point for the account UID",
|
||||
EnvVars: []string{"ACCOUNTS_UID_INDEX_LOWER_BOUND"},
|
||||
Destination: &cfg.Index.UID.Lower,
|
||||
},
|
||||
&cli.Int64Flag{
|
||||
Name: "gid-index-lower-bound",
|
||||
Value: 1000,
|
||||
Usage: "define a starting point for the account GID",
|
||||
EnvVars: []string{"ACCOUNTS_GID_INDEX_LOWER_BOUND"},
|
||||
Destination: &cfg.Index.GID.Lower,
|
||||
},
|
||||
&cli.Int64Flag{
|
||||
Name: "uid-index-upper-bound",
|
||||
Value: 0,
|
||||
Usage: "define an ending point for the account UID",
|
||||
EnvVars: []string{"ACCOUNTS_UID_INDEX_UPPER_BOUND"},
|
||||
Destination: &cfg.Index.UID.Upper,
|
||||
},
|
||||
&cli.Int64Flag{
|
||||
Name: "gid-index-upper-bound",
|
||||
Value: 1000,
|
||||
Usage: "define an ending point for the account GID",
|
||||
EnvVars: []string{"ACCOUNTS_GID_INDEX_UPPER_BOUND"},
|
||||
Destination: &cfg.Index.GID.Upper,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
35
accounts/pkg/indexer/errors/errors.go
Normal file
35
accounts/pkg/indexer/errors/errors.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// AlreadyExistsErr implements the Error interface.
|
||||
type AlreadyExistsErr struct {
|
||||
TypeName, Key, Value string
|
||||
}
|
||||
|
||||
func (e *AlreadyExistsErr) Error() string {
|
||||
return fmt.Sprintf("%s with %s=%s does already exist", e.TypeName, e.Key, e.Value)
|
||||
}
|
||||
|
||||
// IsAlreadyExistsErr checks whether an error is of type AlreadyExistsErr.
|
||||
func IsAlreadyExistsErr(e error) bool {
|
||||
_, ok := e.(*AlreadyExistsErr)
|
||||
return ok
|
||||
}
|
||||
|
||||
// NotFoundErr implements the Error interface.
|
||||
type NotFoundErr struct {
|
||||
TypeName, Key, Value string
|
||||
}
|
||||
|
||||
func (e *NotFoundErr) Error() string {
|
||||
return fmt.Sprintf("%s with %s=%s not found", e.TypeName, e.Key, e.Value)
|
||||
}
|
||||
|
||||
// IsNotFoundErr checks whether an error is of type IsNotFoundErr.
|
||||
func IsNotFoundErr(e error) bool {
|
||||
_, ok := e.(*NotFoundErr)
|
||||
return ok
|
||||
}
|
||||
378
accounts/pkg/indexer/index/cs3/autoincrement.go
Normal file
378
accounts/pkg/indexer/index/cs3/autoincrement.go
Normal file
@@ -0,0 +1,378 @@
|
||||
package cs3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/owncloud/ocis/accounts/pkg/storage"
|
||||
|
||||
idxerrs "github.com/owncloud/ocis/accounts/pkg/indexer/errors"
|
||||
|
||||
v1beta11 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
"github.com/cs3org/reva/pkg/rgrpc/todo/pool"
|
||||
"github.com/cs3org/reva/pkg/token"
|
||||
"github.com/cs3org/reva/pkg/token/manager/jwt"
|
||||
"google.golang.org/grpc/metadata"
|
||||
|
||||
"github.com/owncloud/ocis/accounts/pkg/indexer/index"
|
||||
"github.com/owncloud/ocis/accounts/pkg/indexer/option"
|
||||
"github.com/owncloud/ocis/accounts/pkg/indexer/registry"
|
||||
)
|
||||
|
||||
// Autoincrement are fields for an index of type autoincrement.
|
||||
type Autoincrement struct {
|
||||
indexBy string
|
||||
typeName string
|
||||
filesDir string
|
||||
indexBaseDir string
|
||||
indexRootDir string
|
||||
|
||||
tokenManager token.Manager
|
||||
storageProvider provider.ProviderAPIClient
|
||||
dataProvider dataProviderClient // Used to create and download data via http, bypassing reva upload protocol
|
||||
|
||||
cs3conf *Config
|
||||
bound *option.Bound
|
||||
}
|
||||
|
||||
func init() {
|
||||
registry.IndexConstructorRegistry["cs3"]["autoincrement"] = NewAutoincrementIndex
|
||||
}
|
||||
|
||||
// NewAutoincrementIndex instantiates a new AutoincrementIndex instance.
|
||||
func NewAutoincrementIndex(o ...option.Option) index.Index {
|
||||
opts := &option.Options{}
|
||||
for _, opt := range o {
|
||||
opt(opts)
|
||||
}
|
||||
|
||||
u := &Autoincrement{
|
||||
indexBy: opts.IndexBy,
|
||||
typeName: opts.TypeName,
|
||||
filesDir: opts.FilesDir,
|
||||
bound: opts.Bound,
|
||||
indexBaseDir: path.Join(opts.DataDir, "index.cs3"),
|
||||
indexRootDir: path.Join(path.Join(opts.DataDir, "index.cs3"), strings.Join([]string{"autoincrement", opts.TypeName, opts.IndexBy}, ".")),
|
||||
cs3conf: &Config{
|
||||
ProviderAddr: opts.ProviderAddr,
|
||||
DataURL: opts.DataURL,
|
||||
DataPrefix: opts.DataPrefix,
|
||||
JWTSecret: opts.JWTSecret,
|
||||
ServiceUser: opts.ServiceUser,
|
||||
},
|
||||
dataProvider: dataProviderClient{
|
||||
baseURL: singleJoiningSlash(opts.DataURL, opts.DataPrefix),
|
||||
client: http.Client{
|
||||
Transport: http.DefaultTransport,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return u
|
||||
}
|
||||
|
||||
// Init initializes an autoincrement index.
|
||||
func (idx *Autoincrement) Init() error {
|
||||
tokenManager, err := jwt.New(map[string]interface{}{
|
||||
"secret": idx.cs3conf.JWTSecret,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
idx.tokenManager = tokenManager
|
||||
|
||||
client, err := pool.GetStorageProviderServiceClient(idx.cs3conf.ProviderAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
idx.storageProvider = client
|
||||
|
||||
ctx, err := idx.getAuthenticatedContext(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := idx.makeDirIfNotExists(ctx, idx.indexBaseDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := idx.makeDirIfNotExists(ctx, idx.indexRootDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Lookup exact lookup by value.
|
||||
func (idx *Autoincrement) Lookup(v string) ([]string, error) {
|
||||
searchPath := path.Join(idx.indexRootDir, v)
|
||||
oldname, err := idx.resolveSymlink(searchPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err = &idxerrs.NotFoundErr{TypeName: idx.typeName, Key: idx.indexBy, Value: v}
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return []string{oldname}, nil
|
||||
}
|
||||
|
||||
// Add a new value to the index.
|
||||
func (idx *Autoincrement) Add(id, v string) (string, error) {
|
||||
var newName string
|
||||
if v == "" {
|
||||
next, err := idx.next()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
newName = path.Join(idx.indexRootDir, strconv.Itoa(next))
|
||||
} else {
|
||||
newName = path.Join(idx.indexRootDir, v)
|
||||
}
|
||||
if err := idx.createSymlink(id, newName); err != nil {
|
||||
if os.IsExist(err) {
|
||||
return "", &idxerrs.AlreadyExistsErr{TypeName: idx.typeName, Key: idx.indexBy, Value: v}
|
||||
}
|
||||
|
||||
return "", err
|
||||
}
|
||||
|
||||
return newName, nil
|
||||
}
|
||||
|
||||
// Remove a value v from an index.
|
||||
func (idx *Autoincrement) Remove(id string, v string) error {
|
||||
if v == "" {
|
||||
return nil
|
||||
}
|
||||
searchPath := path.Join(idx.indexRootDir, v)
|
||||
_, err := idx.resolveSymlink(searchPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err = &idxerrs.NotFoundErr{TypeName: idx.typeName, Key: idx.indexBy, Value: v}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, err := idx.getAuthenticatedContext(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
deletePath := path.Join("/meta", idx.indexRootDir, v)
|
||||
resp, err := idx.storageProvider.Delete(ctx, &provider.DeleteRequest{
|
||||
Ref: &provider.Reference{
|
||||
Spec: &provider.Reference_Path{Path: deletePath},
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO Handle other error codes?
|
||||
if resp.Status.Code == v1beta11.Code_CODE_NOT_FOUND {
|
||||
return &idxerrs.NotFoundErr{}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Update index from <oldV> to <newV>.
|
||||
func (idx *Autoincrement) Update(id, oldV, newV string) error {
|
||||
if err := idx.Remove(id, oldV); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := idx.Add(id, newV); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Search allows for glob search on the index.
|
||||
func (idx *Autoincrement) Search(pattern string) ([]string, error) {
|
||||
ctx, err := idx.getAuthenticatedContext(context.Background())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := idx.storageProvider.ListContainer(ctx, &provider.ListContainerRequest{
|
||||
Ref: &provider.Reference{
|
||||
Spec: &provider.Reference_Path{Path: path.Join("/meta", idx.indexRootDir)},
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
searchPath := idx.indexRootDir
|
||||
matches := make([]string, 0)
|
||||
for _, i := range res.GetInfos() {
|
||||
if found, err := filepath.Match(pattern, path.Base(i.Path)); found {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
oldPath, err := idx.resolveSymlink(path.Join(searchPath, path.Base(i.Path)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
matches = append(matches, oldPath)
|
||||
}
|
||||
}
|
||||
|
||||
return matches, nil
|
||||
}
|
||||
|
||||
// CaseInsensitive undocumented.
|
||||
func (idx *Autoincrement) CaseInsensitive() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IndexBy undocumented.
|
||||
func (idx *Autoincrement) IndexBy() string {
|
||||
return idx.indexBy
|
||||
}
|
||||
|
||||
// TypeName undocumented.
|
||||
func (idx *Autoincrement) TypeName() string {
|
||||
return idx.typeName
|
||||
}
|
||||
|
||||
// FilesDir undocumented.
|
||||
func (idx *Autoincrement) FilesDir() string {
|
||||
return idx.filesDir
|
||||
}
|
||||
|
||||
func (idx *Autoincrement) createSymlink(oldname, newname string) error {
|
||||
t, err := idx.authenticate(context.TODO())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := idx.resolveSymlink(newname); err == nil {
|
||||
return os.ErrExist
|
||||
}
|
||||
|
||||
resp, err := idx.dataProvider.put(newname, strings.NewReader(oldname), t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = resp.Body.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (idx *Autoincrement) resolveSymlink(name string) (string, error) {
|
||||
t, err := idx.authenticate(context.TODO())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
resp, err := idx.dataProvider.get(name, t)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
if resp.StatusCode == http.StatusNotFound {
|
||||
return "", os.ErrNotExist
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("could not resolve symlink %s, got status %v", name, resp.StatusCode)
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err = resp.Body.Close(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(b), err
|
||||
}
|
||||
|
||||
func (idx *Autoincrement) makeDirIfNotExists(ctx context.Context, folder string) error {
|
||||
return storage.MakeDirIfNotExist(ctx, idx.storageProvider, folder)
|
||||
}
|
||||
|
||||
func (idx *Autoincrement) authenticate(ctx context.Context) (token string, err error) {
|
||||
return storage.AuthenticateCS3(ctx, idx.cs3conf.ServiceUser, idx.tokenManager)
|
||||
}
|
||||
|
||||
func (idx *Autoincrement) next() (int, error) {
|
||||
ctx, err := idx.getAuthenticatedContext(context.Background())
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
res, err := idx.storageProvider.ListContainer(ctx, &provider.ListContainerRequest{
|
||||
Ref: &provider.Reference{
|
||||
Spec: &provider.Reference_Path{Path: path.Join("/meta", idx.indexRootDir)},
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
if len(res.GetInfos()) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
infos := res.GetInfos()
|
||||
sort.Slice(infos, func(i, j int) bool {
|
||||
a, _ := strconv.Atoi(path.Base(infos[i].Path))
|
||||
b, _ := strconv.Atoi(path.Base(infos[j].Path))
|
||||
return a < b
|
||||
})
|
||||
|
||||
latest, err := strconv.Atoi(path.Base(infos[len(infos)-1].Path)) // would returning a string be a better interface?
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
if int64(latest) < idx.bound.Lower {
|
||||
return int(idx.bound.Lower), nil
|
||||
}
|
||||
|
||||
return latest + 1, nil
|
||||
}
|
||||
|
||||
func (idx *Autoincrement) getAuthenticatedContext(ctx context.Context) (context.Context, error) {
|
||||
t, err := idx.authenticate(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx = metadata.AppendToOutgoingContext(ctx, token.TokenHeader, t)
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
// Delete deletes the index folder from its storage.
|
||||
func (idx *Autoincrement) Delete() error {
|
||||
ctx, err := idx.getAuthenticatedContext(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return deleteIndexRoot(ctx, idx.storageProvider, idx.indexRootDir)
|
||||
}
|
||||
83
accounts/pkg/indexer/index/cs3/autoincrement_test.go
Normal file
83
accounts/pkg/indexer/index/cs3/autoincrement_test.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package cs3
|
||||
|
||||
//
|
||||
//import (
|
||||
// "os"
|
||||
// "testing"
|
||||
//
|
||||
// "github.com/owncloud/ocis/accounts/pkg/config"
|
||||
// "github.com/owncloud/ocis/accounts/pkg/indexer/option"
|
||||
// . "github.com/owncloud/ocis/accounts/pkg/indexer/test"
|
||||
// "github.com/stretchr/testify/assert"
|
||||
//)
|
||||
//
|
||||
//const cs3RootFolder = "/var/tmp/ocis/storage/users/data"
|
||||
//
|
||||
//func TestAutoincrementIndexAdd(t *testing.T) {
|
||||
// dataDir, err := WriteIndexTestData(Data, "ID", cs3RootFolder)
|
||||
// assert.NoError(t, err)
|
||||
// cfg := generateConfig()
|
||||
//
|
||||
// sut := NewAutoincrementIndex(
|
||||
// option.WithTypeName(GetTypeFQN(User{})),
|
||||
// option.WithIndexBy("UID"),
|
||||
// option.WithDataURL(cfg.Repo.CS3.DataURL),
|
||||
// option.WithDataPrefix(cfg.Repo.CS3.DataPrefix),
|
||||
// option.WithJWTSecret(cfg.Repo.CS3.JWTSecret),
|
||||
// option.WithProviderAddr(cfg.Repo.CS3.ProviderAddr),
|
||||
// )
|
||||
//
|
||||
// assert.NoError(t, sut.Init())
|
||||
//
|
||||
// for i := 0; i < 5; i++ {
|
||||
// res, err := sut.Add("abcdefg-123", "")
|
||||
// assert.NoError(t, err)
|
||||
// t.Log(res)
|
||||
// }
|
||||
//
|
||||
// _ = os.RemoveAll(dataDir)
|
||||
//}
|
||||
//
|
||||
//func BenchmarkAutoincrementIndexAdd(b *testing.B) {
|
||||
// dataDir, err := WriteIndexTestData(Data, "ID", cs3RootFolder)
|
||||
// assert.NoError(b, err)
|
||||
// cfg := generateConfig()
|
||||
//
|
||||
// sut := NewAutoincrementIndex(
|
||||
// option.WithTypeName(GetTypeFQN(User{})),
|
||||
// option.WithIndexBy("UID"),
|
||||
// option.WithDataURL(cfg.Repo.CS3.DataURL),
|
||||
// option.WithDataPrefix(cfg.Repo.CS3.DataPrefix),
|
||||
// option.WithJWTSecret(cfg.Repo.CS3.JWTSecret),
|
||||
// option.WithProviderAddr(cfg.Repo.CS3.ProviderAddr),
|
||||
// )
|
||||
//
|
||||
// err = sut.Init()
|
||||
// assert.NoError(b, err)
|
||||
//
|
||||
// for n := 0; n < b.N; n++ {
|
||||
// _, err := sut.Add("abcdefg-123", "")
|
||||
// if err != nil {
|
||||
// b.Error(err)
|
||||
// }
|
||||
// assert.NoError(b, err)
|
||||
// }
|
||||
//
|
||||
// _ = os.RemoveAll(dataDir)
|
||||
//}
|
||||
//
|
||||
//func generateConfig() config.Config {
|
||||
// return config.Config{
|
||||
// Repo: config.Repo{
|
||||
// Disk: config.Disk{
|
||||
// Path: "",
|
||||
// },
|
||||
// CS3: config.CS3{
|
||||
// ProviderAddr: "0.0.0.0:9215",
|
||||
// DataURL: "http://localhost:9216",
|
||||
// DataPrefix: "data",
|
||||
// JWTSecret: "Pive-Fumkiu4",
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
//}
|
||||
45
accounts/pkg/indexer/index/cs3/data_provider_client.go
Normal file
45
accounts/pkg/indexer/index/cs3/data_provider_client.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package cs3
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type dataProviderClient struct {
|
||||
client http.Client
|
||||
baseURL string
|
||||
}
|
||||
|
||||
func (d dataProviderClient) put(url string, body io.Reader, token string) (*http.Response, error) {
|
||||
req, err := http.NewRequest(http.MethodPut, singleJoiningSlash(d.baseURL, url), body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Add("x-access-token", token)
|
||||
return d.client.Do(req)
|
||||
}
|
||||
|
||||
func (d dataProviderClient) get(url string, token string) (*http.Response, error) {
|
||||
req, err := http.NewRequest(http.MethodGet, singleJoiningSlash(d.baseURL, url), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Add("x-access-token", token)
|
||||
return d.client.Do(req)
|
||||
}
|
||||
|
||||
// TODO: this is copied from proxy. Find a better solution or move it to ocis-pkg
|
||||
func singleJoiningSlash(a, b string) string {
|
||||
aslash := strings.HasSuffix(a, "/")
|
||||
bslash := strings.HasPrefix(b, "/")
|
||||
switch {
|
||||
case aslash && bslash:
|
||||
return a + b[1:]
|
||||
case !aslash && !bslash:
|
||||
return a + "/" + b
|
||||
}
|
||||
return a + b
|
||||
}
|
||||
26
accounts/pkg/indexer/index/cs3/helper.go
Normal file
26
accounts/pkg/indexer/index/cs3/helper.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package cs3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path"
|
||||
|
||||
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
)
|
||||
|
||||
func deleteIndexRoot(ctx context.Context, storageProvider provider.ProviderAPIClient, indexRootDir string) error {
|
||||
res, err := storageProvider.Delete(ctx, &provider.DeleteRequest{
|
||||
Ref: &provider.Reference{
|
||||
Spec: &provider.Reference_Path{Path: path.Join("/meta", indexRootDir)},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if res.Status.Code != rpc.Code_CODE_OK {
|
||||
return fmt.Errorf("error deleting index root dir: %v", indexRootDir)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
393
accounts/pkg/indexer/index/cs3/non_unique.go
Normal file
393
accounts/pkg/indexer/index/cs3/non_unique.go
Normal file
@@ -0,0 +1,393 @@
|
||||
package cs3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/owncloud/ocis/accounts/pkg/storage"
|
||||
|
||||
v1beta11 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
"github.com/cs3org/reva/pkg/rgrpc/todo/pool"
|
||||
"github.com/cs3org/reva/pkg/token"
|
||||
"github.com/cs3org/reva/pkg/token/manager/jwt"
|
||||
idxerrs "github.com/owncloud/ocis/accounts/pkg/indexer/errors"
|
||||
"github.com/owncloud/ocis/accounts/pkg/indexer/index"
|
||||
"github.com/owncloud/ocis/accounts/pkg/indexer/option"
|
||||
"github.com/owncloud/ocis/accounts/pkg/indexer/registry"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registry.IndexConstructorRegistry["cs3"]["non_unique"] = NewNonUniqueIndexWithOptions
|
||||
}
|
||||
|
||||
// NonUnique are fields for an index of type non_unique.
|
||||
type NonUnique struct {
|
||||
caseInsensitive bool
|
||||
indexBy string
|
||||
typeName string
|
||||
filesDir string
|
||||
indexBaseDir string
|
||||
indexRootDir string
|
||||
|
||||
tokenManager token.Manager
|
||||
storageProvider provider.ProviderAPIClient
|
||||
dataProvider dataProviderClient // Used to create and download data via http, bypassing reva upload protocol
|
||||
|
||||
cs3conf *Config
|
||||
}
|
||||
|
||||
// NewNonUniqueIndexWithOptions instantiates a new NonUniqueIndex instance.
|
||||
// /var/tmp/ocis-accounts/index.cs3/Pets/Bro*
|
||||
// ├── Brown/
|
||||
// │ └── rebef-123 -> /var/tmp/testfiles-395764020/pets/rebef-123
|
||||
// ├── Green/
|
||||
// │ ├── goefe-789 -> /var/tmp/testfiles-395764020/pets/goefe-789
|
||||
// │ └── xadaf-189 -> /var/tmp/testfiles-395764020/pets/xadaf-189
|
||||
// └── White/
|
||||
// └── wefwe-456 -> /var/tmp/testfiles-395764020/pets/wefwe-456
|
||||
func NewNonUniqueIndexWithOptions(o ...option.Option) index.Index {
|
||||
opts := &option.Options{}
|
||||
for _, opt := range o {
|
||||
opt(opts)
|
||||
}
|
||||
|
||||
return &NonUnique{
|
||||
caseInsensitive: opts.CaseInsensitive,
|
||||
indexBy: opts.IndexBy,
|
||||
typeName: opts.TypeName,
|
||||
filesDir: opts.FilesDir,
|
||||
indexBaseDir: path.Join(opts.DataDir, "index.cs3"),
|
||||
indexRootDir: path.Join(path.Join(opts.DataDir, "index.cs3"), strings.Join([]string{"non_unique", opts.TypeName, opts.IndexBy}, ".")),
|
||||
cs3conf: &Config{
|
||||
ProviderAddr: opts.ProviderAddr,
|
||||
DataURL: opts.DataURL,
|
||||
DataPrefix: opts.DataPrefix,
|
||||
JWTSecret: opts.JWTSecret,
|
||||
ServiceUser: opts.ServiceUser,
|
||||
},
|
||||
dataProvider: dataProviderClient{
|
||||
baseURL: singleJoiningSlash(opts.DataURL, opts.DataPrefix),
|
||||
client: http.Client{
|
||||
Transport: http.DefaultTransport,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Init initializes a non_unique index.
|
||||
func (idx *NonUnique) Init() error {
|
||||
tokenManager, err := jwt.New(map[string]interface{}{
|
||||
"secret": idx.cs3conf.JWTSecret,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
idx.tokenManager = tokenManager
|
||||
|
||||
client, err := pool.GetStorageProviderServiceClient(idx.cs3conf.ProviderAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
idx.storageProvider = client
|
||||
|
||||
ctx := context.Background()
|
||||
tk, err := idx.authenticate(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx = metadata.AppendToOutgoingContext(ctx, token.TokenHeader, tk)
|
||||
|
||||
if err := idx.makeDirIfNotExists(ctx, idx.indexBaseDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := idx.makeDirIfNotExists(ctx, idx.indexRootDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Lookup exact lookup by value.
|
||||
func (idx *NonUnique) Lookup(v string) ([]string, error) {
|
||||
if idx.caseInsensitive {
|
||||
v = strings.ToLower(v)
|
||||
}
|
||||
var matches = make([]string, 0)
|
||||
ctx, err := idx.getAuthenticatedContext(context.Background())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := idx.storageProvider.ListContainer(ctx, &provider.ListContainerRequest{
|
||||
Ref: &provider.Reference{
|
||||
Spec: &provider.Reference_Path{Path: path.Join("/meta", idx.indexRootDir, v)},
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, info := range res.Infos {
|
||||
matches = append(matches, path.Base(info.Path))
|
||||
}
|
||||
|
||||
return matches, nil
|
||||
}
|
||||
|
||||
// Add a new value to the index.
|
||||
func (idx *NonUnique) Add(id, v string) (string, error) {
|
||||
if v == "" {
|
||||
return "", nil
|
||||
}
|
||||
if idx.caseInsensitive {
|
||||
v = strings.ToLower(v)
|
||||
}
|
||||
ctx, err := idx.getAuthenticatedContext(context.Background())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
newName := path.Join(idx.indexRootDir, v)
|
||||
if err := idx.makeDirIfNotExists(ctx, newName); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := idx.createSymlink(id, path.Join(newName, id)); err != nil {
|
||||
if os.IsExist(err) {
|
||||
return "", &idxerrs.AlreadyExistsErr{TypeName: idx.typeName, Key: idx.indexBy, Value: v}
|
||||
}
|
||||
|
||||
return "", err
|
||||
}
|
||||
|
||||
return newName, nil
|
||||
}
|
||||
|
||||
// Remove a value v from an index.
|
||||
func (idx *NonUnique) Remove(id string, v string) error {
|
||||
if v == "" {
|
||||
return nil
|
||||
}
|
||||
if idx.caseInsensitive {
|
||||
v = strings.ToLower(v)
|
||||
}
|
||||
ctx, err := idx.getAuthenticatedContext(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
deletePath := path.Join("/meta", idx.indexRootDir, v, id)
|
||||
resp, err := idx.storageProvider.Delete(ctx, &provider.DeleteRequest{
|
||||
Ref: &provider.Reference{
|
||||
Spec: &provider.Reference_Path{Path: deletePath},
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if resp.Status.Code == v1beta11.Code_CODE_NOT_FOUND {
|
||||
return &idxerrs.NotFoundErr{TypeName: idx.typeName, Key: idx.indexBy, Value: v}
|
||||
}
|
||||
|
||||
toStat := path.Join("/meta", idx.indexRootDir, v)
|
||||
lcResp, err := idx.storageProvider.ListContainer(ctx, &provider.ListContainerRequest{
|
||||
Ref: &provider.Reference{
|
||||
Spec: &provider.Reference_Path{Path: toStat},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(lcResp.Infos) == 0 {
|
||||
deletePath = path.Join("/meta", idx.indexRootDir, v)
|
||||
_, err := idx.storageProvider.Delete(ctx, &provider.DeleteRequest{
|
||||
Ref: &provider.Reference{
|
||||
Spec: &provider.Reference_Path{Path: deletePath},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update index from <oldV> to <newV>.
|
||||
func (idx *NonUnique) Update(id, oldV, newV string) error {
|
||||
if idx.caseInsensitive {
|
||||
oldV = strings.ToLower(oldV)
|
||||
newV = strings.ToLower(newV)
|
||||
}
|
||||
|
||||
if err := idx.Remove(id, oldV); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := idx.Add(id, newV); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Search allows for glob search on the index.
|
||||
func (idx *NonUnique) Search(pattern string) ([]string, error) {
|
||||
if idx.caseInsensitive {
|
||||
pattern = strings.ToLower(pattern)
|
||||
}
|
||||
|
||||
ctx, err := idx.getAuthenticatedContext(context.Background())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
foldersMatched := make([]string, 0)
|
||||
matches := make([]string, 0)
|
||||
res, err := idx.storageProvider.ListContainer(ctx, &provider.ListContainerRequest{
|
||||
Ref: &provider.Reference{
|
||||
Spec: &provider.Reference_Path{Path: path.Join("/meta", idx.indexRootDir)},
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, i := range res.Infos {
|
||||
if found, err := filepath.Match(pattern, path.Base(i.Path)); found {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
foldersMatched = append(foldersMatched, i.Path)
|
||||
}
|
||||
}
|
||||
|
||||
for i := range foldersMatched {
|
||||
res, _ := idx.storageProvider.ListContainer(ctx, &provider.ListContainerRequest{
|
||||
Ref: &provider.Reference{
|
||||
Spec: &provider.Reference_Path{Path: foldersMatched[i]},
|
||||
},
|
||||
})
|
||||
|
||||
for _, info := range res.Infos {
|
||||
matches = append(matches, path.Base(info.Path))
|
||||
}
|
||||
}
|
||||
|
||||
return matches, nil
|
||||
}
|
||||
|
||||
// CaseInsensitive undocumented.
|
||||
func (idx *NonUnique) CaseInsensitive() bool {
|
||||
return idx.caseInsensitive
|
||||
}
|
||||
|
||||
// IndexBy undocumented.
|
||||
func (idx *NonUnique) IndexBy() string {
|
||||
return idx.indexBy
|
||||
}
|
||||
|
||||
// TypeName undocumented.
|
||||
func (idx *NonUnique) TypeName() string {
|
||||
return idx.typeName
|
||||
}
|
||||
|
||||
// FilesDir undocumented.
|
||||
func (idx *NonUnique) FilesDir() string {
|
||||
return idx.filesDir
|
||||
}
|
||||
|
||||
func (idx *NonUnique) makeDirIfNotExists(ctx context.Context, folder string) error {
|
||||
return storage.MakeDirIfNotExist(ctx, idx.storageProvider, folder)
|
||||
}
|
||||
|
||||
func (idx *NonUnique) createSymlink(oldname, newname string) error {
|
||||
t, err := idx.authenticate(context.TODO())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := idx.resolveSymlink(newname); err == nil {
|
||||
return os.ErrExist
|
||||
}
|
||||
|
||||
resp, err := idx.dataProvider.put(newname, strings.NewReader(oldname), t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = resp.Body.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (idx *NonUnique) resolveSymlink(name string) (string, error) {
|
||||
t, err := idx.authenticate(context.TODO())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
resp, err := idx.dataProvider.get(name, t)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
if resp.StatusCode == http.StatusNotFound {
|
||||
return "", os.ErrNotExist
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("could not resolve symlink %s, got status %v", name, resp.StatusCode)
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err = resp.Body.Close(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(b), err
|
||||
}
|
||||
|
||||
func (idx *NonUnique) getAuthenticatedContext(ctx context.Context) (context.Context, error) {
|
||||
t, err := idx.authenticate(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx = metadata.AppendToOutgoingContext(ctx, token.TokenHeader, t)
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
// Delete deletes the index folder from its storage.
|
||||
func (idx *NonUnique) Delete() error {
|
||||
ctx, err := idx.getAuthenticatedContext(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return deleteIndexRoot(ctx, idx.storageProvider, idx.indexRootDir)
|
||||
}
|
||||
|
||||
func (idx *NonUnique) authenticate(ctx context.Context) (token string, err error) {
|
||||
return storage.AuthenticateCS3(ctx, idx.cs3conf.ServiceUser, idx.tokenManager)
|
||||
}
|
||||
68
accounts/pkg/indexer/index/cs3/non_unique_test.go
Normal file
68
accounts/pkg/indexer/index/cs3/non_unique_test.go
Normal file
@@ -0,0 +1,68 @@
|
||||
package cs3
|
||||
|
||||
//
|
||||
//import (
|
||||
// "os"
|
||||
// "path"
|
||||
// "testing"
|
||||
//
|
||||
// "github.com/owncloud/ocis/accounts/pkg/config"
|
||||
// "github.com/owncloud/ocis/accounts/pkg/indexer/option"
|
||||
// . "github.com/owncloud/ocis/accounts/pkg/indexer/test"
|
||||
// "github.com/stretchr/testify/assert"
|
||||
//)
|
||||
//
|
||||
//func TestCS3NonUniqueIndex_FakeSymlink(t *testing.T) {
|
||||
// dataDir, err := WriteIndexTestData(Data, "ID", cs3RootFolder)
|
||||
// assert.NoError(t, err)
|
||||
// cfg := config.Config{
|
||||
// Repo: config.Repo{
|
||||
// Disk: config.Disk{
|
||||
// Path: "",
|
||||
// },
|
||||
// CS3: config.CS3{
|
||||
// ProviderAddr: "0.0.0.0:9215",
|
||||
// DataURL: "http://localhost:9216",
|
||||
// DataPrefix: "data",
|
||||
// JWTSecret: "Pive-Fumkiu4",
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
//
|
||||
// sut := NewNonUniqueIndexWithOptions(
|
||||
// option.WithTypeName(GetTypeFQN(User{})),
|
||||
// option.WithIndexBy("UserName"),
|
||||
// option.WithFilesDir(path.Join(cfg.Repo.Disk.Path, "/meta")),
|
||||
// option.WithDataDir(cfg.Repo.Disk.Path),
|
||||
// option.WithDataURL(cfg.Repo.CS3.DataURL),
|
||||
// option.WithDataPrefix(cfg.Repo.CS3.DataPrefix),
|
||||
// option.WithJWTSecret(cfg.Repo.CS3.JWTSecret),
|
||||
// option.WithProviderAddr(cfg.Repo.CS3.ProviderAddr),
|
||||
// )
|
||||
//
|
||||
// err = sut.Init()
|
||||
// assert.NoError(t, err)
|
||||
//
|
||||
// res, err := sut.Add("abcdefg-123", "mikey")
|
||||
// assert.NoError(t, err)
|
||||
// t.Log(res)
|
||||
//
|
||||
// resLookup, err := sut.Lookup("mikey")
|
||||
// assert.NoError(t, err)
|
||||
// t.Log(resLookup)
|
||||
//
|
||||
// err = sut.Update("abcdefg-123", "mikey", "mickeyX")
|
||||
// assert.NoError(t, err)
|
||||
//
|
||||
// searchRes, err := sut.Search("m*")
|
||||
// assert.NoError(t, err)
|
||||
// assert.Len(t, searchRes, 1)
|
||||
// assert.Equal(t, searchRes[0], "abcdefg-123")
|
||||
//
|
||||
// resp, err := sut.Lookup("mikey")
|
||||
// assert.Len(t, resp, 0)
|
||||
// assert.NoError(t, err)
|
||||
//
|
||||
// _ = os.RemoveAll(dataDir)
|
||||
//
|
||||
//}
|
||||
365
accounts/pkg/indexer/index/cs3/unique.go
Normal file
365
accounts/pkg/indexer/index/cs3/unique.go
Normal file
@@ -0,0 +1,365 @@
|
||||
package cs3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/owncloud/ocis/accounts/pkg/storage"
|
||||
|
||||
"github.com/owncloud/ocis/accounts/pkg/config"
|
||||
|
||||
v1beta11 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
"github.com/cs3org/reva/pkg/rgrpc/todo/pool"
|
||||
"github.com/cs3org/reva/pkg/token"
|
||||
"github.com/cs3org/reva/pkg/token/manager/jwt"
|
||||
idxerrs "github.com/owncloud/ocis/accounts/pkg/indexer/errors"
|
||||
"github.com/owncloud/ocis/accounts/pkg/indexer/index"
|
||||
"github.com/owncloud/ocis/accounts/pkg/indexer/option"
|
||||
"github.com/owncloud/ocis/accounts/pkg/indexer/registry"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
// Unique are fields for an index of type non_unique.
|
||||
type Unique struct {
|
||||
caseInsensitive bool
|
||||
indexBy string
|
||||
typeName string
|
||||
filesDir string
|
||||
indexBaseDir string
|
||||
indexRootDir string
|
||||
|
||||
tokenManager token.Manager
|
||||
storageProvider provider.ProviderAPIClient
|
||||
dataProvider dataProviderClient // Used to create and download data via http, bypassing reva upload protocol
|
||||
|
||||
cs3conf *Config
|
||||
}
|
||||
|
||||
// Config represents cs3conf. Should be deprecated in favor of config.Config.
|
||||
type Config struct {
|
||||
ProviderAddr string
|
||||
DataURL string
|
||||
DataPrefix string
|
||||
JWTSecret string
|
||||
ServiceUser config.ServiceUser
|
||||
}
|
||||
|
||||
func init() {
|
||||
registry.IndexConstructorRegistry["cs3"]["unique"] = NewUniqueIndexWithOptions
|
||||
}
|
||||
|
||||
// NewUniqueIndexWithOptions instantiates a new UniqueIndex instance. Init() should be
|
||||
// called afterward to ensure correct on-disk structure.
|
||||
func NewUniqueIndexWithOptions(o ...option.Option) index.Index {
|
||||
opts := &option.Options{}
|
||||
for _, opt := range o {
|
||||
opt(opts)
|
||||
}
|
||||
|
||||
u := &Unique{
|
||||
caseInsensitive: opts.CaseInsensitive,
|
||||
indexBy: opts.IndexBy,
|
||||
typeName: opts.TypeName,
|
||||
filesDir: opts.FilesDir,
|
||||
indexBaseDir: path.Join(opts.DataDir, "index.cs3"),
|
||||
indexRootDir: path.Join(path.Join(opts.DataDir, "index.cs3"), strings.Join([]string{"unique", opts.TypeName, opts.IndexBy}, ".")),
|
||||
cs3conf: &Config{
|
||||
ProviderAddr: opts.ProviderAddr,
|
||||
DataURL: opts.DataURL,
|
||||
DataPrefix: opts.DataPrefix,
|
||||
JWTSecret: opts.JWTSecret,
|
||||
ServiceUser: opts.ServiceUser,
|
||||
},
|
||||
dataProvider: dataProviderClient{
|
||||
baseURL: singleJoiningSlash(opts.DataURL, opts.DataPrefix),
|
||||
client: http.Client{
|
||||
Transport: http.DefaultTransport,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return u
|
||||
}
|
||||
|
||||
// Init initializes a unique index.
|
||||
func (idx *Unique) Init() error {
|
||||
tokenManager, err := jwt.New(map[string]interface{}{
|
||||
"secret": idx.cs3conf.JWTSecret,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
idx.tokenManager = tokenManager
|
||||
|
||||
client, err := pool.GetStorageProviderServiceClient(idx.cs3conf.ProviderAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
idx.storageProvider = client
|
||||
|
||||
ctx := context.Background()
|
||||
tk, err := idx.authenticate(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx = metadata.AppendToOutgoingContext(ctx, token.TokenHeader, tk)
|
||||
|
||||
if err := idx.makeDirIfNotExists(ctx, idx.indexBaseDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := idx.makeDirIfNotExists(ctx, idx.indexRootDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Lookup exact lookup by value.
|
||||
func (idx *Unique) Lookup(v string) ([]string, error) {
|
||||
if idx.caseInsensitive {
|
||||
v = strings.ToLower(v)
|
||||
}
|
||||
searchPath := path.Join(idx.indexRootDir, v)
|
||||
oldname, err := idx.resolveSymlink(searchPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err = &idxerrs.NotFoundErr{TypeName: idx.typeName, Key: idx.indexBy, Value: v}
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return []string{oldname}, nil
|
||||
}
|
||||
|
||||
// Add adds a value to the index, returns the path to the root-document
|
||||
func (idx *Unique) Add(id, v string) (string, error) {
|
||||
if v == "" {
|
||||
return "", nil
|
||||
}
|
||||
if idx.caseInsensitive {
|
||||
v = strings.ToLower(v)
|
||||
}
|
||||
newName := path.Join(idx.indexRootDir, v)
|
||||
if err := idx.createSymlink(id, newName); err != nil {
|
||||
if os.IsExist(err) {
|
||||
return "", &idxerrs.AlreadyExistsErr{TypeName: idx.typeName, Key: idx.indexBy, Value: v}
|
||||
}
|
||||
|
||||
return "", err
|
||||
}
|
||||
|
||||
return newName, nil
|
||||
}
|
||||
|
||||
// Remove a value v from an index.
|
||||
func (idx *Unique) Remove(id string, v string) error {
|
||||
if v == "" {
|
||||
return nil
|
||||
}
|
||||
if idx.caseInsensitive {
|
||||
v = strings.ToLower(v)
|
||||
}
|
||||
searchPath := path.Join(idx.indexRootDir, v)
|
||||
_, err := idx.resolveSymlink(searchPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err = &idxerrs.NotFoundErr{TypeName: idx.typeName, Key: idx.indexBy, Value: v}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
t, err := idx.authenticate(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
deletePath := path.Join("/meta", idx.indexRootDir, v)
|
||||
ctx = metadata.AppendToOutgoingContext(ctx, token.TokenHeader, t)
|
||||
resp, err := idx.storageProvider.Delete(ctx, &provider.DeleteRequest{
|
||||
Ref: &provider.Reference{
|
||||
Spec: &provider.Reference_Path{Path: deletePath},
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO Handle other error codes?
|
||||
if resp.Status.Code == v1beta11.Code_CODE_NOT_FOUND {
|
||||
return &idxerrs.NotFoundErr{}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Update index from <oldV> to <newV>.
|
||||
func (idx *Unique) Update(id, oldV, newV string) error {
|
||||
if idx.caseInsensitive {
|
||||
oldV = strings.ToLower(oldV)
|
||||
newV = strings.ToLower(newV)
|
||||
}
|
||||
|
||||
if err := idx.Remove(id, oldV); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := idx.Add(id, newV); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Search allows for glob search on the index.
|
||||
func (idx *Unique) Search(pattern string) ([]string, error) {
|
||||
if idx.caseInsensitive {
|
||||
pattern = strings.ToLower(pattern)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
t, err := idx.authenticate(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx = metadata.AppendToOutgoingContext(ctx, token.TokenHeader, t)
|
||||
res, err := idx.storageProvider.ListContainer(ctx, &provider.ListContainerRequest{
|
||||
Ref: &provider.Reference{
|
||||
Spec: &provider.Reference_Path{Path: path.Join("/meta", idx.indexRootDir)},
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
searchPath := idx.indexRootDir
|
||||
matches := make([]string, 0)
|
||||
for _, i := range res.GetInfos() {
|
||||
if found, err := filepath.Match(pattern, path.Base(i.Path)); found {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
oldPath, err := idx.resolveSymlink(path.Join(searchPath, path.Base(i.Path)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
matches = append(matches, oldPath)
|
||||
}
|
||||
}
|
||||
|
||||
return matches, nil
|
||||
}
|
||||
|
||||
// CaseInsensitive undocumented.
|
||||
func (idx *Unique) CaseInsensitive() bool {
|
||||
return idx.caseInsensitive
|
||||
}
|
||||
|
||||
// IndexBy undocumented.
|
||||
func (idx *Unique) IndexBy() string {
|
||||
return idx.indexBy
|
||||
}
|
||||
|
||||
// TypeName undocumented.
|
||||
func (idx *Unique) TypeName() string {
|
||||
return idx.typeName
|
||||
}
|
||||
|
||||
// FilesDir undocumented.
|
||||
func (idx *Unique) FilesDir() string {
|
||||
return idx.filesDir
|
||||
}
|
||||
|
||||
func (idx *Unique) createSymlink(oldname, newname string) error {
|
||||
t, err := idx.authenticate(context.TODO())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := idx.resolveSymlink(newname); err == nil {
|
||||
return os.ErrExist
|
||||
}
|
||||
|
||||
resp, err := idx.dataProvider.put(newname, strings.NewReader(oldname), t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = resp.Body.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (idx *Unique) resolveSymlink(name string) (string, error) {
|
||||
t, err := idx.authenticate(context.TODO())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
resp, err := idx.dataProvider.get(name, t)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
if resp.StatusCode == http.StatusNotFound {
|
||||
return "", os.ErrNotExist
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("could not resolve symlink %s, got status %v", name, resp.StatusCode)
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err = resp.Body.Close(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(b), err
|
||||
}
|
||||
|
||||
func (idx *Unique) makeDirIfNotExists(ctx context.Context, folder string) error {
|
||||
return storage.MakeDirIfNotExist(ctx, idx.storageProvider, folder)
|
||||
}
|
||||
|
||||
func (idx *Unique) authenticate(ctx context.Context) (token string, err error) {
|
||||
return storage.AuthenticateCS3(ctx, idx.cs3conf.ServiceUser, idx.tokenManager)
|
||||
}
|
||||
|
||||
func (idx *Unique) getAuthenticatedContext(ctx context.Context) (context.Context, error) {
|
||||
t, err := idx.authenticate(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx = metadata.AppendToOutgoingContext(ctx, token.TokenHeader, t)
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
// Delete deletes the index folder from its storage.
|
||||
func (idx *Unique) Delete() error {
|
||||
ctx, err := idx.getAuthenticatedContext(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return deleteIndexRoot(ctx, idx.storageProvider, idx.indexRootDir)
|
||||
}
|
||||
107
accounts/pkg/indexer/index/cs3/unique_test.go
Normal file
107
accounts/pkg/indexer/index/cs3/unique_test.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package cs3
|
||||
|
||||
//
|
||||
//import (
|
||||
// "os"
|
||||
// "path"
|
||||
// "testing"
|
||||
//
|
||||
// "github.com/owncloud/ocis/accounts/pkg/config"
|
||||
// "github.com/owncloud/ocis/accounts/pkg/indexer/option"
|
||||
// . "github.com/owncloud/ocis/accounts/pkg/indexer/test"
|
||||
// "github.com/stretchr/testify/assert"
|
||||
//)
|
||||
//
|
||||
//func TestCS3UniqueIndex_FakeSymlink(t *testing.T) {
|
||||
// dataDir, err := WriteIndexTestData(Data, "ID", cs3RootFolder)
|
||||
// assert.NoError(t, err)
|
||||
// cfg := config.Config{
|
||||
// Repo: config.Repo{
|
||||
// Disk: config.Disk{
|
||||
// Path: "",
|
||||
// },
|
||||
// CS3: config.CS3{
|
||||
// ProviderAddr: "0.0.0.0:9215",
|
||||
// DataURL: "http://localhost:9216",
|
||||
// DataPrefix: "data",
|
||||
// JWTSecret: "Pive-Fumkiu4",
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
//
|
||||
// sut := NewUniqueIndexWithOptions(
|
||||
// option.WithTypeName(GetTypeFQN(User{})),
|
||||
// option.WithIndexBy("UserName"),
|
||||
// option.WithFilesDir(path.Join(cfg.Repo.Disk.Path, "/meta")),
|
||||
// option.WithDataDir(cfg.Repo.Disk.Path),
|
||||
// option.WithDataURL(cfg.Repo.CS3.DataURL),
|
||||
// option.WithDataPrefix(cfg.Repo.CS3.DataPrefix),
|
||||
// option.WithJWTSecret(cfg.Repo.CS3.JWTSecret),
|
||||
// option.WithProviderAddr(cfg.Repo.CS3.ProviderAddr),
|
||||
// )
|
||||
//
|
||||
// err = sut.Init()
|
||||
// assert.NoError(t, err)
|
||||
//
|
||||
// res, err := sut.Add("abcdefg-123", "mikey")
|
||||
// assert.NoError(t, err)
|
||||
// t.Log(res)
|
||||
//
|
||||
// resLookup, err := sut.Lookup("mikey")
|
||||
// assert.NoError(t, err)
|
||||
// t.Log(resLookup)
|
||||
//
|
||||
// err = sut.Update("abcdefg-123", "mikey", "mickeyX")
|
||||
// assert.NoError(t, err)
|
||||
//
|
||||
// searchRes, err := sut.Search("m*")
|
||||
// assert.NoError(t, err)
|
||||
// assert.Len(t, searchRes, 1)
|
||||
// assert.Equal(t, searchRes[0], "abcdefg-123")
|
||||
//
|
||||
// _ = os.RemoveAll(dataDir)
|
||||
//}
|
||||
//
|
||||
//func TestCS3UniqueIndexSearch(t *testing.T) {
|
||||
// dataDir, err := WriteIndexTestData(Data, "ID", cs3RootFolder)
|
||||
// assert.NoError(t, err)
|
||||
// cfg := config.Config{
|
||||
// Repo: config.Repo{
|
||||
// Disk: config.Disk{
|
||||
// Path: "",
|
||||
// },
|
||||
// CS3: config.CS3{
|
||||
// ProviderAddr: "0.0.0.0:9215",
|
||||
// DataURL: "http://localhost:9216",
|
||||
// DataPrefix: "data",
|
||||
// JWTSecret: "Pive-Fumkiu4",
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
//
|
||||
// sut := NewUniqueIndexWithOptions(
|
||||
// option.WithTypeName(GetTypeFQN(User{})),
|
||||
// option.WithIndexBy("UserName"),
|
||||
// option.WithFilesDir(path.Join(cfg.Repo.Disk.Path, "/meta")),
|
||||
// option.WithDataDir(cfg.Repo.Disk.Path),
|
||||
// option.WithDataURL(cfg.Repo.CS3.DataURL),
|
||||
// option.WithDataPrefix(cfg.Repo.CS3.DataPrefix),
|
||||
// option.WithJWTSecret(cfg.Repo.CS3.JWTSecret),
|
||||
// option.WithProviderAddr(cfg.Repo.CS3.ProviderAddr),
|
||||
// )
|
||||
//
|
||||
// err = sut.Init()
|
||||
// assert.NoError(t, err)
|
||||
//
|
||||
// _, err = sut.Add("hijklmn-456", "mikey")
|
||||
// assert.NoError(t, err)
|
||||
//
|
||||
// _, err = sut.Add("ewf4ofk-555", "jacky")
|
||||
// assert.NoError(t, err)
|
||||
//
|
||||
// res, err := sut.Search("*y")
|
||||
// assert.NoError(t, err)
|
||||
// t.Log(res)
|
||||
//
|
||||
// _ = os.RemoveAll(dataDir)
|
||||
//}
|
||||
226
accounts/pkg/indexer/index/disk/autoincrement.go
Normal file
226
accounts/pkg/indexer/index/disk/autoincrement.go
Normal file
@@ -0,0 +1,226 @@
|
||||
package disk
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
idxerrs "github.com/owncloud/ocis/accounts/pkg/indexer/errors"
|
||||
|
||||
"github.com/owncloud/ocis/accounts/pkg/indexer/index"
|
||||
"github.com/owncloud/ocis/accounts/pkg/indexer/option"
|
||||
"github.com/owncloud/ocis/accounts/pkg/indexer/registry"
|
||||
)
|
||||
|
||||
// Autoincrement are fields for an index of type autoincrement.
|
||||
type Autoincrement struct {
|
||||
indexBy string
|
||||
typeName string
|
||||
filesDir string
|
||||
indexBaseDir string
|
||||
indexRootDir string
|
||||
|
||||
bound *option.Bound
|
||||
}
|
||||
|
||||
func init() {
|
||||
registry.IndexConstructorRegistry["disk"]["autoincrement"] = NewAutoincrementIndex
|
||||
}
|
||||
|
||||
// NewAutoincrementIndex instantiates a new AutoincrementIndex instance. Init() MUST be called upon instantiation.
|
||||
func NewAutoincrementIndex(o ...option.Option) index.Index {
|
||||
opts := &option.Options{}
|
||||
for _, opt := range o {
|
||||
opt(opts)
|
||||
}
|
||||
|
||||
if opts.Entity == nil {
|
||||
panic("invalid autoincrement index: configured without entity")
|
||||
}
|
||||
|
||||
k, err := getKind(opts.Entity, opts.IndexBy)
|
||||
if !isValidKind(k) || err != nil {
|
||||
panic("invalid autoincrement index: configured on non-numeric field")
|
||||
}
|
||||
|
||||
return &Autoincrement{
|
||||
indexBy: opts.IndexBy,
|
||||
typeName: opts.TypeName,
|
||||
filesDir: opts.FilesDir,
|
||||
bound: opts.Bound,
|
||||
indexBaseDir: path.Join(opts.DataDir, "index.disk"),
|
||||
indexRootDir: path.Join(path.Join(opts.DataDir, "index.disk"), strings.Join([]string{"autoincrement", opts.TypeName, opts.IndexBy}, ".")),
|
||||
}
|
||||
}
|
||||
|
||||
// Init initializes an autoincrement index.
|
||||
func (idx *Autoincrement) Init() error {
|
||||
if _, err := os.Stat(idx.filesDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(idx.indexRootDir, 0777); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Lookup exact lookup by value.
|
||||
func (idx *Autoincrement) Lookup(v string) ([]string, error) {
|
||||
searchPath := path.Join(idx.indexRootDir, v)
|
||||
if err := isValidSymlink(searchPath); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err = &idxerrs.NotFoundErr{TypeName: idx.typeName, Key: idx.indexBy, Value: v}
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p, err := os.Readlink(searchPath)
|
||||
if err != nil {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
return []string{p}, err
|
||||
}
|
||||
|
||||
// Add a new value to the index.
|
||||
func (idx *Autoincrement) Add(id, v string) (string, error) {
|
||||
nextID, err := idx.next()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
oldName := filepath.Join(idx.filesDir, id)
|
||||
var newName string
|
||||
if v == "" {
|
||||
newName = filepath.Join(idx.indexRootDir, strconv.Itoa(nextID))
|
||||
} else {
|
||||
newName = filepath.Join(idx.indexRootDir, v)
|
||||
}
|
||||
err = os.Symlink(oldName, newName)
|
||||
if errors.Is(err, os.ErrExist) {
|
||||
return "", &idxerrs.AlreadyExistsErr{TypeName: idx.typeName, Key: idx.indexBy, Value: v}
|
||||
}
|
||||
|
||||
return newName, err
|
||||
}
|
||||
|
||||
// Remove a value v from an index.
|
||||
func (idx *Autoincrement) Remove(id string, v string) error {
|
||||
if v == "" {
|
||||
return nil
|
||||
}
|
||||
searchPath := path.Join(idx.indexRootDir, v)
|
||||
return os.Remove(searchPath)
|
||||
}
|
||||
|
||||
// Update index from <oldV> to <newV>.
|
||||
func (idx *Autoincrement) Update(id, oldV, newV string) error {
|
||||
oldPath := path.Join(idx.indexRootDir, oldV)
|
||||
if err := isValidSymlink(oldPath); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return &idxerrs.NotFoundErr{TypeName: idx.TypeName(), Key: idx.IndexBy(), Value: oldV}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
newPath := path.Join(idx.indexRootDir, newV)
|
||||
err := isValidSymlink(newPath)
|
||||
if err == nil {
|
||||
return &idxerrs.AlreadyExistsErr{TypeName: idx.typeName, Key: idx.indexBy, Value: newV}
|
||||
}
|
||||
|
||||
if os.IsNotExist(err) {
|
||||
err = os.Rename(oldPath, newPath)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Search allows for glob search on the index.
|
||||
func (idx *Autoincrement) Search(pattern string) ([]string, error) {
|
||||
paths, err := filepath.Glob(path.Join(idx.indexRootDir, pattern))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(paths) == 0 {
|
||||
return nil, &idxerrs.NotFoundErr{TypeName: idx.typeName, Key: idx.indexBy, Value: pattern}
|
||||
}
|
||||
|
||||
res := make([]string, 0)
|
||||
for _, p := range paths {
|
||||
if err := isValidSymlink(p); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
src, err := os.Readlink(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res = append(res, src)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// CaseInsensitive undocumented.
|
||||
func (idx *Autoincrement) CaseInsensitive() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IndexBy undocumented.
|
||||
func (idx *Autoincrement) IndexBy() string {
|
||||
return idx.indexBy
|
||||
}
|
||||
|
||||
// TypeName undocumented.
|
||||
func (idx *Autoincrement) TypeName() string {
|
||||
return idx.typeName
|
||||
}
|
||||
|
||||
// FilesDir undocumented.
|
||||
func (idx *Autoincrement) FilesDir() string {
|
||||
return idx.filesDir
|
||||
}
|
||||
|
||||
func (idx *Autoincrement) next() (int, error) {
|
||||
files, err := readDir(idx.indexRootDir)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
if len(files) == 0 {
|
||||
return int(idx.bound.Lower), nil
|
||||
}
|
||||
|
||||
latest, err := lastValueFromTree(files)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
if int64(latest) < idx.bound.Lower {
|
||||
return int(idx.bound.Lower), nil
|
||||
}
|
||||
|
||||
return latest + 1, nil
|
||||
}
|
||||
|
||||
// Delete deletes the index root folder from the configured storage.
|
||||
func (idx *Autoincrement) Delete() error {
|
||||
return os.RemoveAll(idx.indexRootDir)
|
||||
}
|
||||
|
||||
func lastValueFromTree(files []os.FileInfo) (int, error) {
|
||||
latest, err := strconv.Atoi(path.Base(files[len(files)-1].Name()))
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return latest, nil
|
||||
}
|
||||
299
accounts/pkg/indexer/index/disk/autoincrement_test.go
Normal file
299
accounts/pkg/indexer/index/disk/autoincrement_test.go
Normal file
@@ -0,0 +1,299 @@
|
||||
package disk
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/owncloud/ocis/accounts/pkg/indexer/option"
|
||||
//. "github.com/owncloud/ocis/accounts/pkg/indexer/test"
|
||||
"github.com/owncloud/ocis/accounts/pkg/proto/v0"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestIsValidKind(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
panics bool
|
||||
name string
|
||||
indexBy string
|
||||
entity struct {
|
||||
Number int
|
||||
Name string
|
||||
NumberFloat float32
|
||||
}
|
||||
}{
|
||||
{
|
||||
name: "valid autoincrement index",
|
||||
panics: false,
|
||||
indexBy: "Number",
|
||||
entity: struct {
|
||||
Number int
|
||||
Name string
|
||||
NumberFloat float32
|
||||
}{
|
||||
Name: "tesy-mc-testace",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create autoincrement index on a non-existing field",
|
||||
panics: true,
|
||||
indexBy: "Age",
|
||||
entity: struct {
|
||||
Number int
|
||||
Name string
|
||||
NumberFloat float32
|
||||
}{
|
||||
Name: "tesy-mc-testace",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "attempt to create an autoincrement index with no entity",
|
||||
panics: true,
|
||||
indexBy: "Age",
|
||||
},
|
||||
{
|
||||
name: "create autoincrement index on a non-numeric field",
|
||||
panics: true,
|
||||
indexBy: "Name",
|
||||
entity: struct {
|
||||
Number int
|
||||
Name string
|
||||
NumberFloat float32
|
||||
}{
|
||||
Name: "tesy-mc-testace",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, scenario := range scenarios {
|
||||
t.Run(scenario.name, func(t *testing.T) {
|
||||
if scenario.panics {
|
||||
assert.Panics(t, func() {
|
||||
_ = NewAutoincrementIndex(
|
||||
option.WithEntity(scenario.entity),
|
||||
option.WithIndexBy(scenario.indexBy),
|
||||
)
|
||||
})
|
||||
} else {
|
||||
assert.NotPanics(t, func() {
|
||||
_ = NewAutoincrementIndex(
|
||||
option.WithEntity(scenario.entity),
|
||||
option.WithIndexBy(scenario.indexBy),
|
||||
)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNext(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
name string
|
||||
expected int
|
||||
indexBy string
|
||||
entity interface{}
|
||||
}{
|
||||
{
|
||||
name: "get next value",
|
||||
expected: 0,
|
||||
indexBy: "Number",
|
||||
entity: struct {
|
||||
Number int
|
||||
Name string
|
||||
NumberFloat float32
|
||||
}{
|
||||
Name: "tesy-mc-testace",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, scenario := range scenarios {
|
||||
t.Run(scenario.name, func(t *testing.T) {
|
||||
tmpDir, err := createTmpDirStr()
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = os.MkdirAll(filepath.Join(tmpDir, "data"), 0777)
|
||||
assert.NoError(t, err)
|
||||
|
||||
i := NewAutoincrementIndex(
|
||||
option.WithBounds(&option.Bound{
|
||||
Lower: 0,
|
||||
Upper: 0,
|
||||
}),
|
||||
option.WithDataDir(tmpDir),
|
||||
option.WithFilesDir(filepath.Join(tmpDir, "data")),
|
||||
option.WithEntity(scenario.entity),
|
||||
option.WithTypeName("LambdaType"),
|
||||
option.WithIndexBy(scenario.indexBy),
|
||||
)
|
||||
|
||||
err = i.Init()
|
||||
assert.NoError(t, err)
|
||||
|
||||
tmpFile, err := os.Create(filepath.Join(tmpDir, "data", "test-example"))
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, tmpFile.Close())
|
||||
|
||||
oldName, err := i.Add("test-example", "")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "0", filepath.Base(oldName))
|
||||
|
||||
oldName, err = i.Add("test-example", "")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "1", filepath.Base(oldName))
|
||||
|
||||
oldName, err = i.Add("test-example", "")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "2", filepath.Base(oldName))
|
||||
t.Log(oldName)
|
||||
|
||||
_ = os.RemoveAll(tmpDir)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLowerBound(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
name string
|
||||
expected int
|
||||
indexBy string
|
||||
entity interface{}
|
||||
}{
|
||||
{
|
||||
name: "get next value with a lower bound specified",
|
||||
expected: 0,
|
||||
indexBy: "Number",
|
||||
entity: struct {
|
||||
Number int
|
||||
Name string
|
||||
NumberFloat float32
|
||||
}{
|
||||
Name: "tesy-mc-testace",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, scenario := range scenarios {
|
||||
t.Run(scenario.name, func(t *testing.T) {
|
||||
tmpDir, err := createTmpDirStr()
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = os.MkdirAll(filepath.Join(tmpDir, "data"), 0777)
|
||||
assert.NoError(t, err)
|
||||
|
||||
i := NewAutoincrementIndex(
|
||||
option.WithBounds(&option.Bound{
|
||||
Lower: 1000,
|
||||
}),
|
||||
option.WithDataDir(tmpDir),
|
||||
option.WithFilesDir(filepath.Join(tmpDir, "data")),
|
||||
option.WithEntity(scenario.entity),
|
||||
option.WithTypeName("LambdaType"),
|
||||
option.WithIndexBy(scenario.indexBy),
|
||||
)
|
||||
|
||||
err = i.Init()
|
||||
assert.NoError(t, err)
|
||||
|
||||
tmpFile, err := os.Create(filepath.Join(tmpDir, "data", "test-example"))
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, tmpFile.Close())
|
||||
|
||||
oldName, err := i.Add("test-example", "")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "1000", filepath.Base(oldName))
|
||||
|
||||
oldName, err = i.Add("test-example", "")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "1001", filepath.Base(oldName))
|
||||
|
||||
oldName, err = i.Add("test-example", "")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "1002", filepath.Base(oldName))
|
||||
t.Log(oldName)
|
||||
|
||||
_ = os.RemoveAll(tmpDir)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdd(t *testing.T) {
|
||||
tmpDir, err := createTmpDirStr()
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = os.MkdirAll(filepath.Join(tmpDir, "data"), 0777)
|
||||
assert.NoError(t, err)
|
||||
|
||||
tmpFile, err := os.Create(filepath.Join(tmpDir, "data", "test-example"))
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, tmpFile.Close())
|
||||
|
||||
i := NewAutoincrementIndex(
|
||||
option.WithBounds(&option.Bound{
|
||||
Lower: 0,
|
||||
Upper: 0,
|
||||
}),
|
||||
option.WithDataDir(tmpDir),
|
||||
option.WithFilesDir(filepath.Join(tmpDir, "data")),
|
||||
option.WithEntity(&proto.Account{}),
|
||||
option.WithTypeName("owncloud.Account"),
|
||||
option.WithIndexBy("UidNumber"),
|
||||
)
|
||||
|
||||
err = i.Init()
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = i.Add("test-example", "")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAdd(b *testing.B) {
|
||||
tmpDir, err := createTmpDirStr()
|
||||
assert.NoError(b, err)
|
||||
|
||||
err = os.MkdirAll(filepath.Join(tmpDir, "data"), 0777)
|
||||
assert.NoError(b, err)
|
||||
|
||||
tmpFile, err := os.Create(filepath.Join(tmpDir, "data", "test-example"))
|
||||
assert.NoError(b, err)
|
||||
assert.NoError(b, tmpFile.Close())
|
||||
|
||||
i := NewAutoincrementIndex(
|
||||
option.WithBounds(&option.Bound{
|
||||
Lower: 0,
|
||||
Upper: 0,
|
||||
}),
|
||||
option.WithDataDir(tmpDir),
|
||||
option.WithFilesDir(filepath.Join(tmpDir, "data")),
|
||||
option.WithEntity(struct {
|
||||
Number int
|
||||
Name string
|
||||
NumberFloat float32
|
||||
}{}),
|
||||
option.WithTypeName("LambdaType"),
|
||||
option.WithIndexBy("Number"),
|
||||
)
|
||||
|
||||
err = i.Init()
|
||||
assert.NoError(b, err)
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
_, err := i.Add("test-example", "")
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
assert.NoError(b, err)
|
||||
}
|
||||
}
|
||||
|
||||
func createTmpDirStr() (string, error) {
|
||||
name, err := ioutil.TempDir("/var/tmp", "testfiles-*")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return name, nil
|
||||
}
|
||||
52
accounts/pkg/indexer/index/disk/helper.go
Normal file
52
accounts/pkg/indexer/index/disk/helper.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package disk
|
||||
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var (
|
||||
validKinds = []reflect.Kind{
|
||||
reflect.Int,
|
||||
reflect.Int8,
|
||||
reflect.Int16,
|
||||
reflect.Int32,
|
||||
reflect.Int64,
|
||||
}
|
||||
)
|
||||
|
||||
// verifies an autoincrement field kind on the target struct.
|
||||
func isValidKind(k reflect.Kind) bool {
|
||||
for _, v := range validKinds {
|
||||
if k == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getKind(i interface{}, field string) (reflect.Kind, error) {
|
||||
r := reflect.ValueOf(i)
|
||||
return reflect.Indirect(r).FieldByName(field).Kind(), nil
|
||||
}
|
||||
|
||||
// readDir is an implementation of os.ReadDir but with different sorting.
|
||||
func readDir(dirname string) ([]os.FileInfo, error) {
|
||||
f, err := os.Open(dirname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list, err := f.Readdir(-1)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sort.Slice(list, func(i, j int) bool {
|
||||
a, _ := strconv.Atoi(list[i].Name())
|
||||
b, _ := strconv.Atoi(list[j].Name())
|
||||
return a < b
|
||||
})
|
||||
return list, nil
|
||||
}
|
||||
240
accounts/pkg/indexer/index/disk/non_unique.go
Normal file
240
accounts/pkg/indexer/index/disk/non_unique.go
Normal file
@@ -0,0 +1,240 @@
|
||||
package disk
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
idxerrs "github.com/owncloud/ocis/accounts/pkg/indexer/errors"
|
||||
"github.com/owncloud/ocis/accounts/pkg/indexer/index"
|
||||
"github.com/owncloud/ocis/accounts/pkg/indexer/option"
|
||||
"github.com/owncloud/ocis/accounts/pkg/indexer/registry"
|
||||
)
|
||||
|
||||
// NonUnique is able to index an document by a key which might contain non-unique values
|
||||
//
|
||||
// /var/tmp/testfiles-395764020/index.disk/PetByColor/
|
||||
// ├── Brown
|
||||
// │ └── rebef-123 -> /var/tmp/testfiles-395764020/pets/rebef-123
|
||||
// ├── Green
|
||||
// │ ├── goefe-789 -> /var/tmp/testfiles-395764020/pets/goefe-789
|
||||
// │ └── xadaf-189 -> /var/tmp/testfiles-395764020/pets/xadaf-189
|
||||
// └── White
|
||||
// └── wefwe-456 -> /var/tmp/testfiles-395764020/pets/wefwe-456
|
||||
type NonUnique struct {
|
||||
caseInsensitive bool
|
||||
indexBy string
|
||||
typeName string
|
||||
filesDir string
|
||||
indexBaseDir string
|
||||
indexRootDir string
|
||||
}
|
||||
|
||||
func init() {
|
||||
registry.IndexConstructorRegistry["disk"]["non_unique"] = NewNonUniqueIndexWithOptions
|
||||
}
|
||||
|
||||
// NewNonUniqueIndexWithOptions instantiates a new UniqueIndex instance. Init() should be
|
||||
// called afterward to ensure correct on-disk structure.
|
||||
func NewNonUniqueIndexWithOptions(o ...option.Option) index.Index {
|
||||
opts := &option.Options{}
|
||||
for _, opt := range o {
|
||||
opt(opts)
|
||||
}
|
||||
|
||||
return &NonUnique{
|
||||
caseInsensitive: opts.CaseInsensitive,
|
||||
indexBy: opts.IndexBy,
|
||||
typeName: opts.TypeName,
|
||||
filesDir: opts.FilesDir,
|
||||
indexBaseDir: path.Join(opts.DataDir, "index.disk"),
|
||||
indexRootDir: path.Join(path.Join(opts.DataDir, "index.disk"), strings.Join([]string{"non_unique", opts.TypeName, opts.IndexBy}, ".")),
|
||||
}
|
||||
}
|
||||
|
||||
// Init initializes a unique index.
|
||||
func (idx *NonUnique) Init() error {
|
||||
if _, err := os.Stat(idx.filesDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(idx.indexRootDir, 0777); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Lookup exact lookup by value.
|
||||
func (idx *NonUnique) Lookup(v string) ([]string, error) {
|
||||
if idx.caseInsensitive {
|
||||
v = strings.ToLower(v)
|
||||
}
|
||||
searchPath := path.Join(idx.indexRootDir, v)
|
||||
fi, err := ioutil.ReadDir(searchPath)
|
||||
if os.IsNotExist(err) {
|
||||
return []string{}, &idxerrs.NotFoundErr{TypeName: idx.typeName, Key: idx.indexBy, Value: v}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
|
||||
var ids []string = nil
|
||||
for _, f := range fi {
|
||||
ids = append(ids, f.Name())
|
||||
}
|
||||
|
||||
if len(ids) == 0 {
|
||||
return []string{}, &idxerrs.NotFoundErr{TypeName: idx.typeName, Key: idx.indexBy, Value: v}
|
||||
}
|
||||
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
// Add adds a value to the index, returns the path to the root-document
|
||||
func (idx *NonUnique) Add(id, v string) (string, error) {
|
||||
if v == "" {
|
||||
return "", nil
|
||||
}
|
||||
if idx.caseInsensitive {
|
||||
v = strings.ToLower(v)
|
||||
}
|
||||
oldName := path.Join(idx.filesDir, id)
|
||||
newName := path.Join(idx.indexRootDir, v, id)
|
||||
|
||||
if err := os.MkdirAll(path.Join(idx.indexRootDir, v), 0777); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err := os.Symlink(oldName, newName)
|
||||
if errors.Is(err, os.ErrExist) {
|
||||
return "", &idxerrs.AlreadyExistsErr{TypeName: idx.typeName, Key: idx.indexBy, Value: v}
|
||||
}
|
||||
|
||||
return newName, err
|
||||
|
||||
}
|
||||
|
||||
// Remove a value v from an index.
|
||||
func (idx *NonUnique) Remove(id string, v string) error {
|
||||
if v == "" {
|
||||
return nil
|
||||
}
|
||||
if idx.caseInsensitive {
|
||||
v = strings.ToLower(v)
|
||||
}
|
||||
res, err := filepath.Glob(path.Join(idx.indexRootDir, "/*/", id))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, p := range res {
|
||||
if err := os.Remove(p); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Remove value directory if it is empty
|
||||
valueDir := path.Join(idx.indexRootDir, v)
|
||||
fi, err := ioutil.ReadDir(valueDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(fi) == 0 {
|
||||
if err := os.RemoveAll(valueDir); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update index from <oldV> to <newV>.
|
||||
func (idx *NonUnique) Update(id, oldV, newV string) (err error) {
|
||||
if idx.caseInsensitive {
|
||||
oldV = strings.ToLower(oldV)
|
||||
newV = strings.ToLower(newV)
|
||||
}
|
||||
oldDir := path.Join(idx.indexRootDir, oldV)
|
||||
oldPath := path.Join(oldDir, id)
|
||||
newDir := path.Join(idx.indexRootDir, newV)
|
||||
newPath := path.Join(newDir, id)
|
||||
|
||||
if _, err = os.Stat(oldPath); os.IsNotExist(err) {
|
||||
return &idxerrs.NotFoundErr{TypeName: idx.typeName, Key: idx.indexBy, Value: oldV}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = os.MkdirAll(newDir, 0777); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = os.Rename(oldPath, newPath); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
di, err := ioutil.ReadDir(oldDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(di) == 0 {
|
||||
err = os.RemoveAll(oldDir)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
// Search allows for glob search on the index.
|
||||
func (idx *NonUnique) Search(pattern string) ([]string, error) {
|
||||
if idx.caseInsensitive {
|
||||
pattern = strings.ToLower(pattern)
|
||||
}
|
||||
paths, err := filepath.Glob(path.Join(idx.indexRootDir, pattern, "*"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(paths) == 0 {
|
||||
return nil, &idxerrs.NotFoundErr{TypeName: idx.typeName, Key: idx.indexBy, Value: pattern}
|
||||
}
|
||||
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
// CaseInsensitive undocumented.
|
||||
func (idx *NonUnique) CaseInsensitive() bool {
|
||||
return idx.caseInsensitive
|
||||
}
|
||||
|
||||
// IndexBy undocumented.
|
||||
func (idx *NonUnique) IndexBy() string {
|
||||
return idx.indexBy
|
||||
}
|
||||
|
||||
// TypeName undocumented.
|
||||
func (idx *NonUnique) TypeName() string {
|
||||
return idx.typeName
|
||||
}
|
||||
|
||||
// FilesDir undocumented.
|
||||
func (idx *NonUnique) FilesDir() string {
|
||||
return idx.filesDir
|
||||
}
|
||||
|
||||
// Delete deletes the index folder from its storage.
|
||||
func (idx *NonUnique) Delete() error {
|
||||
return os.RemoveAll(idx.indexRootDir)
|
||||
}
|
||||
113
accounts/pkg/indexer/index/disk/non_unique_test.go
Normal file
113
accounts/pkg/indexer/index/disk/non_unique_test.go
Normal file
@@ -0,0 +1,113 @@
|
||||
package disk
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/owncloud/ocis/accounts/pkg/config"
|
||||
"github.com/owncloud/ocis/accounts/pkg/indexer/errors"
|
||||
"github.com/owncloud/ocis/accounts/pkg/indexer/index"
|
||||
"github.com/owncloud/ocis/accounts/pkg/indexer/option"
|
||||
. "github.com/owncloud/ocis/accounts/pkg/indexer/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNonUniqueIndexAdd(t *testing.T) {
|
||||
sut, dataPath := getNonUniqueIdxSut(t, Pet{}, "Color")
|
||||
|
||||
ids, err := sut.Lookup("Green")
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, []string{"goefe-789", "xadaf-189"}, ids)
|
||||
|
||||
ids, err = sut.Lookup("White")
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, []string{"wefwe-456"}, ids)
|
||||
|
||||
ids, err = sut.Lookup("Cyan")
|
||||
assert.Error(t, err)
|
||||
assert.EqualValues(t, []string{}, ids)
|
||||
|
||||
_ = os.RemoveAll(dataPath)
|
||||
|
||||
}
|
||||
|
||||
func TestNonUniqueIndexUpdate(t *testing.T) {
|
||||
sut, dataPath := getNonUniqueIdxSut(t, Pet{}, "Color")
|
||||
|
||||
err := sut.Update("goefe-789", "Green", "Black")
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = sut.Update("xadaf-189", "Green", "Black")
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.DirExists(t, path.Join(dataPath, fmt.Sprintf("index.disk/non_unique.%v.Color/Black", GetTypeFQN(Pet{}))))
|
||||
assert.NoDirExists(t, path.Join(dataPath, fmt.Sprintf("index.disk/non_unique.%v.Color/Green", GetTypeFQN(Pet{}))))
|
||||
|
||||
_ = os.RemoveAll(dataPath)
|
||||
}
|
||||
|
||||
func TestNonUniqueIndexDelete(t *testing.T) {
|
||||
sut, dataPath := getNonUniqueIdxSut(t, Pet{}, "Color")
|
||||
assert.FileExists(t, path.Join(dataPath, fmt.Sprintf("index.disk/non_unique.%v.Color/Green/goefe-789", GetTypeFQN(Pet{}))))
|
||||
|
||||
err := sut.Remove("goefe-789", "Green")
|
||||
assert.NoError(t, err)
|
||||
assert.NoFileExists(t, path.Join(dataPath, fmt.Sprintf("index.disk/non_unique.%v.Color/Green/goefe-789", GetTypeFQN(Pet{}))))
|
||||
assert.FileExists(t, path.Join(dataPath, fmt.Sprintf("index.disk/non_unique.%v.Color/Green/xadaf-189", GetTypeFQN(Pet{}))))
|
||||
|
||||
_ = os.RemoveAll(dataPath)
|
||||
}
|
||||
|
||||
func TestNonUniqueIndexSearch(t *testing.T) {
|
||||
sut, dataPath := getNonUniqueIdxSut(t, Pet{}, "Email")
|
||||
|
||||
res, err := sut.Search("Gr*")
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, res, 2)
|
||||
|
||||
assert.Equal(t, "goefe-789", path.Base(res[0]))
|
||||
assert.Equal(t, "xadaf-189", path.Base(res[1]))
|
||||
|
||||
_, err = sut.Search("does-not-exist@example.com")
|
||||
assert.Error(t, err)
|
||||
assert.IsType(t, &errors.NotFoundErr{}, err)
|
||||
|
||||
_ = os.RemoveAll(dataPath)
|
||||
}
|
||||
|
||||
// entity: used to get the fully qualified name for the index root path.
|
||||
func getNonUniqueIdxSut(t *testing.T, entity interface{}, indexBy string) (index.Index, string) {
|
||||
dataPath, _ := WriteIndexTestData(Data, "ID", "")
|
||||
cfg := config.Config{
|
||||
Repo: config.Repo{
|
||||
Disk: config.Disk{
|
||||
Path: dataPath,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
sut := NewNonUniqueIndexWithOptions(
|
||||
option.WithTypeName(GetTypeFQN(entity)),
|
||||
option.WithIndexBy(indexBy),
|
||||
option.WithFilesDir(path.Join(cfg.Repo.Disk.Path, "pets")),
|
||||
option.WithDataDir(cfg.Repo.Disk.Path),
|
||||
)
|
||||
err := sut.Init()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, u := range Data["pets"] {
|
||||
pkVal := ValueOf(u, "ID")
|
||||
idxByVal := ValueOf(u, "Color")
|
||||
_, err := sut.Add(pkVal, idxByVal)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
return sut, dataPath
|
||||
}
|
||||
227
accounts/pkg/indexer/index/disk/unique.go
Normal file
227
accounts/pkg/indexer/index/disk/unique.go
Normal file
@@ -0,0 +1,227 @@
|
||||
package disk
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
idxerrs "github.com/owncloud/ocis/accounts/pkg/indexer/errors"
|
||||
"github.com/owncloud/ocis/accounts/pkg/indexer/index"
|
||||
"github.com/owncloud/ocis/accounts/pkg/indexer/option"
|
||||
"github.com/owncloud/ocis/accounts/pkg/indexer/registry"
|
||||
)
|
||||
|
||||
// Unique ensures that only one document of the same type and key-value combination can exist in the index.
|
||||
//
|
||||
// Modeled by creating a indexer-folder per entity and key with symlinks which point to respective documents which contain
|
||||
// the link-filename as value.
|
||||
//
|
||||
// Directory Layout
|
||||
//
|
||||
// /var/data/index.disk/UniqueUserByEmail/
|
||||
// ├── jacky@example.com -> /var/data/users/ewf4ofk-555
|
||||
// ├── jones@example.com -> /var/data/users/rulan54-777
|
||||
// └── mikey@example.com -> /var/data/users/abcdefg-123
|
||||
//
|
||||
// Example user
|
||||
//
|
||||
// {
|
||||
// "Id": "ewf4ofk-555",
|
||||
// "UserName": "jacky",
|
||||
// "Email": "jacky@example.com"
|
||||
// }
|
||||
//
|
||||
type Unique struct {
|
||||
caseInsensitive bool
|
||||
indexBy string
|
||||
typeName string
|
||||
filesDir string
|
||||
indexBaseDir string
|
||||
indexRootDir string
|
||||
}
|
||||
|
||||
func init() {
|
||||
registry.IndexConstructorRegistry["disk"]["unique"] = NewUniqueIndexWithOptions
|
||||
}
|
||||
|
||||
// NewUniqueIndexWithOptions instantiates a new UniqueIndex instance. Init() should be
|
||||
// called afterward to ensure correct on-disk structure.
|
||||
func NewUniqueIndexWithOptions(o ...option.Option) index.Index {
|
||||
opts := &option.Options{}
|
||||
for _, opt := range o {
|
||||
opt(opts)
|
||||
}
|
||||
|
||||
return &Unique{
|
||||
caseInsensitive: opts.CaseInsensitive,
|
||||
indexBy: opts.IndexBy,
|
||||
typeName: opts.TypeName,
|
||||
filesDir: opts.FilesDir,
|
||||
indexBaseDir: path.Join(opts.DataDir, "index.disk"),
|
||||
indexRootDir: path.Join(path.Join(opts.DataDir, "index.disk"), strings.Join([]string{"unique", opts.TypeName, opts.IndexBy}, ".")),
|
||||
}
|
||||
}
|
||||
|
||||
// Init initializes a unique index.
|
||||
func (idx *Unique) Init() error {
|
||||
if _, err := os.Stat(idx.filesDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(idx.indexRootDir, 0777); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add adds a value to the index, returns the path to the root-document
|
||||
func (idx *Unique) Add(id, v string) (string, error) {
|
||||
if v == "" {
|
||||
return "", nil
|
||||
}
|
||||
if idx.caseInsensitive {
|
||||
v = strings.ToLower(v)
|
||||
}
|
||||
oldName := path.Join(idx.filesDir, id)
|
||||
newName := path.Join(idx.indexRootDir, v)
|
||||
err := os.Symlink(oldName, newName)
|
||||
if errors.Is(err, os.ErrExist) {
|
||||
return "", &idxerrs.AlreadyExistsErr{TypeName: idx.typeName, Key: idx.indexBy, Value: v}
|
||||
}
|
||||
|
||||
return newName, err
|
||||
}
|
||||
|
||||
// Remove a value v from an index.
|
||||
func (idx *Unique) Remove(id string, v string) (err error) {
|
||||
if v == "" {
|
||||
return nil
|
||||
}
|
||||
if idx.caseInsensitive {
|
||||
v = strings.ToLower(v)
|
||||
}
|
||||
searchPath := path.Join(idx.indexRootDir, v)
|
||||
return os.Remove(searchPath)
|
||||
}
|
||||
|
||||
// Lookup exact lookup by value.
|
||||
func (idx *Unique) Lookup(v string) (resultPath []string, err error) {
|
||||
if idx.caseInsensitive {
|
||||
v = strings.ToLower(v)
|
||||
}
|
||||
searchPath := path.Join(idx.indexRootDir, v)
|
||||
if err = isValidSymlink(searchPath); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err = &idxerrs.NotFoundErr{TypeName: idx.typeName, Key: idx.indexBy, Value: v}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
p, err := os.Readlink(searchPath)
|
||||
if err != nil {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
return []string{p}, err
|
||||
}
|
||||
|
||||
// Update index from <oldV> to <newV>.
|
||||
func (idx *Unique) Update(id, oldV, newV string) (err error) {
|
||||
if idx.caseInsensitive {
|
||||
oldV = strings.ToLower(oldV)
|
||||
newV = strings.ToLower(newV)
|
||||
}
|
||||
oldPath := path.Join(idx.indexRootDir, oldV)
|
||||
if err = isValidSymlink(oldPath); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return &idxerrs.NotFoundErr{TypeName: idx.TypeName(), Key: idx.IndexBy(), Value: oldV}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
newPath := path.Join(idx.indexRootDir, newV)
|
||||
if err = isValidSymlink(newPath); err == nil {
|
||||
return &idxerrs.AlreadyExistsErr{TypeName: idx.typeName, Key: idx.indexBy, Value: newV}
|
||||
}
|
||||
|
||||
if os.IsNotExist(err) {
|
||||
err = os.Rename(oldPath, newPath)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Search allows for glob search on the index.
|
||||
func (idx *Unique) Search(pattern string) ([]string, error) {
|
||||
if idx.caseInsensitive {
|
||||
pattern = strings.ToLower(pattern)
|
||||
}
|
||||
paths, err := filepath.Glob(path.Join(idx.indexRootDir, pattern))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(paths) == 0 {
|
||||
return nil, &idxerrs.NotFoundErr{TypeName: idx.typeName, Key: idx.indexBy, Value: pattern}
|
||||
}
|
||||
|
||||
res := make([]string, 0)
|
||||
for _, p := range paths {
|
||||
if err := isValidSymlink(p); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
src, err := os.Readlink(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res = append(res, src)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// CaseInsensitive undocumented.
|
||||
func (idx *Unique) CaseInsensitive() bool {
|
||||
return idx.caseInsensitive
|
||||
}
|
||||
|
||||
// IndexBy undocumented.
|
||||
func (idx *Unique) IndexBy() string {
|
||||
return idx.indexBy
|
||||
}
|
||||
|
||||
// TypeName undocumented.
|
||||
func (idx *Unique) TypeName() string {
|
||||
return idx.typeName
|
||||
}
|
||||
|
||||
// FilesDir undocumented.
|
||||
func (idx *Unique) FilesDir() string {
|
||||
return idx.filesDir
|
||||
}
|
||||
|
||||
func isValidSymlink(path string) (err error) {
|
||||
var symInfo os.FileInfo
|
||||
if symInfo, err = os.Lstat(path); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if symInfo.Mode()&os.ModeSymlink == 0 {
|
||||
err = fmt.Errorf("%s is not a valid symlink (bug/corruption?)", path)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Delete deletes the index folder from its storage.
|
||||
func (idx *Unique) Delete() error {
|
||||
return os.RemoveAll(idx.indexRootDir)
|
||||
}
|
||||
133
accounts/pkg/indexer/index/disk/unique_test.go
Normal file
133
accounts/pkg/indexer/index/disk/unique_test.go
Normal file
@@ -0,0 +1,133 @@
|
||||
package disk
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/owncloud/ocis/accounts/pkg/config"
|
||||
"github.com/owncloud/ocis/accounts/pkg/indexer/errors"
|
||||
"github.com/owncloud/ocis/accounts/pkg/indexer/index"
|
||||
"github.com/owncloud/ocis/accounts/pkg/indexer/option"
|
||||
. "github.com/owncloud/ocis/accounts/pkg/indexer/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestUniqueLookupSingleEntry(t *testing.T) {
|
||||
uniq, dataDir := getUniqueIdxSut(t, "Email", User{})
|
||||
filesDir := path.Join(dataDir, "users")
|
||||
|
||||
t.Log("existing lookup")
|
||||
resultPath, err := uniq.Lookup("mikey@example.com")
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, []string{path.Join(filesDir, "abcdefg-123")}, resultPath)
|
||||
|
||||
t.Log("non-existing lookup")
|
||||
resultPath, err = uniq.Lookup("doesnotExists@example.com")
|
||||
assert.Error(t, err)
|
||||
assert.IsType(t, &errors.NotFoundErr{}, err)
|
||||
assert.Empty(t, resultPath)
|
||||
|
||||
_ = os.RemoveAll(dataDir)
|
||||
|
||||
}
|
||||
|
||||
func TestUniqueUniqueConstraint(t *testing.T) {
|
||||
uniq, dataDir := getUniqueIdxSut(t, "Email", User{})
|
||||
|
||||
_, err := uniq.Add("abcdefg-123", "mikey@example.com")
|
||||
assert.Error(t, err)
|
||||
assert.IsType(t, &errors.AlreadyExistsErr{}, err)
|
||||
|
||||
_ = os.RemoveAll(dataDir)
|
||||
}
|
||||
|
||||
func TestUniqueRemove(t *testing.T) {
|
||||
uniq, dataDir := getUniqueIdxSut(t, "Email", User{})
|
||||
|
||||
err := uniq.Remove("", "mikey@example.com")
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = uniq.Lookup("mikey@example.com")
|
||||
assert.Error(t, err)
|
||||
assert.IsType(t, &errors.NotFoundErr{}, err)
|
||||
|
||||
_ = os.RemoveAll(dataDir)
|
||||
}
|
||||
|
||||
func TestUniqueUpdate(t *testing.T) {
|
||||
uniq, dataDir := getUniqueIdxSut(t, "Email", User{})
|
||||
|
||||
t.Log("successful update")
|
||||
err := uniq.Update("", "mikey@example.com", "mikey2@example.com")
|
||||
assert.NoError(t, err)
|
||||
|
||||
t.Log("failed update because already exists")
|
||||
err = uniq.Update("", "mikey2@example.com", "mikey2@example.com")
|
||||
assert.Error(t, err)
|
||||
assert.IsType(t, &errors.AlreadyExistsErr{}, err)
|
||||
|
||||
t.Log("failed update because not found")
|
||||
err = uniq.Update("", "nonexisting@example.com", "something2@example.com")
|
||||
assert.Error(t, err)
|
||||
assert.IsType(t, &errors.NotFoundErr{}, err)
|
||||
|
||||
_ = os.RemoveAll(dataDir)
|
||||
}
|
||||
|
||||
func TestUniqueIndexSearch(t *testing.T) {
|
||||
sut, dataDir := getUniqueIdxSut(t, "Email", User{})
|
||||
|
||||
res, err := sut.Search("j*@example.com")
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, res, 2)
|
||||
|
||||
assert.Equal(t, "ewf4ofk-555", path.Base(res[0]))
|
||||
assert.Equal(t, "rulan54-777", path.Base(res[1]))
|
||||
|
||||
_, err = sut.Search("does-not-exist@example.com")
|
||||
assert.Error(t, err)
|
||||
assert.IsType(t, &errors.NotFoundErr{}, err)
|
||||
|
||||
_ = os.RemoveAll(dataDir)
|
||||
}
|
||||
|
||||
func TestErrors(t *testing.T) {
|
||||
assert.True(t, errors.IsAlreadyExistsErr(&errors.AlreadyExistsErr{}))
|
||||
assert.True(t, errors.IsNotFoundErr(&errors.NotFoundErr{}))
|
||||
}
|
||||
|
||||
func getUniqueIdxSut(t *testing.T, indexBy string, entityType interface{}) (index.Index, string) {
|
||||
dataPath, _ := WriteIndexTestData(Data, "ID", "")
|
||||
cfg := config.Config{
|
||||
Repo: config.Repo{
|
||||
Disk: config.Disk{
|
||||
Path: dataPath,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
sut := NewUniqueIndexWithOptions(
|
||||
option.WithTypeName(GetTypeFQN(entityType)),
|
||||
option.WithIndexBy(indexBy),
|
||||
option.WithFilesDir(path.Join(cfg.Repo.Disk.Path, "users")),
|
||||
option.WithDataDir(cfg.Repo.Disk.Path),
|
||||
)
|
||||
err := sut.Init()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, u := range Data["users"] {
|
||||
pkVal := ValueOf(u, "ID")
|
||||
idxByVal := ValueOf(u, "Email")
|
||||
_, err := sut.Add(pkVal, idxByVal)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
return sut, dataPath
|
||||
}
|
||||
17
accounts/pkg/indexer/index/index.go
Normal file
17
accounts/pkg/indexer/index/index.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package index
|
||||
|
||||
// Index can be implemented to create new indexer-strategies. See Unique for example.
|
||||
// Each indexer implementation is bound to one data-column (IndexBy) and a data-type (TypeName)
|
||||
type Index interface {
|
||||
Init() error
|
||||
Lookup(v string) ([]string, error)
|
||||
Add(id, v string) (string, error)
|
||||
Remove(id string, v string) error
|
||||
Update(id, oldV, newV string) error
|
||||
Search(pattern string) ([]string, error)
|
||||
CaseInsensitive() bool
|
||||
IndexBy() string
|
||||
TypeName() string
|
||||
FilesDir() string
|
||||
Delete() error // Delete deletes the index folder from its storage.
|
||||
}
|
||||
238
accounts/pkg/indexer/indexer.go
Normal file
238
accounts/pkg/indexer/indexer.go
Normal file
@@ -0,0 +1,238 @@
|
||||
// Package indexer provides symlink-based indexer for on-disk document-directories.
|
||||
package indexer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
|
||||
"github.com/iancoleman/strcase"
|
||||
"github.com/owncloud/ocis/accounts/pkg/config"
|
||||
"github.com/owncloud/ocis/accounts/pkg/indexer/errors"
|
||||
"github.com/owncloud/ocis/accounts/pkg/indexer/index"
|
||||
_ "github.com/owncloud/ocis/accounts/pkg/indexer/index/cs3" // to populate index
|
||||
_ "github.com/owncloud/ocis/accounts/pkg/indexer/index/disk" // to populate index
|
||||
"github.com/owncloud/ocis/accounts/pkg/indexer/option"
|
||||
"github.com/owncloud/ocis/accounts/pkg/indexer/registry"
|
||||
)
|
||||
|
||||
// Indexer is a facade to configure and query over multiple indices.
|
||||
type Indexer struct {
|
||||
config *config.Config
|
||||
indices typeMap
|
||||
}
|
||||
|
||||
// IdxAddResult represents the result of an Add call on an index
|
||||
type IdxAddResult struct {
|
||||
Field, Value string
|
||||
}
|
||||
|
||||
// CreateIndexer creates a new Indexer.
|
||||
func CreateIndexer(cfg *config.Config) *Indexer {
|
||||
return &Indexer{
|
||||
config: cfg,
|
||||
indices: typeMap{},
|
||||
}
|
||||
}
|
||||
|
||||
func getRegistryStrategy(cfg *config.Config) string {
|
||||
if cfg.Repo.Disk.Path != "" {
|
||||
return "disk"
|
||||
}
|
||||
|
||||
return "cs3"
|
||||
}
|
||||
|
||||
// Reset takes care of deleting all indices from storage and from the internal map of indices
|
||||
func (i Indexer) Reset() error {
|
||||
for j := range i.indices {
|
||||
for _, indices := range i.indices[j].IndicesByField {
|
||||
for _, idx := range indices {
|
||||
err := idx.Delete()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
delete(i.indices, j)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddIndex adds a new index to the indexer receiver.
|
||||
func (i Indexer) AddIndex(t interface{}, indexBy, pkName, entityDirName, indexType string, bound *option.Bound, caseInsensitive bool) error {
|
||||
strategy := getRegistryStrategy(i.config)
|
||||
f := registry.IndexConstructorRegistry[strategy][indexType]
|
||||
var idx index.Index
|
||||
|
||||
if strategy == "disk" {
|
||||
idx = f(
|
||||
option.CaseInsensitive(caseInsensitive),
|
||||
option.WithEntity(t),
|
||||
option.WithBounds(bound),
|
||||
option.WithTypeName(getTypeFQN(t)),
|
||||
option.WithIndexBy(indexBy),
|
||||
option.WithFilesDir(path.Join(i.config.Repo.Disk.Path, entityDirName)),
|
||||
option.WithDataDir(i.config.Repo.Disk.Path),
|
||||
)
|
||||
} else if strategy == "cs3" {
|
||||
idx = f(
|
||||
option.CaseInsensitive(caseInsensitive),
|
||||
option.WithEntity(t),
|
||||
option.WithBounds(bound),
|
||||
option.WithTypeName(getTypeFQN(t)),
|
||||
option.WithIndexBy(indexBy),
|
||||
option.WithDataURL(i.config.Repo.CS3.DataURL),
|
||||
option.WithDataPrefix(i.config.Repo.CS3.DataPrefix),
|
||||
option.WithJWTSecret(i.config.Repo.CS3.JWTSecret),
|
||||
option.WithProviderAddr(i.config.Repo.CS3.ProviderAddr),
|
||||
option.WithServiceUser(i.config.ServiceUser),
|
||||
)
|
||||
}
|
||||
|
||||
i.indices.addIndex(getTypeFQN(t), pkName, idx)
|
||||
return idx.Init()
|
||||
}
|
||||
|
||||
// Add a new entry to the indexer
|
||||
func (i Indexer) Add(t interface{}) ([]IdxAddResult, error) {
|
||||
typeName := getTypeFQN(t)
|
||||
var results []IdxAddResult
|
||||
if fields, ok := i.indices[typeName]; ok {
|
||||
for _, indices := range fields.IndicesByField {
|
||||
for _, idx := range indices {
|
||||
pkVal := valueOf(t, fields.PKFieldName)
|
||||
idxByVal := valueOf(t, idx.IndexBy())
|
||||
value, err := idx.Add(pkVal, idxByVal)
|
||||
if err != nil {
|
||||
return []IdxAddResult{}, err
|
||||
}
|
||||
if value == "" {
|
||||
continue
|
||||
}
|
||||
results = append(results, IdxAddResult{Field: idx.IndexBy(), Value: value})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// FindBy finds a value on an index by field and value.
|
||||
func (i Indexer) FindBy(t interface{}, field string, val string) ([]string, error) {
|
||||
typeName := getTypeFQN(t)
|
||||
resultPaths := make([]string, 0)
|
||||
if fields, ok := i.indices[typeName]; ok {
|
||||
for _, idx := range fields.IndicesByField[strcase.ToCamel(field)] {
|
||||
idxVal := val
|
||||
res, err := idx.Lookup(idxVal)
|
||||
if err != nil {
|
||||
if errors.IsNotFoundErr(err) {
|
||||
continue
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
resultPaths = append(resultPaths, res...)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
result := make([]string, 0, len(resultPaths))
|
||||
for _, v := range resultPaths {
|
||||
result = append(result, path.Base(v))
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Delete deletes all indexed fields of a given type t on the Indexer.
|
||||
func (i Indexer) Delete(t interface{}) error {
|
||||
typeName := getTypeFQN(t)
|
||||
if fields, ok := i.indices[typeName]; ok {
|
||||
for _, indices := range fields.IndicesByField {
|
||||
for _, idx := range indices {
|
||||
pkVal := valueOf(t, fields.PKFieldName)
|
||||
idxByVal := valueOf(t, idx.IndexBy())
|
||||
if err := idx.Remove(pkVal, idxByVal); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindByPartial allows for glob search across all indexes.
|
||||
func (i Indexer) FindByPartial(t interface{}, field string, pattern string) ([]string, error) {
|
||||
typeName := getTypeFQN(t)
|
||||
resultPaths := make([]string, 0)
|
||||
if fields, ok := i.indices[typeName]; ok {
|
||||
for _, idx := range fields.IndicesByField[strcase.ToCamel(field)] {
|
||||
res, err := idx.Search(pattern)
|
||||
if err != nil {
|
||||
if errors.IsNotFoundErr(err) {
|
||||
continue
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
resultPaths = append(resultPaths, res...)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
result := make([]string, 0, len(resultPaths))
|
||||
for _, v := range resultPaths {
|
||||
result = append(result, path.Base(v))
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
||||
}
|
||||
|
||||
// Update updates all indexes on a value <from> to a value <to>.
|
||||
func (i Indexer) Update(from, to interface{}) error {
|
||||
typeNameFrom := getTypeFQN(from)
|
||||
typeNameTo := getTypeFQN(to)
|
||||
if typeNameFrom != typeNameTo {
|
||||
return fmt.Errorf("update types do not match: from %v to %v", typeNameFrom, typeNameTo)
|
||||
}
|
||||
|
||||
if fields, ok := i.indices[typeNameFrom]; ok {
|
||||
for fName, indices := range fields.IndicesByField {
|
||||
oldV := valueOf(from, fName)
|
||||
newV := valueOf(to, fName)
|
||||
pkVal := valueOf(from, fields.PKFieldName)
|
||||
for _, idx := range indices {
|
||||
if oldV == newV {
|
||||
continue
|
||||
}
|
||||
if oldV == "" {
|
||||
if _, err := idx.Add(pkVal, newV); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
if newV == "" {
|
||||
if err := idx.Remove(pkVal, oldV); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err := idx.Update(pkVal, oldV, newV); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
275
accounts/pkg/indexer/indexer_test.go
Normal file
275
accounts/pkg/indexer/indexer_test.go
Normal file
@@ -0,0 +1,275 @@
|
||||
package indexer
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/owncloud/ocis/accounts/pkg/indexer/option"
|
||||
|
||||
"github.com/owncloud/ocis/accounts/pkg/config"
|
||||
_ "github.com/owncloud/ocis/accounts/pkg/indexer/index/cs3"
|
||||
_ "github.com/owncloud/ocis/accounts/pkg/indexer/index/disk"
|
||||
. "github.com/owncloud/ocis/accounts/pkg/indexer/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
//const cs3RootFolder = "/var/tmp/ocis/storage/users/data"
|
||||
//
|
||||
//func TestIndexer_CS3_AddWithUniqueIndex(t *testing.T) {
|
||||
// dataDir, err := WriteIndexTestData(Data, "ID", cs3RootFolder)
|
||||
// assert.NoError(t, err)
|
||||
// indexer := createCs3Indexer()
|
||||
//
|
||||
// err = indexer.AddIndex(&User{}, "UserName", "ID", "users", "unique", nil, false)
|
||||
// assert.NoError(t, err)
|
||||
//
|
||||
// u := &User{ID: "abcdefg-123", UserName: "mikey", Email: "mikey@example.com"}
|
||||
// _, err = indexer.Add(u)
|
||||
// assert.NoError(t, err)
|
||||
//
|
||||
// _ = os.RemoveAll(dataDir)
|
||||
//}
|
||||
//
|
||||
//func TestIndexer_CS3_AddWithNonUniqueIndex(t *testing.T) {
|
||||
// dataDir, err := WriteIndexTestData(Data, "ID", cs3RootFolder)
|
||||
// assert.NoError(t, err)
|
||||
// indexer := createCs3Indexer()
|
||||
//
|
||||
// err = indexer.AddIndex(&User{}, "UserName", "ID", "users", "non_unique", nil, false)
|
||||
// assert.NoError(t, err)
|
||||
//
|
||||
// u := &User{ID: "abcdefg-123", UserName: "mikey", Email: "mikey@example.com"}
|
||||
// _, err = indexer.Add(u)
|
||||
// assert.NoError(t, err)
|
||||
//
|
||||
// _ = os.RemoveAll(dataDir)
|
||||
//}
|
||||
|
||||
func TestIndexer_Disk_FindByWithUniqueIndex(t *testing.T) {
|
||||
dataDir, err := WriteIndexTestData(Data, "ID", "")
|
||||
assert.NoError(t, err)
|
||||
indexer := createDiskIndexer(dataDir)
|
||||
|
||||
err = indexer.AddIndex(&User{}, "UserName", "ID", "users", "unique", nil, false)
|
||||
assert.NoError(t, err)
|
||||
|
||||
u := &User{ID: "abcdefg-123", UserName: "mikey", Email: "mikey@example.com"}
|
||||
_, err = indexer.Add(u)
|
||||
assert.NoError(t, err)
|
||||
|
||||
res, err := indexer.FindBy(User{}, "UserName", "mikey")
|
||||
assert.NoError(t, err)
|
||||
t.Log(res)
|
||||
|
||||
_ = os.RemoveAll(dataDir)
|
||||
}
|
||||
|
||||
func TestIndexer_Disk_AddWithUniqueIndex(t *testing.T) {
|
||||
dataDir, err := WriteIndexTestData(Data, "ID", "")
|
||||
assert.NoError(t, err)
|
||||
indexer := createDiskIndexer(dataDir)
|
||||
|
||||
err = indexer.AddIndex(&User{}, "UserName", "ID", "users", "unique", nil, false)
|
||||
assert.NoError(t, err)
|
||||
|
||||
u := &User{ID: "abcdefg-123", UserName: "mikey", Email: "mikey@example.com"}
|
||||
_, err = indexer.Add(u)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_ = os.RemoveAll(dataDir)
|
||||
}
|
||||
|
||||
func TestIndexer_Disk_AddWithNonUniqueIndex(t *testing.T) {
|
||||
dataDir, err := WriteIndexTestData(Data, "ID", "")
|
||||
assert.NoError(t, err)
|
||||
indexer := createDiskIndexer(dataDir)
|
||||
|
||||
err = indexer.AddIndex(&Pet{}, "Kind", "ID", "pets", "non_unique", nil, false)
|
||||
assert.NoError(t, err)
|
||||
|
||||
pet1 := Pet{ID: "goefe-789", Kind: "Hog", Color: "Green", Name: "Dicky"}
|
||||
pet2 := Pet{ID: "xadaf-189", Kind: "Hog", Color: "Green", Name: "Ricky"}
|
||||
|
||||
_, err = indexer.Add(pet1)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = indexer.Add(pet2)
|
||||
assert.NoError(t, err)
|
||||
|
||||
res, err := indexer.FindBy(Pet{}, "Kind", "Hog")
|
||||
assert.NoError(t, err)
|
||||
|
||||
t.Log(res)
|
||||
|
||||
_ = os.RemoveAll(dataDir)
|
||||
}
|
||||
|
||||
func TestIndexer_Disk_AddWithAutoincrementIndex(t *testing.T) {
|
||||
dataDir, err := WriteIndexTestData(Data, "ID", "")
|
||||
assert.NoError(t, err)
|
||||
indexer := createDiskIndexer(dataDir)
|
||||
|
||||
err = indexer.AddIndex(&User{}, "UID", "ID", "users", "autoincrement", &option.Bound{Lower: 5}, false)
|
||||
assert.NoError(t, err)
|
||||
|
||||
res1, err := indexer.Add(Data["users"][0])
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "UID", res1[0].Field)
|
||||
assert.Equal(t, "5", path.Base(res1[0].Value))
|
||||
|
||||
res2, err := indexer.Add(Data["users"][1])
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "UID", res2[0].Field)
|
||||
assert.Equal(t, "6", path.Base(res2[0].Value))
|
||||
|
||||
resFindBy, err := indexer.FindBy(User{}, "UID", "6")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "hijklmn-456", resFindBy[0])
|
||||
t.Log(resFindBy)
|
||||
|
||||
_ = os.RemoveAll(dataDir)
|
||||
}
|
||||
|
||||
func TestIndexer_Disk_DeleteWithNonUniqueIndex(t *testing.T) {
|
||||
dataDir, err := WriteIndexTestData(Data, "ID", "")
|
||||
assert.NoError(t, err)
|
||||
indexer := createDiskIndexer(dataDir)
|
||||
|
||||
err = indexer.AddIndex(&Pet{}, "Kind", "ID", "pets", "non_unique", nil, false)
|
||||
assert.NoError(t, err)
|
||||
|
||||
pet1 := Pet{ID: "goefe-789", Kind: "Hog", Color: "Green", Name: "Dicky"}
|
||||
pet2 := Pet{ID: "xadaf-189", Kind: "Hog", Color: "Green", Name: "Ricky"}
|
||||
|
||||
_, err = indexer.Add(pet1)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = indexer.Add(pet2)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = indexer.Delete(pet2)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_ = os.RemoveAll(dataDir)
|
||||
}
|
||||
|
||||
func TestIndexer_Disk_SearchWithNonUniqueIndex(t *testing.T) {
|
||||
dataDir, err := WriteIndexTestData(Data, "ID", "")
|
||||
assert.NoError(t, err)
|
||||
indexer := createDiskIndexer(dataDir)
|
||||
|
||||
err = indexer.AddIndex(&Pet{}, "Name", "ID", "pets", "non_unique", nil, false)
|
||||
assert.NoError(t, err)
|
||||
|
||||
pet1 := Pet{ID: "goefe-789", Kind: "Hog", Color: "Green", Name: "Dicky"}
|
||||
pet2 := Pet{ID: "xadaf-189", Kind: "Hog", Color: "Green", Name: "Ricky"}
|
||||
|
||||
_, err = indexer.Add(pet1)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = indexer.Add(pet2)
|
||||
assert.NoError(t, err)
|
||||
|
||||
res, err := indexer.FindByPartial(pet2, "Name", "*ky")
|
||||
assert.NoError(t, err)
|
||||
|
||||
t.Log(res)
|
||||
_ = os.RemoveAll(dataDir)
|
||||
}
|
||||
|
||||
func TestIndexer_Disk_UpdateWithUniqueIndex(t *testing.T) {
|
||||
dataDir, err := WriteIndexTestData(Data, "ID", "")
|
||||
assert.NoError(t, err)
|
||||
indexer := createDiskIndexer(dataDir)
|
||||
|
||||
err = indexer.AddIndex(&User{}, "UserName", "ID", "users", "unique", nil, false)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = indexer.AddIndex(&User{}, "Email", "ID", "users", "unique", nil, false)
|
||||
assert.NoError(t, err)
|
||||
|
||||
user1 := &User{ID: "abcdefg-123", UserName: "mikey", Email: "mikey@example.com"}
|
||||
user2 := &User{ID: "hijklmn-456", UserName: "frank", Email: "frank@example.com"}
|
||||
|
||||
_, err = indexer.Add(user1)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = indexer.Add(user2)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = indexer.Update(user1, &User{
|
||||
ID: "abcdefg-123",
|
||||
UserName: "mikey-new",
|
||||
Email: "mikey@example.com",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
v, err1 := indexer.FindBy(&User{}, "UserName", "mikey-new")
|
||||
assert.NoError(t, err1)
|
||||
assert.Len(t, v, 1)
|
||||
v, err2 := indexer.FindBy(&User{}, "UserName", "mikey")
|
||||
assert.NoError(t, err2)
|
||||
assert.Len(t, v, 0)
|
||||
|
||||
err1 = indexer.Update(&User{
|
||||
ID: "abcdefg-123",
|
||||
UserName: "mikey-new",
|
||||
Email: "mikey@example.com",
|
||||
}, &User{
|
||||
ID: "abcdefg-123",
|
||||
UserName: "mikey-newest",
|
||||
Email: "mikey-new@example.com",
|
||||
})
|
||||
assert.NoError(t, err1)
|
||||
fbUserName, err2 := indexer.FindBy(&User{}, "UserName", "mikey-newest")
|
||||
assert.NoError(t, err2)
|
||||
assert.Len(t, fbUserName, 1)
|
||||
fbEmail, err3 := indexer.FindBy(&User{}, "Email", "mikey-new@example.com")
|
||||
assert.NoError(t, err3)
|
||||
assert.Len(t, fbEmail, 1)
|
||||
|
||||
_ = os.RemoveAll(dataDir)
|
||||
}
|
||||
|
||||
func TestIndexer_Disk_UpdateWithNonUniqueIndex(t *testing.T) {
|
||||
dataDir, err := WriteIndexTestData(Data, "ID", "")
|
||||
assert.NoError(t, err)
|
||||
indexer := createDiskIndexer(dataDir)
|
||||
|
||||
err = indexer.AddIndex(&Pet{}, "Name", "ID", "pets", "non_unique", nil, false)
|
||||
assert.NoError(t, err)
|
||||
|
||||
pet1 := Pet{ID: "goefe-789", Kind: "Hog", Color: "Green", Name: "Dicky"}
|
||||
pet2 := Pet{ID: "xadaf-189", Kind: "Hog", Color: "Green", Name: "Ricky"}
|
||||
|
||||
_, err = indexer.Add(pet1)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = indexer.Add(pet2)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_ = os.RemoveAll(dataDir)
|
||||
}
|
||||
|
||||
//func createCs3Indexer() *Indexer {
|
||||
// return CreateIndexer(&config.Config{
|
||||
// Repo: config.Repo{
|
||||
// CS3: config.CS3{
|
||||
// ProviderAddr: "0.0.0.0:9215",
|
||||
// DataURL: "http://localhost:9216",
|
||||
// DataPrefix: "data",
|
||||
// JWTSecret: "Pive-Fumkiu4",
|
||||
// },
|
||||
// },
|
||||
// })
|
||||
//}
|
||||
|
||||
func createDiskIndexer(dataDir string) *Indexer {
|
||||
return CreateIndexer(&config.Config{
|
||||
Repo: config.Repo{
|
||||
Disk: config.Disk{
|
||||
Path: dataDir,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
27
accounts/pkg/indexer/map.go
Normal file
27
accounts/pkg/indexer/map.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package indexer
|
||||
|
||||
import "github.com/owncloud/ocis/accounts/pkg/indexer/index"
|
||||
|
||||
// typeMap stores the indexer layout at runtime.
|
||||
|
||||
type typeMap map[tName]typeMapping
|
||||
type tName = string
|
||||
type fieldName = string
|
||||
|
||||
type typeMapping struct {
|
||||
PKFieldName string
|
||||
IndicesByField map[fieldName][]index.Index
|
||||
}
|
||||
|
||||
func (m typeMap) addIndex(typeName string, pkName string, idx index.Index) {
|
||||
if val, ok := m[typeName]; ok {
|
||||
val.IndicesByField[idx.IndexBy()] = append(val.IndicesByField[idx.IndexBy()], idx)
|
||||
return
|
||||
}
|
||||
m[typeName] = typeMapping{
|
||||
PKFieldName: pkName,
|
||||
IndicesByField: map[string][]index.Index{
|
||||
idx.IndexBy(): {idx},
|
||||
},
|
||||
}
|
||||
}
|
||||
126
accounts/pkg/indexer/option/option.go
Normal file
126
accounts/pkg/indexer/option/option.go
Normal file
@@ -0,0 +1,126 @@
|
||||
package option
|
||||
|
||||
import "github.com/owncloud/ocis/accounts/pkg/config"
|
||||
|
||||
// Option defines a single option function.
|
||||
type Option func(o *Options)
|
||||
|
||||
// Bound represents a lower and upper bound range for an index.
|
||||
// todo: if we would like to provide an upper bound then we would need to deal with ranges, in which case this is why the
|
||||
// upper bound attribute is here.
|
||||
type Bound struct {
|
||||
Lower, Upper int64
|
||||
}
|
||||
|
||||
// Options defines the available options for this package.
|
||||
type Options struct {
|
||||
CaseInsensitive bool
|
||||
Bound *Bound
|
||||
|
||||
// Disk Options
|
||||
TypeName string
|
||||
IndexBy string
|
||||
FilesDir string
|
||||
IndexBaseDir string
|
||||
DataDir string
|
||||
EntityDirName string
|
||||
Entity interface{}
|
||||
|
||||
// CS3 options
|
||||
DataURL string
|
||||
DataPrefix string
|
||||
JWTSecret string
|
||||
ProviderAddr string
|
||||
ServiceUser config.ServiceUser
|
||||
}
|
||||
|
||||
// CaseInsensitive sets the CaseInsensitive field.
|
||||
func CaseInsensitive(val bool) Option {
|
||||
return func(o *Options) {
|
||||
o.CaseInsensitive = val
|
||||
}
|
||||
}
|
||||
|
||||
// WithBounds sets the Bounds field.
|
||||
func WithBounds(val *Bound) Option {
|
||||
return func(o *Options) {
|
||||
o.Bound = val
|
||||
}
|
||||
}
|
||||
|
||||
// WithEntity sets the Entity field.
|
||||
func WithEntity(val interface{}) Option {
|
||||
return func(o *Options) {
|
||||
o.Entity = val
|
||||
}
|
||||
}
|
||||
|
||||
// WithJWTSecret sets the JWTSecret field.
|
||||
func WithJWTSecret(val string) Option {
|
||||
return func(o *Options) {
|
||||
o.JWTSecret = val
|
||||
}
|
||||
}
|
||||
|
||||
// WithDataURL sets the DataURl field.
|
||||
func WithDataURL(val string) Option {
|
||||
return func(o *Options) {
|
||||
o.DataURL = val
|
||||
}
|
||||
}
|
||||
|
||||
// WithDataPrefix sets the DataPrefix field.
|
||||
func WithDataPrefix(val string) Option {
|
||||
return func(o *Options) {
|
||||
o.DataPrefix = val
|
||||
}
|
||||
}
|
||||
|
||||
// WithEntityDirName sets the EntityDirName field.
|
||||
func WithEntityDirName(val string) Option {
|
||||
return func(o *Options) {
|
||||
o.EntityDirName = val
|
||||
}
|
||||
}
|
||||
|
||||
// WithDataDir sets the DataDir option.
|
||||
func WithDataDir(val string) Option {
|
||||
return func(o *Options) {
|
||||
o.DataDir = val
|
||||
}
|
||||
}
|
||||
|
||||
// WithTypeName sets the TypeName option.
|
||||
func WithTypeName(val string) Option {
|
||||
return func(o *Options) {
|
||||
o.TypeName = val
|
||||
}
|
||||
}
|
||||
|
||||
// WithIndexBy sets the option IndexBy.
|
||||
func WithIndexBy(val string) Option {
|
||||
return func(o *Options) {
|
||||
o.IndexBy = val
|
||||
}
|
||||
}
|
||||
|
||||
// WithFilesDir sets the option FilesDir.
|
||||
func WithFilesDir(val string) Option {
|
||||
return func(o *Options) {
|
||||
o.FilesDir = val
|
||||
}
|
||||
}
|
||||
|
||||
// WithProviderAddr sets the option ProviderAddr.
|
||||
func WithProviderAddr(val string) Option {
|
||||
return func(o *Options) {
|
||||
o.ProviderAddr = val
|
||||
}
|
||||
}
|
||||
|
||||
// WithServiceUser sets the option ServiceUser.
|
||||
func WithServiceUser(val config.ServiceUser) Option {
|
||||
return func(o *Options) {
|
||||
o.ServiceUser = val
|
||||
}
|
||||
}
|
||||
41
accounts/pkg/indexer/reflect.go
Normal file
41
accounts/pkg/indexer/reflect.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package indexer
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"path"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func getType(v interface{}) (reflect.Value, error) {
|
||||
rv := reflect.ValueOf(v)
|
||||
for rv.Kind() == reflect.Ptr || rv.Kind() == reflect.Interface {
|
||||
rv = rv.Elem()
|
||||
}
|
||||
if !rv.IsValid() {
|
||||
return reflect.Value{}, errors.New("failed to read value via reflection")
|
||||
}
|
||||
|
||||
return rv, nil
|
||||
}
|
||||
|
||||
func getTypeFQN(t interface{}) string {
|
||||
typ, _ := getType(t)
|
||||
typeName := path.Join(typ.Type().PkgPath(), typ.Type().Name())
|
||||
typeName = strings.ReplaceAll(typeName, "/", ".")
|
||||
return typeName
|
||||
}
|
||||
|
||||
func valueOf(v interface{}, field string) string {
|
||||
r := reflect.ValueOf(v)
|
||||
f := reflect.Indirect(r).FieldByName(field)
|
||||
|
||||
if f.Kind() == reflect.String {
|
||||
return f.String()
|
||||
}
|
||||
if f.IsZero() {
|
||||
return ""
|
||||
}
|
||||
return strconv.Itoa(int(f.Int()))
|
||||
}
|
||||
53
accounts/pkg/indexer/reflect_test.go
Normal file
53
accounts/pkg/indexer/reflect_test.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package indexer
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_getTypeFQN(t *testing.T) {
|
||||
type someT struct{}
|
||||
|
||||
type args struct {
|
||||
t interface{}
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
}{
|
||||
{name: "ByValue", args: args{&someT{}}, want: "github.com.owncloud.ocis.accounts.pkg.indexer.someT"},
|
||||
{name: "ByRef", args: args{someT{}}, want: "github.com.owncloud.ocis.accounts.pkg.indexer.someT"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := getTypeFQN(tt.args.t); got != tt.want {
|
||||
t.Errorf("getTypeFQN() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_valueOf(t *testing.T) {
|
||||
type someT struct {
|
||||
val string
|
||||
}
|
||||
type args struct {
|
||||
v interface{}
|
||||
field string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
}{
|
||||
{name: "ByValue", args: args{v: someT{val: "hello"}, field: "val"}, want: "hello"},
|
||||
{name: "ByRef", args: args{v: &someT{val: "hello"}, field: "val"}, want: "hello"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := valueOf(tt.args.v, tt.args.field); got != tt.want {
|
||||
t.Errorf("valueOf() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
15
accounts/pkg/indexer/registry/registry.go
Normal file
15
accounts/pkg/indexer/registry/registry.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"github.com/owncloud/ocis/accounts/pkg/indexer/index"
|
||||
"github.com/owncloud/ocis/accounts/pkg/indexer/option"
|
||||
)
|
||||
|
||||
// IndexConstructor is a constructor function for creating index.Index.
|
||||
type IndexConstructor func(o ...option.Option) index.Index
|
||||
|
||||
// IndexConstructorRegistry undocumented.
|
||||
var IndexConstructorRegistry = map[string]map[string]IndexConstructor{
|
||||
"disk": {},
|
||||
"cs3": {},
|
||||
}
|
||||
92
accounts/pkg/indexer/test/data.go
Normal file
92
accounts/pkg/indexer/test/data.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
// User is a user.
|
||||
type User struct {
|
||||
ID, UserName, Email string
|
||||
UID int
|
||||
}
|
||||
|
||||
// Pet is a pet.
|
||||
type Pet struct {
|
||||
ID, Kind, Color, Name string
|
||||
UID int
|
||||
}
|
||||
|
||||
// Data mock data.
|
||||
var Data = map[string][]interface{}{
|
||||
"users": {
|
||||
User{ID: "abcdefg-123", UserName: "mikey", Email: "mikey@example.com"},
|
||||
User{ID: "hijklmn-456", UserName: "frank", Email: "frank@example.com"},
|
||||
User{ID: "ewf4ofk-555", UserName: "jacky", Email: "jacky@example.com"},
|
||||
User{ID: "rulan54-777", UserName: "jones", Email: "jones@example.com"},
|
||||
},
|
||||
"pets": {
|
||||
Pet{ID: "rebef-123", Kind: "Dog", Color: "Brown", Name: "Waldo"},
|
||||
Pet{ID: "wefwe-456", Kind: "Cat", Color: "White", Name: "Snowy"},
|
||||
Pet{ID: "goefe-789", Kind: "Hog", Color: "Green", Name: "Dicky"},
|
||||
Pet{ID: "xadaf-189", Kind: "Hog", Color: "Green", Name: "Ricky"},
|
||||
},
|
||||
}
|
||||
|
||||
// WriteIndexTestData writes mock data to disk.
|
||||
func WriteIndexTestData(m map[string][]interface{}, privateKey, dir string) (string, error) {
|
||||
rootDir, err := getRootDir(dir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = writeData(m, privateKey, rootDir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return rootDir, nil
|
||||
}
|
||||
|
||||
// getRootDir allows for some minimal behavior on destination on disk. Testing the cs3 api behavior locally means
|
||||
// keeping track of where the cs3 data lives on disk, this function allows for multiplexing whether or not to use a
|
||||
// temporary folder or an already defined one.
|
||||
func getRootDir(dir string) (string, error) {
|
||||
var rootDir string
|
||||
var err error
|
||||
|
||||
if dir != "" {
|
||||
rootDir = dir
|
||||
} else {
|
||||
rootDir, err = CreateTmpDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return rootDir, nil
|
||||
}
|
||||
|
||||
// writeData writes test data to disk on rootDir location Marshaled as json.
|
||||
func writeData(m map[string][]interface{}, privateKey string, rootDir string) error {
|
||||
for dirName := range m {
|
||||
fileTypePath := path.Join(rootDir, dirName)
|
||||
|
||||
if err := os.MkdirAll(fileTypePath, 0777); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, u := range m[dirName] {
|
||||
data, err := json.Marshal(u)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pkVal := ValueOf(u, privateKey)
|
||||
if err := ioutil.WriteFile(path.Join(fileTypePath, pkVal), data, 0777); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
48
accounts/pkg/indexer/test/helpers.go
Normal file
48
accounts/pkg/indexer/test/helpers.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CreateTmpDir creates a temporary dir for tests data.
|
||||
func CreateTmpDir() (string, error) {
|
||||
name, err := ioutil.TempDir("/var/tmp", "testfiles-")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return name, nil
|
||||
}
|
||||
|
||||
// ValueOf gets the value of a type v on a given field <field>.
|
||||
func ValueOf(v interface{}, field string) string {
|
||||
r := reflect.ValueOf(v)
|
||||
f := reflect.Indirect(r).FieldByName(field)
|
||||
|
||||
return f.String()
|
||||
}
|
||||
|
||||
func getType(v interface{}) (reflect.Value, error) {
|
||||
rv := reflect.ValueOf(v)
|
||||
for rv.Kind() == reflect.Ptr || rv.Kind() == reflect.Interface {
|
||||
rv = rv.Elem()
|
||||
}
|
||||
if !rv.IsValid() {
|
||||
return reflect.Value{}, errors.New("failed to read value via reflection")
|
||||
}
|
||||
|
||||
return rv, nil
|
||||
}
|
||||
|
||||
// GetTypeFQN formats a valid name from a type <t>. This is a duplication of the already existing function in the
|
||||
// indexer package, but since there is a circular dependency we chose to duplicate it.
|
||||
func GetTypeFQN(t interface{}) string {
|
||||
typ, _ := getType(t)
|
||||
typeName := path.Join(typ.Type().PkgPath(), typ.Type().Name())
|
||||
typeName = strings.ReplaceAll(typeName, "/", ".")
|
||||
return typeName
|
||||
}
|
||||
34
accounts/pkg/indexer/test/test.go
Normal file
34
accounts/pkg/indexer/test/test.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/owncloud/ocis/storage/pkg/command"
|
||||
mcfg "github.com/owncloud/ocis/storage/pkg/config"
|
||||
)
|
||||
|
||||
func init() {
|
||||
go setupMetadataStorage()
|
||||
}
|
||||
|
||||
func setupMetadataStorage() {
|
||||
cfg := mcfg.New()
|
||||
app := cli.App{
|
||||
Name: "storage-metadata-for-tests",
|
||||
Commands: []*cli.Command{command.StorageMetadata(cfg)},
|
||||
}
|
||||
|
||||
_ = app.Command("storage-metadata").Run(cli.NewContext(&app, &flag.FlagSet{}, &cli.Context{Context: context.Background()}))
|
||||
|
||||
// wait until port is open
|
||||
d := net.Dialer{Timeout: 5 * time.Second}
|
||||
conn, err := d.Dial("tcp", "localhost:9125")
|
||||
if err != nil {
|
||||
panic("timeout waiting for storage")
|
||||
}
|
||||
conn.Close()
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -551,3 +551,77 @@ func (h *groupsServiceHandler) RemoveMember(ctx context.Context, in *RemoveMembe
|
||||
func (h *groupsServiceHandler) ListMembers(ctx context.Context, in *ListMembersRequest, out *ListMembersResponse) error {
|
||||
return h.GroupsServiceHandler.ListMembers(ctx, in, out)
|
||||
}
|
||||
|
||||
// Api Endpoints for IndexService service
|
||||
|
||||
func NewIndexServiceEndpoints() []*api.Endpoint {
|
||||
return []*api.Endpoint{
|
||||
&api.Endpoint{
|
||||
Name: "IndexService.RebuildIndex",
|
||||
Path: []string{"/api/v0/index/rebuild"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Client API for IndexService service
|
||||
|
||||
type IndexService interface {
|
||||
RebuildIndex(ctx context.Context, in *RebuildIndexRequest, opts ...client.CallOption) (*RebuildIndexResponse, error)
|
||||
}
|
||||
|
||||
type indexService struct {
|
||||
c client.Client
|
||||
name string
|
||||
}
|
||||
|
||||
func NewIndexService(name string, c client.Client) IndexService {
|
||||
return &indexService{
|
||||
c: c,
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *indexService) RebuildIndex(ctx context.Context, in *RebuildIndexRequest, opts ...client.CallOption) (*RebuildIndexResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "IndexService.RebuildIndex", in)
|
||||
out := new(RebuildIndexResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Server API for IndexService service
|
||||
|
||||
type IndexServiceHandler interface {
|
||||
RebuildIndex(context.Context, *RebuildIndexRequest, *RebuildIndexResponse) error
|
||||
}
|
||||
|
||||
func RegisterIndexServiceHandler(s server.Server, hdlr IndexServiceHandler, opts ...server.HandlerOption) error {
|
||||
type indexService interface {
|
||||
RebuildIndex(ctx context.Context, in *RebuildIndexRequest, out *RebuildIndexResponse) error
|
||||
}
|
||||
type IndexService struct {
|
||||
indexService
|
||||
}
|
||||
h := &indexServiceHandler{hdlr}
|
||||
opts = append(opts, api.WithEndpoint(&api.Endpoint{
|
||||
Name: "IndexService.RebuildIndex",
|
||||
Path: []string{"/api/v0/index/rebuild"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
}))
|
||||
return s.Handle(s.NewHandler(&IndexService{h}, opts...))
|
||||
}
|
||||
|
||||
type indexServiceHandler struct {
|
||||
IndexServiceHandler
|
||||
}
|
||||
|
||||
func (h *indexServiceHandler) RebuildIndex(ctx context.Context, in *RebuildIndexRequest, out *RebuildIndexResponse) error {
|
||||
return h.IndexServiceHandler.RebuildIndex(ctx, in, out)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -25,51 +26,88 @@ import (
|
||||
|
||||
var service = grpc.Service{}
|
||||
|
||||
const dataPath = "./accounts-store"
|
||||
var dataPath = createTmpDir()
|
||||
|
||||
var newCreatedAccounts = []string{}
|
||||
var newCreatedGroups = []string{}
|
||||
|
||||
var mockedRoleAssignment = map[string]string{}
|
||||
|
||||
var (
|
||||
user1 = proto.Account{
|
||||
Id: "f9149a32-2b8e-4f04-9e8d-937d81712b9a",
|
||||
AccountEnabled: true,
|
||||
IsResourceAccount: true,
|
||||
CreationType: "",
|
||||
DisplayName: "User One",
|
||||
PreferredName: "user1",
|
||||
OnPremisesSamAccountName: "user1",
|
||||
UidNumber: 20009,
|
||||
GidNumber: 30000,
|
||||
Mail: "user1@example.com",
|
||||
Identities: []*proto.Identities{nil},
|
||||
PasswordProfile: &proto.PasswordProfile{Password: "heysdjfsdlk"},
|
||||
MemberOf: []*proto.Group{
|
||||
{Id: "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa"}, // users
|
||||
},
|
||||
}
|
||||
user2 = proto.Account{
|
||||
Id: "e9149a32-2b8e-4f04-9e8d-937d81712b9a",
|
||||
AccountEnabled: true,
|
||||
IsResourceAccount: true,
|
||||
CreationType: "",
|
||||
DisplayName: "User Two",
|
||||
PreferredName: "user2",
|
||||
OnPremisesSamAccountName: "user2",
|
||||
UidNumber: 20010,
|
||||
GidNumber: 30000,
|
||||
Mail: "user2@example.com",
|
||||
Identities: []*proto.Identities{nil},
|
||||
PasswordProfile: &proto.PasswordProfile{Password: "hello123"},
|
||||
MemberOf: []*proto.Group{
|
||||
{Id: "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa"}, // users
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
service = grpc.NewService(
|
||||
grpc.Namespace("com.owncloud.api"),
|
||||
grpc.Name("accounts"),
|
||||
grpc.Address("localhost:9180"),
|
||||
)
|
||||
|
||||
cfg := config.New()
|
||||
cfg.Server.AccountsDataPath = dataPath
|
||||
cfg.Repo.Disk.Path = dataPath
|
||||
var hdlr *svc.Service
|
||||
var err error
|
||||
|
||||
if hdlr, err = svc.New(svc.Logger(command.NewLogger(cfg)), svc.Config(cfg), svc.RoleService(buildRoleServiceMock())); err != nil {
|
||||
log.Fatalf("Could not create new service")
|
||||
}
|
||||
|
||||
err = proto.RegisterAccountsServiceHandler(service.Server(), hdlr)
|
||||
if err != nil {
|
||||
log.Fatal("could not register the Accounts handler")
|
||||
}
|
||||
err = proto.RegisterGroupsServiceHandler(service.Server(), hdlr)
|
||||
if err != nil {
|
||||
log.Fatal("could not register the Groups handler")
|
||||
}
|
||||
|
||||
err = service.Server().Start()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func getAccount(user string) *proto.Account {
|
||||
switch user {
|
||||
case "user1":
|
||||
return &proto.Account{
|
||||
Id: "f9149a32-2b8e-4f04-9e8d-937d81712b9a",
|
||||
AccountEnabled: true,
|
||||
IsResourceAccount: true,
|
||||
CreationType: "",
|
||||
DisplayName: "User One",
|
||||
PreferredName: "user1",
|
||||
OnPremisesSamAccountName: "user1",
|
||||
UidNumber: 20009,
|
||||
GidNumber: 30000,
|
||||
Mail: "user1@example.com",
|
||||
Identities: []*proto.Identities{nil},
|
||||
PasswordProfile: &proto.PasswordProfile{Password: "heysdjfsdlk"},
|
||||
MemberOf: []*proto.Group{
|
||||
{Id: "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa"}, // users
|
||||
},
|
||||
}
|
||||
return &user1
|
||||
case "user2":
|
||||
return &proto.Account{
|
||||
Id: "e9149a32-2b8e-4f04-9e8d-937d81712b9a",
|
||||
AccountEnabled: true,
|
||||
IsResourceAccount: true,
|
||||
CreationType: "",
|
||||
DisplayName: "User Two",
|
||||
PreferredName: "user2",
|
||||
OnPremisesSamAccountName: "user2",
|
||||
UidNumber: 20009,
|
||||
GidNumber: 30000,
|
||||
Mail: "user2@example.com",
|
||||
Identities: []*proto.Identities{nil},
|
||||
PasswordProfile: &proto.PasswordProfile{Password: "hello123"},
|
||||
MemberOf: []*proto.Group{
|
||||
{Id: "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa"}, // users
|
||||
},
|
||||
}
|
||||
return &user2
|
||||
default:
|
||||
return &proto.Account{
|
||||
Id: fmt.Sprintf("new-id-%s", user),
|
||||
@@ -154,38 +192,6 @@ func getTestGroups(group string) *proto.Group {
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
service = grpc.NewService(
|
||||
grpc.Namespace("com.owncloud.api"),
|
||||
grpc.Name("accounts"),
|
||||
grpc.Address("localhost:9180"),
|
||||
)
|
||||
|
||||
cfg := config.New()
|
||||
cfg.Server.AccountsDataPath = dataPath
|
||||
cfg.Repo.Disk.Path = dataPath
|
||||
var hdlr *svc.Service
|
||||
var err error
|
||||
|
||||
if hdlr, err = svc.New(svc.Logger(command.NewLogger(cfg)), svc.Config(cfg), svc.RoleService(buildRoleServiceMock())); err != nil {
|
||||
log.Fatalf("Could not create new service")
|
||||
}
|
||||
|
||||
err = proto.RegisterAccountsServiceHandler(service.Server(), hdlr)
|
||||
if err != nil {
|
||||
log.Fatal("could not register the Accounts handler")
|
||||
}
|
||||
err = proto.RegisterGroupsServiceHandler(service.Server(), hdlr)
|
||||
if err != nil {
|
||||
log.Fatal("could not register the Groups handler")
|
||||
}
|
||||
|
||||
err = service.Server().Start()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func buildRoleServiceMock() settings.RoleService {
|
||||
return settings.MockRoleService{
|
||||
AssignRoleToUserFunc: func(ctx context.Context, req *settings.AssignRoleToUserRequest, opts ...client.CallOption) (res *settings.AssignRoleToUserResponse, err error) {
|
||||
@@ -208,8 +214,7 @@ func cleanUp(t *testing.T) {
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
_, err := deleteAccount(t, id)
|
||||
checkError(t, err)
|
||||
_, _ = deleteAccount(t, id)
|
||||
}
|
||||
|
||||
datastore = filepath.Join(dataPath, "groups")
|
||||
@@ -219,8 +224,7 @@ func cleanUp(t *testing.T) {
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
_, err := deleteGroup(t, id)
|
||||
checkError(t, err)
|
||||
_, _ = deleteGroup(t, id)
|
||||
}
|
||||
|
||||
newCreatedAccounts = []string{}
|
||||
@@ -309,12 +313,6 @@ func assertGroupHasMember(t *testing.T, grp *proto.Group, memberId string) {
|
||||
t.Fatalf("Member with id %s expected to be in group '%s', but not found", memberId, grp.DisplayName)
|
||||
}
|
||||
|
||||
func checkError(t *testing.T, err error) {
|
||||
if err != nil {
|
||||
t.Fatalf("Expected Error to be nil but got %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func createAccount(t *testing.T, user string) (*proto.Account, error) {
|
||||
client := service.Client()
|
||||
cl := proto.NewAccountsService("com.owncloud.api.accounts", client)
|
||||
@@ -368,7 +366,7 @@ func listGroups(t *testing.T) *proto.ListGroupsResponse {
|
||||
cl := proto.NewGroupsService("com.owncloud.api.accounts", client)
|
||||
|
||||
response, err := cl.ListGroups(context.Background(), request)
|
||||
checkError(t, err)
|
||||
assert.NoError(t, err)
|
||||
return response
|
||||
}
|
||||
|
||||
@@ -390,18 +388,27 @@ func deleteGroup(t *testing.T, id string) (*empty.Empty, error) {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// createTmpDir creates a temporary dir for tests data.
|
||||
func createTmpDir() string {
|
||||
name, err := ioutil.TempDir("/var/tmp", "ocis-accounts-store-")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
|
||||
// https://github.com/owncloud/ocis/accounts/issues/61
|
||||
func TestCreateAccount(t *testing.T) {
|
||||
|
||||
resp, err := createAccount(t, "user1")
|
||||
checkError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assertUserExists(t, getAccount("user1"))
|
||||
assert.IsType(t, &proto.Account{}, resp)
|
||||
// Account is not returned in response
|
||||
// assertAccountsSame(t, getAccount("user1"), resp)
|
||||
|
||||
resp, err = createAccount(t, "user2")
|
||||
checkError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assertUserExists(t, getAccount("user2"))
|
||||
assert.IsType(t, &proto.Account{}, resp)
|
||||
// Account is not returned in response
|
||||
@@ -410,24 +417,25 @@ func TestCreateAccount(t *testing.T) {
|
||||
cleanUp(t)
|
||||
}
|
||||
|
||||
// https://github.com/owncloud/ocis/accounts/issues/62
|
||||
// https://github.com/owncloud/ocis-accounts/issues/62
|
||||
func TestCreateExistingUser(t *testing.T) {
|
||||
createAccount(t, "user1")
|
||||
_, err := createAccount(t, "user1")
|
||||
var err error
|
||||
_, err = createAccount(t, "user1")
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Should give error but it does not
|
||||
checkError(t, err)
|
||||
_, err = createAccount(t, "user1")
|
||||
assert.Error(t, err)
|
||||
assertUserExists(t, getAccount("user1"))
|
||||
|
||||
cleanUp(t)
|
||||
}
|
||||
|
||||
// All tests fail after running this
|
||||
// https://github.com/owncloud/ocis/accounts/issues/62
|
||||
// https://github.com/owncloud/ocis/accounts-issues/62
|
||||
func TestCreateAccountInvalidUserName(t *testing.T) {
|
||||
|
||||
resp, err := listAccounts(t)
|
||||
checkError(t, err)
|
||||
assert.NoError(t, err)
|
||||
numAccounts := len(resp.GetAccounts())
|
||||
|
||||
testData := []string{
|
||||
@@ -448,7 +456,7 @@ func TestCreateAccountInvalidUserName(t *testing.T) {
|
||||
|
||||
// resp should have the same number of accounts
|
||||
resp, err = listAccounts(t)
|
||||
checkError(t, err)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, numAccounts, len(resp.GetAccounts()))
|
||||
|
||||
@@ -456,57 +464,58 @@ func TestCreateAccountInvalidUserName(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUpdateAccount(t *testing.T) {
|
||||
_, _ = createAccount(t, "user1")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
userAccount *proto.Account
|
||||
name string
|
||||
userAccount *proto.Account
|
||||
expectedErrOnUpdate error
|
||||
}{
|
||||
{
|
||||
"Update user (demonstration of updatable fields)",
|
||||
&proto.Account{
|
||||
DisplayName: "Alice Hansen",
|
||||
PreferredName: "Wonderful Alice",
|
||||
OnPremisesDistinguishedName: "Alice",
|
||||
UidNumber: 20010,
|
||||
GidNumber: 30001,
|
||||
Mail: "alice@example.com",
|
||||
DisplayName: "Alice Hansen",
|
||||
PreferredName: "Wonderful-Alice",
|
||||
OnPremisesSamAccountName: "Alice",
|
||||
UidNumber: 20010,
|
||||
GidNumber: 30001,
|
||||
Mail: "alice@example.com",
|
||||
},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"Update user with unicode data",
|
||||
&proto.Account{
|
||||
DisplayName: "एलिस हेन्सेन",
|
||||
PreferredName: "अद्भुत एलिस",
|
||||
OnPremisesDistinguishedName: "एलिस",
|
||||
UidNumber: 20010,
|
||||
GidNumber: 30001,
|
||||
Mail: "एलिस@उदाहरण.com",
|
||||
DisplayName: "एलिस हेन्सेन",
|
||||
PreferredName: "अद्भुत-एलिस",
|
||||
OnPremisesSamAccountName: "एलिस",
|
||||
UidNumber: 20010,
|
||||
GidNumber: 30001,
|
||||
Mail: "एलिस@उदाहरण.com",
|
||||
},
|
||||
merrors.BadRequest(".", "preferred_name 'अद्भुत-एलिस' must be at least the local part of an email"),
|
||||
},
|
||||
{
|
||||
"Update user with empty data values",
|
||||
&proto.Account{
|
||||
DisplayName: "",
|
||||
PreferredName: "",
|
||||
OnPremisesDistinguishedName: "",
|
||||
UidNumber: 0,
|
||||
GidNumber: 0,
|
||||
Mail: "",
|
||||
DisplayName: "",
|
||||
PreferredName: "",
|
||||
OnPremisesSamAccountName: "",
|
||||
UidNumber: 0,
|
||||
GidNumber: 0,
|
||||
Mail: "",
|
||||
},
|
||||
merrors.BadRequest(".", "preferred_name '' must be at least the local part of an email"),
|
||||
},
|
||||
{
|
||||
"Update user with strange data",
|
||||
&proto.Account{
|
||||
DisplayName: "12345",
|
||||
PreferredName: "12345",
|
||||
OnPremisesDistinguishedName: "54321",
|
||||
UidNumber: 1000,
|
||||
GidNumber: 1000,
|
||||
// No email validation
|
||||
// https://github.com/owncloud/ocis/accounts/issues/77
|
||||
Mail: "1.2@3.c_@",
|
||||
DisplayName: "12345",
|
||||
PreferredName: "a12345",
|
||||
OnPremisesSamAccountName: "a54321",
|
||||
UidNumber: 1000,
|
||||
GidNumber: 1000,
|
||||
Mail: "1.2@3.c_@",
|
||||
},
|
||||
merrors.BadRequest(".", "mail '1.2@3.c_@' must be a valid email"),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -524,20 +533,25 @@ func TestUpdateAccount(t *testing.T) {
|
||||
}
|
||||
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.userAccount.Id = "f9149a32-2b8e-4f04-9e8d-937d81712b9a"
|
||||
acc, err := createAccount(t, "user1")
|
||||
assert.NoError(t, err)
|
||||
|
||||
tt.userAccount.Id = acc.Id
|
||||
tt.userAccount.AccountEnabled = false
|
||||
tt.userAccount.IsResourceAccount = false
|
||||
resp, err := updateAccount(t, tt.userAccount, updateMask)
|
||||
|
||||
checkError(t, err)
|
||||
|
||||
assert.IsType(t, &proto.Account{}, resp)
|
||||
assertAccountsSame(t, tt.userAccount, resp)
|
||||
assertUserExists(t, tt.userAccount)
|
||||
if tt.expectedErrOnUpdate != nil {
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, tt.expectedErrOnUpdate, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.IsType(t, &proto.Account{}, resp)
|
||||
assertAccountsSame(t, tt.userAccount, resp)
|
||||
assertUserExists(t, tt.userAccount)
|
||||
}
|
||||
cleanUp(t)
|
||||
})
|
||||
}
|
||||
|
||||
cleanUp(t)
|
||||
}
|
||||
|
||||
func TestUpdateNonUpdatableFieldsInAccount(t *testing.T) {
|
||||
@@ -554,6 +568,7 @@ func TestUpdateNonUpdatableFieldsInAccount(t *testing.T) {
|
||||
"CreationType",
|
||||
},
|
||||
&proto.Account{
|
||||
Id: user1.Id,
|
||||
CreationType: "Type Test",
|
||||
},
|
||||
},
|
||||
@@ -563,6 +578,7 @@ func TestUpdateNonUpdatableFieldsInAccount(t *testing.T) {
|
||||
"PasswordProfile",
|
||||
},
|
||||
&proto.Account{
|
||||
Id: user1.Id,
|
||||
PasswordProfile: &proto.PasswordProfile{Password: "new password"},
|
||||
},
|
||||
},
|
||||
@@ -572,6 +588,7 @@ func TestUpdateNonUpdatableFieldsInAccount(t *testing.T) {
|
||||
"MemberOf",
|
||||
},
|
||||
&proto.Account{
|
||||
Id: user1.Id,
|
||||
MemberOf: []*proto.Group{
|
||||
{Id: "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa"},
|
||||
},
|
||||
@@ -580,7 +597,6 @@ func TestUpdateNonUpdatableFieldsInAccount(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.userAccount.Id = "f9149a32-2b8e-4f04-9e8d-937d81712b9a"
|
||||
res, err := updateAccount(t, tt.userAccount, tt.updateMask)
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error while updating non updatable field, but found none.")
|
||||
@@ -602,6 +618,8 @@ func TestUpdateNonUpdatableFieldsInAccount(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
cleanUp(t)
|
||||
}
|
||||
|
||||
func TestListAccounts(t *testing.T) {
|
||||
@@ -609,7 +627,7 @@ func TestListAccounts(t *testing.T) {
|
||||
createAccount(t, "user2")
|
||||
|
||||
resp, err := listAccounts(t)
|
||||
checkError(t, err)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.IsType(t, &proto.ListAccountsResponse{}, resp)
|
||||
assert.Equal(t, 8, len(resp.Accounts))
|
||||
@@ -622,31 +640,128 @@ func TestListAccounts(t *testing.T) {
|
||||
|
||||
func TestListWithoutUserCreation(t *testing.T) {
|
||||
resp, err := listAccounts(t)
|
||||
|
||||
checkError(t, err)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Only 5 default users
|
||||
assert.Equal(t, 6, len(resp.Accounts))
|
||||
cleanUp(t)
|
||||
}
|
||||
|
||||
func TestListAccountsWithFilterQuery(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
name string
|
||||
query string
|
||||
expectedIDs []string
|
||||
}{
|
||||
// FIXME: disabled test scenarios need to be supported when implementing OData support
|
||||
// OData implementation tracked in https://github.com/owncloud/ocis/issues/716
|
||||
//{
|
||||
// name: "ListAccounts with exact match on preferred_name",
|
||||
// query: "preferred_name eq 'user1'",
|
||||
// expectedIDs: []string{user1.Id},
|
||||
//},
|
||||
{
|
||||
name: "ListAccounts with exact match on on_premises_sam_account_name",
|
||||
query: "on_premises_sam_account_name eq 'user1'",
|
||||
expectedIDs: []string{user1.Id},
|
||||
},
|
||||
{
|
||||
name: "ListAccounts with exact match on mail",
|
||||
query: "mail eq 'user1@example.com'",
|
||||
expectedIDs: []string{user1.Id},
|
||||
},
|
||||
//{
|
||||
// name: "ListAccounts with exact match on id",
|
||||
// query: "id eq 'f9149a32-2b8e-4f04-9e8d-937d81712b9a'",
|
||||
// expectedIDs: []string{user1.Id},
|
||||
//},
|
||||
//{
|
||||
// name: "ListAccounts without match on preferred_name",
|
||||
// query: "preferred_name eq 'wololo'",
|
||||
// expectedIDs: []string{},
|
||||
//},
|
||||
//{
|
||||
// name: "ListAccounts with exact match on preferred_name AND mail",
|
||||
// query: "preferred_name eq 'user1' and mail eq 'user1@example.com'",
|
||||
// expectedIDs: []string{user1.Id},
|
||||
//},
|
||||
//{
|
||||
// name: "ListAccounts without match on preferred_name AND mail",
|
||||
// query: "preferred_name eq 'user1' and mail eq 'wololo@example.com'",
|
||||
// expectedIDs: []string{},
|
||||
//},
|
||||
//{
|
||||
// name: "ListAccounts with exact match on preferred_name OR mail, preferred_name exists, mail exists",
|
||||
// query: "preferred_name eq 'user1' or mail eq 'user1@example.com'",
|
||||
// expectedIDs: []string{user1.Id},
|
||||
//},
|
||||
//{
|
||||
// name: "ListAccounts with exact match on preferred_name OR mail, preferred_name exists, mail does not exist",
|
||||
// query: "preferred_name eq 'user1' or mail eq 'wololo@example.com'",
|
||||
// expectedIDs: []string{user1.Id},
|
||||
//},
|
||||
//{
|
||||
// name: "ListAccounts with exact match on preferred_name OR mail, preferred_name does not exists, mail exists",
|
||||
// query: "preferred_name eq 'wololo' or mail eq 'user1@example.com'",
|
||||
// expectedIDs: []string{user1.Id},
|
||||
//},
|
||||
//{
|
||||
// name: "ListAccounts without match on preferred_name OR mail, preferred_name and mail do not exist",
|
||||
// query: "preferred_name eq 'wololo' or mail eq 'wololo@example.com'",
|
||||
// expectedIDs: []string{},
|
||||
//},
|
||||
//{
|
||||
// name: "ListAccounts with multiple matches on preferred_name",
|
||||
// query: "startswith(preferred_name,'user*')",
|
||||
// expectedIDs: []string{user1.Id, user2.Id},
|
||||
//},
|
||||
//{
|
||||
// name: "ListAccounts with multiple matches on on_premises_sam_account_name",
|
||||
// query: "startswith(on_premises_sam_account_name,'user*')",
|
||||
// expectedIDs: []string{user1.Id, user2.Id},
|
||||
//},
|
||||
}
|
||||
|
||||
cl := proto.NewAccountsService("com.owncloud.api.accounts", service.Client())
|
||||
|
||||
for _, scenario := range scenarios {
|
||||
t.Run(scenario.name, func(t *testing.T) {
|
||||
_, err := createAccount(t, "user1")
|
||||
assert.NoError(t, err)
|
||||
_, err = createAccount(t, "user2")
|
||||
assert.NoError(t, err)
|
||||
|
||||
req := &proto.ListAccountsRequest{Query: scenario.query}
|
||||
res, err := cl.ListAccounts(context.Background(), req)
|
||||
assert.NoError(t, err)
|
||||
ids := make([]string, 0)
|
||||
for _, acc := range res.Accounts {
|
||||
ids = append(ids, acc.Id)
|
||||
}
|
||||
assert.Equal(t, scenario.expectedIDs, ids)
|
||||
cleanUp(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAccount(t *testing.T) {
|
||||
createAccount(t, "user1")
|
||||
|
||||
req := &proto.GetAccountRequest{Id: getAccount("user1").Id}
|
||||
|
||||
client := service.Client()
|
||||
cl := proto.NewAccountsService("com.owncloud.api.accounts", client)
|
||||
cl := proto.NewAccountsService("com.owncloud.api.accounts", service.Client())
|
||||
|
||||
resp, err := cl.GetAccount(context.Background(), req)
|
||||
|
||||
checkError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.IsType(t, &proto.Account{}, resp)
|
||||
assertAccountsSame(t, getAccount("user1"), resp)
|
||||
|
||||
cleanUp(t)
|
||||
}
|
||||
|
||||
//TODO: This segfaults! WIP
|
||||
|
||||
func TestDeleteAccount(t *testing.T) {
|
||||
createAccount(t, "user1")
|
||||
createAccount(t, "user2")
|
||||
@@ -657,7 +772,7 @@ func TestDeleteAccount(t *testing.T) {
|
||||
cl := proto.NewAccountsService("com.owncloud.api.accounts", client)
|
||||
|
||||
resp, err := cl.DeleteAccount(context.Background(), req)
|
||||
checkError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.IsType(t, resp, &empty.Empty{})
|
||||
|
||||
// Check the account doesn't exists anymore
|
||||
@@ -675,9 +790,9 @@ func TestListGroups(t *testing.T) {
|
||||
cl := proto.NewGroupsService("com.owncloud.api.accounts", client)
|
||||
|
||||
resp, err := cl.ListGroups(context.Background(), req)
|
||||
checkError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.IsType(t, &proto.ListGroupsResponse{}, resp)
|
||||
assert.Equal(t, len(resp.Groups), 9)
|
||||
assert.Equal(t, 9, len(resp.Groups))
|
||||
|
||||
groups := []string{
|
||||
"sysusers",
|
||||
@@ -718,7 +833,7 @@ func TestGetGroups(t *testing.T) {
|
||||
req := &proto.GetGroupRequest{Id: group.Id}
|
||||
resp, err := cl.GetGroup(context.Background(), req)
|
||||
|
||||
checkError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.IsType(t, &proto.Group{}, resp)
|
||||
assertGroupsSame(t, group, resp)
|
||||
}
|
||||
@@ -733,7 +848,7 @@ func TestCreateGroup(t *testing.T) {
|
||||
}}
|
||||
|
||||
res, err := createGroup(t, group)
|
||||
checkError(t, err)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.IsType(t, &proto.Group{}, res)
|
||||
|
||||
@@ -772,12 +887,12 @@ func TestDeleteGroup(t *testing.T) {
|
||||
req := &proto.DeleteGroupRequest{Id: grp1.Id}
|
||||
res, err := cl.DeleteGroup(context.Background(), req)
|
||||
assert.IsType(t, res, &empty.Empty{})
|
||||
checkError(t, err)
|
||||
assert.NoError(t, err)
|
||||
|
||||
req = &proto.DeleteGroupRequest{Id: grp2.Id}
|
||||
res, err = cl.DeleteGroup(context.Background(), req)
|
||||
assert.IsType(t, res, &empty.Empty{})
|
||||
checkError(t, err)
|
||||
assert.NoError(t, err)
|
||||
|
||||
groupsResponse := listGroups(t)
|
||||
assertResponseNotContainsGroup(t, groupsResponse, grp1)
|
||||
@@ -870,7 +985,7 @@ func TestAddMember(t *testing.T) {
|
||||
req := &proto.AddMemberRequest{GroupId: grp1.Id, AccountId: account.Id}
|
||||
|
||||
res, err := cl.AddMember(context.Background(), req)
|
||||
checkError(t, err)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.IsType(t, &proto.Group{}, res)
|
||||
|
||||
@@ -904,7 +1019,7 @@ func TestAddMemberAlreadyInGroup(t *testing.T) {
|
||||
res, err := cl.AddMember(context.Background(), req)
|
||||
|
||||
// Should Give Error
|
||||
checkError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.IsType(t, &proto.Group{}, res)
|
||||
//assert.Equal(t, proto.Group{}, *res)
|
||||
//assertGroupsSame(t, updatedGroup, res)
|
||||
@@ -975,7 +1090,7 @@ func TestRemoveMember(t *testing.T) {
|
||||
req := &proto.RemoveMemberRequest{GroupId: grp1.Id, AccountId: account.Id}
|
||||
|
||||
res, err := cl.RemoveMember(context.Background(), req)
|
||||
checkError(t, err)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.IsType(t, &proto.Group{}, res)
|
||||
//assert.Equal(t, proto.Group{}, *res)
|
||||
@@ -1034,7 +1149,7 @@ func TestRemoveMemberNotInGroup(t *testing.T) {
|
||||
res, err := cl.RemoveMember(context.Background(), req)
|
||||
|
||||
// Should give an error
|
||||
checkError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.IsType(t, &proto.Group{}, res)
|
||||
|
||||
//assert.Error(t, err)
|
||||
@@ -1071,7 +1186,7 @@ func TestListMembers(t *testing.T) {
|
||||
req := &proto.ListMembersRequest{Id: expectedGroup.Id}
|
||||
|
||||
res, err := cl.ListMembers(context.Background(), req)
|
||||
checkError(t, err)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, len(res.Members), len(expectedGroup.Members))
|
||||
|
||||
@@ -1093,19 +1208,23 @@ func TestListMembers(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListMembersEmptyGroup(t *testing.T) {
|
||||
group := &proto.Group{Id: "5d58e5ec-842e-498b-8800-61f2ec6f911c", GidNumber: 30002, OnPremisesSamAccountName: "quantum-group", DisplayName: "Quantum Group", Members: []*proto.Account{}}
|
||||
|
||||
createGroup(t, group)
|
||||
group := &proto.Group{Id: "5d58e5ec-842e-498b-8800-61f2ec6f911c", GidNumber: 60000, OnPremisesSamAccountName: "quantum-group", DisplayName: "Quantum Group", Members: []*proto.Account{}}
|
||||
|
||||
client := service.Client()
|
||||
cl := proto.NewGroupsService("com.owncloud.api.accounts", client)
|
||||
|
||||
request := &proto.CreateGroupRequest{Group: group}
|
||||
_, err := cl.CreateGroup(context.Background(), request)
|
||||
if err == nil {
|
||||
newCreatedGroups = append(newCreatedGroups, group.Id)
|
||||
}
|
||||
|
||||
req := &proto.ListMembersRequest{Id: group.Id}
|
||||
|
||||
res, err := cl.ListMembers(context.Background(), req)
|
||||
listRes, err := cl.ListMembers(context.Background(), req)
|
||||
|
||||
checkError(t, err)
|
||||
assert.Empty(t, res.Members)
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, listRes.Members)
|
||||
|
||||
cleanUp(t)
|
||||
}
|
||||
@@ -1120,12 +1239,12 @@ func TestAccountUpdateMask(t *testing.T) {
|
||||
Account: &proto.Account{
|
||||
Id: user1.Id,
|
||||
DisplayName: "ShouldBeUpdated",
|
||||
PreferredName: "ShouldStaySame",
|
||||
PreferredName: "ShouldStaySame And Is Invalid Anyway",
|
||||
}}
|
||||
|
||||
cl := proto.NewAccountsService("com.owncloud.api.accounts", client)
|
||||
res, err := cl.UpdateAccount(context.Background(), req)
|
||||
checkError(t, err)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "ShouldBeUpdated", res.DisplayName)
|
||||
assert.Equal(t, user1.PreferredName, res.PreferredName)
|
||||
|
||||
@@ -372,6 +372,120 @@ func RegisterGroupsServiceWeb(r chi.Router, i GroupsServiceHandler, middlewares
|
||||
r.MethodFunc("POST", "/api/v0/groups/{id=*}/members/$ref", handler.ListMembers)
|
||||
}
|
||||
|
||||
type webIndexServiceHandler struct {
|
||||
r chi.Router
|
||||
h IndexServiceHandler
|
||||
}
|
||||
|
||||
func (h *webIndexServiceHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
h.r.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func (h *webIndexServiceHandler) RebuildIndex(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
req := &RebuildIndexRequest{}
|
||||
|
||||
resp := &RebuildIndexResponse{}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusPreconditionFailed)
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.h.RebuildIndex(
|
||||
r.Context(),
|
||||
req,
|
||||
resp,
|
||||
); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
render.Status(r, http.StatusCreated)
|
||||
render.JSON(w, r, resp)
|
||||
}
|
||||
|
||||
func RegisterIndexServiceWeb(r chi.Router, i IndexServiceHandler, middlewares ...func(http.Handler) http.Handler) {
|
||||
handler := &webIndexServiceHandler{
|
||||
r: r,
|
||||
h: i,
|
||||
}
|
||||
|
||||
r.MethodFunc("POST", "/api/v0/index/rebuild", handler.RebuildIndex)
|
||||
}
|
||||
|
||||
// RebuildIndexRequestJSONMarshaler describes the default jsonpb.Marshaler used by all
|
||||
// instances of RebuildIndexRequest. This struct is safe to replace or modify but
|
||||
// should not be done so concurrently.
|
||||
var RebuildIndexRequestJSONMarshaler = new(jsonpb.Marshaler)
|
||||
|
||||
// MarshalJSON satisfies the encoding/json Marshaler interface. This method
|
||||
// uses the more correct jsonpb package to correctly marshal the message.
|
||||
func (m *RebuildIndexRequest) MarshalJSON() ([]byte, error) {
|
||||
if m == nil {
|
||||
return json.Marshal(nil)
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
if err := RebuildIndexRequestJSONMarshaler.Marshal(buf, m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
var _ json.Marshaler = (*RebuildIndexRequest)(nil)
|
||||
|
||||
// RebuildIndexRequestJSONUnmarshaler describes the default jsonpb.Unmarshaler used by all
|
||||
// instances of RebuildIndexRequest. This struct is safe to replace or modify but
|
||||
// should not be done so concurrently.
|
||||
var RebuildIndexRequestJSONUnmarshaler = new(jsonpb.Unmarshaler)
|
||||
|
||||
// UnmarshalJSON satisfies the encoding/json Unmarshaler interface. This method
|
||||
// uses the more correct jsonpb package to correctly unmarshal the message.
|
||||
func (m *RebuildIndexRequest) UnmarshalJSON(b []byte) error {
|
||||
return RebuildIndexRequestJSONUnmarshaler.Unmarshal(bytes.NewReader(b), m)
|
||||
}
|
||||
|
||||
var _ json.Unmarshaler = (*RebuildIndexRequest)(nil)
|
||||
|
||||
// RebuildIndexResponseJSONMarshaler describes the default jsonpb.Marshaler used by all
|
||||
// instances of RebuildIndexResponse. This struct is safe to replace or modify but
|
||||
// should not be done so concurrently.
|
||||
var RebuildIndexResponseJSONMarshaler = new(jsonpb.Marshaler)
|
||||
|
||||
// MarshalJSON satisfies the encoding/json Marshaler interface. This method
|
||||
// uses the more correct jsonpb package to correctly marshal the message.
|
||||
func (m *RebuildIndexResponse) MarshalJSON() ([]byte, error) {
|
||||
if m == nil {
|
||||
return json.Marshal(nil)
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
if err := RebuildIndexResponseJSONMarshaler.Marshal(buf, m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
var _ json.Marshaler = (*RebuildIndexResponse)(nil)
|
||||
|
||||
// RebuildIndexResponseJSONUnmarshaler describes the default jsonpb.Unmarshaler used by all
|
||||
// instances of RebuildIndexResponse. This struct is safe to replace or modify but
|
||||
// should not be done so concurrently.
|
||||
var RebuildIndexResponseJSONUnmarshaler = new(jsonpb.Unmarshaler)
|
||||
|
||||
// UnmarshalJSON satisfies the encoding/json Unmarshaler interface. This method
|
||||
// uses the more correct jsonpb package to correctly unmarshal the message.
|
||||
func (m *RebuildIndexResponse) UnmarshalJSON(b []byte) error {
|
||||
return RebuildIndexResponseJSONUnmarshaler.Unmarshal(bytes.NewReader(b), m)
|
||||
}
|
||||
|
||||
var _ json.Unmarshaler = (*RebuildIndexResponse)(nil)
|
||||
|
||||
// ListAccountsRequestJSONMarshaler describes the default jsonpb.Marshaler used by all
|
||||
// instances of ListAccountsRequest. This struct is safe to replace or modify but
|
||||
// should not be done so concurrently.
|
||||
|
||||
@@ -136,6 +136,23 @@ service GroupsService {
|
||||
|
||||
}
|
||||
|
||||
service IndexService {
|
||||
rpc RebuildIndex(RebuildIndexRequest) returns (RebuildIndexResponse) {
|
||||
// All request parameters go into body.
|
||||
option (google.api.http) = {
|
||||
// URLs are broken
|
||||
post: "/api/v0/index/rebuild"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
message RebuildIndexRequest {
|
||||
}
|
||||
|
||||
message RebuildIndexResponse {
|
||||
}
|
||||
|
||||
message ListAccountsRequest {
|
||||
// Optional. The maximum number of accounts to return in the response
|
||||
int32 page_size = 1 [(google.api.field_behavior) = OPTIONAL];
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,13 +0,0 @@
|
||||
package proto
|
||||
|
||||
// BleveAccount wraps the generated Account and adds a bleve type that is used to distinguish documents in the index
|
||||
type BleveAccount struct {
|
||||
Account
|
||||
BleveType string `json:"bleve_type"`
|
||||
}
|
||||
|
||||
// BleveGroup wraps the generated Group and adds a bleve type that is used to distinguish documents in the index
|
||||
type BleveGroup struct {
|
||||
Group
|
||||
BleveType string `json:"bleve_type"`
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/CiscoM31/godata"
|
||||
"github.com/blevesearch/bleve"
|
||||
"github.com/blevesearch/bleve/search/query"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// add (ap)prox filter
|
||||
godata.GlobalFilterTokenizer = FilterTokenizer()
|
||||
godata.GlobalFilterParser.DefineOperator("ap", 2, godata.OpAssociationLeft, 4, false)
|
||||
}
|
||||
|
||||
// BuildBleveQuery converts a GoDataFilterQuery into a bleve query
|
||||
func BuildBleveQuery(r *godata.GoDataFilterQuery) (query.Query, error) {
|
||||
return recursiveBuildQuery(r.Tree)
|
||||
}
|
||||
|
||||
// Builds the filter recursively using DFS
|
||||
func recursiveBuildQuery(n *godata.ParseNode) (query.Query, error) {
|
||||
if n.Token.Type == godata.FilterTokenFunc {
|
||||
switch n.Token.Value {
|
||||
case "startswith":
|
||||
if len(n.Children) != 2 {
|
||||
return nil, errors.New("startswith match must have two children")
|
||||
}
|
||||
if n.Children[0].Token.Type != godata.FilterTokenLiteral {
|
||||
return nil, errors.New("startswith expected a literal as the first param")
|
||||
}
|
||||
if n.Children[1].Token.Type != godata.FilterTokenString {
|
||||
return nil, errors.New("startswith expected a string as the second param")
|
||||
} // remove enclosing ' of string tokens (looks like 'some ol'' string')
|
||||
value := n.Children[1].Token.Value[1 : len(n.Children[1].Token.Value)-1]
|
||||
// unescape '' as '
|
||||
unescaped := strings.ReplaceAll(value, "''", "'")
|
||||
q := bleve.NewPrefixQuery(unescaped)
|
||||
q.SetField(n.Children[0].Token.Value)
|
||||
return q, nil
|
||||
// TODO contains as regex?
|
||||
// TODO endswith as regex?
|
||||
default:
|
||||
return nil, godata.NotImplementedError(n.Token.Value + " is not implemented.")
|
||||
}
|
||||
}
|
||||
if n.Token.Type == godata.FilterTokenLogical {
|
||||
switch n.Token.Value {
|
||||
case "eq":
|
||||
if len(n.Children) != 2 {
|
||||
return nil, errors.New("equality match must have two children")
|
||||
}
|
||||
if n.Children[0].Token.Type != godata.FilterTokenLiteral {
|
||||
return nil, errors.New("equality expected a literal on the lhs")
|
||||
}
|
||||
if n.Children[1].Token.Type == godata.FilterTokenString {
|
||||
// for escape rules see http://docs.oasis-open.org/odata/odata/v4.01/cs01/part2-url-conventions/odata-v4.01-cs01-part2-url-conventions.html#sec_URLComponents
|
||||
// remove enclosing ' of string tokens (looks like 'some ol'' string')
|
||||
value := n.Children[1].Token.Value[1 : len(n.Children[1].Token.Value)-1]
|
||||
// unescape '' as '
|
||||
unescaped := strings.ReplaceAll(value, "''", "'")
|
||||
// use a match query, so the field mapping, e.g. lowercase is applied to the value
|
||||
// remember we defined the field mapping for `preferred_name` to be lowercase
|
||||
// a term query like `preferred_name eq 'Artur'` would use `Artur` to search in the index and come up empty
|
||||
// a match query will apply the field mapping (lowercasing `Artur` to `artur`) before doing the search
|
||||
// TODO there is a mismatch between the LDAP and odata filters:
|
||||
// - LDAP matching rules depend on the attribute: see https://ldapwiki.com/wiki/MatchingRule
|
||||
// - odata has functions like `startswith`, `contains`, `tolower`, `toupper`, `matchesPattern` andy more: see http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_BuiltinQueryFunctions
|
||||
// - ocis-glauth should do the mapping between LDAP and odata filter
|
||||
q := bleve.NewMatchQuery(unescaped)
|
||||
q.SetField(n.Children[0].Token.Value)
|
||||
return q, nil
|
||||
} else if n.Children[1].Token.Type == godata.FilterTokenInteger {
|
||||
v, err := strconv.ParseFloat(n.Children[1].Token.Value, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
incl := true
|
||||
q := bleve.NewNumericRangeInclusiveQuery(&v, &v, &incl, &incl)
|
||||
q.SetField(n.Children[0].Token.Value)
|
||||
return q, nil
|
||||
}
|
||||
return nil, fmt.Errorf("equality expected a string or int on the rhs, got %d", n.Children[1].Token.Type)
|
||||
case "and":
|
||||
q := query.NewConjunctionQuery([]query.Query{})
|
||||
for _, child := range n.Children {
|
||||
subQuery, err := recursiveBuildQuery(child)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if subQuery != nil {
|
||||
q.AddQuery(subQuery)
|
||||
}
|
||||
}
|
||||
return q, nil
|
||||
case "or":
|
||||
q := query.NewDisjunctionQuery([]query.Query{})
|
||||
for _, child := range n.Children {
|
||||
subQuery, err := recursiveBuildQuery(child)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if subQuery != nil {
|
||||
q.AddQuery(subQuery)
|
||||
}
|
||||
}
|
||||
return q, nil
|
||||
case "Not":
|
||||
if len(n.Children) != 1 {
|
||||
return nil, errors.New("not filter must have only one child")
|
||||
}
|
||||
subQuery, err := recursiveBuildQuery(n.Children[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
q := query.NewBooleanQuery(nil, nil, []query.Query{subQuery})
|
||||
return q, nil
|
||||
default:
|
||||
return nil, godata.NotImplementedError(n.Token.Value + " is not implemented.")
|
||||
}
|
||||
}
|
||||
|
||||
return nil, godata.NotImplementedError(n.Token.Value + " is not implemented.")
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package provider
|
||||
|
||||
import "github.com/CiscoM31/godata"
|
||||
|
||||
// FilterTokenizer creates a tokenizer capable of tokenizing filter statements
|
||||
// TODO disable tokens we don't handle anyway
|
||||
func FilterTokenizer() *godata.Tokenizer {
|
||||
t := godata.Tokenizer{}
|
||||
t.Add("^[0-9]{4,4}-[0-9]{2,2}-[0-9]{2,2}T[0-9]{2,2}:[0-9]{2,2}(:[0-9]{2,2}(.[0-9]+)?)?(Z|[+-][0-9]{2,2}:[0-9]{2,2})", godata.FilterTokenDateTime)
|
||||
t.Add("^-?[0-9]{4,4}-[0-9]{2,2}-[0-9]{2,2}", godata.FilterTokenDate)
|
||||
t.Add("^[0-9]{2,2}:[0-9]{2,2}(:[0-9]{2,2}(.[0-9]+)?)?", godata.FilterTokenTime)
|
||||
t.Add("^\\(", godata.FilterTokenOpenParen)
|
||||
t.Add("^\\)", godata.FilterTokenCloseParen)
|
||||
t.Add("^/", godata.FilterTokenNav)
|
||||
t.Add("^:", godata.FilterTokenColon)
|
||||
t.Add("^,", godata.FilterTokenComma)
|
||||
t.Add("^(geo.distance|geo.intersects|geo.length)", godata.FilterTokenFunc)
|
||||
t.Add("^(substringof|substring|length|indexof)", godata.FilterTokenFunc)
|
||||
// only change from the global tokenizer is the added ap
|
||||
t.Add("^(eq|ne|gt|ge|lt|le|and|or|not|has|in|ap)", godata.FilterTokenLogical)
|
||||
t.Add("^(add|sub|mul|divby|div|mod)", godata.FilterTokenOp)
|
||||
t.Add("^(contains|endswith|startswith|tolower|toupper|"+
|
||||
"trim|concat|year|month|day|hour|minute|second|fractionalseconds|date|"+
|
||||
"time|totaloffsetminutes|now|maxdatetime|mindatetime|totalseconds|round|"+
|
||||
"floor|ceiling|isof|cast)", godata.FilterTokenFunc)
|
||||
t.Add("^(any|all)", godata.FilterTokenLambda)
|
||||
t.Add("^null", godata.FilterTokenNull)
|
||||
t.Add("^\\$it", godata.FilterTokenIt)
|
||||
t.Add("^\\$root", godata.FilterTokenRoot)
|
||||
t.Add("^-?[0-9]+\\.[0-9]+", godata.FilterTokenFloat)
|
||||
t.Add("^-?[0-9]+", godata.FilterTokenInteger)
|
||||
t.Add("^'(''|[^'])*'", godata.FilterTokenString)
|
||||
t.Add("^(true|false)", godata.FilterTokenBoolean)
|
||||
t.Add("^@*[a-zA-Z][a-zA-Z0-9_.]*", godata.FilterTokenLiteral) // The optional '@' character is used to identify parameter aliases
|
||||
t.Ignore("^ ", godata.FilterTokenWhitespace)
|
||||
|
||||
return &t
|
||||
}
|
||||
@@ -26,6 +26,9 @@ func Server(opts ...Option) grpc.Service {
|
||||
if err := proto.RegisterGroupsServiceHandler(service.Server(), handler); err != nil {
|
||||
options.Logger.Fatal().Err(err).Msg("could not register groups handler")
|
||||
}
|
||||
if err := proto.RegisterIndexServiceHandler(service.Server(), handler); err != nil {
|
||||
options.Logger.Fatal().Err(err).Msg("could not register index handler")
|
||||
}
|
||||
|
||||
service.Init()
|
||||
return service
|
||||
|
||||
@@ -3,19 +3,20 @@ package service
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"path"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/CiscoM31/godata"
|
||||
"github.com/blevesearch/bleve"
|
||||
"github.com/owncloud/ocis/ocis-pkg/log"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
p "github.com/golang/protobuf/proto"
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
fieldmask_utils "github.com/mennanov/fieldmask-utils"
|
||||
merrors "github.com/micro/go-micro/v2/errors"
|
||||
"github.com/owncloud/ocis/accounts/pkg/proto/v0"
|
||||
"github.com/owncloud/ocis/accounts/pkg/provider"
|
||||
"github.com/owncloud/ocis/accounts/pkg/storage"
|
||||
"github.com/owncloud/ocis/ocis-pkg/roles"
|
||||
settings "github.com/owncloud/ocis/settings/pkg/proto/v0"
|
||||
@@ -35,22 +36,6 @@ import (
|
||||
// accLock mutually exclude readers from writers on account files
|
||||
var accLock sync.Mutex
|
||||
|
||||
func (s Service) indexAccount(id string) error {
|
||||
a := &proto.BleveAccount{
|
||||
BleveType: "account",
|
||||
}
|
||||
if err := s.repo.LoadAccount(context.Background(), id, &a.Account); err != nil {
|
||||
s.log.Error().Err(err).Str("account", id).Msg("could not load account")
|
||||
return err
|
||||
}
|
||||
s.log.Debug().Interface("account", a).Msg("found account")
|
||||
if err := s.index.Index(a.Id, a); err != nil {
|
||||
s.log.Error().Err(err).Interface("account", a).Msg("could not index account")
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// an auth request is currently hardcoded and has to match this regex
|
||||
// login eq \"teddy\" and password eq \"F&1!b90t111!\"
|
||||
var authQuery = regexp.MustCompile(`^login eq '(.*)' and password eq '(.*)'$`) // TODO how is ' escaped in the password?
|
||||
@@ -74,17 +59,6 @@ func (s Service) expandMemberOf(a *proto.Account) {
|
||||
a.MemberOf = expanded
|
||||
}
|
||||
|
||||
func (s Service) passwordIsValid(hash string, pwd string) (ok bool) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
s.log.Error().Err(fmt.Errorf("%s", r)).Str("hash", hash).Msg("password lib panicked")
|
||||
}
|
||||
}()
|
||||
|
||||
c := crypt.NewFromHash(hash)
|
||||
return c.Verify(hash, []byte(pwd)) == nil
|
||||
}
|
||||
|
||||
func (s Service) hasAccountManagementPermissions(ctx context.Context) bool {
|
||||
// get roles from context
|
||||
roleIDs, ok := roles.ReadRoleIDsFromContext(ctx)
|
||||
@@ -104,15 +78,12 @@ func (s Service) hasAccountManagementPermissions(ctx context.Context) bool {
|
||||
// serviceUserToIndex temporarily adds a service user to the index, which is supposed to be removed before the lock on the handler function is released
|
||||
func (s Service) serviceUserToIndex() (teardownServiceUser func()) {
|
||||
if s.Config.ServiceUser.Username != "" && s.Config.ServiceUser.UUID != "" {
|
||||
err := s.index.Index(s.Config.ServiceUser.UUID, &proto.BleveAccount{
|
||||
BleveType: "account",
|
||||
Account: s.getInMemoryServiceUser(),
|
||||
})
|
||||
_, err := s.index.Add(s.getInMemoryServiceUser())
|
||||
if err != nil {
|
||||
s.log.Logger.Err(err).Msg("service user was configured but failed to be added to the index")
|
||||
} else {
|
||||
return func() {
|
||||
_ = s.index.Delete(s.Config.ServiceUser.UUID)
|
||||
_ = s.index.Delete(s.getInMemoryServiceUser())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -140,83 +111,76 @@ func (s Service) ListAccounts(ctx context.Context, in *proto.ListAccountsRequest
|
||||
|
||||
accLock.Lock()
|
||||
defer accLock.Unlock()
|
||||
var password string
|
||||
|
||||
teardownServiceUser := s.serviceUserToIndex()
|
||||
defer teardownServiceUser()
|
||||
|
||||
// check if this looks like an auth request
|
||||
match := authQuery.FindStringSubmatch(in.Query)
|
||||
if len(match) == 3 {
|
||||
in.Query = fmt.Sprintf("on_premises_sam_account_name eq '%s'", match[1]) // todo fetch email? make query configurable
|
||||
password = match[2]
|
||||
if password == "" {
|
||||
return merrors.Unauthorized(s.id, "password must not be empty")
|
||||
}
|
||||
}
|
||||
|
||||
// only search for accounts
|
||||
tq := bleve.NewTermQuery("account")
|
||||
tq.SetField("bleve_type")
|
||||
|
||||
query := bleve.NewConjunctionQuery(tq)
|
||||
|
||||
if in.Query != "" {
|
||||
// parse the query like an odata filter
|
||||
var q *godata.GoDataFilterQuery
|
||||
if q, err = godata.ParseFilterString(in.Query); err != nil {
|
||||
s.log.Error().Err(err).Msg("could not parse query")
|
||||
return merrors.InternalServerError(s.id, "could not parse query: %v", err.Error())
|
||||
match, authRequest := getAuthQueryMatch(in.Query)
|
||||
if authRequest {
|
||||
password := match[2]
|
||||
if len(password) == 0 {
|
||||
return merrors.Unauthorized(s.id, "account not found or invalid credentials")
|
||||
}
|
||||
|
||||
// convert to bleve query
|
||||
bq, err := provider.BuildBleveQuery(q)
|
||||
if err != nil {
|
||||
s.log.Error().Err(err).Msg("could not build bleve query")
|
||||
return merrors.InternalServerError(s.id, "could not build bleve query: %v", err.Error())
|
||||
ids, err := s.index.FindBy(&proto.Account{}, "OnPremisesSamAccountName", match[1])
|
||||
if err != nil || len(ids) > 1 {
|
||||
return merrors.Unauthorized(s.id, "account not found or invalid credentials")
|
||||
}
|
||||
if len(ids) == 0 {
|
||||
ids, err = s.index.FindBy(&proto.Account{}, "Mail", match[1])
|
||||
if err != nil || len(ids) != 1 {
|
||||
return merrors.Unauthorized(s.id, "account not found or invalid credentials")
|
||||
}
|
||||
}
|
||||
query.AddQuery(bq)
|
||||
}
|
||||
|
||||
s.log.Debug().Interface("query", query).Msg("using query")
|
||||
|
||||
searchRequest := bleve.NewSearchRequest(query)
|
||||
var searchResult *bleve.SearchResult
|
||||
searchResult, err = s.index.Search(searchRequest)
|
||||
if err != nil {
|
||||
s.log.Error().Err(err).Msg("could not execute bleve search")
|
||||
return merrors.InternalServerError(s.id, "could not execute bleve search: %v", err.Error())
|
||||
}
|
||||
|
||||
s.log.Debug().Interface("result", searchResult).Msg("result")
|
||||
|
||||
out.Accounts = make([]*proto.Account, 0)
|
||||
|
||||
for _, hit := range searchResult.Hits {
|
||||
a := &proto.Account{}
|
||||
if hit.ID == s.Config.ServiceUser.UUID {
|
||||
err = s.repo.LoadAccount(ctx, ids[0], a)
|
||||
if err != nil || a.PasswordProfile == nil || len(a.PasswordProfile.Password) == 0 {
|
||||
return merrors.Unauthorized(s.id, "account not found or invalid credentials")
|
||||
}
|
||||
if !isPasswordValid(s.log, a.PasswordProfile.Password, password) {
|
||||
return merrors.Unauthorized(s.id, "account not found or invalid credentials")
|
||||
}
|
||||
a.PasswordProfile.Password = ""
|
||||
out.Accounts = []*proto.Account{a}
|
||||
return nil
|
||||
}
|
||||
|
||||
if in.Query == "" {
|
||||
err = s.repo.LoadAccounts(ctx, &out.Accounts)
|
||||
if err != nil {
|
||||
s.log.Err(err).Msg("failed to load all accounts from storage")
|
||||
return merrors.InternalServerError(s.id, "failed to load all accounts")
|
||||
}
|
||||
for i := range out.Accounts {
|
||||
a := out.Accounts[i]
|
||||
|
||||
// TODO add groups only if requested
|
||||
// if in.FieldMask ...
|
||||
s.expandMemberOf(a)
|
||||
|
||||
if a.PasswordProfile != nil {
|
||||
a.PasswordProfile.Password = ""
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
searchResults, err := s.findAccountsByQuery(ctx, in.Query)
|
||||
out.Accounts = make([]*proto.Account, 0, len(searchResults))
|
||||
|
||||
for _, hit := range searchResults {
|
||||
a := &proto.Account{}
|
||||
if hit == s.Config.ServiceUser.UUID {
|
||||
acc := s.getInMemoryServiceUser()
|
||||
a = &acc
|
||||
} else if err = s.repo.LoadAccount(ctx, hit.ID, a); err != nil {
|
||||
s.log.Error().Err(err).Str("account", hit.ID).Msg("could not load account, skipping")
|
||||
} else if err = s.repo.LoadAccount(ctx, hit, a); err != nil {
|
||||
s.log.Error().Err(err).Str("account", hit).Msg("could not load account, skipping")
|
||||
continue
|
||||
}
|
||||
var currentHash string
|
||||
if a.PasswordProfile != nil {
|
||||
currentHash = a.PasswordProfile.Password
|
||||
}
|
||||
|
||||
s.debugLogAccount(a).Msg("found account")
|
||||
|
||||
if password != "" {
|
||||
if a.PasswordProfile == nil {
|
||||
s.debugLogAccount(a).Msg("no password profile")
|
||||
return merrors.Unauthorized(s.id, "invalid password")
|
||||
}
|
||||
if !s.passwordIsValid(currentHash, password) {
|
||||
return merrors.Unauthorized(s.id, "invalid password")
|
||||
}
|
||||
}
|
||||
// TODO add groups if requested
|
||||
// if in.FieldMask ...
|
||||
s.expandMemberOf(a)
|
||||
@@ -232,6 +196,86 @@ func (s Service) ListAccounts(ctx context.Context, in *proto.ListAccountsRequest
|
||||
return
|
||||
}
|
||||
|
||||
func (s Service) findAccountsByQuery(ctx context.Context, query string) ([]string, error) {
|
||||
var searchResults []string
|
||||
var err error
|
||||
|
||||
// TODO: more explicit queries have to be on top
|
||||
var onPremOrMailQuery = regexp.MustCompile(`^on_premises_sam_account_name eq '(.*)' or mail eq '(.*)'$`)
|
||||
match := onPremOrMailQuery.FindStringSubmatch(query)
|
||||
if len(match) == 3 {
|
||||
resSam, err := s.index.FindByPartial(&proto.Account{}, "OnPremisesSamAccountName", match[1]+"*")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resMail, err := s.index.FindByPartial(&proto.Account{}, "Mail", match[2]+"*")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
searchResults = append(resSam, resMail...)
|
||||
return unique(searchResults), nil
|
||||
}
|
||||
|
||||
// startswith(on_premises_sam_account_name,'mar') or startswith(display_name,'mar') or startswith(mail,'mar')
|
||||
var searchQuery = regexp.MustCompile(`^startswith\(on_premises_sam_account_name,'(.*)'\) or startswith\(display_name,'(.*)'\) or startswith\(mail,'(.*)'\)$`)
|
||||
match = searchQuery.FindStringSubmatch(query)
|
||||
if len(match) == 4 {
|
||||
resSam, err := s.index.FindByPartial(&proto.Account{}, "OnPremisesSamAccountName", match[1]+"*")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resDisp, err := s.index.FindByPartial(&proto.Account{}, "DisplayName", match[2]+"*")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resMail, err := s.index.FindByPartial(&proto.Account{}, "Mail", match[3]+"*")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
searchResults = append(resSam, append(resDisp, resMail...)...)
|
||||
return unique(searchResults), nil
|
||||
}
|
||||
|
||||
// id eq 'marie' or on_premises_sam_account_name eq 'marie'
|
||||
// id eq 'f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c' or on_premises_sam_account_name eq 'f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c'
|
||||
var idOrQuery = regexp.MustCompile(`^id eq '(.*)' or on_premises_sam_account_name eq '(.*)'$`)
|
||||
match = idOrQuery.FindStringSubmatch(query)
|
||||
if len(match) == 3 {
|
||||
qID, qSam := match[1], match[2]
|
||||
tmp := &proto.Account{}
|
||||
err = s.repo.LoadAccount(ctx, qID, tmp)
|
||||
if err != nil && !storage.IsNotFoundErr(err) {
|
||||
return nil, err
|
||||
}
|
||||
searchResults, err = s.index.FindBy(&proto.Account{}, "OnPremisesSamAccountName", qSam)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if tmp.Id != "" {
|
||||
searchResults = append(searchResults, tmp.Id)
|
||||
}
|
||||
|
||||
return unique(searchResults), nil
|
||||
}
|
||||
|
||||
var onPremQuery = regexp.MustCompile(`^on_premises_sam_account_name eq '(.*)'$`) // TODO how is ' escaped in the password?
|
||||
match = onPremQuery.FindStringSubmatch(query)
|
||||
if len(match) == 2 {
|
||||
return s.index.FindBy(&proto.Account{}, "OnPremisesSamAccountName", match[1])
|
||||
}
|
||||
|
||||
var mailQuery = regexp.MustCompile(`^mail eq '(.*)'$`)
|
||||
match = mailQuery.FindStringSubmatch(query)
|
||||
if len(match) == 2 {
|
||||
return s.index.FindBy(&proto.Account{}, "Mail", match[1])
|
||||
}
|
||||
|
||||
return nil, merrors.BadRequest(s.id, "unsupported query %s", query)
|
||||
}
|
||||
|
||||
// GetAccount implements the AccountsServiceHandler interface
|
||||
func (s Service) GetAccount(ctx context.Context, in *proto.GetAccountRequest, out *proto.Account) (err error) {
|
||||
if !s.hasAccountManagementPermissions(ctx) {
|
||||
@@ -277,35 +321,43 @@ func (s Service) CreateAccount(ctx context.Context, in *proto.CreateAccountReque
|
||||
accLock.Lock()
|
||||
defer accLock.Unlock()
|
||||
var id string
|
||||
var acc = in.Account
|
||||
if acc == nil {
|
||||
return merrors.BadRequest(s.id, "account missing")
|
||||
}
|
||||
if acc.Id == "" {
|
||||
acc.Id = uuid.Must(uuid.NewV4()).String()
|
||||
}
|
||||
if !s.isValidUsername(acc.PreferredName) {
|
||||
return merrors.BadRequest(s.id, "preferred_name '%s' must be at least the local part of an email", acc.PreferredName)
|
||||
}
|
||||
if !s.isValidEmail(acc.Mail) {
|
||||
return merrors.BadRequest(s.id, "mail '%s' must be a valid email", acc.Mail)
|
||||
|
||||
if in.Account == nil {
|
||||
return merrors.InternalServerError(s.id, "invalid account: empty")
|
||||
}
|
||||
|
||||
if id, err = cleanupID(acc.Id); err != nil {
|
||||
p.Merge(out, in.Account)
|
||||
|
||||
if out.Id == "" {
|
||||
out.Id = uuid.Must(uuid.NewV4()).String()
|
||||
}
|
||||
if err = validateAccount(s.id, out); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if id, err = cleanupID(out.Id); err != nil {
|
||||
return merrors.InternalServerError(s.id, "could not clean up account id: %v", err.Error())
|
||||
}
|
||||
|
||||
if acc.PasswordProfile != nil {
|
||||
if acc.PasswordProfile.Password != "" {
|
||||
exists, err := s.accountExists(ctx, out.PreferredName, out.Mail, out.Id)
|
||||
if err != nil {
|
||||
return merrors.InternalServerError(s.id, "could not check if account exists: %v", err.Error())
|
||||
}
|
||||
if exists {
|
||||
return merrors.BadRequest(s.id, "account already exists")
|
||||
}
|
||||
|
||||
if out.PasswordProfile != nil {
|
||||
if out.PasswordProfile.Password != "" {
|
||||
// encrypt password
|
||||
c := crypt.New(crypt.SHA512)
|
||||
if acc.PasswordProfile.Password, err = c.Generate([]byte(acc.PasswordProfile.Password), nil); err != nil {
|
||||
if out.PasswordProfile.Password, err = c.Generate([]byte(out.PasswordProfile.Password), nil); err != nil {
|
||||
s.log.Error().Err(err).Str("id", id).Msg("could not hash password")
|
||||
return merrors.InternalServerError(s.id, "could not hash password: %v", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if err := passwordPoliciesValid(acc.PasswordProfile.PasswordPolicies); err != nil {
|
||||
if err := passwordPoliciesValid(out.PasswordProfile.PasswordPolicies); err != nil {
|
||||
return merrors.BadRequest(s.id, "%s", err)
|
||||
}
|
||||
}
|
||||
@@ -314,27 +366,54 @@ func (s Service) CreateAccount(ctx context.Context, in *proto.CreateAccountReque
|
||||
// TODO groups should be ignored during create, use groups.AddMember? return error?
|
||||
|
||||
// write and index account - note: don't do anything else in between!
|
||||
if err = s.repo.WriteAccount(ctx, acc); err != nil {
|
||||
if err = s.repo.WriteAccount(ctx, out); err != nil {
|
||||
s.log.Error().Err(err).Str("id", id).Msg("could not persist new account")
|
||||
s.debugLogAccount(acc).Msg("could not persist new account")
|
||||
s.debugLogAccount(out).Msg("could not persist new account")
|
||||
return merrors.InternalServerError(s.id, "could not persist new account: %v", err.Error())
|
||||
}
|
||||
if err = s.indexAccount(acc.Id); err != nil {
|
||||
return merrors.InternalServerError(s.id, "could not index new account: %v", err.Error())
|
||||
}
|
||||
s.log.Debug().Interface("account", acc).Msg("account after indexing")
|
||||
indexResults, err := s.index.Add(out)
|
||||
if err != nil {
|
||||
s.rollbackCreateAccount(ctx, out)
|
||||
return merrors.BadRequest(s.id, "Account already exists %v", err.Error())
|
||||
|
||||
if acc.PasswordProfile != nil {
|
||||
acc.PasswordProfile.Password = ""
|
||||
}
|
||||
s.log.Debug().Interface("account", out).Msg("account after indexing")
|
||||
|
||||
for _, r := range indexResults {
|
||||
if r.Field == "UidNumber" {
|
||||
id, err := strconv.Atoi(path.Base(r.Value))
|
||||
if err != nil {
|
||||
s.rollbackCreateAccount(ctx, out)
|
||||
return err
|
||||
}
|
||||
out.UidNumber = int64(id)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
out.Id = acc.Id
|
||||
out.Mail = acc.Mail
|
||||
out.PreferredName = acc.PreferredName
|
||||
out.AccountEnabled = acc.AccountEnabled
|
||||
out.DisplayName = acc.DisplayName
|
||||
out.OnPremisesSamAccountName = acc.OnPremisesSamAccountName
|
||||
if out.GidNumber == 0 {
|
||||
out.GidNumber = userDefaultGID
|
||||
}
|
||||
|
||||
r := proto.ListGroupsResponse{}
|
||||
err = s.ListGroups(ctx, &proto.ListGroupsRequest{}, &r)
|
||||
if err != nil {
|
||||
// rollback account creation
|
||||
return err
|
||||
}
|
||||
|
||||
for _, group := range r.Groups {
|
||||
if group.GidNumber == out.GidNumber {
|
||||
out.MemberOf = append(out.MemberOf, group)
|
||||
}
|
||||
}
|
||||
//acc.MemberOf = append(acc.MemberOf, &group)
|
||||
if err := s.repo.WriteAccount(context.Background(), out); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if out.PasswordProfile != nil {
|
||||
out.PasswordProfile.Password = ""
|
||||
}
|
||||
|
||||
// TODO: assign user role to all new users for now, as create Account request does not have any role field
|
||||
@@ -342,7 +421,7 @@ func (s Service) CreateAccount(ctx context.Context, in *proto.CreateAccountReque
|
||||
return merrors.InternalServerError(s.id, "could not assign role to account: roleService not configured")
|
||||
}
|
||||
if _, err = s.RoleService.AssignRoleToUser(ctx, &settings.AssignRoleToUserRequest{
|
||||
AccountUuid: acc.Id,
|
||||
AccountUuid: out.Id,
|
||||
RoleId: settings_svc.BundleUUIDRoleUser,
|
||||
}); err != nil {
|
||||
return merrors.InternalServerError(s.id, "could not assign role to account: %v", err.Error())
|
||||
@@ -351,6 +430,18 @@ func (s Service) CreateAccount(ctx context.Context, in *proto.CreateAccountReque
|
||||
return
|
||||
}
|
||||
|
||||
// rollbackCreateAccount tries to rollback changes made by `CreateAccount` if parts of it failed.
|
||||
func (s Service) rollbackCreateAccount(ctx context.Context, acc *proto.Account) {
|
||||
err := s.index.Delete(acc)
|
||||
if err != nil {
|
||||
s.log.Err(err).Msg("failed to rollback account from indices")
|
||||
}
|
||||
err = s.repo.DeleteAccount(ctx, acc.Id)
|
||||
if err != nil {
|
||||
s.log.Err(err).Msg("failed to rollback account from repo")
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateAccount implements the AccountsServiceHandler interface
|
||||
// read only fields are ignored
|
||||
// TODO how can we unset specific values? using the update mask
|
||||
@@ -373,8 +464,6 @@ func (s Service) UpdateAccount(ctx context.Context, in *proto.UpdateAccountReque
|
||||
return merrors.InternalServerError(s.id, "could not clean up account id: %v", err.Error())
|
||||
}
|
||||
|
||||
path := filepath.Join(s.Config.Server.AccountsDataPath, "accounts", id)
|
||||
|
||||
if err = s.repo.LoadAccount(ctx, id, out); err != nil {
|
||||
if storage.IsNotFoundErr(err) {
|
||||
return merrors.NotFound(s.id, "account not found: %v", err.Error())
|
||||
@@ -396,6 +485,24 @@ func (s Service) UpdateAccount(ctx context.Context, in *proto.UpdateAccountReque
|
||||
return merrors.BadRequest(s.id, "%s", err)
|
||||
}
|
||||
|
||||
if _, exists := validMask.Filter("PreferredName"); exists {
|
||||
if err = validateAccountPreferredName(s.id, in.Account); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, exists := validMask.Filter("OnPremisesSamAccountName"); exists {
|
||||
if err = validateAccountOnPremisesSamAccountName(s.id, in.Account); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, exists := validMask.Filter("Mail"); exists {
|
||||
if in.Account.Mail != "" {
|
||||
if err = validateAccountEmail(s.id, in.Account); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := fieldmask_utils.StructToStruct(validMask, in.Account, out); err != nil {
|
||||
return merrors.InternalServerError(s.id, "%s", err)
|
||||
}
|
||||
@@ -434,13 +541,20 @@ func (s Service) UpdateAccount(ctx context.Context, in *proto.UpdateAccountReque
|
||||
out.ExternalUserStateChangeDateTime = tsnow
|
||||
}
|
||||
|
||||
// We need to reload the old account state to be able to compute the update
|
||||
old := &proto.Account{}
|
||||
if err = s.repo.LoadAccount(ctx, id, old); err != nil {
|
||||
s.log.Error().Err(err).Str("id", out.Id).Msg("could not load old account representation during update, maybe the account got deleted meanwhile?")
|
||||
return merrors.InternalServerError(s.id, "could not load current account for update: %v", err.Error())
|
||||
}
|
||||
|
||||
if err = s.repo.WriteAccount(ctx, out); err != nil {
|
||||
s.log.Error().Err(err).Str("id", out.Id).Msg("could not persist updated account")
|
||||
return merrors.InternalServerError(s.id, "could not persist updated account: %v", err.Error())
|
||||
}
|
||||
|
||||
if err = s.indexAccount(id); err != nil {
|
||||
s.log.Error().Err(err).Str("id", id).Str("path", path).Msg("could not index new account")
|
||||
if err = s.index.Update(old, out); err != nil {
|
||||
s.log.Error().Err(err).Str("id", id).Msg("could not index new account")
|
||||
return merrors.InternalServerError(s.id, "could not index updated account: %v", err.Error())
|
||||
}
|
||||
|
||||
@@ -514,7 +628,7 @@ func (s Service) DeleteAccount(ctx context.Context, in *proto.DeleteAccountReque
|
||||
return merrors.InternalServerError(s.id, "could not remove account: %v", err.Error())
|
||||
}
|
||||
|
||||
if err = s.index.Delete(id); err != nil {
|
||||
if err = s.index.Delete(a); err != nil {
|
||||
s.log.Error().Err(err).Str("id", id).Str("accountId", id).Msg("could not remove account from index")
|
||||
return merrors.InternalServerError(s.id, "could not remove account from index: %v", err.Error())
|
||||
}
|
||||
@@ -523,12 +637,46 @@ func (s Service) DeleteAccount(ctx context.Context, in *proto.DeleteAccountReque
|
||||
return
|
||||
}
|
||||
|
||||
func validateAccount(serviceID string, a *proto.Account) error {
|
||||
if err := validateAccountPreferredName(serviceID, a); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateAccountOnPremisesSamAccountName(serviceID, a); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateAccountEmail(serviceID, a); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateAccountPreferredName(serviceID string, a *proto.Account) error {
|
||||
if !isValidUsername(a.PreferredName) {
|
||||
return merrors.BadRequest(serviceID, "preferred_name '%s' must be at least the local part of an email", a.PreferredName)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateAccountOnPremisesSamAccountName(serviceID string, a *proto.Account) error {
|
||||
if !isValidUsername(a.OnPremisesSamAccountName) {
|
||||
return merrors.BadRequest(serviceID, "on_premises_sam_account_name '%s' must be at least the local part of an email", a.OnPremisesSamAccountName)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateAccountEmail(serviceID string, a *proto.Account) error {
|
||||
if !isValidEmail(a.Mail) {
|
||||
return merrors.BadRequest(serviceID, "mail '%s' must be a valid email", a.Mail)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// We want to allow email addresses as usernames so they show up when using them in ACLs on storages that allow intergration with our glauth LDAP service
|
||||
// so we are adding a few restrictions from https://stackoverflow.com/questions/6949667/what-are-the-real-rules-for-linux-usernames-on-centos-6-and-rhel-6
|
||||
// names should not start with numbers
|
||||
var usernameRegex = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]*(@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)*$")
|
||||
|
||||
func (s Service) isValidUsername(e string) bool {
|
||||
func isValidUsername(e string) bool {
|
||||
if len(e) < 1 && len(e) > 254 {
|
||||
return false
|
||||
}
|
||||
@@ -538,7 +686,7 @@ func (s Service) isValidUsername(e string) bool {
|
||||
// regex from https://www.w3.org/TR/2016/REC-html51-20161101/sec-forms.html#valid-e-mail-address
|
||||
var emailRegex = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
|
||||
|
||||
func (s Service) isValidEmail(e string) bool {
|
||||
func isValidEmail(e string) bool {
|
||||
if len(e) < 3 && len(e) > 254 {
|
||||
return false
|
||||
}
|
||||
@@ -613,3 +761,68 @@ func (s Service) debugLogAccount(a *proto.Account) *zerolog.Event {
|
||||
"DeletedDateTime": a.DeletedDateTime,
|
||||
})
|
||||
}
|
||||
|
||||
func unique(strSlice []string) []string {
|
||||
keys := make(map[string]bool)
|
||||
list := []string{}
|
||||
for _, entry := range strSlice {
|
||||
if _, value := keys[entry]; !value {
|
||||
keys[entry] = true
|
||||
list = append(list, entry)
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func (s Service) accountExists(ctx context.Context, username, mail, id string) (exists bool, err error) {
|
||||
var ids []string
|
||||
ids, err = s.index.FindBy(&proto.Account{}, "preferred_name", username)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if len(ids) > 0 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
ids, err = s.index.FindBy(&proto.Account{}, "on_premises_sam_account_name", username)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if len(ids) > 0 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
ids, err = s.index.FindBy(&proto.Account{}, "mail", mail)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if len(ids) > 0 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
a := &proto.Account{}
|
||||
err = s.repo.LoadAccount(ctx, id, a)
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
if !storage.IsNotFoundErr(err) {
|
||||
return true, err
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func getAuthQueryMatch(query string) (match []string, authRequest bool) {
|
||||
match = authQuery.FindStringSubmatch(query)
|
||||
return match, len(match) == 3
|
||||
}
|
||||
|
||||
func isPasswordValid(logger log.Logger, hash string, pwd string) (ok bool) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logger.Error().Err(fmt.Errorf("%s", r)).Str("hash", hash).Msg("password lib panicked")
|
||||
}
|
||||
}()
|
||||
|
||||
c := crypt.NewFromHash(hash)
|
||||
return c.Verify(hash, []byte(pwd)) == nil
|
||||
}
|
||||
|
||||
@@ -2,34 +2,18 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/owncloud/ocis/accounts/pkg/storage"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"github.com/CiscoM31/godata"
|
||||
"github.com/blevesearch/bleve"
|
||||
"github.com/gofrs/uuid"
|
||||
p "github.com/golang/protobuf/proto"
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
merrors "github.com/micro/go-micro/v2/errors"
|
||||
"github.com/owncloud/ocis/accounts/pkg/proto/v0"
|
||||
"github.com/owncloud/ocis/accounts/pkg/provider"
|
||||
"github.com/owncloud/ocis/accounts/pkg/storage"
|
||||
)
|
||||
|
||||
func (s Service) indexGroup(id string) error {
|
||||
g := &proto.BleveGroup{
|
||||
BleveType: "group",
|
||||
}
|
||||
if err := s.repo.LoadGroup(context.Background(), id, &g.Group); err != nil {
|
||||
s.log.Error().Err(err).Str("group", id).Msg("could not load group")
|
||||
return err
|
||||
}
|
||||
s.log.Debug().Interface("group", g).Msg("found group")
|
||||
if err := s.index.Index(g.Id, g); err != nil {
|
||||
s.log.Error().Err(err).Interface("group", g).Msg("could not index group")
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s Service) expandMembers(g *proto.Group) {
|
||||
if g == nil {
|
||||
return
|
||||
@@ -41,7 +25,7 @@ func (s Service) expandMembers(g *proto.Group) {
|
||||
if err := s.repo.LoadAccount(context.Background(), g.Members[i].Id, a); err == nil {
|
||||
expanded = append(expanded, a)
|
||||
} else {
|
||||
// log errors but continue execution for now
|
||||
// log errors but con/var/tmp/ocis-accounts-store-408341811tinue execution for now
|
||||
s.log.Error().Err(err).Str("id", g.Members[i].Id).Msg("could not load account")
|
||||
}
|
||||
}
|
||||
@@ -67,49 +51,25 @@ func (s Service) deflateMembers(g *proto.Group) {
|
||||
|
||||
// ListGroups implements the GroupsServiceHandler interface
|
||||
func (s Service) ListGroups(c context.Context, in *proto.ListGroupsRequest, out *proto.ListGroupsResponse) (err error) {
|
||||
|
||||
// only search for groups
|
||||
tq := bleve.NewTermQuery("group")
|
||||
tq.SetField("bleve_type")
|
||||
|
||||
query := bleve.NewConjunctionQuery(tq)
|
||||
|
||||
if in.Query != "" {
|
||||
// parse the query like an odata filter
|
||||
var q *godata.GoDataFilterQuery
|
||||
if q, err = godata.ParseFilterString(in.Query); err != nil {
|
||||
s.log.Error().Err(err).Msg("could not parse query")
|
||||
return merrors.InternalServerError(s.id, "could not parse query: %v", err.Error())
|
||||
}
|
||||
|
||||
// convert to bleve query
|
||||
bq, err := provider.BuildBleveQuery(q)
|
||||
if err != nil {
|
||||
s.log.Error().Err(err).Msg("could not build bleve query")
|
||||
return merrors.InternalServerError(s.id, "could not build bleve query: %v", err.Error())
|
||||
}
|
||||
query.AddQuery(bq)
|
||||
}
|
||||
|
||||
s.log.Debug().Interface("query", query).Msg("using query")
|
||||
|
||||
searchRequest := bleve.NewSearchRequest(query)
|
||||
var searchResult *bleve.SearchResult
|
||||
searchResult, err = s.index.Search(searchRequest)
|
||||
if err != nil {
|
||||
s.log.Error().Err(err).Msg("could not execute bleve search")
|
||||
return merrors.InternalServerError(s.id, "could not execute bleve search: %v", err.Error())
|
||||
}
|
||||
|
||||
s.log.Debug().Interface("result", searchResult).Msg("result")
|
||||
var searchResults []string
|
||||
|
||||
out.Groups = make([]*proto.Group, 0)
|
||||
if in.Query == "" {
|
||||
searchResults, _ = s.index.FindByPartial(&proto.Group{}, "DisplayName", "*")
|
||||
}
|
||||
|
||||
for _, hit := range searchResult.Hits {
|
||||
/*
|
||||
var startsWithIDQuery = regexp.MustCompile(`^startswith\(id,'(.*)'\)$`)
|
||||
match := startsWithIDQuery.FindStringSubmatch(in.Query)
|
||||
if len(match) == 2 {
|
||||
searchResults = []string{match[1]}
|
||||
}
|
||||
*/
|
||||
|
||||
for _, hit := range searchResults {
|
||||
g := &proto.Group{}
|
||||
if err = s.repo.LoadGroup(c, hit.ID, g); err != nil {
|
||||
s.log.Error().Err(err).Str("group", hit.ID).Msg("could not load group, skipping")
|
||||
if err = s.repo.LoadGroup(c, hit, g); err != nil {
|
||||
s.log.Error().Err(err).Str("group", hit).Msg("could not load group, skipping")
|
||||
continue
|
||||
}
|
||||
s.log.Debug().Interface("group", g).Msg("found group")
|
||||
@@ -150,33 +110,59 @@ func (s Service) GetGroup(c context.Context, in *proto.GetGroupRequest, out *pro
|
||||
|
||||
// CreateGroup implements the GroupsServiceHandler interface
|
||||
func (s Service) CreateGroup(c context.Context, in *proto.CreateGroupRequest, out *proto.Group) (err error) {
|
||||
var id string
|
||||
if in.Group == nil {
|
||||
return merrors.BadRequest(s.id, "account missing")
|
||||
return merrors.InternalServerError(s.id, "invalid group: empty")
|
||||
}
|
||||
if in.Group.Id == "" {
|
||||
in.Group.Id = uuid.Must(uuid.NewV4()).String()
|
||||
p.Merge(out, in.Group)
|
||||
|
||||
if out.Id == "" {
|
||||
out.Id = uuid.Must(uuid.NewV4()).String()
|
||||
}
|
||||
|
||||
if id, err = cleanupID(in.Group.Id); err != nil {
|
||||
if _, err = cleanupID(out.Id); err != nil {
|
||||
return merrors.InternalServerError(s.id, "could not clean up account id: %v", err.Error())
|
||||
}
|
||||
|
||||
// extract member id
|
||||
s.deflateMembers(in.Group)
|
||||
s.deflateMembers(out)
|
||||
|
||||
if err = s.repo.WriteGroup(c, in.Group); err != nil {
|
||||
s.log.Error().Err(err).Interface("group", in.Group).Msg("could not persist new group")
|
||||
if err = s.repo.WriteGroup(c, out); err != nil {
|
||||
s.log.Error().Err(err).Interface("group", out).Msg("could not persist new group")
|
||||
return merrors.InternalServerError(s.id, "could not persist new group: %v", err.Error())
|
||||
}
|
||||
|
||||
if err = s.indexGroup(id); err != nil {
|
||||
indexResults, err := s.index.Add(out)
|
||||
if err != nil {
|
||||
s.rollbackCreateGroup(c, out)
|
||||
return merrors.InternalServerError(s.id, "could not index new group: %v", err.Error())
|
||||
}
|
||||
|
||||
for _, r := range indexResults {
|
||||
if r.Field == "GidNumber" {
|
||||
gid, err := strconv.Atoi(path.Base(r.Value))
|
||||
if err != nil {
|
||||
s.rollbackCreateGroup(c, out)
|
||||
return err
|
||||
}
|
||||
out.GidNumber = int64(gid)
|
||||
return s.repo.WriteGroup(context.Background(), out)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// rollbackCreateGroup tries to rollback changes made by `CreateGroup` if parts of it failed.
|
||||
func (s Service) rollbackCreateGroup(ctx context.Context, group *proto.Group) {
|
||||
err := s.index.Delete(group)
|
||||
if err != nil {
|
||||
s.log.Err(err).Msg("failed to rollback group from indices")
|
||||
}
|
||||
err = s.repo.DeleteGroup(ctx, group.Id)
|
||||
if err != nil {
|
||||
s.log.Err(err).Msg("failed to rollback group from repo")
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateGroup implements the GroupsServiceHandler interface
|
||||
func (s Service) UpdateGroup(c context.Context, in *proto.UpdateGroupRequest, out *proto.Group) (err error) {
|
||||
return merrors.InternalServerError(s.id, "not implemented")
|
||||
@@ -217,7 +203,7 @@ func (s Service) DeleteGroup(c context.Context, in *proto.DeleteGroupRequest, ou
|
||||
return merrors.InternalServerError(s.id, "could not load group: %v", err.Error())
|
||||
}
|
||||
|
||||
if err = s.index.Delete(id); err != nil {
|
||||
if err = s.index.Delete(g); err != nil {
|
||||
s.log.Error().Err(err).Str("id", id).Str("path", path).Msg("could not remove group from index")
|
||||
return merrors.InternalServerError(s.id, "could not remove group from index: %v", err.Error())
|
||||
}
|
||||
|
||||
100
accounts/pkg/service/v0/index.go
Normal file
100
accounts/pkg/service/v0/index.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/owncloud/ocis/accounts/pkg/storage"
|
||||
|
||||
"github.com/owncloud/ocis/accounts/pkg/config"
|
||||
"github.com/owncloud/ocis/accounts/pkg/indexer"
|
||||
"github.com/owncloud/ocis/accounts/pkg/indexer/option"
|
||||
"github.com/owncloud/ocis/accounts/pkg/proto/v0"
|
||||
)
|
||||
|
||||
// RebuildIndex deletes all indices (in memory and on storage) and rebuilds them from scratch.
|
||||
func (s Service) RebuildIndex(ctx context.Context, request *proto.RebuildIndexRequest, response *proto.RebuildIndexResponse) error {
|
||||
if err := s.index.Reset(); err != nil {
|
||||
return fmt.Errorf("failed to delete index containers: %w", err)
|
||||
}
|
||||
|
||||
if err := recreateContainers(s.index, s.Config); err != nil {
|
||||
return fmt.Errorf("failed to recreate index containers: %w", err)
|
||||
}
|
||||
|
||||
if err := reindexDocuments(ctx, s.repo, s.index); err != nil {
|
||||
return fmt.Errorf("failed to reindex documents: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// recreateContainers adds all indices to the indexer that we have for this service.
|
||||
func recreateContainers(idx *indexer.Indexer, cfg *config.Config) error {
|
||||
// Accounts
|
||||
if err := idx.AddIndex(&proto.Account{}, "DisplayName", "Id", "accounts", "non_unique", nil, true); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := idx.AddIndex(&proto.Account{}, "Mail", "Id", "accounts", "unique", nil, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := idx.AddIndex(&proto.Account{}, "OnPremisesSamAccountName", "Id", "accounts", "unique", nil, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := idx.AddIndex(&proto.Account{}, "PreferredName", "Id", "accounts", "unique", nil, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := idx.AddIndex(&proto.Account{}, "UidNumber", "Id", "accounts", "autoincrement", &option.Bound{
|
||||
Lower: cfg.Index.UID.Lower,
|
||||
Upper: cfg.Index.UID.Upper,
|
||||
}, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Groups
|
||||
if err := idx.AddIndex(&proto.Group{}, "OnPremisesSamAccountName", "Id", "groups", "unique", nil, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := idx.AddIndex(&proto.Group{}, "DisplayName", "Id", "groups", "non_unique", nil, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := idx.AddIndex(&proto.Group{}, "GidNumber", "Id", "groups", "autoincrement", &option.Bound{
|
||||
Lower: cfg.Index.GID.Lower,
|
||||
Upper: cfg.Index.GID.Upper,
|
||||
}, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// reindexDocuments loads all existing documents and adds them to the index.
|
||||
func reindexDocuments(ctx context.Context, repo storage.Repo, index *indexer.Indexer) error {
|
||||
accounts := make([]*proto.Account, 0)
|
||||
if err := repo.LoadAccounts(ctx, &accounts); err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range accounts {
|
||||
_, err := index.Add(accounts[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
groups := make([]*proto.Group, 0)
|
||||
if err := repo.LoadGroups(ctx, &groups); err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range groups {
|
||||
_, err := index.Add(groups[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -3,21 +3,18 @@ package service
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/owncloud/ocis/accounts/pkg/storage"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/blevesearch/bleve"
|
||||
"github.com/blevesearch/bleve/analysis/analyzer/custom"
|
||||
"github.com/blevesearch/bleve/analysis/analyzer/keyword"
|
||||
"github.com/blevesearch/bleve/analysis/analyzer/simple"
|
||||
"github.com/blevesearch/bleve/analysis/analyzer/standard"
|
||||
"github.com/blevesearch/bleve/analysis/token/lowercase"
|
||||
"github.com/blevesearch/bleve/analysis/tokenizer/unicode"
|
||||
"github.com/owncloud/ocis/accounts/pkg/indexer"
|
||||
"github.com/owncloud/ocis/accounts/pkg/storage"
|
||||
|
||||
mclient "github.com/micro/go-micro/v2/client"
|
||||
"github.com/owncloud/ocis/accounts/pkg/config"
|
||||
idxerrs "github.com/owncloud/ocis/accounts/pkg/indexer/errors"
|
||||
"github.com/owncloud/ocis/accounts/pkg/proto/v0"
|
||||
"github.com/owncloud/ocis/ocis-pkg/log"
|
||||
"github.com/owncloud/ocis/ocis-pkg/roles"
|
||||
@@ -25,6 +22,9 @@ import (
|
||||
settings_svc "github.com/owncloud/ocis/settings/pkg/service/v0"
|
||||
)
|
||||
|
||||
// userDefaultGID is the default integer representing the "users" group.
|
||||
const userDefaultGID = 30000
|
||||
|
||||
// New returns a new instance of Service
|
||||
func New(opts ...Option) (s *Service, err error) {
|
||||
options := newOptions(opts...)
|
||||
@@ -72,92 +72,14 @@ func New(opts ...Option) (s *Service, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (s Service) buildIndex() (index bleve.Index, err error) {
|
||||
indexDir := filepath.Join(s.Config.Server.AccountsDataPath, "index.bleve")
|
||||
if index, err = bleve.Open(indexDir); err != nil {
|
||||
if err != bleve.ErrorIndexPathDoesNotExist {
|
||||
s.log.Error().Err(err).Msg("failed to read index")
|
||||
return
|
||||
}
|
||||
func (s Service) buildIndex() (*indexer.Indexer, error) {
|
||||
idx := indexer.CreateIndexer(s.Config)
|
||||
|
||||
indexMapping := bleve.NewIndexMapping()
|
||||
// keep all symbols in terms to allow exact maching, eg. emails
|
||||
indexMapping.DefaultAnalyzer = keyword.Name
|
||||
// TODO don't bother to store fields as we will load the account from disk
|
||||
|
||||
// Reusable mapping for text
|
||||
standardTextFieldMapping := bleve.NewTextFieldMapping()
|
||||
standardTextFieldMapping.Analyzer = standard.Name
|
||||
standardTextFieldMapping.Store = false
|
||||
|
||||
// Reusable mapping for text, uses english stop word removal
|
||||
simpleTextFieldMapping := bleve.NewTextFieldMapping()
|
||||
simpleTextFieldMapping.Analyzer = simple.Name
|
||||
simpleTextFieldMapping.Store = false
|
||||
|
||||
// Reusable mapping for keyword text
|
||||
keywordFieldMapping := bleve.NewTextFieldMapping()
|
||||
keywordFieldMapping.Analyzer = keyword.Name
|
||||
keywordFieldMapping.Store = false
|
||||
|
||||
// Reusable mapping for lowercase text
|
||||
err = indexMapping.AddCustomAnalyzer("lowercase",
|
||||
map[string]interface{}{
|
||||
"type": custom.Name,
|
||||
"tokenizer": unicode.Name,
|
||||
"token_filters": []string{
|
||||
lowercase.Name,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
lowercaseTextFieldMapping := bleve.NewTextFieldMapping()
|
||||
lowercaseTextFieldMapping.Analyzer = "lowercase"
|
||||
lowercaseTextFieldMapping.Store = true
|
||||
|
||||
// accounts
|
||||
accountMapping := bleve.NewDocumentMapping()
|
||||
indexMapping.AddDocumentMapping("account", accountMapping)
|
||||
|
||||
// Text
|
||||
accountMapping.AddFieldMappingsAt("display_name", standardTextFieldMapping)
|
||||
accountMapping.AddFieldMappingsAt("description", standardTextFieldMapping)
|
||||
|
||||
// Lowercase
|
||||
accountMapping.AddFieldMappingsAt("on_premises_sam_account_name", lowercaseTextFieldMapping)
|
||||
accountMapping.AddFieldMappingsAt("preferred_name", lowercaseTextFieldMapping)
|
||||
|
||||
// Keywords
|
||||
accountMapping.AddFieldMappingsAt("mail", keywordFieldMapping)
|
||||
|
||||
// groups
|
||||
groupMapping := bleve.NewDocumentMapping()
|
||||
indexMapping.AddDocumentMapping("group", groupMapping)
|
||||
|
||||
// Text
|
||||
groupMapping.AddFieldMappingsAt("display_name", standardTextFieldMapping)
|
||||
groupMapping.AddFieldMappingsAt("description", standardTextFieldMapping)
|
||||
|
||||
// Lowercase
|
||||
groupMapping.AddFieldMappingsAt("on_premises_sam_account_name", lowercaseTextFieldMapping)
|
||||
|
||||
// Tell blevesearch how to determine the type of the structs that are indexed.
|
||||
// The referenced field needs to match the struct field exactly and it must be public.
|
||||
// See pkg/proto/v0/bleve.go how we wrap the generated Account and Group to add a
|
||||
// BleveType property which is indexed as `bleve_type` so we can also distinguish the
|
||||
// documents in the index by querying for that property.
|
||||
indexMapping.TypeField = "BleveType"
|
||||
|
||||
// for now recreate index on every start
|
||||
if err = os.RemoveAll(indexDir); err != nil {
|
||||
return
|
||||
}
|
||||
if index, err = bleve.New(indexDir, indexMapping); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := recreateContainers(idx, s.Config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return
|
||||
return idx, nil
|
||||
|
||||
}
|
||||
|
||||
func (s Service) createDefaultAccounts() (err error) {
|
||||
@@ -275,8 +197,35 @@ func (s Service) createDefaultAccounts() (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.indexAccount(accounts[i].Id); err != nil {
|
||||
return err
|
||||
results, err := s.index.Add(&accounts[i])
|
||||
if err != nil {
|
||||
if idxerrs.IsAlreadyExistsErr(err) {
|
||||
continue
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: can be removed again as soon as we respect the predefined UIDs and GIDs from the account. Then no autoincrement is happening, therefore we don't need to update accounts.
|
||||
changed := false
|
||||
for _, r := range results {
|
||||
if r.Field == "UidNumber" || r.Field == "GidNumber" {
|
||||
id, err := strconv.ParseInt(path.Base(r.Value), 10, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if r.Field == "UidNumber" {
|
||||
accounts[i].UidNumber = id
|
||||
} else {
|
||||
accounts[i].GidNumber = id
|
||||
}
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
if changed {
|
||||
if err := s.repo.WriteAccount(context.Background(), &accounts[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,8 +286,28 @@ func (s Service) createDefaultGroups() (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.indexGroup(groups[i].Id); err != nil {
|
||||
return err
|
||||
results, err := s.index.Add(&groups[i])
|
||||
if err != nil {
|
||||
if idxerrs.IsAlreadyExistsErr(err) {
|
||||
continue
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: can be removed again as soon as we respect the predefined GIDs from the group. Then no autoincrement is happening, therefore we don't need to update groups.
|
||||
for _, r := range results {
|
||||
if r.Field == "GidNumber" {
|
||||
gid, err := strconv.ParseInt(path.Base(r.Value), 10, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
groups[i].GidNumber = gid
|
||||
if err := s.repo.WriteGroup(context.Background(), &groups[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -374,7 +343,7 @@ type Service struct {
|
||||
id string
|
||||
log log.Logger
|
||||
Config *config.Config
|
||||
index bleve.Index
|
||||
index *indexer.Indexer
|
||||
RoleService settings.RoleService
|
||||
RoleManager *roles.Manager
|
||||
repo storage.Repo
|
||||
|
||||
1
accounts/pkg/service/v0/staticcheck.conf
Normal file
1
accounts/pkg/service/v0/staticcheck.conf
Normal file
@@ -0,0 +1 @@
|
||||
checks = ["all", "-ST1003", "-ST1000", "-SA1019"]
|
||||
@@ -8,16 +8,20 @@ import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
v1beta11 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
|
||||
"github.com/cs3org/reva/pkg/rgrpc/todo/pool"
|
||||
"github.com/cs3org/reva/pkg/token"
|
||||
"github.com/cs3org/reva/pkg/token/manager/jwt"
|
||||
"github.com/owncloud/ocis/accounts/pkg/config"
|
||||
"github.com/owncloud/ocis/accounts/pkg/proto/v0"
|
||||
olog "github.com/owncloud/ocis/ocis-pkg/log"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
@@ -73,8 +77,14 @@ func (r CS3Repo) WriteAccount(ctx context.Context, a *proto.Account) (err error)
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.dataProvider.put(r.accountURL(a.Id), bytes.NewReader(by), t)
|
||||
return err
|
||||
resp, err := r.dataProvider.put(r.accountURL(a.Id), bytes.NewReader(by), t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = resp.Body.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadAccount loads an account via cs3 by id and writes it to the provided account
|
||||
@@ -84,12 +94,46 @@ func (r CS3Repo) LoadAccount(ctx context.Context, id string, a *proto.Account) (
|
||||
return err
|
||||
}
|
||||
|
||||
return r.loadAccount(id, t, a)
|
||||
}
|
||||
|
||||
// LoadAccounts loads all the accounts from the cs3 api
|
||||
func (r CS3Repo) LoadAccounts(ctx context.Context, a *[]*proto.Account) (err error) {
|
||||
t, err := r.authenticate(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx = metadata.AppendToOutgoingContext(ctx, token.TokenHeader, t)
|
||||
res, err := r.storageProvider.ListContainer(ctx, &provider.ListContainerRequest{
|
||||
Ref: &provider.Reference{
|
||||
Spec: &provider.Reference_Path{Path: path.Join("/meta", accountsFolder)},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log := olog.NewLogger(olog.Pretty(r.cfg.Log.Pretty), olog.Color(r.cfg.Log.Color), olog.Level(r.cfg.Log.Level))
|
||||
for i := range res.Infos {
|
||||
acc := &proto.Account{}
|
||||
err := r.loadAccount(filepath.Base(res.Infos[i].Path), t, acc)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("could not load account")
|
||||
continue
|
||||
}
|
||||
*a = append(*a, acc)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r CS3Repo) loadAccount(id string, t string, a *proto.Account) error {
|
||||
resp, err := r.dataProvider.get(r.accountURL(id), t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if resp.StatusCode == http.StatusNotFound {
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return ¬FoundErr{"account", id}
|
||||
}
|
||||
|
||||
@@ -147,8 +191,14 @@ func (r CS3Repo) WriteGroup(ctx context.Context, g *proto.Group) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.dataProvider.put(r.groupURL(g.Id), bytes.NewReader(by), t)
|
||||
return err
|
||||
resp, err := r.dataProvider.put(r.groupURL(g.Id), bytes.NewReader(by), t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = resp.Body.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadGroup loads a group via cs3 by id and writes it to the provided group
|
||||
@@ -158,6 +208,40 @@ func (r CS3Repo) LoadGroup(ctx context.Context, id string, g *proto.Group) (err
|
||||
return err
|
||||
}
|
||||
|
||||
return r.loadGroup(id, t, g)
|
||||
}
|
||||
|
||||
// LoadGroups loads all the groups from the cs3 api
|
||||
func (r CS3Repo) LoadGroups(ctx context.Context, g *[]*proto.Group) (err error) {
|
||||
t, err := r.authenticate(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx = metadata.AppendToOutgoingContext(ctx, token.TokenHeader, t)
|
||||
res, err := r.storageProvider.ListContainer(ctx, &provider.ListContainerRequest{
|
||||
Ref: &provider.Reference{
|
||||
Spec: &provider.Reference_Path{Path: path.Join("/meta", groupsFolder)},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log := olog.NewLogger(olog.Pretty(r.cfg.Log.Pretty), olog.Color(r.cfg.Log.Color), olog.Level(r.cfg.Log.Level))
|
||||
for i := range res.Infos {
|
||||
grp := &proto.Group{}
|
||||
err := r.loadGroup(filepath.Base(res.Infos[i].Path), t, grp)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("could not load account")
|
||||
continue
|
||||
}
|
||||
*g = append(*g, grp)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r CS3Repo) loadGroup(id string, t string, g *proto.Group) error {
|
||||
resp, err := r.dataProvider.get(r.groupURL(id), t)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -205,14 +289,30 @@ func (r CS3Repo) DeleteGroup(ctx context.Context, id string) (err error) {
|
||||
}
|
||||
|
||||
func (r CS3Repo) authenticate(ctx context.Context) (token string, err error) {
|
||||
return AuthenticateCS3(ctx, r.cfg.ServiceUser, r.tm)
|
||||
}
|
||||
|
||||
// AuthenticateCS3 mints an auth token for communicating with cs3 storage based on a service user from config
|
||||
func AuthenticateCS3(ctx context.Context, su config.ServiceUser, tm token.Manager) (token string, err error) {
|
||||
u := &user.User{
|
||||
Id: &user.UserId{},
|
||||
Id: &user.UserId{
|
||||
OpaqueId: su.UUID,
|
||||
},
|
||||
Groups: []string{},
|
||||
Opaque: &types.Opaque{
|
||||
Map: map[string]*types.OpaqueEntry{
|
||||
"uid": {
|
||||
Decoder: "plain",
|
||||
Value: []byte(strconv.FormatInt(su.UID, 10)),
|
||||
},
|
||||
"gid": {
|
||||
Decoder: "plain",
|
||||
Value: []byte(strconv.FormatInt(su.GID, 10)),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if r.cfg.ServiceUser.Username != "" {
|
||||
u.Id.OpaqueId = r.cfg.ServiceUser.UUID
|
||||
}
|
||||
return r.tm.MintToken(ctx, u)
|
||||
return tm.MintToken(ctx, u)
|
||||
}
|
||||
|
||||
func (r CS3Repo) accountURL(id string) string {
|
||||
@@ -224,11 +324,16 @@ func (r CS3Repo) groupURL(id string) string {
|
||||
}
|
||||
|
||||
func (r CS3Repo) makeRootDirIfNotExist(ctx context.Context, folder string) error {
|
||||
return MakeDirIfNotExist(ctx, r.storageProvider, folder)
|
||||
}
|
||||
|
||||
// MakeDirIfNotExist will create a root node in the metadata storage. Requires an authenticated context.
|
||||
func MakeDirIfNotExist(ctx context.Context, sp provider.ProviderAPIClient, folder string) error {
|
||||
var rootPathRef = &provider.Reference{
|
||||
Spec: &provider.Reference_Path{Path: path.Join("/meta", folder)},
|
||||
}
|
||||
|
||||
resp, err := r.storageProvider.Stat(ctx, &provider.StatRequest{
|
||||
resp, err := sp.Stat(ctx, &provider.StatRequest{
|
||||
Ref: rootPathRef,
|
||||
})
|
||||
|
||||
@@ -237,7 +342,7 @@ func (r CS3Repo) makeRootDirIfNotExist(ctx context.Context, folder string) error
|
||||
}
|
||||
|
||||
if resp.Status.Code == v1beta11.Code_CODE_NOT_FOUND {
|
||||
_, err := r.storageProvider.CreateContainer(ctx, &provider.CreateContainerRequest{
|
||||
_, err := sp.CreateContainer(ctx, &provider.CreateContainerRequest{
|
||||
Ref: rootPathRef,
|
||||
})
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@ var groupLock sync.Mutex
|
||||
|
||||
// DiskRepo provides a local filesystem implementation of the Repo interface
|
||||
type DiskRepo struct {
|
||||
cfg *config.Config
|
||||
log olog.Logger
|
||||
cfg *config.Config
|
||||
log olog.Logger
|
||||
}
|
||||
|
||||
// NewDiskRepo creates a new disk repo
|
||||
@@ -37,8 +37,8 @@ func NewDiskRepo(cfg *config.Config, log olog.Logger) DiskRepo {
|
||||
}
|
||||
}
|
||||
return DiskRepo{
|
||||
cfg: cfg,
|
||||
log: log,
|
||||
cfg: cfg,
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,20 @@ func (r DiskRepo) LoadAccount(ctx context.Context, id string, a *proto.Account)
|
||||
return json.Unmarshal(data, a)
|
||||
}
|
||||
|
||||
// LoadAccounts loads all the accounts from the local filesystem
|
||||
func (r DiskRepo) LoadAccounts(ctx context.Context, a *[]*proto.Account) (err error) {
|
||||
root := filepath.Join(r.cfg.Repo.Disk.Path, accountsFolder)
|
||||
return filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
|
||||
acc := &proto.Account{}
|
||||
if e := r.LoadAccount(ctx, filepath.Base(path), acc); e != nil {
|
||||
r.log.Err(e).Msg("could not load account")
|
||||
return nil
|
||||
}
|
||||
*a = append(*a, acc)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteAccount from the local filesystem
|
||||
func (r DiskRepo) DeleteAccount(ctx context.Context, id string) (err error) {
|
||||
path := filepath.Join(r.cfg.Repo.Disk.Path, accountsFolder, id)
|
||||
@@ -118,6 +132,20 @@ func (r DiskRepo) LoadGroup(ctx context.Context, id string, g *proto.Group) (err
|
||||
return json.Unmarshal(data, g)
|
||||
}
|
||||
|
||||
// LoadGroups loads all the groups from the local filesystem
|
||||
func (r DiskRepo) LoadGroups(ctx context.Context, g *[]*proto.Group) (err error) {
|
||||
root := filepath.Join(r.cfg.Repo.Disk.Path, groupsFolder)
|
||||
return filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
|
||||
grp := &proto.Group{}
|
||||
if e := r.LoadGroup(ctx, filepath.Base(path), grp); e != nil {
|
||||
r.log.Err(e).Msg("could not load group")
|
||||
return nil
|
||||
}
|
||||
*g = append(*g, grp)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteGroup from the local filesystem
|
||||
func (r DiskRepo) DeleteGroup(ctx context.Context, id string) (err error) {
|
||||
path := filepath.Join(r.cfg.Repo.Disk.Path, groupsFolder, id)
|
||||
@@ -127,10 +155,7 @@ func (r DiskRepo) DeleteGroup(ctx context.Context, id string) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
//r.log.Error().Err(err).Str("id", id).Str("path", path).Msg("could not remove group")
|
||||
//return merrors.InternalServerError(r.serviceID, "could not remove group: %v", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// deflateMemberOf replaces the groups of a user with an instance that only contains the id
|
||||
|
||||
@@ -2,20 +2,23 @@ package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/owncloud/ocis/accounts/pkg/proto/v0"
|
||||
)
|
||||
|
||||
const (
|
||||
accountsFolder = "accounts"
|
||||
groupsFolder = "groups"
|
||||
groupsFolder = "groups"
|
||||
)
|
||||
|
||||
// Repo defines the storage operations
|
||||
type Repo interface {
|
||||
WriteAccount(ctx context.Context, a *proto.Account) (err error)
|
||||
LoadAccount(ctx context.Context, id string, a *proto.Account) (err error)
|
||||
LoadAccounts(ctx context.Context, a *[]*proto.Account) (err error)
|
||||
DeleteAccount(ctx context.Context, id string) (err error)
|
||||
WriteGroup(ctx context.Context, g *proto.Group) (err error)
|
||||
LoadGroup(ctx context.Context, id string, g *proto.Group) (err error)
|
||||
LoadGroups(ctx context.Context, g *[]*proto.Group) (err error)
|
||||
DeleteGroup(ctx context.Context, id string) (err error)
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ const navItems = [
|
||||
name: 'accounts',
|
||||
path: `/${appInfo.id}/`
|
||||
},
|
||||
menu: 'user'
|
||||
menu: 'apps'
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
---
|
||||
|
||||
# OpenID Connect client registry.
|
||||
clients:
|
||||
- id: phoenix
|
||||
name: OCIS
|
||||
application_type: web
|
||||
insecure: yes
|
||||
trusted: yes
|
||||
redirect_uris:
|
||||
- https://ocis-server:9200/oidc-callback.html
|
||||
- https://ocis-server:9200/
|
||||
origins:
|
||||
- https://ocis-server:9200
|
||||
|
||||
authorities:
|
||||
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"server": "https://ocis-server:9200",
|
||||
"theme": "owncloud",
|
||||
"version": "0.1.0",
|
||||
"openIdConnect": {
|
||||
"metadata_url": "https://ocis-server:9200/.well-known/openid-configuration",
|
||||
"authority": "https://ocis-server:9200",
|
||||
"client_id": "phoenix",
|
||||
"response_type": "code",
|
||||
"scope": "openid profile email"
|
||||
},
|
||||
"apps": [
|
||||
"files",
|
||||
"draw-io",
|
||||
"markdown-editor",
|
||||
"media-viewer"
|
||||
],
|
||||
"external_apps": [
|
||||
{
|
||||
"id": "accounts",
|
||||
"path": "https://ocis-server:9200/accounts.js",
|
||||
"config": {
|
||||
"url": "https://ocis-server:9200"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1467,9 +1467,9 @@ binary-extensions@^2.0.0:
|
||||
integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==
|
||||
|
||||
bl@^4.0.1:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/bl/-/bl-4.0.2.tgz#52b71e9088515d0606d9dd9cc7aa48dc1f98e73a"
|
||||
integrity sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ==
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/bl/-/bl-4.0.3.tgz#12d6287adc29080e22a705e5764b2a9522cdc489"
|
||||
integrity sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==
|
||||
dependencies:
|
||||
buffer "^5.5.0"
|
||||
inherits "^2.0.4"
|
||||
@@ -4163,9 +4163,9 @@ lodash.union@^4.6.0:
|
||||
integrity sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=
|
||||
|
||||
lodash@^4.15.0, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4:
|
||||
version "4.17.15"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
|
||||
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
|
||||
version "4.17.20"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
|
||||
integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
|
||||
|
||||
log-symbols@2.2.0:
|
||||
version "2.2.0"
|
||||
@@ -4488,10 +4488,10 @@ node-environment-flags@1.0.5:
|
||||
object.getownpropertydescriptors "^2.0.3"
|
||||
semver "^5.7.0"
|
||||
|
||||
node-fetch@^2.6.0:
|
||||
version "2.6.0"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd"
|
||||
integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==
|
||||
node-fetch@^2.6.1:
|
||||
version "2.6.1"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
||||
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
||||
|
||||
node-modules-regexp@^1.0.0:
|
||||
version "1.0.0"
|
||||
|
||||
15
changelog/unreleased/accounts-fs-based-index.md
Normal file
15
changelog/unreleased/accounts-fs-based-index.md
Normal file
@@ -0,0 +1,15 @@
|
||||
Change: Filesystem based index
|
||||
|
||||
Tags: accounts, storage
|
||||
|
||||
We replaced `bleve` with a new filesystem based index implementation. There is an `indexer` which is capable of
|
||||
orchestrating different index types to build indices on documents by field. You can choose from the index types `unique`,
|
||||
`non-unique` or `autoincrement`. Indices can be utilized to run search queries (full matches or globbing) on document
|
||||
fields. The accounts service is using this index internally to run the search queries coming in via `ListAccounts` and
|
||||
`ListGroups` and to generate UIDs for new accounts as well as GIDs for new groups.
|
||||
|
||||
The accounts service can be configured to store the index on the local FS / a NFS (`disk` implementation of the index)
|
||||
or to use an arbitrary storage ( `cs3` implementation of the index). `cs3` is the new default, which is configured to
|
||||
use the `metadata` storage.
|
||||
|
||||
https://github.com/owncloud/ocis/pull/709
|
||||
9
changelog/unreleased/accounts-index-rebuild.md
Normal file
9
changelog/unreleased/accounts-index-rebuild.md
Normal file
@@ -0,0 +1,9 @@
|
||||
Change: Rebuild index command for accounts
|
||||
|
||||
Tags: accounts
|
||||
|
||||
The index for the accounts service can now be rebuilt by running the cli command `./bin/ocis accounts rebuild`.
|
||||
It deletes all configured indices and rebuilds them from the documents found on storage. For this we also introduced
|
||||
a `LoadAccounts` and `LoadGroups` function on storage for loading all existing documents.
|
||||
|
||||
https://github.com/owncloud/ocis/pull/748
|
||||
7
changelog/unreleased/cs3-index-lowerbound.md
Normal file
7
changelog/unreleased/cs3-index-lowerbound.md
Normal file
@@ -0,0 +1,7 @@
|
||||
Bugfix: Lower Bound was not working for the cs3 api index implementation
|
||||
|
||||
Tags: accounts
|
||||
|
||||
Lower bound working on the cs3 index implementation
|
||||
|
||||
https://github.com/owncloud/ocis/pull/741
|
||||
@@ -0,0 +1,7 @@
|
||||
Bugfix: Fix id or username query handling
|
||||
|
||||
Tags: accounts
|
||||
|
||||
The code was stopping execution when encountering an error while loading an account by id. But for or queries we can continue execution.
|
||||
|
||||
https://github.com/owncloud/ocis/pull/745
|
||||
7
changelog/unreleased/glauth-micro-client
Normal file
7
changelog/unreleased/glauth-micro-client
Normal file
@@ -0,0 +1,7 @@
|
||||
Bugfix: Use micro default client
|
||||
|
||||
Tags: glauth
|
||||
|
||||
We found a file descriptor leak in the glauth connections to the accounts service. Fixed it by using the micro default client.
|
||||
|
||||
https://github.com/owncloud/ocis/pull/718
|
||||
8
changelog/unreleased/mint-token-with-uid.md
Normal file
8
changelog/unreleased/mint-token-with-uid.md
Normal file
@@ -0,0 +1,8 @@
|
||||
Bugfix: Mint token with uid and gid
|
||||
|
||||
Tags: accounts
|
||||
|
||||
The eos driver expects the uid and gid from the opaque map of a user. While the proxy does mint tokens correctly, the accounts service wasn't.
|
||||
|
||||
https://github.com/owncloud/ocis/pull/737
|
||||
|
||||
9
changelog/unreleased/ocs-map-userid-to-username.md
Normal file
9
changelog/unreleased/ocs-map-userid-to-username.md
Normal file
@@ -0,0 +1,9 @@
|
||||
Change: Remove username field in OCS
|
||||
|
||||
Tags: ocs
|
||||
|
||||
We use the incoming userid as both the `id` and the `on_premises_sam_account_name` for new accounts in the accounts
|
||||
service. The userid in OCS requests is in fact the username, not our internal account id. We need to enforce the userid
|
||||
as our internal account id though, because the account id is part of various `path` formats.
|
||||
|
||||
https://github.com/owncloud/ocis/pull/709
|
||||
7
changelog/unreleased/phoenix-default-apps.md
Normal file
7
changelog/unreleased/phoenix-default-apps.md
Normal file
@@ -0,0 +1,7 @@
|
||||
Change: Default apps in ownCloud Web
|
||||
|
||||
Tags: web
|
||||
|
||||
We changed the default apps for ownCloud Web to be only files and media-viewer. Markdown-editor and draw-io have been removed as defaults.
|
||||
|
||||
https://github.com/owncloud/ocis/pull/688
|
||||
7
changelog/unreleased/prevent-duplicate-accounts.md
Normal file
7
changelog/unreleased/prevent-duplicate-accounts.md
Normal file
@@ -0,0 +1,7 @@
|
||||
Bugfix: Don't create account if id/mail/username already taken
|
||||
|
||||
Tags: accounts
|
||||
|
||||
We don't allow anymore to create a new account if the provided id/mail/username is already taken.
|
||||
|
||||
https://github.com/owncloud/ocis/pull/709
|
||||
7
changelog/unreleased/themed-kopano
Normal file
7
changelog/unreleased/themed-kopano
Normal file
@@ -0,0 +1,7 @@
|
||||
Change: Bring oC theme
|
||||
|
||||
Tags: konnectd
|
||||
|
||||
We've styled our konnectd login page to reflect ownCloud theme.
|
||||
|
||||
https://github.com/owncloud/ocis/pull/698
|
||||
27
changelog/unreleased/update-konnectd.md
Normal file
27
changelog/unreleased/update-konnectd.md
Normal file
@@ -0,0 +1,27 @@
|
||||
Enhancement: Update konnectd to v0.33.8
|
||||
|
||||
This update adds options which allow the configuration of oidc-token expiration
|
||||
parameters: KONNECTD_ACCESS_TOKEN_EXPIRATION, KONNECTD_ID_TOKEN_EXPIRATION and
|
||||
KONNECTD_REFRESH_TOKEN_EXPIRATION.
|
||||
|
||||
Other changes from upstream:
|
||||
|
||||
- Generate random endsession state for external authority
|
||||
- Update dependencies in Dockerfile
|
||||
- Set prompt=None to avoid loops with external authority
|
||||
- Update Jenkins reporting plugin from checkstyle to recordIssues
|
||||
- Remove extra kty key from JWKS top level document
|
||||
- Fix regression which encodes URL fragments twice
|
||||
- Avoid generating fragmet/query URLs with wrong order
|
||||
- Return state for oidc endsession response redirects
|
||||
- Use server provided username to avoid case mismatch
|
||||
- Use signed-out-uri if set as fallback for goodbye redirect on saml slo
|
||||
- Add checks to ensure post_logout_redirect_uri is not empty
|
||||
- Fix SAML2 logout request parsing
|
||||
- Cure panic when no state is found in saml esr
|
||||
- Use SAML IdP Issuer value from meta data entityID
|
||||
- Allow configuration of expiration of oidc access, id and refresh tokens
|
||||
- Implement trampolin for external OIDC authority end session
|
||||
- Update ca-certificates version
|
||||
|
||||
https://github.com/owncloud/ocis/pull/744
|
||||
8
changelog/unreleased/update-phoenix-0.21.0
Normal file
8
changelog/unreleased/update-phoenix-0.21.0
Normal file
@@ -0,0 +1,8 @@
|
||||
Change: Update phoenix to v0.21.0
|
||||
|
||||
Tags: web
|
||||
|
||||
We updated phoenix to v0.21.0. Please refer to the changelog (linked) for details on the phoenix release.
|
||||
|
||||
https://github.com/owncloud/ocis/pull/728
|
||||
https://github.com/owncloud/phoenix/releases/tag/v0.21.0
|
||||
6
changelog/unreleased/update-reva-to-cdb3d6688da5.md
Normal file
6
changelog/unreleased/update-reva-to-cdb3d6688da5.md
Normal file
@@ -0,0 +1,6 @@
|
||||
Enhancement: Update reva to cdb3d6688da5
|
||||
|
||||
* let the gateway filter invalid references
|
||||
|
||||
https://github.com/owncloud/ocis/pull/748
|
||||
https://github.com/cs3org/reva/pull/1274
|
||||
6
changelog/unreleased/update-reva-to-dd3a8c0f38.md
Normal file
6
changelog/unreleased/update-reva-to-dd3a8c0f38.md
Normal file
@@ -0,0 +1,6 @@
|
||||
Enhancement: Update reva to dd3a8c0f38
|
||||
|
||||
* fixes etag propagation in the ocis driver
|
||||
|
||||
https://github.com/owncloud/ocis/pull/725
|
||||
https://github.com/cs3org/reva/pull/1264
|
||||
5
changelog/unreleased/update-storage-env-vars.md
Normal file
5
changelog/unreleased/update-storage-env-vars.md
Normal file
@@ -0,0 +1,5 @@
|
||||
Change: Clarify storage driver env vars
|
||||
|
||||
After renaming ocsi-reva to storage and combining the storage and data providers some env vars were confusingly named `STORAGE_STORAGE_...`. We are changing the prefix for driver related env vars to `STORAGE_DRIVER_...`. This makes changing the storage driver using eg.: `STORAGE_HOME_DRIVER=eos` and setting driver options using `STORAGE_DRIVER_EOS_LAYOUT=...` less confusing.
|
||||
|
||||
https://github.com/owncloud/ocis/pull/729
|
||||
@@ -190,7 +190,7 @@ If you prefer to configure the service with commandline flags you can see the av
|
||||
: Version, defaults to `0.1.0`
|
||||
|
||||
--web-config-app
|
||||
: Provide multiple apps, defaults to `""`. Usage: `phoenix --web-config-app files --web-config-app pdf-viewer`
|
||||
: Provide multiple apps, defaults to `""`. Usage: `phoenix --web-config-app files --web-config-app media-viewer`
|
||||
|
||||
--oidc-metadata-url
|
||||
: OpenID Connect metadata URL, defaults to `http://localhost:9130/.well-known/openid-configuration`
|
||||
|
||||
@@ -26,20 +26,12 @@ It uses the port range 9140-9179 to preconfigure several services.
|
||||
| 9149 | authbearer debug |
|
||||
| 9150 | sharing |
|
||||
| 9151 | sharing debug |
|
||||
| 9152 | storage root |
|
||||
| 9153 | storage root debug |
|
||||
| 9154 | storage home |
|
||||
| 9155 | storage home debug |
|
||||
| 9156 | storage home data |
|
||||
| 9157 | storage home data debug |
|
||||
| 9158 | storage eos |
|
||||
| 9159 | storage eos debug |
|
||||
| 9160 | storage eos data |
|
||||
| 9161 | storage eos data debug |
|
||||
| 9162 | storage oc |
|
||||
| 9163 | storage oc debug |
|
||||
| 9164 | storage oc data |
|
||||
| 9165 | storage oc data debug |
|
||||
| 9155 | storage home data |
|
||||
| 9156 | storage home debug |
|
||||
| 9157 | storage users |
|
||||
| 9158 | storage users data |
|
||||
| 9159 | storage users debug |
|
||||
| 9166-9177 | reserved for s3, wnd, custom + data providers |
|
||||
| 9178 | storage public link |
|
||||
| 9179 | storage public link data |
|
||||
| 9179 | storage public link debug |
|
||||
|
||||
@@ -158,11 +158,11 @@ For a simple docker-compose setup, you can create a volume which will be used by
|
||||
The CERN eos storage has evolved with ownCloud and natively supports id based lookup, ETag propagation, subtree size accounting, sharing, trash and versions. To use it you need to change the default configuration of the `storage storage-home` command (or have a look at the Makefile ̀ eos-start` target):
|
||||
|
||||
```
|
||||
export STORAGE_STORAGE_HOME_DRIVER=eos
|
||||
export STORAGE_STORAGE_EOS_NAMESPACE=/eos
|
||||
export STORAGE_STORAGE_EOS_MASTER_URL="root://eos-mgm1.eoscluster.cern.ch:1094"
|
||||
export STORAGE_STORAGE_EOS_ENABLE_HOME=true
|
||||
export STORAGE_STORAGE_EOS_LAYOUT="dockertest/{{.Username}}"
|
||||
export STORAGE_HOME_DRIVER=eos
|
||||
export STORAGE_DRIVER_EOS_NAMESPACE=/eos
|
||||
export STORAGE_DRIVER_EOS_MASTER_URL="root://eos-mgm1.eoscluster.cern.ch:1094"
|
||||
export STORAGE_DRIVER_EOS_ENABLE_HOME=true
|
||||
export STORAGE_DRIVER_EOS_LAYOUT="dockertest/{{.Username}}"
|
||||
```
|
||||
|
||||
Running it locally also requires the `eos` and `xrootd` binaries. Running it using `make eos-start` will use CentOS based containers that already have the necessary packages installed.
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
---
|
||||
title: "Testing"
|
||||
date: 2018-05-02T00:00:00+00:00
|
||||
weight: 37
|
||||
geekdocRepo: https://github.com/owncloud/ocis
|
||||
geekdocEditPath: edit/master/docs/extensions/storage
|
||||
geekdocFilePath: testing.md
|
||||
---
|
||||
|
||||
|
||||
## API Acceptance tests
|
||||
|
||||
We are using the ownCloud 10 API acceptance testsuite against ocis. To set this up you need the owncloud 10 core repo, a ldap server that the acceptance tests can use to manage users, a redis server for file-versions and the ocis-reva code.
|
||||
|
||||
### Getting the tests
|
||||
|
||||
All you need to do to get the acceptance tests is check out the core repo:
|
||||
```
|
||||
git clone https://github.com/owncloud/core.git
|
||||
```
|
||||
|
||||
### Run a ldap server in a docker container
|
||||
|
||||
The ownCloud 10 acceptance tests will need write permission. You can start a suitable ldap server in a docker container with:
|
||||
|
||||
```
|
||||
docker run --hostname ldap.my-company.com \
|
||||
-e LDAP_TLS_VERIFY_CLIENT=never \
|
||||
-e LDAP_DOMAIN=owncloud.com \
|
||||
-e LDAP_ORGANISATION=ownCloud \
|
||||
-e LDAP_ADMIN_PASSWORD=admin \
|
||||
--name docker-slapd \
|
||||
-p 127.0.0.1:389:389 \
|
||||
-p 636:636 -d osixia/openldap
|
||||
```
|
||||
### Run a redis server in a docker container
|
||||
|
||||
File versions need a redis server. Start one with docker by using:
|
||||
|
||||
`docker run -e REDIS_DATABASES=1 -p 6379:6379 -d webhippie/redis:latest`
|
||||
|
||||
### Run ocis-reva with that ldap server
|
||||
|
||||
`storage` provides multiple subcommands. To configure them all via env vars you can export these environment variables.
|
||||
|
||||
```
|
||||
export STORAGE_USERS_DRIVER=ldap
|
||||
export STORAGE_LDAP_HOSTNAME=localhost
|
||||
export STORAGE_LDAP_PORT=636
|
||||
export STORAGE_LDAP_BASE_DN='dc=owncloud,dc=com'
|
||||
export STORAGE_LDAP_USERFILTER='(&(objectclass=posixAccount)(cn=%s))'
|
||||
export STORAGE_LDAP_GROUPFILTER='(&(objectclass=posixGroup)(cn=%s))'
|
||||
export STORAGE_LDAP_BIND_DN='cn=admin,dc=owncloud,dc=com'
|
||||
export STORAGE_LDAP_BIND_PASSWORD=admin
|
||||
export STORAGE_LDAP_SCHEMA_UID=uid
|
||||
export STORAGE_LDAP_SCHEMA_MAIL=mail
|
||||
export STORAGE_LDAP_SCHEMA_DISPLAYNAME=displayName
|
||||
export STORAGE_LDAP_SCHEMA_CN=cn
|
||||
export STORAGE_FRONTEND_URL=http://localhost:9140 # needed because the proxy is not started
|
||||
export STORAGE_DATAGATEWAY_URL=http://localhost:9140/data # needed because the proxy is not started
|
||||
```
|
||||
|
||||
Then you need to start the ocis-reva services
|
||||
```
|
||||
bin/storage frontend & \
|
||||
bin/storage gateway & \
|
||||
bin/storage auth-basic & \
|
||||
bin/storage auth-bearer & \
|
||||
bin/storage sharing & \
|
||||
bin/storage storage-home & \
|
||||
bin/storage storage-home-data & \
|
||||
bin/storage storage-oc & \
|
||||
bin/storage storage-oc-data & \
|
||||
bin/storage users &
|
||||
```
|
||||
|
||||
### Run the API acceptance tests
|
||||
|
||||
In the ownCloud 10 core repo run
|
||||
|
||||
```
|
||||
make test-acceptance-api \
|
||||
TEST_SERVER_URL=https://localhost:9200 \
|
||||
TEST_EXTERNAL_USER_BACKENDS=true \
|
||||
TEST_OCIS=true \
|
||||
OCIS_REVA_DATA_ROOT=/var/tmp/ocis/owncloud/ \
|
||||
BEHAT_FILTER_TAGS='~@notToImplementOnOCIS&&~@toImplementOnOCIS&&~@preview-extension-required' \
|
||||
SKELETON_DIR=apps/testing/data/apiSkeleton
|
||||
```
|
||||
|
||||
Make sure to adjust the settings `TEST_SERVER_URL`,`OCIS_REVA_DATA_ROOT` and `SKELETON_DIR` according to your environment.
|
||||
|
||||
This will run all tests that are relevant to OCIS.
|
||||
|
||||
To run a single test add `BEHAT_FEATURE=<feature file>` and specify the path to the feature file and an optional line number. For example: `BEHAT_FEATURE='tests/acceptance/features/apiWebdavUpload1/uploadFile.feature:12'`
|
||||
|
||||
### use existing tests for BDD
|
||||
|
||||
As a lot of scenarios are written for oC10, we can use those tests for Behaviour driven development in ocis.
|
||||
Every scenario that does not work in OCIS with OC storage, is listed in `tests/acceptance/expected-failures-on-OC-storage.txt` with a link to the related issue.
|
||||
Similarly, scenarios that do not work in OCIS with EOS storage are listed in `tests/acceptance/expected-failures-on-EOS-storage.txt`.
|
||||
Scenarios from the oC10 API acceptance tests are run in the ordinary acceptance test pipeline in CI. The scenarios that fail are checked against the
|
||||
expected failures. If there are any differences then the CI pipeline fails.
|
||||
|
||||
Additionally, some issues have scenarios that demonstrate the current buggy behaviour in ocis(reva).
|
||||
Those scenarios are in this ocis-reva repository in `tests/acceptance/features/apiOcisSpecific`.
|
||||
Have a look into the [documentation](https://doc.owncloud.com/server/developer_manual/testing/acceptance-tests.html#writing-scenarios-for-bugs) to understand why we are writing those tests.
|
||||
Also, ocis behaves partly differently with EOS-Storage and OC-Storage. There are scenarios that do not work in OCIS when run on EOS-storage, but works when on OC-Storage, and vice-versa. For those kind of scenarios, ` @skipOnOcis-EOS-Storage` and `@skipOnOcis-OC-Storage` tags are used. For instance, for a scenario that fails on EOS-Storage but passes on OC-Storage, we use `@skipOnOcis-EOS-Storage` tag to let it run on OC-Storage, where it works as expected, instead of skipping the test completely.
|
||||
|
||||
If you want to work on a specific issue
|
||||
|
||||
1. adjust the core commit id to the latest commit in core so that CI will run the latest test code and scenarios from core.
|
||||
For that change `coreCommit` in the `config` section:
|
||||
|
||||
config = {
|
||||
'apiTests': {
|
||||
'coreBranch': 'master',
|
||||
'coreCommit': 'a06b1bd5ba8e5244bfaf7fa04f441961e6fb0daa',
|
||||
'numberOfParts': 2
|
||||
}
|
||||
}
|
||||
|
||||
2. locally run each of the tests marked with that issue in the expected failures file:
|
||||
|
||||
E.g.:
|
||||
```
|
||||
make test-acceptance-api \
|
||||
TEST_SERVER_URL=https://localhost:9200 \
|
||||
TEST_EXTERNAL_USER_BACKENDS=true \
|
||||
TEST_OCIS=true \
|
||||
OCIS_REVA_DATA_ROOT=/var/tmp/ocis/owncloud/ \
|
||||
BEHAT_FEATURE='tests/acceptance/features/apiComments/comments.feature:123'
|
||||
```
|
||||
|
||||
3. the tests will fail, try to understand how and why they are failing
|
||||
4. fix the code
|
||||
5. go back to 2. and repeat till the tests are passing.
|
||||
6. remove those tests from the expected failures file.
|
||||
7. run each of the local tests that were demonstrating the **buggy** behavior. They should fail.
|
||||
8. delete each of the local tests that were demonstrating the **buggy** behavior.
|
||||
9. make a PR that has the fixed code, relevant lines removed from the expected failures file and bug demonstration tests deleted.
|
||||
|
||||
If the changes also affect the `ocis` repository make sure the changes get ported over there.
|
||||
That will need the fixed code in `storage` to be applied to `ocis` along with the test-related changes.
|
||||
|
||||
### Notes
|
||||
- in a normal case the test-code cleans up users after the test-run, but if a test-run is interrupted (e.g. by CTRL+C) users might have been left on the LDAP server. In that case rerunning the tests requires wiping the users in the ldap server, otherwise the tests will fail when trying to populate the users. This can be done by simply running `docker stop docker-slapd && docker rm docker-slapd` and [restarting the LDAP server container](#run-a-ldap-server-in-a-docker-container)
|
||||
- the tests usually create users in the OU `TestUsers` with usernames specified in the feature file. If not defined in the feature file, most users have the password `123456`, defined by `regularUserPassword` in `behat.yml`, but other passwords are also used, see [`\FeatureContext::getPasswordForUser()`](https://github.com/owncloud/core/blob/master/tests/acceptance/features/bootstrap/FeatureContext.php#L386) for mapping and [`\FeatureContext::__construct`](https://github.com/owncloud/core/blob/master/tests/acceptance/features/bootstrap/FeatureContext.php#L1668) for the password definitions.
|
||||
@@ -1,232 +0,0 @@
|
||||
---
|
||||
title: "Configuration"
|
||||
date: "2020-10-05T21:19:39+0200"
|
||||
weight: 20
|
||||
geekdocRepo: https://github.com/owncloud/ocis
|
||||
geekdocEditPath: edit/master/docs/ocis
|
||||
geekdocFilePath: configuration.md
|
||||
---
|
||||
|
||||
{{< toc >}}
|
||||
|
||||
## Configuration
|
||||
|
||||
oCIS Single Binary is not responsible for configuring extensions. Instead, each extension could either be configured by environment variables, cli flags or config files.
|
||||
|
||||
Each extension has its dedicated documentation page (e.g. https://owncloud.github.io/extensions/ocis_proxy/configuration) which lists all possible configurations. Config files and environment variables are picked up if you use the `./bin/ocis server` command within the oCIS single binary. Command line flags must be set explicitly on the extensions subcommands.
|
||||
|
||||
### Configuration using config files
|
||||
|
||||
Out of the box extensions will attempt to read configuration details from:
|
||||
|
||||
```console
|
||||
/etc/ocis
|
||||
$HOME/.ocis
|
||||
./config
|
||||
```
|
||||
|
||||
For this configuration to be picked up, have a look at your extension `root` command and look for which default config name it has assigned. *i.e: ocis-proxy reads `proxy.json | yaml | toml ...`*.
|
||||
|
||||
So far we support the file formats `JSON` and `YAML`, if you want to get a full example configuration just take a look at [our repository](https://github.com/owncloud/ocis/tree/master/config), there you can always see the latest configuration format. These example configurations include all available options and the default values. The configuration file will be automatically loaded if it's placed at `/etc/ocis/ocis.yml`, `${HOME}/.ocis/ocis.yml` or `$(pwd)/config/ocis.yml`.
|
||||
|
||||
### Envrionment variables
|
||||
|
||||
If you prefer to configure the service with environment variables you can see the available variables below.
|
||||
|
||||
### Commandline flags
|
||||
|
||||
If you prefer to configure the service with commandline flags you can see the available variables below. Command line flags are only working when calling the subcommand directly.
|
||||
|
||||
## Root Command
|
||||
|
||||
ownCloud Infinite Scale Stack
|
||||
|
||||
Usage: `ocis [global options] command [command options] [arguments...]`
|
||||
|
||||
--config-file | $OCIS_CONFIG_FILE
|
||||
: Path to config file.
|
||||
|
||||
--log-level | $OCIS_LOG_LEVEL
|
||||
: Set logging level. Default: `info`.
|
||||
|
||||
--log-pretty | $OCIS_LOG_PRETTY
|
||||
: Enable pretty logging. Default: `true`.
|
||||
|
||||
--log-color | $OCIS_LOG_COLOR
|
||||
: Enable colored logging. Default: `true`.
|
||||
|
||||
## Sub Commands
|
||||
|
||||
### ocis kill
|
||||
|
||||
Kill an extension by name
|
||||
|
||||
Usage: `ocis kill [command options] [arguments...]`
|
||||
|
||||
### ocis server
|
||||
|
||||
Start fullstack server
|
||||
|
||||
Usage: `ocis server [command options] [arguments...]`
|
||||
|
||||
--tracing-enabled | $OCIS_TRACING_ENABLED
|
||||
: Enable sending traces.
|
||||
|
||||
--tracing-type | $OCIS_TRACING_TYPE
|
||||
: Tracing backend type. Default: `jaeger`.
|
||||
|
||||
--tracing-endpoint | $OCIS_TRACING_ENDPOINT
|
||||
: Endpoint for the agent. Default: `localhost:6831`.
|
||||
|
||||
--tracing-collector | $OCIS_TRACING_COLLECTOR
|
||||
: Endpoint for the collector. Default: `http://localhost:14268/api/traces`.
|
||||
|
||||
--tracing-service | $OCIS_TRACING_SERVICE
|
||||
: Service name for tracing. Default: `ocis`.
|
||||
|
||||
--debug-addr | $OCIS_DEBUG_ADDR
|
||||
: Address to bind debug server. Default: `0.0.0.0:9010`.
|
||||
|
||||
--debug-token | $OCIS_DEBUG_TOKEN
|
||||
: Token to grant metrics access.
|
||||
|
||||
--debug-pprof | $OCIS_DEBUG_PPROF
|
||||
: Enable pprof debugging.
|
||||
|
||||
--debug-zpages | $OCIS_DEBUG_ZPAGES
|
||||
: Enable zpages debugging.
|
||||
|
||||
--http-addr | $OCIS_HTTP_ADDR
|
||||
: Address to bind http server. Default: `0.0.0.0:9000`.
|
||||
|
||||
--http-root | $OCIS_HTTP_ROOT
|
||||
: Root path of http server. Default: `/`.
|
||||
|
||||
--grpc-addr | $OCIS_GRPC_ADDR
|
||||
: Address to bind grpc server. Default: `0.0.0.0:9001`.
|
||||
|
||||
### ocis run
|
||||
|
||||
Runs an extension
|
||||
|
||||
Usage: `ocis run [command options] [arguments...]`
|
||||
|
||||
### ocis health
|
||||
|
||||
Check health status
|
||||
|
||||
Usage: `ocis health [command options] [arguments...]`
|
||||
|
||||
--debug-addr | $OCIS_DEBUG_ADDR
|
||||
: Address to debug endpoint. Default: `0.0.0.0:9010`.
|
||||
|
||||
### ocis list
|
||||
|
||||
Lists running ocis extensions
|
||||
|
||||
Usage: `ocis list [command options] [arguments...]`
|
||||
|
||||
### List of available Extension subcommands
|
||||
|
||||
There are more subcommands to start the individual extensions. Please check the documentation about their usage and options in the dedicated section of the documentation.
|
||||
|
||||
#### ocis konnectd
|
||||
|
||||
Start konnectd server
|
||||
|
||||
#### ocis storage-frontend
|
||||
|
||||
Start storage frontend
|
||||
|
||||
#### ocis accounts
|
||||
|
||||
Start accounts server
|
||||
|
||||
#### ocis storage-storage-root
|
||||
|
||||
Start storage root storage
|
||||
|
||||
#### ocis storage-gateway
|
||||
|
||||
Start storage gateway
|
||||
|
||||
#### ocis storage-storage-home
|
||||
|
||||
Start storage storage service for home mount
|
||||
|
||||
#### ocis storage-storage-public-link
|
||||
|
||||
Start storage public link storage
|
||||
|
||||
#### ocis store
|
||||
|
||||
Start a go-micro store
|
||||
|
||||
#### ocis glauth
|
||||
|
||||
Start glauth server
|
||||
|
||||
#### ocis storage-auth-bearer
|
||||
|
||||
Start storage auth-bearer service
|
||||
|
||||
#### ocis storage-sharing
|
||||
|
||||
Start storage sharing service
|
||||
|
||||
#### ocis webdav
|
||||
|
||||
Start webdav server
|
||||
|
||||
#### ocis storage-storage-oc-data
|
||||
|
||||
Start storage storage data provider for oc mount
|
||||
|
||||
#### ocis thumbnails
|
||||
|
||||
Start thumbnails server
|
||||
|
||||
#### ocis proxy
|
||||
|
||||
Start proxy server
|
||||
|
||||
#### ocis settings
|
||||
|
||||
Start settings server
|
||||
|
||||
#### ocis storage-auth-basic
|
||||
|
||||
Start storage auth-basic service
|
||||
|
||||
#### ocis storage-storage-metadata
|
||||
|
||||
Start storage storage service for metadata mount
|
||||
|
||||
#### ocis ocs
|
||||
|
||||
Start ocs server
|
||||
|
||||
#### ocis storage-storage-eos
|
||||
|
||||
Start storage storage service for eos mount
|
||||
|
||||
#### ocis storage-storage-eos-data
|
||||
|
||||
Start storage storage data provider for eos mount
|
||||
|
||||
#### ocis storage-storage-home-data
|
||||
|
||||
Start storage storage data provider for home mount
|
||||
|
||||
#### ocis storage-storage-oc
|
||||
|
||||
Start storage storage service for oc mount
|
||||
|
||||
#### ocis storage-users
|
||||
|
||||
Start storage users service
|
||||
|
||||
#### ocis phoenix
|
||||
|
||||
Start phoenix server
|
||||
|
||||
@@ -45,8 +45,8 @@ Then run the api acceptance tests with the following command:
|
||||
make test-acceptance-api \
|
||||
TEST_SERVER_URL=https://localhost:9200 \
|
||||
TEST_OCIS=true \
|
||||
OCIS_REVA_DATA_ROOT=/var/tmp/reva/ \
|
||||
SKELETON_DIR=apps/testing/data/apiSkeleton \
|
||||
DELETE_USER_DATA_CMD='rm -rf /var/tmp/ocis/storage/users/nodes/root/* /var/tmp/ocis/storage/users/nodes/*-*-*-*' \
|
||||
BEHAT_FILTER_TAGS='~@notToImplementOnOCIS&&~@toImplementOnOCIS'
|
||||
```
|
||||
|
||||
@@ -59,13 +59,14 @@ To run a single test add `BEHAT_FEATURE=<feature file>`
|
||||
### use existing tests for BDD
|
||||
|
||||
As a lot of scenarios are written for oC10, we can use those tests for Behaviour driven development in ocis.
|
||||
Every scenario that does not work in OCIS with OC storage, is listed in `tests/acceptance/expected-failures-on-OC-storage.txt` with a link to the related issue.
|
||||
Every scenario that does not work in OCIS with "owncloud" storage, is listed in `ocis/tests/acceptance/expected-failures-on-OWNCLOUD-storage.txt` with a link to the related issue.
|
||||
Every scenario that does not work in OCIS with "ocis" storage, is listed in `ocis/tests/acceptance/expected-failures-on-OCIS-storage.txt` with a link to the related issue.
|
||||
|
||||
Those scenarios are run in the ordinary acceptance test pipeline in CI. The scenarios that fail are checked against the
|
||||
expected failures. If there are any differences then the CI pipeline fails.
|
||||
Similarly, scenarios that do not work in OCIS with EOS storage are listed in `tests/acceptance/expected-failures-on-EOS-storage.txt`.
|
||||
Similarly, scenarios that do not work in OCIS with EOS storage are listed in `ocis/tests/acceptance/expected-failures-on-EOS-storage.txt`.
|
||||
Additionally, some issues have scenarios that demonstrate the current buggy behaviour in ocis(reva).
|
||||
Those scenarios are in this ocis repository in `tests/acceptance/features/apiOcisSpecific`.
|
||||
Those scenarios are in this ocis repository in `ocis/tests/acceptance/features/apiOcisSpecific`.
|
||||
Have a look into the [documentation](https://doc.owncloud.com/server/developer_manual/testing/acceptance-tests.html#writing-scenarios-for-bugs) to understand why we are writing those tests.
|
||||
|
||||
If you want to work on a specific issue
|
||||
@@ -77,7 +78,7 @@ If you want to work on a specific issue
|
||||
'apiTests': {
|
||||
'coreBranch': 'master',
|
||||
'coreCommit': 'a06b1bd5ba8e5244bfaf7fa04f441961e6fb0daa',
|
||||
'numberOfParts': 2
|
||||
'numberOfParts': 6
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,7 +89,7 @@ If you want to work on a specific issue
|
||||
make test-acceptance-api \
|
||||
TEST_SERVER_URL=https://localhost:9200 \
|
||||
TEST_OCIS=true \
|
||||
OCIS_REVA_DATA_ROOT=/var/tmp/reva/ \
|
||||
DELETE_USER_DATA_CMD='rm -rf /var/tmp/ocis/storage/users/nodes/root/* /var/tmp/ocis/storage/users/nodes/*-*-*-*' \
|
||||
BEHAT_FEATURE='tests/acceptance/features/apiComments/comments.feature:123'
|
||||
```
|
||||
|
||||
@@ -99,9 +100,3 @@ If you want to work on a specific issue
|
||||
7. run each of the local tests that were demonstrating the **buggy** behavior. They should fail.
|
||||
8. delete each of the local tests that were demonstrating the **buggy** behavior.
|
||||
9. make a PR that has the fixed code, relevant lines removed from the expected failures file and bug demonstration tests deleted.
|
||||
|
||||
If the changes also affect the `ocis-reva` repository make sure the changes get ported over there.
|
||||
|
||||
### Notes
|
||||
- in a normal case the test-code cleans up users after the test-run, but if a test-run is interrupted (e.g. by CTRL+C) users might have been left on the LDAP server. In that case rerunning the tests requires wiping the users in the ldap server, otherwise the tests will fail when trying to populate the users.
|
||||
- the tests usually create users in the OU `TestUsers` with usernames specified in the feature file. If not defined in the feature file, most users have the password `123456`, defined by `regularUserPassword` in `behat.yml`, but other passwords are also used, see [`\FeatureContext::getPasswordForUser()`](https://github.com/owncloud/core/blob/master/tests/acceptance/features/bootstrap/FeatureContext.php#L386) for mapping and [`\FeatureContext::__construct`](https://github.com/owncloud/core/blob/master/tests/acceptance/features/bootstrap/FeatureContext.php#L1668) for the password definitions.
|
||||
|
||||
@@ -49,34 +49,59 @@ uid=20000(einstein) gid=30000(users) groups=30000(users),30001(sailing-lovers),3
|
||||
If the user is not found at first you might need to wait a few more minutes in case the ocis container is still compiling.
|
||||
{{< /hint >}}
|
||||
|
||||
We also need to restart the storage-users service, so it picks up the changed environment. Without a restart it is not able to resolve users from LDAP.
|
||||
We also need to restart the storage-userprovider service, so it picks up the changed environment. Without a restart it is not able to resolve users from LDAP.
|
||||
```
|
||||
docker-compose exec ocis ./bin/ocis kill storage-users
|
||||
docker-compose exec ocis ./bin/ocis run storage-users
|
||||
docker-compose exec ocis ./bin/ocis kill storage-userprovider
|
||||
docker-compose exec ocis ./bin/ocis run storage-userprovider
|
||||
```
|
||||
|
||||
### 3. Home storage
|
||||
|
||||
Kill the home storage. By default it uses the `owncloud` storage driver. We need to switch it to the `eoshome` driver and make it use the storage id of the eos storage provider:
|
||||
Kill the home storage. By default it uses the `ocis` storage driver. We need to switch it to the `eoshome` driver:
|
||||
|
||||
```
|
||||
docker-compose exec ocis ./bin/ocis kill storage-storage-home
|
||||
docker-compose exec -e STORAGE_STORAGE_HOME_DRIVER=eoshome -e STORAGE_STORAGE_HOME_MOUNT_ID=1284d238-aa92-42ce-bdc4-0b0000009158 ocis ./bin/ocis run storage-storage-home
|
||||
docker-compose exec ocis ./bin/ocis kill storage-home
|
||||
docker-compose exec -e STORAGE_HOME_DRIVER=eoshome ocis ./bin/ocis run storage-home
|
||||
```
|
||||
|
||||
### 4. Home data provider
|
||||
### 4. Users storage
|
||||
|
||||
Kill the home data provider. By default it uses the `owncloud` storage driver. We need to switch it to the `eoshome` driver and make it use the storage id of the eos storage provider:
|
||||
Kill the users storage. By default it uses the `ocis` storage driver. We need to switch it to the `eos` driver:
|
||||
|
||||
```
|
||||
docker-compose exec ocis ./bin/ocis kill storage-storage-home-data
|
||||
docker-compose exec -e STORAGE_STORAGE_HOME_DATA_DRIVER=eoshome ocis ./bin/ocis run storage-storage-home-data
|
||||
docker-compose exec ocis ./bin/ocis kill storage-users
|
||||
docker-compose exec -e STORAGE_USERS_DRIVER=eos ocis ./bin/ocis run storage-users
|
||||
```
|
||||
|
||||
### 5. Metadata storage
|
||||
|
||||
First we need to create the metadata root in eos and set an owner:
|
||||
```
|
||||
docker-compose exec ocis eos mkdir -p /eos/dockertest/ocis/metadata
|
||||
docker-compose exec ocis eos chown 2:2 /eos/dockertest/ocis/metadata
|
||||
```
|
||||
|
||||
{{< hint info >}}
|
||||
The difference between the *home storage* and the *home data provider* are that the former is responsible for metadata changes while the latter is responsible for actual data transfer. The *home storage* uses the cs3 api to manage a folder hierarchy, while the *home data provider* is responsible for moving bytes to and from the storage.
|
||||
The uid and gid `2` are referencing the user `daemon` inside the ocis container. That user is also configured when restarting the accounts service later. For production systems you should create a dedicated user for the metadata storage.
|
||||
{{< /hint >}}
|
||||
|
||||
Kill the metadata storage. By default it uses the `ocis` storage driver. We need to switch it to the `eos` driver:
|
||||
|
||||
```
|
||||
docker-compose exec ocis ./bin/ocis kill storage-metadata
|
||||
docker-compose exec -e STORAGE_METADATA_DRIVER=eos -e STORAGE_METADATA_ROOT=/eos/dockertest/ocis/metadata ocis ./bin/ocis run storage-metadata
|
||||
```
|
||||
|
||||
|
||||
### 6. Accounts service
|
||||
|
||||
Kill the accounts service. By default it uses the `ocis` storage driver. We need to switch it to the `eos` driver:
|
||||
|
||||
```
|
||||
docker-compose exec ocis ./bin/ocis kill accounts
|
||||
docker-compose exec -e ACCOUNTS_SERVICE_USER_USERNAME=daemon -e ACCOUNTS_SERVICE_USER_UID=2 -e ACCOUNTS_SERVICE_USER_GID=2 ocis ./bin/ocis run accounts
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
Login with `einstein / relativity`, upload a file to einsteins home and verify the file is there using
|
||||
@@ -199,7 +224,7 @@ The ocis logs can be accessed using `docker-compose logs ocis`. Add `-f` for fol
|
||||
1. `docker-compose exec ocis make clean build` to update the binary
|
||||
2. `docker-compose exec ocis ./bin/ocis kill <service>` to kill the service
|
||||
3. `docker-compose exec ocis ./bin/ocis run <service>` to start the service. Do not forget to set any env vars, eg.
|
||||
`docker-compose exec -e STORAGE_STORAGE_EOS_LAYOUT="{{substr 0 1 .Id.OpaqueId}}/{{.Id.OpaqueId}}" -e STORAGE_STORAGE_HOME_DRIVER=eoshome ocis ./bin/ocis run storage-storage-home`
|
||||
`docker-compose exec -e STORAGE_HOME_DRIVER=eoshome -e STORAGE_DRIVER_EOS_LAYOUT="{{substr 0 1 .Id.OpaqueId}}/{{.Id.OpaqueId}}" ocis ./bin/ocis run storage-home`
|
||||
|
||||
### Creation and upload of files does not work
|
||||
|
||||
|
||||
173
glauth/go.sum
173
glauth/go.sum
@@ -29,6 +29,7 @@ contrib.go.opencensus.io/exporter/ocagent v0.6.0 h1:Z1n6UAyr0QwM284yUuh5Zd8JlvxU
|
||||
contrib.go.opencensus.io/exporter/ocagent v0.6.0/go.mod h1:zmKjrJcdo0aYcVS7bmEeSEBLPA9YJp5bjrofdU3pIXs=
|
||||
contrib.go.opencensus.io/exporter/ocagent v0.7.0 h1:BEfdCTXfMV30tLZD8c9n64V/tIZX5+9sXiuFLnrr1k8=
|
||||
contrib.go.opencensus.io/exporter/ocagent v0.7.0/go.mod h1:IshRmMJBhDfFj5Y67nVhMYTTIze91RUeT73ipWKs/GY=
|
||||
contrib.go.opencensus.io/exporter/prometheus v0.2.0 h1:9PUk0/8V0LGoPqVCrf8fQZJkFGBxudu8jOjQSMwoD6w=
|
||||
contrib.go.opencensus.io/exporter/prometheus v0.2.0/go.mod h1:TYmVAyE8Tn1lyPcltF5IYYfWp2KHu7lQGIZnj8iZMys=
|
||||
contrib.go.opencensus.io/exporter/zipkin v0.1.1 h1:PR+1zWqY8ceXs1qDQQIlgXe+sdiwCf0n32bH4+Epk8g=
|
||||
contrib.go.opencensus.io/exporter/zipkin v0.1.1/go.mod h1:GMvdSl3eJ2gapOaLKzTKE3qDgUkJ86k9k3yY2eqwkzc=
|
||||
@@ -49,12 +50,11 @@ github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocm
|
||||
github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8=
|
||||
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
|
||||
github.com/Azure/go-autorest/tracing v0.1.0/go.mod h1:ROEEAFwXycQw7Sn3DXNtEedEvdeRAgDr0izn4z5Ij88=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/CiscoM31/godata v0.0.0-20191007193734-c2c4ebb1b415 h1:rATYsVSP89BOyS9/o+cdGZ8qU7AHh4frtS59nFPoX8k=
|
||||
github.com/CiscoM31/godata v0.0.0-20191007193734-c2c4ebb1b415/go.mod h1:tjaihnMBH6p5DVnGBksDQQHpErbrLvb9ek6cEWuyc7E=
|
||||
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||
github.com/GeertJohan/yubigo v0.0.0-20190917122436-175bc097e60e h1:Bqtt5C+uVk+vH/t5dmB47uDCTwxw16EYHqvJnmY2aQc=
|
||||
github.com/GeertJohan/yubigo v0.0.0-20190917122436-175bc097e60e/go.mod h1:njRCDrl+1RQ/A/+KVU8Ho2EWAxUSkohOWczdW3dzDG0=
|
||||
@@ -77,10 +77,9 @@ github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcy
|
||||
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
|
||||
github.com/Microsoft/hcsshim v0.8.7-0.20191101173118-65519b62243c/go.mod h1:7xhjOwRV2+0HXGmM0jxaEu+ZiXJFoVZOTfL/dmqbrD8=
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
|
||||
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks=
|
||||
github.com/RoaringBitmap/roaring v0.4.21 h1:WJ/zIlNX4wQZ9x8Ey33O1UaD9TCTakYsdLFSBcTwH+8=
|
||||
github.com/RoaringBitmap/roaring v0.4.21/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/UnnoTed/fileb0x v1.1.4 h1:IUgFzgBipF/ujNx9wZgkrKOF3oltUuXMSoaejrBws+A=
|
||||
@@ -91,8 +90,10 @@ github.com/alangpierce/go-forceexport v0.0.0-20160317203124-8f1d6941cd75/go.mod
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190808125512-07798873deee/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ=
|
||||
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
|
||||
@@ -110,12 +111,16 @@ github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:l
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496 h1:zV3ejI06GQ59hwDQAvmK1qxOQGB3WuVTRoY0okPTAv0=
|
||||
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
|
||||
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0S6Vi7/lbWECcX0j45yZReDZ56BQsrVBOEEY=
|
||||
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
|
||||
github.com/ascarter/requestid v0.0.0-20170313220838-5b76ab3d4aee h1:3T/l+vMotQ7cDSLWNAn2Vg1SAQ3mdyLgBWWBitSS3uU=
|
||||
github.com/ascarter/requestid v0.0.0-20170313220838-5b76ab3d4aee/go.mod h1:u7Wtt4WATGGgae9mURNGQQqxAudPKrxfsbSDSGOso+g=
|
||||
github.com/aws/aws-sdk-go v1.20.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.23.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.23.19/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.33.19/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
||||
github.com/aws/aws-sdk-go v1.34.12 h1:7UbBEYDUa4uW0YmRnOd806MS1yoJMcaodBWDzvBShAI=
|
||||
github.com/aws/aws-sdk-go v1.34.12/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
||||
github.com/aws/aws-xray-sdk-go v0.9.4/go.mod h1:XtMKdBQfpVut+tJEwI7+dJFRxxRdxHDyVNp2tHXRq04=
|
||||
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
@@ -127,34 +132,16 @@ github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkN
|
||||
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
|
||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||
github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
github.com/blevesearch/bleve v1.0.9 h1:kqw/Ank/61UV9/Bx9kCcnfH6qWPgmS8O5LNfpsgzASg=
|
||||
github.com/blevesearch/bleve v1.0.9/go.mod h1:tb04/rbU29clbtNgorgFd8XdJea4x3ybYaOjWKr+UBU=
|
||||
github.com/blevesearch/blevex v0.0.0-20190916190636-152f0fe5c040 h1:SjYVcfJVZoCfBlg+fkaq2eoZHTf5HaJfaTeTkOtyfHQ=
|
||||
github.com/blevesearch/blevex v0.0.0-20190916190636-152f0fe5c040/go.mod h1:WH+MU2F4T0VmSdaPX+Wu5GYoZBrYWdOZWSjzvYcDmqQ=
|
||||
github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo=
|
||||
github.com/blevesearch/go-porterstemmer v1.0.3/go.mod h1:angGc5Ht+k2xhJdZi511LtmxuEf0OVpvUUNrwmM1P7M=
|
||||
github.com/blevesearch/mmap-go v1.0.2 h1:JtMHb+FgQCTTYIhtMvimw15dJwu1Y5lrZDMOFXVWPk0=
|
||||
github.com/blevesearch/mmap-go v1.0.2/go.mod h1:ol2qBqYaOUsGdm7aRMRrYGgPvnwLe6Y+7LMvAB5IbSA=
|
||||
github.com/blevesearch/segment v0.9.0 h1:5lG7yBCx98or7gK2cHMKPukPZ/31Kag7nONpoBt22Ac=
|
||||
github.com/blevesearch/segment v0.9.0/go.mod h1:9PfHYUdQCgHktBgvtUOF4x+pc4/l8rdH0u5spnW85UQ=
|
||||
github.com/blevesearch/snowballstem v0.9.0 h1:lMQ189YspGP6sXvZQ4WZ+MLawfV8wOmPoD/iWeNXm8s=
|
||||
github.com/blevesearch/snowballstem v0.9.0/go.mod h1:PivSj3JMc8WuaFkTSRDW2SlrulNWPl4ABg1tC/hlgLs=
|
||||
github.com/blevesearch/zap/v11 v11.0.9 h1:wlSrDBeGN1G4M51NQHIXca23ttwUfQpWaK7uhO5lRSo=
|
||||
github.com/blevesearch/zap/v11 v11.0.9/go.mod h1:47hzinvmY2EvvJruzsSCJpro7so8L1neseaGjrtXHOY=
|
||||
github.com/blevesearch/zap/v12 v12.0.9 h1:PpatkY+BLVFZf0Ok3/fwgI/I4RU0z5blXFGuQANmqXk=
|
||||
github.com/blevesearch/zap/v12 v12.0.9/go.mod h1:paQuvxy7yXor+0Mx8p2KNmJgygQbQNN+W6HRfL5Hvwc=
|
||||
github.com/blevesearch/zap/v13 v13.0.1 h1:NSCM6uKu77Vn/x9nlPp4pE1o/bftqcOWZEHSyZVpGBQ=
|
||||
github.com/blevesearch/zap/v13 v13.0.1/go.mod h1:XmyNLMvMf8Z5FjLANXwUeDW3e1+o77TTGUWrth7T9WI=
|
||||
github.com/blevesearch/zap/v14 v14.0.0 h1:HF8Ysjm13qxB0jTGaKLlatNXmJbQD8bY+PrPxm5v4hE=
|
||||
github.com/blevesearch/zap/v14 v14.0.0/go.mod h1:sUc/gPGJlFbSQ2ZUh/wGRYwkKx+Dg/5p+dd+eq6QMXk=
|
||||
github.com/bmatcuk/doublestar v1.1.1 h1:YroD6BJCZBYx06yYFEWvUuKVWQn3vLLQAVmDmvTSaiQ=
|
||||
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
||||
github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40 h1:y4B3+GPxKlrigF1ha5FFErxK+sr6sWxQovRMzwMhejo=
|
||||
github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c=
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
github.com/bwmarrin/discordgo v0.20.2/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q=
|
||||
github.com/c-bata/go-prompt v0.2.3/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34=
|
||||
github.com/caddyserver/certmagic v0.10.6/go.mod h1:Y8jcUBctgk/IhpAzlHKfimZNyXCkfGgRTC0orl8gROQ=
|
||||
github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/cenkalti/backoff/v4 v4.0.0/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg=
|
||||
@@ -170,6 +157,7 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
|
||||
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
|
||||
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
|
||||
github.com/cheggaaa/pb v1.0.28/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
|
||||
github.com/cheggaaa/pb v1.0.29/go.mod h1:W40334L7FMC5JKWldsTWbdGjLo0RxUKK73K+TuPxX30=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
@@ -207,10 +195,6 @@ github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+
|
||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/couchbase/ghistogram v0.1.0/go.mod h1:s1Jhy76zqfEecpNWJfWUiKZookAFaiGOEoyzgHt9i7k=
|
||||
github.com/couchbase/moss v0.1.0/go.mod h1:9MaHIaRuy9pvLPUJxB8sh8OrLfyDczECVL37grCIubs=
|
||||
github.com/couchbase/vellum v1.0.1 h1:qrj9ohvZedvc51S5KzPfJ6P6z0Vqzv7Lx7k3mVc2WOk=
|
||||
github.com/couchbase/vellum v1.0.1/go.mod h1:FcwrEivFpNi24R3jLOs3n+fs5RnuQnQqCLBJ1uAg1W4=
|
||||
github.com/cpu/goacmedns v0.0.1/go.mod h1:sesf/pNnCYwUevQEQfEwY0Y3DydlQWSGZbaMElOWxok=
|
||||
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
@@ -220,21 +204,26 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cs3org/cato v0.0.0-20200626150132-28a40e643719/go.mod h1:XJEZ3/EQuI3BXTp/6DUzFr850vlxq11I6satRtz0YQ4=
|
||||
github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e/go.mod h1:XJEZ3/EQuI3BXTp/6DUzFr850vlxq11I6satRtz0YQ4=
|
||||
github.com/cs3org/go-cs3apis v0.0.0-20200730121022-c4f3d4f7ddfd h1:uMaudkC7znaiIKT9rxIhoRYzrhTg1Nc78X7XEqhmjSk=
|
||||
github.com/cs3org/go-cs3apis v0.0.0-20200730121022-c4f3d4f7ddfd/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY=
|
||||
github.com/cs3org/go-cs3apis v0.0.0-20200810113633-b00aca449666 h1:E7VsSSN/2YZLSwrDMJJdAWU11lP7W1qkcXbrslb0PM0=
|
||||
github.com/cs3org/go-cs3apis v0.0.0-20200810113633-b00aca449666/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY=
|
||||
github.com/cs3org/reva v1.1.0 h1:Gih6ECHvMMGSx523SFluFlDmNMuhYelXYShdWvjvW38=
|
||||
github.com/cs3org/reva v1.1.0/go.mod h1:fBzTrNuAKdQ62ybjpdu8nyhBin90/3/3s6DGQDCdBp4=
|
||||
github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d h1:SwD98825d6bdB+pEuTxWOXiSjBrHdOl/UVp75eI7JT8=
|
||||
github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8=
|
||||
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 h1:iwZdTE0PVqJCos1vaoKsclOGD3ADKpshg3SRtYBbwso=
|
||||
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM=
|
||||
github.com/cznic/strutil v0.0.0-20181122101858-275e90344537 h1:MZRmHqDBd0vxNwenEbKSQqRVT24d3C05ft8kduSwlqM=
|
||||
github.com/cznic/strutil v0.0.0-20181122101858-275e90344537/go.mod h1:AHHPPPXTw0h6pVabbcbyGRK1DckRn7r/STdZEeIDzZc=
|
||||
github.com/cs3org/reva v1.2.2-0.20200924071957-e6676516e61e h1:khITGSnfDXtByQsLezoXgocUgGHJBBn0BPsUihGvk7w=
|
||||
github.com/cs3org/reva v1.2.2-0.20200924071957-e6676516e61e/go.mod h1:DOV5SjpOBKN+aWfOHLdA4KiLQkpyC786PQaXEdRAZ0M=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgraph-io/ristretto v0.0.1/go.mod h1:T40EBc7CJke8TkpiYfGGKAeFjSaxuFXhuXRyumBd6RE=
|
||||
github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
|
||||
github.com/dgraph-io/ristretto v0.0.3 h1:jh22xisGBjrEVnRZ1DVTpBVQm0Xndu8sMl0CWDzSIBI=
|
||||
github.com/dgraph-io/ristretto v0.0.3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
|
||||
github.com/dnaeon/go-vcr v0.0.0-20180814043457-aafff18a5cc2/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
|
||||
@@ -258,15 +247,11 @@ github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/evanphx/json-patch/v5 v5.0.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
|
||||
github.com/eventials/go-tus v0.0.0-20200718001131-45c7ec8f5d59 h1:t2+zxJPT/jq/YOx/JRsoByAZI/GHOxYJ7MKeillEX4U=
|
||||
github.com/eventials/go-tus v0.0.0-20200718001131-45c7ec8f5d59/go.mod h1:XYuK1S5+kS6FGhlIUFuZFPvWiSrOIoLk6+ro33Xce3Y=
|
||||
github.com/exoscale/egoscale v0.18.1/go.mod h1:Z7OOdzzTOz1Q1PjQXumlz9Wn/CddH0zSYdCF3rnBKXE=
|
||||
github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0=
|
||||
github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64=
|
||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A=
|
||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg=
|
||||
github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpmbhCOZJ293Lz68O7PYrF2EzeiFMwCLk=
|
||||
github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
github.com/fatih/structs v1.0.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
|
||||
@@ -274,6 +259,8 @@ github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI
|
||||
github.com/forestgiant/sliceutil v0.0.0-20160425183142-94783f95db6c/go.mod h1:pFdJbAhRf7rh6YYMUdIQGyzne6zYL1tCUW8QV2B3UfY=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsouza/go-dockerclient v1.6.0/go.mod h1:YWwtNPuL4XTX1SKJQk86cWPmmqwx+4np9qfPbb+znGc=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
@@ -283,11 +270,8 @@ github.com/glauth/glauth v1.1.3-0.20201005201919-4d42af8aacbf h1:3ejnL7OvxCJ6XiE
|
||||
github.com/glauth/glauth v1.1.3-0.20201005201919-4d42af8aacbf/go.mod h1:ygO1z1pcp79iBrjbA6vqrsUxIonStjBncosl2a9/Dx8=
|
||||
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
|
||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2 h1:Ujru1hufTHVb++eG6OuNDKMxZnGIvF6o/u8q/8h2+I4=
|
||||
github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
|
||||
github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31 h1:gclg6gY70GLy3PbkQ1AERPfmLMMagS60DKF78eWwLn8=
|
||||
github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
|
||||
github.com/go-acme/lego/v3 v3.4.0/go.mod h1:xYbLDuxq3Hy4bMUT1t9JIuz6GWIWb3m5X+TeTHYaT7M=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-bindata/go-bindata v3.1.1+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo=
|
||||
github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec=
|
||||
@@ -310,6 +294,7 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2
|
||||
github.com/go-ini/ini v1.44.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-ldap/ldap/v3 v3.2.3 h1:FBt+5w3q/vPVPb4eYMQSn+pOiz4zewPamYhlGMmc7yM=
|
||||
github.com/go-ldap/ldap/v3 v3.2.3/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
@@ -521,6 +506,7 @@ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
@@ -540,8 +526,7 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
|
||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k=
|
||||
github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||
@@ -568,11 +553,14 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
|
||||
github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gophercloud/gophercloud v0.3.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181004151105-1babbf986f6f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
@@ -597,6 +585,8 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 h1:THDBEeQ9xZ8JEaCLyLQqXMMdR
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 h1:0IKlLyQ3Hs9nDaiK5cSHAGmcQEIC8l2Ts1u6x5Dfrqg=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.2.0/go.mod h1:mJzapYve32yjrKlk9GbyCZHuPgZsrbyIbyKhSzOpg6s=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 h1:FlFbCRLd5Jr4iYXZufAvgWN6Ao0JrI5chLINnUXDDr0=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
@@ -625,6 +615,7 @@ github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09
|
||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk=
|
||||
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
@@ -642,6 +633,8 @@ github.com/huandu/xstrings v1.3.0 h1:gvV6jG9dTgFEncxo+AF7PH6MZXi/vZl25owA/8Dg8Wo
|
||||
github.com/huandu/xstrings v1.3.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs=
|
||||
github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/iancoleman/strcase v0.1.2 h1:gnomlvw9tnV3ITTAxzKSgTF+8kFWcU/f+TgttpXGz1U=
|
||||
github.com/iancoleman/strcase v0.1.2/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4=
|
||||
github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
@@ -649,6 +642,7 @@ github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
|
||||
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
|
||||
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ=
|
||||
github.com/jackc/pgx v3.2.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
|
||||
@@ -659,9 +653,8 @@ github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJS
|
||||
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a h1:zPPuIq2jAWWPTrGt70eK/BSch+gFAGrNzecsoENgu2o=
|
||||
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc=
|
||||
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
|
||||
github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U=
|
||||
github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ=
|
||||
github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0/go.mod h1:IiEW3SEiiErVyFdH8NTuWjSifiEQKUoyK3LNqr2kCHU=
|
||||
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
|
||||
github.com/joho/godotenv v1.2.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
@@ -693,12 +686,12 @@ github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/kljensen/snowball v0.6.0/go.mod h1:27N7E8fVU5H68RlUmnWwZCfxgt4POBJfENGMvNRhldw=
|
||||
github.com/kolo/xmlrpc v0.0.0-20190717152603-07c4ee3fd181/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
@@ -751,11 +744,16 @@ github.com/marten-seemann/qtls v0.4.1 h1:YlT8QP3WCCvvok7MGEZkMldXbyqgr8oFg5/n8Gt
|
||||
github.com/marten-seemann/qtls v0.4.1/go.mod h1:pxVXcHHw1pNIt8Qo0pwSYQEoZ8yYOOPXTCZLQQunvRc=
|
||||
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
|
||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
|
||||
@@ -765,10 +763,13 @@ github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m
|
||||
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
|
||||
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
|
||||
github.com/mattn/goveralls v0.0.5/go.mod h1:Xg2LHi51faXLyKXwsndxiW6uxEEQT9+3sjGzzwU4xy0=
|
||||
github.com/mattn/goveralls v0.0.6 h1:cr8Y0VMo/MnEZBjxNN/vh6G90SZ7IMb6lms1dzMoO+Y=
|
||||
github.com/mattn/goveralls v0.0.6/go.mod h1:h8b4ow6FxSPMQHF6o2ve3qsclnffZjYTNEKmLesRwqw=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mennanov/fieldmask-utils v0.3.2 h1:AkHXYBEOoyvocl8YhzoStATRnto5OH1PY4Rj78I5Cuc=
|
||||
@@ -806,6 +807,7 @@ github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.3.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8=
|
||||
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
|
||||
@@ -818,13 +820,11 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||
github.com/monoculum/formam v0.0.0-20180901015400-4e68be1d79ba/go.mod h1:RKgILGEJq24YyJ2ban8EO0RUVSJlF1pGsEvoLEACr/Q=
|
||||
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/moul/http2curl v0.0.0-20170919181001-9ac6cf4d929b/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
|
||||
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
|
||||
github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM=
|
||||
github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8=
|
||||
github.com/nats-io/jwt v0.3.2 h1:+RB5hMpXUUA2dfxuhBTEkMOrYmM+gKIZYS1KjSostMI=
|
||||
@@ -859,6 +859,7 @@ github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQ
|
||||
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
|
||||
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/oleiade/reflections v1.0.0 h1:0ir4pc6v8/PJ0yw5AEtMddfXpWBXg9cnG7SgSoJuCgY=
|
||||
github.com/oleiade/reflections v1.0.0/go.mod h1:RbATFBbKYkVdqmSFtx13Bb/tVhR0lgOBXunWTZKeL4w=
|
||||
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
|
||||
@@ -890,15 +891,23 @@ github.com/oracle/oci-go-sdk v7.0.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukw
|
||||
github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs=
|
||||
github.com/ory/fosite v0.29.0/go.mod h1:0atSZmXO7CAcs6NPMI/Qtot8tmZYj04Nddoold4S2h0=
|
||||
github.com/ory/fosite v0.32.2/go.mod h1:UeBhRgW6nAjTcd8S7kAo0IFsY/rTPyOXPq/t8N20Q8I=
|
||||
github.com/ory/fosite v0.33.0 h1:tK+3Luazv4vIBJY3uagOBryAQ3IG3cs6kfo8piGBhAY=
|
||||
github.com/ory/fosite v0.33.0/go.mod h1:h+ize9gk0GvRyGjabriqSEmTkMhny+O95cijb8DVqPE=
|
||||
github.com/ory/go-acc v0.0.0-20181118080137-ddc355013f90/go.mod h1:sxnvPCxChFuSmTJGj8FdMupeq1BezCiEpDjTUXQ4hf4=
|
||||
github.com/ory/go-acc v0.2.1/go.mod h1:0omgy2aa3nDBJ45VAKeLHH8ccPBudxLeic4xiDRtug0=
|
||||
github.com/ory/go-acc v0.2.5 h1:31irXHzG2vnKQSE4weJm7AdfrnpaVjVCq3nD7viXCJE=
|
||||
github.com/ory/go-acc v0.2.5/go.mod h1:4Kb/UnPcT8qRAk3IAxta+hvVapdxTLWtrr7bFLlEgpw=
|
||||
github.com/ory/go-convenience v0.1.0 h1:zouLKfF2GoSGnJwGq+PE/nJAE6dj2Zj5QlTgmMTsTS8=
|
||||
github.com/ory/go-convenience v0.1.0/go.mod h1:uEY/a60PL5c12nYz4V5cHY03IBmwIAEm8TWB0yn9KNs=
|
||||
github.com/ory/gojsonreference v0.0.0-20190720135523-6b606c2d8ee8/go.mod h1:wsH1C4nIeeQClDtD5AH7kF1uTS6zWyqfjVDTmB0Em7A=
|
||||
github.com/ory/gojsonschema v1.1.1-0.20190919112458-f254ca73d5e9/go.mod h1:BNZpdJgB74KOLSsWFvzw6roXg1I6O51WO8roMmW+T7Y=
|
||||
github.com/ory/herodot v0.6.2/go.mod h1:3BOneqcyBsVybCPAJoi92KN2BpJHcmDqAMcAAaJiJow=
|
||||
github.com/ory/viper v1.5.6/go.mod h1:TYmpFpKLxjQwvT4f0QPpkOn4sDXU1kDgAwJpgLYiQ28=
|
||||
github.com/ory/viper v1.7.5 h1:+xVdq7SU3e1vNaCsk/ixsfxE4zylk1TJUiJrY647jUE=
|
||||
github.com/ory/viper v1.7.5/go.mod h1:ypOuyJmEUb3oENywQZRgeAMwqgOyDqwboO1tj3DjTaM=
|
||||
github.com/ory/x v0.0.85/go.mod h1:s44V8t3xyjWZREcU+mWlp4h302rTuM4aLXcW+y5FbQ8=
|
||||
github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014/go.mod h1:joRatxRJaZBsY3JAOEMcoOp05CnZzsx4scTxi95DHyQ=
|
||||
github.com/owncloud/flaex v0.0.0-20200411150708-dce59891a203/go.mod h1:jip86t4OVURJTf8CM/0e2qcji/Y4NG3l2lR8kex4JWw=
|
||||
github.com/owncloud/ocis-pkg/v2 v2.4.0/go.mod h1:FSzIvhx9HcZcq4jgNaDowNvM7PTX/XCyoMvyfzidUpE=
|
||||
github.com/owncloud/ocis-pkg/v2 v2.4.1-0.20200902134813-1e87c6173ada h1:iVknnuT/z8QCAeBpHEcbI/AiQ9FOBvF5aOAFT3TIM+I=
|
||||
github.com/owncloud/ocis-pkg/v2 v2.4.1-0.20200902134813-1e87c6173ada/go.mod h1:WdcVM54z0X7aQzS8eyGl7S5sjEMVBtLpfpzsPX3Z+Pw=
|
||||
@@ -906,19 +915,22 @@ github.com/owncloud/ocis-settings v0.3.2-0.20200828091056-47af10a0e872 h1:VGWM/e
|
||||
github.com/owncloud/ocis-settings v0.3.2-0.20200828091056-47af10a0e872/go.mod h1:vRge9QDkOsc6j76gPBmZs1Z5uOPrV4DIkZCgZCEFwBA=
|
||||
github.com/owncloud/ocis/settings v0.0.0-20200918114005-1a0ddd2190ee h1:LaKP3hCTJ6WwB3W40m5UhddvokMRIbOXrjogxON4jAI=
|
||||
github.com/owncloud/ocis/settings v0.0.0-20200918114005-1a0ddd2190ee/go.mod h1:5w91idmyOd8LgYK3eGuqsFBOfVJnSDeEp7S6dHheW14=
|
||||
github.com/owncloud/ocis/storage v0.0.0-20201015120921-38358ba4d4df h1:PhRLD+WTGIfQ1T4MqBabp6/1Q8H/iwxjlygh6xzao0A=
|
||||
github.com/owncloud/ocis/storage v0.0.0-20201015120921-38358ba4d4df/go.mod h1:s9kJvxtBlHEi5qc1TuPAdz2bprk9yGFe+FSOeC76Pbs=
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
|
||||
github.com/parnurzeal/gorequest v0.2.15/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg=
|
||||
github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
|
||||
github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ=
|
||||
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
|
||||
github.com/pelletier/go-toml v1.8.0 h1:Keo9qb7iRJs2voHvunFtuuYFsbWeOBh8/P9v/kVMFtw=
|
||||
github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs=
|
||||
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@@ -927,6 +939,9 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
|
||||
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||
github.com/pkg/term v0.0.0-20200520122047-c3ffed290a03/go.mod h1:Z9+Ul5bCbBKnbCvdOWbLqTHhJiYV414CURZJba6L8qA=
|
||||
github.com/pkg/xattr v0.4.1 h1:dhclzL6EqOXNaPDWqoeb9tIxATfBSmjqL0b4DpSjwRw=
|
||||
github.com/pkg/xattr v0.4.1/go.mod h1:W2cGD0TBEus7MkUgv0tNZ9JutLtVO3cXu+IBRuHqnFs=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
@@ -970,13 +985,11 @@ github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDa
|
||||
github.com/prometheus/procfs v0.0.6/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||
github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/statsd_exporter v0.15.0 h1:UiwC1L5HkxEPeapXdm2Ye0u1vUJfTj7uwT5yydYpa1E=
|
||||
github.com/prometheus/statsd_exporter v0.15.0/go.mod h1:Dv8HnkoLQkeEjkIE4/2ndAA7WL1zHKK7WMqFQqu72rw=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKcyumwBO6qip7RNQ5r77yrssm9bfCowcLEBcU5IA=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/restic/calens v0.2.0 h1:LVNAtmFc+Pb4ODX66qdX1T3Di1P0OTLyUsVyvM/xD7E=
|
||||
github.com/restic/calens v0.2.0/go.mod h1:UXwyAKS4wsgUZGEc7NrzzygJbLsQZIo3wl+62Q1wvmU=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
@@ -986,6 +999,7 @@ github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
|
||||
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
|
||||
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/zerolog v1.19.0 h1:hYz4ZVdUgjXTBUmrkrw55j1nHx68LfOKIQk5IYtyScg=
|
||||
@@ -1038,11 +1052,14 @@ github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
|
||||
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.2.0/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/afero v1.3.2 h1:GDarE4TJQI52kYSbSAmLiId1Elfj+xgSDqrUZxFhxlU=
|
||||
github.com/spf13/afero v1.3.2/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
|
||||
github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
|
||||
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
@@ -1050,6 +1067,8 @@ github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
|
||||
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||
@@ -1061,14 +1080,13 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
|
||||
github.com/spf13/viper v1.2.1/go.mod h1:P4AexN0a+C9tGAnUFNwDMYYZv3pjFuvmeiMyKRaNVlI=
|
||||
github.com/spf13/viper v1.3.1/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||
github.com/spf13/viper v1.6.3 h1:pDDu1OyEDTKzpJwdq4TiuLyMsUgRa/BT5cn5O62NoHs=
|
||||
github.com/spf13/viper v1.6.3/go.mod h1:jUMtyi0/lB5yZH/FjyGAoH7IMNrIhlBf6pXZmbMDvzw=
|
||||
github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=
|
||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
|
||||
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/steveyen/gtreap v0.1.0 h1:CjhzTa274PyJLJuMZwIzCO1PfC00oRa8d1Kc78bFXJM=
|
||||
github.com/steveyen/gtreap v0.1.0/go.mod h1:kl/5J7XbrOmlIbYIXdRHDDE5QxHqpk0cmkT7Z4dM9/Y=
|
||||
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@@ -1080,22 +1098,20 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/studio-b12/gowebdav v0.0.0-20200303150724-9380631c29a1 h1:TPyHV/OgChqNcnYqCoCvIFjR9TU60gFXXBKnhOBzVEI=
|
||||
github.com/studio-b12/gowebdav v0.0.0-20200303150724-9380631c29a1/go.mod h1:gCcfDlA1Y7GqOaeEKw5l9dOGx1VLdc/HuQSlQAaZ30s=
|
||||
github.com/subosito/gotenv v1.1.1/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||
github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzHWCjJB1zZfXPIAaDpzXIEJ0eS6B5Ok=
|
||||
github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8=
|
||||
github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog=
|
||||
github.com/tidwall/gjson v1.3.2/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls=
|
||||
github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E=
|
||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/tidwall/sjson v1.0.4/go.mod h1:bURseu1nuBkFpIES5cz6zBtjmYeOQmEESshn7VpF15Y=
|
||||
github.com/timewasted/linode v0.0.0-20160829202747-37e84520dcf7/go.mod h1:imsgLplxEC/etjIhdr3dNzV3JeT27LbVu5pYWm0JCBY=
|
||||
github.com/tinylib/msgp v1.1.0 h1:9fQd+ICuRIu/ue4vxJZu6/LzxN0HwMds2nq/0cFvxHU=
|
||||
github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20200122045848-3419fae592fc h1:yUaosFVTJwnltaHbSNC3i82I92quFs+OFPRl8kNMVwo=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20200122045848-3419fae592fc/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
@@ -1107,6 +1123,7 @@ github.com/tredoe/goutil v0.0.0-20200111155331-68cefb6d3cdc/go.mod h1:dp4VPOLeEF
|
||||
github.com/tredoe/osutil v1.0.5 h1:mfXjHBJU46GoJDOUcHyV895fauUuVikR9U8yRbGBrqw=
|
||||
github.com/tredoe/osutil v1.0.5/go.mod h1:DDO4G4Mwys6NJi5JmEVLnfFbQWIfVVri8L6HuXb/v98=
|
||||
github.com/tus/tusd v1.1.0/go.mod h1:3DWPOdeCnjBwKtv98y5dSws3itPqfce5TVa0s59LRiA=
|
||||
github.com/tus/tusd v1.1.1-0.20200416115059-9deabf9d80c2 h1:rcji4q9wMuSrz0tZt3kgIr/3WsB5kUqFja6RrgeCGEo=
|
||||
github.com/tus/tusd v1.1.1-0.20200416115059-9deabf9d80c2/go.mod h1:ygrT4B9ZSb27dx3uTnobX5nOFDnutBL6iWKLH4+KpA0=
|
||||
github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g=
|
||||
github.com/uber/jaeger-client-go v2.15.0+incompatible h1:NP3qsSqNxh8VYr956ur1N/1C1PjvOJnJykCzcD5QHbk=
|
||||
@@ -1127,8 +1144,6 @@ github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4 h1:gKMu1Bf6QI
|
||||
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4/go.mod h1:50wTf68f99/Zt14pr046Tgt3Lp2vLyFZKzbFXTOabXw=
|
||||
github.com/vimeo/go-util v1.2.0/go.mod h1:s13SMDTSO7AjH1nbgp707mfN5JFIWUFDU5MDDuRRtKs=
|
||||
github.com/vultr/govultr v0.1.4/go.mod h1:9H008Uxr/C4vFNGLqKx232C206GL0PBHzOP0809bGNA=
|
||||
github.com/willf/bitset v1.1.10 h1:NotGKqX0KwQ72NUzqrjZq5ipPNDQex9lo3WpaS8L2sc=
|
||||
github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
||||
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
|
||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
@@ -1142,6 +1157,7 @@ github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63M
|
||||
github.com/yaegashi/msgraph.go v0.1.1-0.20200221123608-2d438cf2a7cc h1:ejaC8rvIvCWmsaFrvmGOxhBuMxxhBB1xRshuM98XQ7M=
|
||||
github.com/yaegashi/msgraph.go v0.1.1-0.20200221123608-2d438cf2a7cc/go.mod h1:tso14hwzqX4VbnWTNsxiL0DvMb2OwbGISFA7jDibdWc=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
@@ -1196,17 +1212,20 @@ golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaE
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200320181102-891825fb96df/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 h1:3zb4D3T4G8jdExgVU/95+vQXfpEPiMdCaZgmGVxjNHM=
|
||||
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig=
|
||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@@ -1219,6 +1238,7 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
@@ -1287,6 +1307,7 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2 h1:eDrdRpKgkcCqKZQwyZRyeFZgfqt37SL7Kv3tok06cKE=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
@@ -1336,7 +1357,6 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181206074257-70b957f3b65e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181221143128-b4a75ba826a6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190102155601-82a175fd1598/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190116161447-11f53e031339/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -1356,12 +1376,13 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -1369,6 +1390,7 @@ golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c h1:jceGD5YNJGgGMkJz79agzOln1K9TaZUjv5ird16qniQ=
|
||||
@@ -1378,15 +1400,21 @@ golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121 h1:rITEj+UZHYC927n8GT97eC3zrpzXdb/voyeOuVKS46o=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666 h1:gVCS+QOncANNPlmlO1AhlU3oxs4V9z+gTtPwIk3p2N8=
|
||||
golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0 h1:xQwXv67TxFo9nC1GJFyab5eq/5B590r6RlnL/G8Sz7w=
|
||||
@@ -1456,6 +1484,8 @@ golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapK
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200522201501-cb1345f3a375/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200721223218-6123e77877b2/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200811215021-48a8ffc5b207 h1:8Kg+JssU1jBZs8GIrL5pl4nVyaqyyhdmHAR4D1zGErg=
|
||||
golang.org/x/tools v0.0.0-20200811215021-48a8ffc5b207/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -1515,6 +1545,7 @@ google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece h1:1YM0uhfumvoDu9sx8+RyWwTI63zoCQvI23IYFRlvte0=
|
||||
@@ -1538,6 +1569,7 @@ google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
gopkg.in/Acconut/lockfile.v1 v1.1.0/go.mod h1:6UCz3wJ8tSFUsPR6uP/j8uegEtDuEEqFxlpi0JI4Umw=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
|
||||
gopkg.in/amz.v1 v1.0.0-20150111123259-ad23e96a31d2 h1:FMrsB0OTjHsPDA1NM7AhRmmZzkBPu3iGdxK/5MFfBmk=
|
||||
@@ -1562,6 +1594,8 @@ gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.57.0 h1:9unxIsFcTt4I55uWluz+UmL95q4kdJ0buvQ1ZIqVQww=
|
||||
gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/mail.v2 v2.0.0-20180731213649-a0242b2233b4/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw=
|
||||
gopkg.in/ns1/ns1-go.v2 v2.0.0-20190730140822-b51389932cbc/go.mod h1:VV+3haRsgDiVLxyifmMBrBIuCWFBPYKbRssXB9z67Hw=
|
||||
gopkg.in/resty.v1 v1.9.1/go.mod h1:vo52Hzryw9PnPHcJfPsBiFW62XhNx5OczbV9y+IMpgc=
|
||||
@@ -1572,6 +1606,8 @@ gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4
|
||||
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/square/go-jose.v2 v2.5.0 h1:OZ4sdq+Y+SHfYB7vfthi1Ei8b0vkP8ZPQgUfUwdUSqo=
|
||||
gopkg.in/square/go-jose.v2 v2.5.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w=
|
||||
gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/telegram-bot-api.v4 v4.6.4/go.mod h1:5DpGO5dbumb40px+dXcwCpcjmeHNYLpk0bp3XRNvWDM=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
@@ -1585,6 +1621,9 @@ gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c h1:grhR+C34yXImVGp7EzNk+DTIk+323eIUWOmEevy6bDo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@@ -17,7 +17,6 @@ import (
|
||||
glauthcfg "github.com/glauth/glauth/pkg/config"
|
||||
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/micro/go-micro/v2"
|
||||
"github.com/micro/go-micro/v2/client"
|
||||
"github.com/oklog/run"
|
||||
openzipkin "github.com/openzipkin/zipkin-go"
|
||||
@@ -192,11 +191,7 @@ func Server(cfg *config.Config) *cli.Command {
|
||||
}
|
||||
}
|
||||
|
||||
as, gs, err := getAccountsServices()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
as, gs := getAccountsServices()
|
||||
server, err := glauth.Server(
|
||||
glauth.AccountsService(as),
|
||||
glauth.GroupsService(gs),
|
||||
@@ -312,19 +307,7 @@ func Server(cfg *config.Config) *cli.Command {
|
||||
}
|
||||
|
||||
// getAccountsServices returns an ocis-accounts service
|
||||
func getAccountsServices() (accounts.AccountsService, accounts.GroupsService, error) {
|
||||
service := micro.NewService()
|
||||
|
||||
// parse command line flags
|
||||
service.Init()
|
||||
|
||||
err := service.Client().Init(
|
||||
client.ContentType("application/json"),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return accounts.NewAccountsService("com.owncloud.api.accounts", service.Client()),
|
||||
accounts.NewGroupsService("com.owncloud.api.accounts", service.Client()),
|
||||
nil
|
||||
func getAccountsServices() (accounts.AccountsService, accounts.GroupsService) {
|
||||
return accounts.NewAccountsService("com.owncloud.api.accounts", client.DefaultClient),
|
||||
accounts.NewGroupsService("com.owncloud.api.accounts", client.DefaultClient)
|
||||
}
|
||||
|
||||
5
konnectd/.env
Normal file
5
konnectd/.env
Normal file
@@ -0,0 +1,5 @@
|
||||
PORT=3001
|
||||
HOST=127.0.0.1
|
||||
BROWSER=none
|
||||
INLINE_RUNTIME_CHUNK=false
|
||||
EXTEND_ESLINT=true
|
||||
3
konnectd/.eslintignore
Normal file
3
konnectd/.eslintignore
Normal file
@@ -0,0 +1,3 @@
|
||||
build/*
|
||||
node_modules/*
|
||||
|
||||
30
konnectd/.eslintrc.json
Normal file
30
konnectd/.eslintrc.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"plugins": ["react-intl-format"],
|
||||
"extends": ["plugin:react/recommended", "plugin:jest/recommended"],
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "detect"
|
||||
}
|
||||
},
|
||||
"parser": "babel-eslint",
|
||||
"rules": {
|
||||
"react-intl-format/missing-formatted-message": [
|
||||
"error",
|
||||
{
|
||||
"noTrailingWhitespace": false,
|
||||
"ignoreLinks": false
|
||||
}
|
||||
],
|
||||
"react-intl-format/missing-attribute": [
|
||||
"error",
|
||||
{
|
||||
"noTrailingWhitespace": false,
|
||||
"noSpreadOperator": true
|
||||
}
|
||||
],
|
||||
"react-intl-format/missing-values": [
|
||||
"error"
|
||||
],
|
||||
"react/display-name": "off"
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,10 @@ GENERATE ?= $(PACKAGES)
|
||||
|
||||
TAGS ?=
|
||||
|
||||
# Assets
|
||||
LOGO_URL = https://raw.githubusercontent.com/owncloud/assets/main/logo.svg
|
||||
FAVICON_URL = https://raw.githubusercontent.com/owncloud/assets/main/favicon.ico
|
||||
|
||||
ifndef OUTPUT
|
||||
ifneq ($(DRONE_TAG),)
|
||||
OUTPUT ?= $(subst v,,$(DRONE_TAG))
|
||||
@@ -77,9 +81,15 @@ lint:
|
||||
for PKG in $(PACKAGES); do go run golang.org/x/lint/golint -set_exit_status $$PKG || exit 1; done;
|
||||
|
||||
.PHONY: generate
|
||||
generate:
|
||||
generate: assets
|
||||
go generate $(GENERATE)
|
||||
|
||||
.PHONY: assets
|
||||
assets:
|
||||
mkdir -p assets/identifier/static
|
||||
curl -o assets/identifier/static/logo.svg ${LOGO_URL}
|
||||
curl -o assets/identifier/static/favicon.ico ${FAVICON_URL}
|
||||
|
||||
.PHONY: changelog
|
||||
changelog:
|
||||
go run github.com/restic/calens >| CHANGELOG.md
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"identifier-app.js": "./static/js/identifier-app.b36b0d07.chunk.js",
|
||||
"identifier-app.js.map": "./static/js/identifier-app.b36b0d07.chunk.js.map",
|
||||
"identifier-container.js": "./static/js/identifier-container.569688ae.chunk.js",
|
||||
"identifier-container.js.map": "./static/js/identifier-container.569688ae.chunk.js.map",
|
||||
"main.css": "./static/css/main.ed0ebb7d.chunk.css",
|
||||
"main.js": "./static/js/main.c5511071.chunk.js",
|
||||
"main.js.map": "./static/js/main.c5511071.chunk.js.map",
|
||||
"runtime~main.js": "./static/js/runtime~main.766bf48a.js",
|
||||
"runtime~main.js.map": "./static/js/runtime~main.766bf48a.js.map",
|
||||
"static/js/4.f92d7884.chunk.js": "./static/js/4.f92d7884.chunk.js",
|
||||
"static/js/4.f92d7884.chunk.js.map": "./static/js/4.f92d7884.chunk.js.map",
|
||||
"static/js/5.b1222fed.chunk.js": "./static/js/5.b1222fed.chunk.js",
|
||||
"static/js/5.b1222fed.chunk.js.map": "./static/js/5.b1222fed.chunk.js.map",
|
||||
"index.html": "./index.html",
|
||||
"precache-manifest.ea8b619599c55ba5f128f51d796152cd.js": "./precache-manifest.ea8b619599c55ba5f128f51d796152cd.js",
|
||||
"service-worker.js": "./service-worker.js",
|
||||
"static/css/main.ed0ebb7d.chunk.css.map": "./static/css/main.ed0ebb7d.chunk.css.map",
|
||||
"static/media/kopano-logo.svg": "./static/media/kopano-logo.10e256c7.svg",
|
||||
"static/media/fancy-background.css": "./static/media/loginscreen-bg.cc3ef0e4.jpg",
|
||||
"static/media/index.css": "./static/media/roboto-latin-900italic.bc833e72.woff"
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
<!DOCTYPE html><html lang="en"><head data-kopano-build="0.28.1-3-gabe57b2-dirty"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="#ffffff"><link rel="shortcut icon" href="./static/favicon.ico" type="image/x-icon"><meta property="csp-nonce" content="__CSP_NONCE__"><title>Kopano Sign in</title><link href="./static/css/main.ed0ebb7d.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="bg"><div id="bg-thumb"></div><div id="bg-enhanced"></div></div><div id="root" data-path-prefix="__PATH_PREFIX__"></div><div id="font-preloader"><span>aA</span>Bb</div><script src="./static/js/runtime~main.766bf48a.js"></script><script src="./static/js/main.c5511071.chunk.js"></script></body></html>
|
||||
@@ -1,142 +0,0 @@
|
||||
self.__precacheManifest = [
|
||||
{
|
||||
"revision": "7aa085561004b3906a54",
|
||||
"url": "./static/js/identifier-app.b36b0d07.chunk.js"
|
||||
},
|
||||
{
|
||||
"revision": "086f2ac22fca8d45f2f1",
|
||||
"url": "./static/js/identifier-container.569688ae.chunk.js"
|
||||
},
|
||||
{
|
||||
"revision": "ec7a9d03e50fe60db0c9",
|
||||
"url": "./static/css/main.ed0ebb7d.chunk.css"
|
||||
},
|
||||
{
|
||||
"revision": "ec7a9d03e50fe60db0c9",
|
||||
"url": "./static/js/main.c5511071.chunk.js"
|
||||
},
|
||||
{
|
||||
"revision": "9987e5e6817f0b1d1511",
|
||||
"url": "./static/js/runtime~main.766bf48a.js"
|
||||
},
|
||||
{
|
||||
"revision": "de913ae19ba466b785d7",
|
||||
"url": "./static/js/4.f92d7884.chunk.js"
|
||||
},
|
||||
{
|
||||
"revision": "435f63f2b0ecc59d0119",
|
||||
"url": "./static/js/5.b1222fed.chunk.js"
|
||||
},
|
||||
{
|
||||
"revision": "d704bb3d579b7d5e40880c75705c8a71",
|
||||
"url": "./static/media/roboto-latin-100italic.d704bb3d.woff"
|
||||
},
|
||||
{
|
||||
"revision": "55536c8e9e9a532651e3cf374f290ea3",
|
||||
"url": "./static/media/roboto-latin-300.55536c8e.woff2"
|
||||
},
|
||||
{
|
||||
"revision": "a1471d1d6431c893582a5f6a250db3f9",
|
||||
"url": "./static/media/roboto-latin-300.a1471d1d.woff"
|
||||
},
|
||||
{
|
||||
"revision": "6232f43d15b0e7a0bf0fe82e295bdd06",
|
||||
"url": "./static/media/roboto-latin-100italic.6232f43d.woff2"
|
||||
},
|
||||
{
|
||||
"revision": "d69924b98acd849cdeba9fbff3f88ea6",
|
||||
"url": "./static/media/roboto-latin-300italic.d69924b9.woff2"
|
||||
},
|
||||
{
|
||||
"revision": "210a7c781f5a354a0e4985656ab456d9",
|
||||
"url": "./static/media/roboto-latin-300italic.210a7c78.woff"
|
||||
},
|
||||
{
|
||||
"revision": "987b84570ea69ee660455b8d5e91f5f1",
|
||||
"url": "./static/media/roboto-latin-100.987b8457.woff2"
|
||||
},
|
||||
{
|
||||
"revision": "e9dbbe8a693dd275c16d32feb101f1c1",
|
||||
"url": "./static/media/roboto-latin-100.e9dbbe8a.woff"
|
||||
},
|
||||
{
|
||||
"revision": "5d4aeb4e5f5ef754e307d7ffaef688bd",
|
||||
"url": "./static/media/roboto-latin-400.5d4aeb4e.woff2"
|
||||
},
|
||||
{
|
||||
"revision": "bafb105baeb22d965c70fe52ba6b49d9",
|
||||
"url": "./static/media/roboto-latin-400.bafb105b.woff"
|
||||
},
|
||||
{
|
||||
"revision": "d8bcbe724fd6f4ba44d0ee6a2675890f",
|
||||
"url": "./static/media/roboto-latin-400italic.d8bcbe72.woff2"
|
||||
},
|
||||
{
|
||||
"revision": "9680d5a0c32d2fd084e07bbc4c8b2923",
|
||||
"url": "./static/media/roboto-latin-400italic.9680d5a0.woff"
|
||||
},
|
||||
{
|
||||
"revision": "285467176f7fe6bb6a9c6873b3dad2cc",
|
||||
"url": "./static/media/roboto-latin-500.28546717.woff2"
|
||||
},
|
||||
{
|
||||
"revision": "de8b7431b74642e830af4d4f4b513ec9",
|
||||
"url": "./static/media/roboto-latin-500.de8b7431.woff"
|
||||
},
|
||||
{
|
||||
"revision": "510dec37fa69fba39593e01a469ee018",
|
||||
"url": "./static/media/roboto-latin-500italic.510dec37.woff2"
|
||||
},
|
||||
{
|
||||
"revision": "ffcc050b2d92d4b14a4fcb527ee0bcc8",
|
||||
"url": "./static/media/roboto-latin-500italic.ffcc050b.woff"
|
||||
},
|
||||
{
|
||||
"revision": "037d830416495def72b7881024c14b7b",
|
||||
"url": "./static/media/roboto-latin-700.037d8304.woff2"
|
||||
},
|
||||
{
|
||||
"revision": "cf6613d1adf490972c557a8e318e0868",
|
||||
"url": "./static/media/roboto-latin-700.cf6613d1.woff"
|
||||
},
|
||||
{
|
||||
"revision": "010c1aeee3c6d1cbb1d5761d80353823",
|
||||
"url": "./static/media/roboto-latin-700italic.010c1aee.woff2"
|
||||
},
|
||||
{
|
||||
"revision": "19b7a0adfdd4f808b53af7e2ce2ad4e5",
|
||||
"url": "./static/media/roboto-latin-900.19b7a0ad.woff2"
|
||||
},
|
||||
{
|
||||
"revision": "846d1890aee87fde5d8ced8eba360c3a",
|
||||
"url": "./static/media/roboto-latin-700italic.846d1890.woff"
|
||||
},
|
||||
{
|
||||
"revision": "8c2ade503b34e31430d6c98aa29a52a3",
|
||||
"url": "./static/media/roboto-latin-900.8c2ade50.woff"
|
||||
},
|
||||
{
|
||||
"revision": "7b770d6c53423deb1a8e49d3c9175184",
|
||||
"url": "./static/media/roboto-latin-900italic.7b770d6c.woff2"
|
||||
},
|
||||
{
|
||||
"revision": "bc833e725c137257c2c42a789845d82f",
|
||||
"url": "./static/media/roboto-latin-900italic.bc833e72.woff"
|
||||
},
|
||||
{
|
||||
"revision": "cc3ef0e44832d84dcdb8fce769840ecd",
|
||||
"url": "./static/media/loginscreen-bg.cc3ef0e4.jpg"
|
||||
},
|
||||
{
|
||||
"revision": "6f9a3fb01c61fa1d416601814b69f57a",
|
||||
"url": "./static/media/loginscreen-bg-overlay.6f9a3fb0.svg"
|
||||
},
|
||||
{
|
||||
"revision": "10e256c785b6ad2fe0a337b2aa9e6da6",
|
||||
"url": "./static/media/kopano-logo.10e256c7.svg"
|
||||
},
|
||||
{
|
||||
"revision": "02abc6e9f1d6def38abd7f6b448b00f0",
|
||||
"url": "./index.html"
|
||||
}
|
||||
];
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
|
Before Width: | Height: | Size: 106 KiB |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user