mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2025-12-30 17:00:57 -06:00
Add 'ocs/' from commit '7ca52baa61c4370a1bad0e2f74e85073798bdde9'
git-subtree-dir: ocs git-subtree-mainline:b274cac8c2git-subtree-split:7ca52baa61
This commit is contained in:
8
ocs/.codacy.yml
Normal file
8
ocs/.codacy.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
exclude_paths:
|
||||
- CHANGELOG.md
|
||||
- changelog/**
|
||||
- docs/**
|
||||
- pkg/proto/**
|
||||
|
||||
...
|
||||
2
ocs/.dockerignore
Normal file
2
ocs/.dockerignore
Normal file
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!bin/
|
||||
698
ocs/.drone.star
Normal file
698
ocs/.drone.star
Normal file
@@ -0,0 +1,698 @@
|
||||
def main(ctx):
|
||||
before = [
|
||||
testing(ctx),
|
||||
]
|
||||
|
||||
stages = [
|
||||
docker(ctx, 'amd64'),
|
||||
docker(ctx, 'arm64'),
|
||||
docker(ctx, 'arm'),
|
||||
binary(ctx, 'linux'),
|
||||
binary(ctx, 'darwin'),
|
||||
binary(ctx, 'windows'),
|
||||
]
|
||||
|
||||
after = [
|
||||
manifest(ctx),
|
||||
changelog(ctx),
|
||||
readme(ctx),
|
||||
badges(ctx),
|
||||
website(ctx),
|
||||
]
|
||||
|
||||
return before + stages + after
|
||||
|
||||
def testing(ctx):
|
||||
return {
|
||||
'kind': 'pipeline',
|
||||
'type': 'docker',
|
||||
'name': 'testing',
|
||||
'platform': {
|
||||
'os': 'linux',
|
||||
'arch': 'amd64',
|
||||
},
|
||||
'steps': [
|
||||
{
|
||||
'name': 'generate',
|
||||
'image': 'webhippie/golang:1.13',
|
||||
'pull': 'always',
|
||||
'commands': [
|
||||
'make generate',
|
||||
],
|
||||
'volumes': [
|
||||
{
|
||||
'name': 'gopath',
|
||||
'path': '/srv/app',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'name': 'vet',
|
||||
'image': 'webhippie/golang:1.13',
|
||||
'pull': 'always',
|
||||
'commands': [
|
||||
'make vet',
|
||||
],
|
||||
'volumes': [
|
||||
{
|
||||
'name': 'gopath',
|
||||
'path': '/srv/app',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'name': 'staticcheck',
|
||||
'image': 'webhippie/golang:1.13',
|
||||
'pull': 'always',
|
||||
'commands': [
|
||||
'make staticcheck',
|
||||
],
|
||||
'volumes': [
|
||||
{
|
||||
'name': 'gopath',
|
||||
'path': '/srv/app',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'name': 'lint',
|
||||
'image': 'webhippie/golang:1.13',
|
||||
'pull': 'always',
|
||||
'commands': [
|
||||
'make lint',
|
||||
],
|
||||
'volumes': [
|
||||
{
|
||||
'name': 'gopath',
|
||||
'path': '/srv/app',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'name': 'build',
|
||||
'image': 'webhippie/golang:1.13',
|
||||
'pull': 'always',
|
||||
'commands': [
|
||||
'make build',
|
||||
],
|
||||
'volumes': [
|
||||
{
|
||||
'name': 'gopath',
|
||||
'path': '/srv/app',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'name': 'test',
|
||||
'image': 'webhippie/golang:1.13',
|
||||
'pull': 'always',
|
||||
'commands': [
|
||||
'make test',
|
||||
],
|
||||
'volumes': [
|
||||
{
|
||||
'name': 'gopath',
|
||||
'path': '/srv/app',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'name': 'codacy',
|
||||
'image': 'plugins/codacy:1',
|
||||
'pull': 'always',
|
||||
'settings': {
|
||||
'token': {
|
||||
'from_secret': 'codacy_token',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
'volumes': [
|
||||
{
|
||||
'name': 'gopath',
|
||||
'temp': {},
|
||||
},
|
||||
],
|
||||
'trigger': {
|
||||
'ref': [
|
||||
'refs/heads/master',
|
||||
'refs/tags/**',
|
||||
'refs/pull/**',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
def docker(ctx, arch):
|
||||
return {
|
||||
'kind': 'pipeline',
|
||||
'type': 'docker',
|
||||
'name': arch,
|
||||
'platform': {
|
||||
'os': 'linux',
|
||||
'arch': arch,
|
||||
},
|
||||
'steps': [
|
||||
{
|
||||
'name': 'generate',
|
||||
'image': 'webhippie/golang:1.13',
|
||||
'pull': 'always',
|
||||
'commands': [
|
||||
'make generate',
|
||||
],
|
||||
'volumes': [
|
||||
{
|
||||
'name': 'gopath',
|
||||
'path': '/srv/app',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'name': 'build',
|
||||
'image': 'webhippie/golang:1.13',
|
||||
'pull': 'always',
|
||||
'commands': [
|
||||
'make build',
|
||||
],
|
||||
'volumes': [
|
||||
{
|
||||
'name': 'gopath',
|
||||
'path': '/srv/app',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'name': 'dryrun',
|
||||
'image': 'plugins/docker:18.09',
|
||||
'pull': 'always',
|
||||
'settings': {
|
||||
'dry_run': True,
|
||||
'tags': 'linux-%s' % (arch),
|
||||
'dockerfile': 'docker/Dockerfile.linux.%s' % (arch),
|
||||
'repo': ctx.repo.slug,
|
||||
},
|
||||
'when': {
|
||||
'ref': {
|
||||
'include': [
|
||||
'refs/pull/**',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
'name': 'docker',
|
||||
'image': 'plugins/docker:18.09',
|
||||
'pull': 'always',
|
||||
'settings': {
|
||||
'username': {
|
||||
'from_secret': 'docker_username',
|
||||
},
|
||||
'password': {
|
||||
'from_secret': 'docker_password',
|
||||
},
|
||||
'auto_tag': True,
|
||||
'auto_tag_suffix': 'linux-%s' % (arch),
|
||||
'dockerfile': 'docker/Dockerfile.linux.%s' % (arch),
|
||||
'repo': ctx.repo.slug,
|
||||
},
|
||||
'when': {
|
||||
'ref': {
|
||||
'exclude': [
|
||||
'refs/pull/**',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
'volumes': [
|
||||
{
|
||||
'name': 'gopath',
|
||||
'temp': {},
|
||||
},
|
||||
],
|
||||
'depends_on': [
|
||||
'testing',
|
||||
],
|
||||
'trigger': {
|
||||
'ref': [
|
||||
'refs/heads/master',
|
||||
'refs/tags/**',
|
||||
'refs/pull/**',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
def binary(ctx, name):
|
||||
if ctx.build.event == "tag":
|
||||
settings = {
|
||||
'endpoint': {
|
||||
'from_secret': 's3_endpoint',
|
||||
},
|
||||
'access_key': {
|
||||
'from_secret': 'aws_access_key_id',
|
||||
},
|
||||
'secret_key': {
|
||||
'from_secret': 'aws_secret_access_key',
|
||||
},
|
||||
'bucket': {
|
||||
'from_secret': 's3_bucket',
|
||||
},
|
||||
'path_style': True,
|
||||
'strip_prefix': 'dist/release/',
|
||||
'source': 'dist/release/*',
|
||||
'target': '/ocis/%s/%s' % (ctx.repo.name.replace("ocis-", ""), ctx.build.ref.replace("refs/tags/v", "")),
|
||||
}
|
||||
else:
|
||||
settings = {
|
||||
'endpoint': {
|
||||
'from_secret': 's3_endpoint',
|
||||
},
|
||||
'access_key': {
|
||||
'from_secret': 'aws_access_key_id',
|
||||
},
|
||||
'secret_key': {
|
||||
'from_secret': 'aws_secret_access_key',
|
||||
},
|
||||
'bucket': {
|
||||
'from_secret': 's3_bucket',
|
||||
},
|
||||
'path_style': True,
|
||||
'strip_prefix': 'dist/release/',
|
||||
'source': 'dist/release/*',
|
||||
'target': '/ocis/%s/testing' % (ctx.repo.name.replace("ocis-", "")),
|
||||
}
|
||||
|
||||
return {
|
||||
'kind': 'pipeline',
|
||||
'type': 'docker',
|
||||
'name': name,
|
||||
'platform': {
|
||||
'os': 'linux',
|
||||
'arch': 'amd64',
|
||||
},
|
||||
'steps': [
|
||||
{
|
||||
'name': 'generate',
|
||||
'image': 'webhippie/golang:1.13',
|
||||
'pull': 'always',
|
||||
'commands': [
|
||||
'make generate',
|
||||
],
|
||||
'volumes': [
|
||||
{
|
||||
'name': 'gopath',
|
||||
'path': '/srv/app',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'name': 'build',
|
||||
'image': 'webhippie/golang:1.13',
|
||||
'pull': 'always',
|
||||
'commands': [
|
||||
'make release-%s' % (name),
|
||||
],
|
||||
'volumes': [
|
||||
{
|
||||
'name': 'gopath',
|
||||
'path': '/srv/app',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'name': 'finish',
|
||||
'image': 'webhippie/golang:1.13',
|
||||
'pull': 'always',
|
||||
'commands': [
|
||||
'make release-finish',
|
||||
],
|
||||
'volumes': [
|
||||
{
|
||||
'name': 'gopath',
|
||||
'path': '/srv/app',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'name': 'upload',
|
||||
'image': 'plugins/s3:1',
|
||||
'pull': 'always',
|
||||
'settings': settings,
|
||||
'when': {
|
||||
'ref': [
|
||||
'refs/heads/master',
|
||||
'refs/tags/**',
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
'name': 'changelog',
|
||||
'image': 'toolhippie/calens:latest',
|
||||
'pull': 'always',
|
||||
'commands': [
|
||||
'calens --version %s -o dist/CHANGELOG.md' % ctx.build.ref.replace("refs/tags/v", "").split("-")[0],
|
||||
],
|
||||
'when': {
|
||||
'ref': [
|
||||
'refs/tags/**',
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
'name': 'release',
|
||||
'image': 'plugins/github-release:1',
|
||||
'pull': 'always',
|
||||
'settings': {
|
||||
'api_key': {
|
||||
'from_secret': 'github_token',
|
||||
},
|
||||
'files': [
|
||||
'dist/release/*',
|
||||
],
|
||||
'title': ctx.build.ref.replace("refs/tags/v", ""),
|
||||
'note': 'dist/CHANGELOG.md',
|
||||
'overwrite': True,
|
||||
'prerelease': len(ctx.build.ref.split("-")) > 1,
|
||||
},
|
||||
'when': {
|
||||
'ref': [
|
||||
'refs/tags/**',
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
'volumes': [
|
||||
{
|
||||
'name': 'gopath',
|
||||
'temp': {},
|
||||
},
|
||||
],
|
||||
'depends_on': [
|
||||
'testing',
|
||||
],
|
||||
'trigger': {
|
||||
'ref': [
|
||||
'refs/heads/master',
|
||||
'refs/tags/**',
|
||||
'refs/pull/**',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
def manifest(ctx):
|
||||
return {
|
||||
'kind': 'pipeline',
|
||||
'type': 'docker',
|
||||
'name': 'manifest',
|
||||
'platform': {
|
||||
'os': 'linux',
|
||||
'arch': 'amd64',
|
||||
},
|
||||
'steps': [
|
||||
{
|
||||
'name': 'execute',
|
||||
'image': 'plugins/manifest:1',
|
||||
'pull': 'always',
|
||||
'settings': {
|
||||
'username': {
|
||||
'from_secret': 'docker_username',
|
||||
},
|
||||
'password': {
|
||||
'from_secret': 'docker_password',
|
||||
},
|
||||
'spec': 'docker/manifest.tmpl',
|
||||
'auto_tag': True,
|
||||
'ignore_missing': True,
|
||||
},
|
||||
},
|
||||
],
|
||||
'depends_on': [
|
||||
'amd64',
|
||||
'arm64',
|
||||
'arm',
|
||||
'linux',
|
||||
'darwin',
|
||||
'windows',
|
||||
],
|
||||
'trigger': {
|
||||
'ref': [
|
||||
'refs/heads/master',
|
||||
'refs/tags/**',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
def changelog(ctx):
|
||||
repo_slug = ctx.build.source_repo if ctx.build.source_repo else ctx.repo.slug
|
||||
return {
|
||||
'kind': 'pipeline',
|
||||
'type': 'docker',
|
||||
'name': 'changelog',
|
||||
'platform': {
|
||||
'os': 'linux',
|
||||
'arch': 'amd64',
|
||||
},
|
||||
'clone': {
|
||||
'disable': True,
|
||||
},
|
||||
'steps': [
|
||||
{
|
||||
'name': 'clone',
|
||||
'image': 'plugins/git-action:1',
|
||||
'pull': 'always',
|
||||
'settings': {
|
||||
'actions': [
|
||||
'clone',
|
||||
],
|
||||
'remote': 'https://github.com/%s' % (repo_slug),
|
||||
'branch': ctx.build.source if ctx.build.event == 'pull_request' else 'master',
|
||||
'path': '/drone/src',
|
||||
'netrc_machine': 'github.com',
|
||||
'netrc_username': {
|
||||
'from_secret': 'github_username',
|
||||
},
|
||||
'netrc_password': {
|
||||
'from_secret': 'github_token',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
'name': 'generate',
|
||||
'image': 'webhippie/golang:1.13',
|
||||
'pull': 'always',
|
||||
'commands': [
|
||||
'make changelog',
|
||||
],
|
||||
},
|
||||
{
|
||||
'name': 'diff',
|
||||
'image': 'owncloud/alpine:latest',
|
||||
'pull': 'always',
|
||||
'commands': [
|
||||
'git diff',
|
||||
],
|
||||
},
|
||||
{
|
||||
'name': 'output',
|
||||
'image': 'owncloud/alpine:latest',
|
||||
'pull': 'always',
|
||||
'commands': [
|
||||
'cat CHANGELOG.md',
|
||||
],
|
||||
},
|
||||
{
|
||||
'name': 'publish',
|
||||
'image': 'plugins/git-action:1',
|
||||
'pull': 'always',
|
||||
'settings': {
|
||||
'actions': [
|
||||
'commit',
|
||||
'push',
|
||||
],
|
||||
'message': 'Automated changelog update [skip ci]',
|
||||
'branch': 'master',
|
||||
'author_email': 'devops@owncloud.com',
|
||||
'author_name': 'ownClouders',
|
||||
'netrc_machine': 'github.com',
|
||||
'netrc_username': {
|
||||
'from_secret': 'github_username',
|
||||
},
|
||||
'netrc_password': {
|
||||
'from_secret': 'github_token',
|
||||
},
|
||||
},
|
||||
'when': {
|
||||
'ref': {
|
||||
'exclude': [
|
||||
'refs/pull/**',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
'depends_on': [
|
||||
'manifest',
|
||||
],
|
||||
'trigger': {
|
||||
'ref': [
|
||||
'refs/heads/master',
|
||||
'refs/pull/**',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
def readme(ctx):
|
||||
return {
|
||||
'kind': 'pipeline',
|
||||
'type': 'docker',
|
||||
'name': 'readme',
|
||||
'platform': {
|
||||
'os': 'linux',
|
||||
'arch': 'amd64',
|
||||
},
|
||||
'steps': [
|
||||
{
|
||||
'name': 'execute',
|
||||
'image': 'sheogorath/readme-to-dockerhub:latest',
|
||||
'pull': 'always',
|
||||
'environment': {
|
||||
'DOCKERHUB_USERNAME': {
|
||||
'from_secret': 'docker_username',
|
||||
},
|
||||
'DOCKERHUB_PASSWORD': {
|
||||
'from_secret': 'docker_password',
|
||||
},
|
||||
'DOCKERHUB_REPO_PREFIX': ctx.repo.namespace,
|
||||
'DOCKERHUB_REPO_NAME': ctx.repo.name,
|
||||
'SHORT_DESCRIPTION': 'Docker images for %s' % (ctx.repo.name),
|
||||
'README_PATH': 'README.md',
|
||||
},
|
||||
},
|
||||
],
|
||||
'depends_on': [
|
||||
'changelog',
|
||||
],
|
||||
'trigger': {
|
||||
'ref': [
|
||||
'refs/heads/master',
|
||||
'refs/tags/**',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
def badges(ctx):
|
||||
return {
|
||||
'kind': 'pipeline',
|
||||
'type': 'docker',
|
||||
'name': 'badges',
|
||||
'platform': {
|
||||
'os': 'linux',
|
||||
'arch': 'amd64',
|
||||
},
|
||||
'steps': [
|
||||
{
|
||||
'name': 'execute',
|
||||
'image': 'plugins/webhook:1',
|
||||
'pull': 'always',
|
||||
'settings': {
|
||||
'urls': {
|
||||
'from_secret': 'microbadger_url',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
'depends_on': [
|
||||
'readme',
|
||||
],
|
||||
'trigger': {
|
||||
'ref': [
|
||||
'refs/heads/master',
|
||||
'refs/tags/**',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
def website(ctx):
|
||||
return {
|
||||
'kind': 'pipeline',
|
||||
'type': 'docker',
|
||||
'name': 'website',
|
||||
'platform': {
|
||||
'os': 'linux',
|
||||
'arch': 'amd64',
|
||||
},
|
||||
'steps': [
|
||||
{
|
||||
'name': 'prepare',
|
||||
'image': 'owncloudci/alpine:latest',
|
||||
'commands': [
|
||||
'make docs-copy'
|
||||
],
|
||||
},
|
||||
{
|
||||
'name': 'test',
|
||||
'image': 'webhippie/hugo:latest',
|
||||
'commands': [
|
||||
'cd hugo',
|
||||
'hugo',
|
||||
],
|
||||
},
|
||||
{
|
||||
'name': 'list',
|
||||
'image': 'owncloudci/alpine:latest',
|
||||
'commands': [
|
||||
'tree hugo/public',
|
||||
],
|
||||
},
|
||||
{
|
||||
'name': 'publish',
|
||||
'image': 'plugins/gh-pages:1',
|
||||
'pull': 'always',
|
||||
'settings': {
|
||||
'username': {
|
||||
'from_secret': 'github_username',
|
||||
},
|
||||
'password': {
|
||||
'from_secret': 'github_token',
|
||||
},
|
||||
'pages_directory': 'docs/',
|
||||
'target_branch': 'docs',
|
||||
},
|
||||
'when': {
|
||||
'ref': {
|
||||
'exclude': [
|
||||
'refs/pull/**',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
'name': 'downstream',
|
||||
'image': 'plugins/downstream',
|
||||
'settings': {
|
||||
'server': 'https://cloud.drone.io/',
|
||||
'token': {
|
||||
'from_secret': 'drone_token',
|
||||
},
|
||||
'repositories': [
|
||||
'owncloud/owncloud.github.io@source',
|
||||
],
|
||||
},
|
||||
'when': {
|
||||
'ref': {
|
||||
'exclude': [
|
||||
'refs/pull/**',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
'depends_on': [
|
||||
'badges',
|
||||
],
|
||||
'trigger': {
|
||||
'ref': [
|
||||
'refs/heads/master',
|
||||
'refs/pull/**',
|
||||
],
|
||||
},
|
||||
}
|
||||
35
ocs/.editorconfig
Normal file
35
ocs/.editorconfig
Normal file
@@ -0,0 +1,35 @@
|
||||
# http://editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
|
||||
[*.go]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
|
||||
[*.starlark]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.{yml,json}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.{js,vue}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.{css,less}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = true
|
||||
12
ocs/.github/config.yml
vendored
Normal file
12
ocs/.github/config.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# Configuration for update-docs - https://github.com/behaviorbot/update-docs
|
||||
|
||||
# Comment to be posted to on PRs that don't update documentation
|
||||
updateDocsComment: >
|
||||
Thanks for opening this pull request! The maintainers of this repository would appreciate it if you would create a [changelog](https://github.com/owncloud/ocis-ocs/blob/master/changelog/README.md) item based on your changes.
|
||||
updateDocsWhiteList:
|
||||
- Tests-only
|
||||
- tests-only
|
||||
- Tests-Only
|
||||
|
||||
updateDocsTargetFiles:
|
||||
- changelog/unreleased/
|
||||
0
ocs/.github/issue_template.md
vendored
Normal file
0
ocs/.github/issue_template.md
vendored
Normal file
0
ocs/.github/pull_request_template.md
vendored
Normal file
0
ocs/.github/pull_request_template.md
vendored
Normal file
98
ocs/.github/settings.yml
vendored
Normal file
98
ocs/.github/settings.yml
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
---
|
||||
repository:
|
||||
name: ocis-ocs
|
||||
description: ':atom_symbol: Serve OCS API for oCIS'
|
||||
homepage: https://owncloud.github.io/ocis-ocs/
|
||||
topics: reva, ocis
|
||||
|
||||
private: false
|
||||
has_issues: true
|
||||
has_projects: false
|
||||
has_wiki: false
|
||||
has_downloads: false
|
||||
|
||||
default_branch: master
|
||||
|
||||
allow_squash_merge: true
|
||||
allow_merge_commit: true
|
||||
allow_rebase_merge: true
|
||||
|
||||
labels:
|
||||
- name: bug
|
||||
color: d73a4a
|
||||
description: Something isn't working
|
||||
- name: documentation
|
||||
color: 0075ca
|
||||
description: Improvements or additions to documentation
|
||||
- name: duplicate
|
||||
color: cfd3d7
|
||||
description: This issue or pull request already exists
|
||||
- name: enhancement
|
||||
color: a2eeef
|
||||
description: New feature or request
|
||||
- name: good first issue
|
||||
color: 7057ff
|
||||
description: Good for newcomers
|
||||
- name: help wanted
|
||||
color: 008672
|
||||
description: Extra attention is needed
|
||||
- name: invalid
|
||||
color: e4e669
|
||||
description: This doesn't seem right
|
||||
- name: question
|
||||
color: d876e3
|
||||
description: Further information is requested
|
||||
- name: wontfix
|
||||
color: ffffff
|
||||
description: This will not be worked on
|
||||
- name: effort/trivial
|
||||
color: c2e0c6
|
||||
description: Required effort to finish task
|
||||
- name: effort/0.25d
|
||||
color: c2e0c6
|
||||
description: Required effort to finish task
|
||||
- name: effort/0.5d
|
||||
color: c2e0c6
|
||||
description: Required effort to finish task
|
||||
- name: effort/1d
|
||||
color: c2e0c6
|
||||
description: Required effort to finish task
|
||||
- name: effort/2d
|
||||
color: c2e0c6
|
||||
description: Required effort to finish task
|
||||
- name: effort/4d
|
||||
color: c2e0c6
|
||||
description: Required effort to finish task
|
||||
- name: effort/5d
|
||||
color: c2e0c6
|
||||
description: Required effort to finish task
|
||||
- name: effort/10d
|
||||
color: c2e0c6
|
||||
description: Required effort to finish task
|
||||
|
||||
teams:
|
||||
- name: ci
|
||||
permission: admin
|
||||
- name: employees
|
||||
permission: push
|
||||
|
||||
branches:
|
||||
- name: master
|
||||
protection:
|
||||
required_pull_request_reviews:
|
||||
required_approving_review_count: 1
|
||||
dismiss_stale_reviews: false
|
||||
require_code_owner_reviews: false
|
||||
dismissal_restrictions: {}
|
||||
required_status_checks:
|
||||
strict: true
|
||||
contexts:
|
||||
- continuous-integration/drone/pr
|
||||
enforce_admins: false
|
||||
restrictions:
|
||||
users: []
|
||||
teams:
|
||||
- ci
|
||||
- employees
|
||||
|
||||
...
|
||||
12
ocs/.gitignore
vendored
Normal file
12
ocs/.gitignore
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
coverage.out
|
||||
|
||||
/bin
|
||||
/dist
|
||||
/hugo
|
||||
|
||||
/node_modules
|
||||
/assets
|
||||
|
||||
# test artifacts
|
||||
pkg/server/http/accounts-store/
|
||||
pkg/server/http/settings-store/
|
||||
180
ocs/CHANGELOG.md
Normal file
180
ocs/CHANGELOG.md
Normal file
@@ -0,0 +1,180 @@
|
||||
# Changelog for [unreleased] (UNRELEASED)
|
||||
|
||||
The following sections list the changes in ocis-ocs unreleased.
|
||||
|
||||
[unreleased]: https://github.com/owncloud/ocis-ocs/compare/v0.3.1...master
|
||||
|
||||
## Summary
|
||||
|
||||
* Bugfix - Match the user response to the OC10 format: [#181](https://github.com/owncloud/product/issues/181)
|
||||
|
||||
## Details
|
||||
|
||||
* Bugfix - Match the user response to the OC10 format: [#181](https://github.com/owncloud/product/issues/181)
|
||||
|
||||
The user response contained the field `displayname` but for certain responses the field
|
||||
`display-name` is expected. The field `display-name` was added and now both fields are
|
||||
returned to the client.
|
||||
|
||||
https://github.com/owncloud/product/issues/181
|
||||
https://github.com/owncloud/ocis-ocs/pull/61
|
||||
|
||||
# Changelog for [0.3.1] (2020-09-02)
|
||||
|
||||
The following sections list the changes in ocis-ocs 0.3.1.
|
||||
|
||||
[0.3.1]: https://github.com/owncloud/ocis-ocs/compare/v0.3.0...v0.3.1
|
||||
|
||||
## Summary
|
||||
|
||||
* Bugfix - Add the top level response structure to json responses: [#181](https://github.com/owncloud/product/issues/181)
|
||||
* Enhancement - Update ocis-accounts: [#42](https://github.com/owncloud/ocis-ocs/pull/42)
|
||||
|
||||
## Details
|
||||
|
||||
* Bugfix - Add the top level response structure to json responses: [#181](https://github.com/owncloud/product/issues/181)
|
||||
|
||||
Probably during moving the ocs code into the ocis-ocs repo the response format was changed.
|
||||
This change adds the top level response to json responses. Doing that the reponse should be
|
||||
compatible to the responses from OC10.
|
||||
|
||||
https://github.com/owncloud/product/issues/181
|
||||
https://github.com/owncloud/product/issues/181#issuecomment-683604168
|
||||
|
||||
|
||||
* Enhancement - Update ocis-accounts: [#42](https://github.com/owncloud/ocis-ocs/pull/42)
|
||||
|
||||
Update ocis-accounts to v0.4.2-0.20200828150703-2ca83cf4ac20
|
||||
|
||||
https://github.com/owncloud/ocis-ocs/pull/42
|
||||
|
||||
# Changelog for [0.3.0] (2020-08-27)
|
||||
|
||||
The following sections list the changes in ocis-ocs 0.3.0.
|
||||
|
||||
[0.3.0]: https://github.com/owncloud/ocis-ocs/compare/v0.2.0...v0.3.0
|
||||
|
||||
## Summary
|
||||
|
||||
* Bugfix - Mimic oc10 user enabled as string in provisioning api: [#39](https://github.com/owncloud/ocis-ocs/pull/39)
|
||||
* Bugfix - Use opaque ID of a user for signing keys: [#436](https://github.com/owncloud/ocis/issues/436)
|
||||
* Enhancement - Add option to create user with uidnumber and gidnumber: [#34](https://github.com/owncloud/ocis-ocs/pull/34)
|
||||
|
||||
## Details
|
||||
|
||||
* Bugfix - Mimic oc10 user enabled as string in provisioning api: [#39](https://github.com/owncloud/ocis-ocs/pull/39)
|
||||
|
||||
The oc10 user provisioning API uses a string for the boolean `enabled` flag. 😭
|
||||
|
||||
https://github.com/owncloud/ocis-ocs/pull/39
|
||||
|
||||
|
||||
* Bugfix - Use opaque ID of a user for signing keys: [#436](https://github.com/owncloud/ocis/issues/436)
|
||||
|
||||
OCIS switched from user the user's opaque ID (UUID) everywhere, so to keep compatible we have
|
||||
adjusted the signing keys endpoint to also use the UUID when storing and generating the keys.
|
||||
|
||||
https://github.com/owncloud/ocis/issues/436
|
||||
https://github.com/owncloud/ocis-ocs/pull/32
|
||||
|
||||
|
||||
* Enhancement - Add option to create user with uidnumber and gidnumber: [#34](https://github.com/owncloud/ocis-ocs/pull/34)
|
||||
|
||||
We have added an option to pass uidnumber and gidnumber to the ocis api while creating a new user
|
||||
|
||||
https://github.com/owncloud/ocis-ocs/pull/34
|
||||
|
||||
# Changelog for [0.2.0] (2020-08-17)
|
||||
|
||||
The following sections list the changes in ocis-ocs 0.2.0.
|
||||
|
||||
[0.2.0]: https://github.com/owncloud/ocis-ocs/compare/v0.1.0...v0.2.0
|
||||
|
||||
## Summary
|
||||
|
||||
* Bugfix - Fix file descriptor leak: [#79](https://github.com/owncloud/ocis-accounts/issues/79)
|
||||
* Enhancement - Add Group management for OCS Povisioning API: [#25](https://github.com/owncloud/ocis-ocs/pull/25)
|
||||
* Enhancement - Basic Support for the User Provisioning API: [#23](https://github.com/owncloud/ocis-ocs/pull/23)
|
||||
|
||||
## Details
|
||||
|
||||
* Bugfix - Fix file descriptor leak: [#79](https://github.com/owncloud/ocis-accounts/issues/79)
|
||||
|
||||
Only use a single instance of go-micro's GRPC client as it already does connection pooling.
|
||||
This prevents connection and file descriptor leaks.
|
||||
|
||||
https://github.com/owncloud/ocis-accounts/issues/79
|
||||
https://github.com/owncloud/ocis-ocs/pull/29
|
||||
|
||||
|
||||
* Enhancement - Add Group management for OCS Povisioning API: [#25](https://github.com/owncloud/ocis-ocs/pull/25)
|
||||
|
||||
We added support for the group management related set of API calls of the provisioning API.
|
||||
[Reference](https://doc.owncloud.com/server/admin_manual/configuration/user/user_provisioning_api.html)
|
||||
|
||||
https://github.com/owncloud/ocis-ocs/pull/25
|
||||
|
||||
|
||||
* Enhancement - Basic Support for the User Provisioning API: [#23](https://github.com/owncloud/ocis-ocs/pull/23)
|
||||
|
||||
We added support for a basic set of API calls for the user provisioning API.
|
||||
[Reference](https://doc.owncloud.com/server/admin_manual/configuration/user/user_provisioning_api.html)
|
||||
|
||||
https://github.com/owncloud/ocis-ocs/pull/23
|
||||
|
||||
# Changelog for [0.1.0] (2020-07-23)
|
||||
|
||||
The following sections list the changes in ocis-ocs 0.1.0.
|
||||
|
||||
[0.1.0]: https://github.com/owncloud/ocis-ocs/compare/acd6d6e7f59d1a44bcedb4dd60564910b474c38a...v0.1.0
|
||||
|
||||
## Summary
|
||||
|
||||
* Bugfix - Build docker images with alpine:latest instead of alpine:edge: [#20](https://github.com/owncloud/ocis-ocs/pull/20)
|
||||
* Change - Initial release of basic version: [#1](https://github.com/owncloud/ocis-ocs/issues/1)
|
||||
* Change - Upgrade micro libraries: [#11](https://github.com/owncloud/ocis-ocs/issues/11)
|
||||
* Enhancement - Configuration: [#14](https://github.com/owncloud/ocis-ocs/pull/14)
|
||||
* Enhancement - Support signing key: [#18](https://github.com/owncloud/ocis-ocs/pull/18)
|
||||
|
||||
## Details
|
||||
|
||||
* Bugfix - Build docker images with alpine:latest instead of alpine:edge: [#20](https://github.com/owncloud/ocis-ocs/pull/20)
|
||||
|
||||
ARM builds were failing when built on alpine:edge, so we switched to alpine:latest instead.
|
||||
|
||||
https://github.com/owncloud/ocis-ocs/pull/20
|
||||
|
||||
|
||||
* Change - Initial release of basic version: [#1](https://github.com/owncloud/ocis-ocs/issues/1)
|
||||
|
||||
Just prepared an initial basic version to serve OCS for the ownCloud Infinite Scale project. It
|
||||
just provides a minimal viable product to demonstrate the microservice pattern.
|
||||
|
||||
https://github.com/owncloud/ocis-ocs/issues/1
|
||||
|
||||
|
||||
* Change - Upgrade micro libraries: [#11](https://github.com/owncloud/ocis-ocs/issues/11)
|
||||
|
||||
Updated the micro and ocis-pkg libraries to version 2.
|
||||
|
||||
https://github.com/owncloud/ocis-ocs/issues/11
|
||||
|
||||
|
||||
* Enhancement - Configuration: [#14](https://github.com/owncloud/ocis-ocs/pull/14)
|
||||
|
||||
Extensions should be responsible of configuring themselves. We use Viper for config loading
|
||||
from default paths. Environment variables **WILL** take precedence over config files.
|
||||
|
||||
https://github.com/owncloud/ocis-ocs/pull/14
|
||||
|
||||
|
||||
* Enhancement - Support signing key: [#18](https://github.com/owncloud/ocis-ocs/pull/18)
|
||||
|
||||
We added support for the `/v[12].php/cloud/user/signing-key` endpoint that is used by the
|
||||
owncloud-sdk to generate signed URLs. This allows directly downloading large files with
|
||||
browsers instead of using `blob://` urls, which eats memory ...
|
||||
|
||||
https://github.com/owncloud/ocis-ocs/pull/18
|
||||
https://github.com/owncloud/ocis-proxy/pull/75
|
||||
https://github.com/owncloud/owncloud-sdk/pull/504
|
||||
|
||||
202
ocs/LICENSE
Normal file
202
ocs/LICENSE
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
195
ocs/Makefile
Normal file
195
ocs/Makefile
Normal file
@@ -0,0 +1,195 @@
|
||||
SHELL := bash
|
||||
NAME := ocis-ocs
|
||||
IMPORT := github.com/owncloud/$(NAME)
|
||||
BIN := bin
|
||||
DIST := dist
|
||||
HUGO := hugo
|
||||
|
||||
ifeq ($(OS), Windows_NT)
|
||||
EXECUTABLE := $(NAME).exe
|
||||
UNAME := Windows
|
||||
else
|
||||
EXECUTABLE := $(NAME)
|
||||
UNAME := $(shell uname -s)
|
||||
endif
|
||||
|
||||
ifeq ($(UNAME), Darwin)
|
||||
GOBUILD ?= go build -i
|
||||
else
|
||||
GOBUILD ?= go build
|
||||
endif
|
||||
|
||||
PACKAGES ?= $(shell go list ./...)
|
||||
SOURCES ?= $(shell find . -name "*.go" -type f -not -path "./node_modules/*")
|
||||
GENERATE ?= $(PACKAGES)
|
||||
|
||||
TAGS ?=
|
||||
|
||||
ifndef OUTPUT
|
||||
ifneq ($(DRONE_TAG),)
|
||||
OUTPUT ?= $(subst v,,$(DRONE_TAG))
|
||||
else
|
||||
OUTPUT ?= testing
|
||||
endif
|
||||
endif
|
||||
|
||||
ifndef VERSION
|
||||
ifneq ($(DRONE_TAG),)
|
||||
VERSION ?= $(subst v,,$(DRONE_TAG))
|
||||
else
|
||||
VERSION ?= $(shell git rev-parse --short HEAD)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifndef DATE
|
||||
DATE := $(shell date -u '+%Y%m%d')
|
||||
endif
|
||||
|
||||
LDFLAGS += -s -w -X "$(IMPORT)/pkg/version.String=$(VERSION)" -X "$(IMPORT)/pkg/version.Date=$(DATE)"
|
||||
DEBUG_LDFLAGS += -X "$(IMPORT)/pkg/version.String=$(VERSION)" -X "$(IMPORT)/pkg/version.Date=$(DATE)"
|
||||
GCFLAGS += all=-N -l
|
||||
|
||||
.PHONY: all
|
||||
all: build
|
||||
|
||||
.PHONY: sync
|
||||
sync:
|
||||
go mod download
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
go clean -i ./...
|
||||
rm -rf $(BIN) $(DIST) $(HUGO)
|
||||
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
gofmt -s -w $(SOURCES)
|
||||
|
||||
.PHONY: vet
|
||||
vet:
|
||||
go vet $(PACKAGES)
|
||||
|
||||
.PHONY: staticcheck
|
||||
staticcheck:
|
||||
go run honnef.co/go/tools/cmd/staticcheck -tags '$(TAGS)' $(PACKAGES)
|
||||
|
||||
.PHONY: lint
|
||||
lint:
|
||||
for PKG in $(PACKAGES); do go run golang.org/x/lint/golint -set_exit_status $$PKG || exit 1; done;
|
||||
|
||||
.PHONY: generate
|
||||
generate:
|
||||
go generate $(GENERATE)
|
||||
|
||||
.PHONY: changelog
|
||||
changelog:
|
||||
go run github.com/restic/calens >| CHANGELOG.md
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
go run github.com/haya14busa/goverage -v -coverprofile coverage.out $(PACKAGES)
|
||||
|
||||
.PHONY: install
|
||||
install: $(SOURCES)
|
||||
go install -v -tags '$(TAGS)' -ldflags '$(LDFLAGS)' ./cmd/$(NAME)
|
||||
|
||||
.PHONY: build
|
||||
build: $(BIN)/$(EXECUTABLE) $(BIN)/$(EXECUTABLE)-debug
|
||||
|
||||
$(BIN)/$(EXECUTABLE): $(SOURCES)
|
||||
$(GOBUILD) -v -tags '$(TAGS)' -ldflags '$(LDFLAGS)' -o $@ ./cmd/$(NAME)
|
||||
|
||||
$(BIN)/$(EXECUTABLE)-debug: $(SOURCES)
|
||||
$(GOBUILD) -v -tags '$(TAGS)' -ldflags '$(DEBUG_LDFLAGS)' -gcflags '$(GCFLAGS)' -o $@ ./cmd/$(NAME)
|
||||
|
||||
.PHONY: release
|
||||
release: release-dirs release-linux release-windows release-darwin release-copy release-check
|
||||
|
||||
.PHONY: release-dirs
|
||||
release-dirs:
|
||||
mkdir -p $(DIST)/binaries $(DIST)/release
|
||||
|
||||
.PHONY: release-linux
|
||||
release-linux: release-dirs
|
||||
go run github.com/mitchellh/gox -tags 'netgo $(TAGS)' -ldflags '-extldflags "-static" $(LDFLAGS)' -os 'linux' -arch 'amd64 386 arm64 arm' -output '$(DIST)/binaries/$(EXECUTABLE)-$(OUTPUT)-{{.OS}}-{{.Arch}}' ./cmd/$(NAME)
|
||||
|
||||
.PHONY: release-windows
|
||||
release-windows: release-dirs
|
||||
go run github.com/mitchellh/gox -tags 'netgo $(TAGS)' -ldflags '-extldflags "-static" $(LDFLAGS)' -os 'windows' -arch 'amd64' -output '$(DIST)/binaries/$(EXECUTABLE)-$(OUTPUT)-{{.OS}}-{{.Arch}}' ./cmd/$(NAME)
|
||||
|
||||
.PHONY: release-darwin
|
||||
release-darwin: release-dirs
|
||||
go run github.com/mitchellh/gox -tags 'netgo $(TAGS)' -ldflags '$(LDFLAGS)' -os 'darwin' -arch 'amd64' -output '$(DIST)/binaries/$(EXECUTABLE)-$(OUTPUT)-{{.OS}}-{{.Arch}}' ./cmd/$(NAME)
|
||||
|
||||
.PHONY: release-copy
|
||||
release-copy:
|
||||
$(foreach file,$(wildcard $(DIST)/binaries/$(EXECUTABLE)-*),cp $(file) $(DIST)/release/$(notdir $(file));)
|
||||
|
||||
.PHONY: release-check
|
||||
release-check:
|
||||
cd $(DIST)/release; $(foreach file,$(wildcard $(DIST)/release/$(EXECUTABLE)-*),sha256sum $(notdir $(file)) > $(notdir $(file)).sha256;)
|
||||
|
||||
.PHONY: release-finish
|
||||
release-finish: release-copy release-check
|
||||
|
||||
.PHONY: docs-copy
|
||||
docs-copy:
|
||||
mkdir -p $(HUGO); \
|
||||
mkdir -p $(HUGO)/content/extensions; \
|
||||
cd $(HUGO); \
|
||||
git init; \
|
||||
git remote rm origin; \
|
||||
git remote add origin https://github.com/owncloud/owncloud.github.io; \
|
||||
git fetch; \
|
||||
git checkout origin/source -f; \
|
||||
rsync --delete -ax ../docs/ content/extensions/$(NAME)
|
||||
|
||||
.PHONY: docs-build
|
||||
docs-build:
|
||||
cd $(HUGO); hugo
|
||||
|
||||
.PHONY: docs
|
||||
docs: docs-copy docs-build
|
||||
|
||||
.PHONY: watch
|
||||
watch:
|
||||
go run github.com/cespare/reflex -c reflex.conf
|
||||
|
||||
# $(GOPATH)/bin/protoc-gen-go:
|
||||
# GO111MODULE=off go get -v github.com/golang/protobuf/protoc-gen-go
|
||||
|
||||
# $(GOPATH)/bin/protoc-gen-micro:
|
||||
# GO111MODULE=off go get -v github.com/micro/protoc-gen-micro
|
||||
|
||||
# $(GOPATH)/bin/protoc-gen-microweb:
|
||||
# GO111MODULE=off go get -v github.com/webhippie/protoc-gen-microweb
|
||||
|
||||
# $(GOPATH)/bin/protoc-gen-swagger:
|
||||
# GO111MODULE=off go get -v github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger
|
||||
|
||||
# pkg/proto/v0/example.pb.go: pkg/proto/v0/example.proto
|
||||
# protoc \
|
||||
# -I=third_party/ \
|
||||
# -I=pkg/proto/v0/ \
|
||||
# --go_out=logtostderr=true:pkg/proto/v0 example.proto
|
||||
|
||||
# pkg/proto/v0/example.pb.micro.go: pkg/proto/v0/example.proto
|
||||
# protoc \
|
||||
# -I=third_party/ \
|
||||
# -I=pkg/proto/v0/ \
|
||||
# --micro_out=logtostderr=true:pkg/proto/v0 example.proto
|
||||
|
||||
# pkg/proto/v0/example.pb.web.go: pkg/proto/v0/example.proto
|
||||
# protoc \
|
||||
# -I=third_party/ \
|
||||
# -I=pkg/proto/v0/ \
|
||||
# --microweb_out=logtostderr=true:pkg/proto/v0 example.proto
|
||||
|
||||
# pkg/proto/v0/example.swagger.json: pkg/proto/v0/example.proto
|
||||
# protoc \
|
||||
# -I=third_party/ \
|
||||
# -I=pkg/proto/v0/ \
|
||||
# --swagger_out=logtostderr=true:pkg/proto/v0 example.proto
|
||||
|
||||
# .PHONY: protobuf
|
||||
# protobuf: $(GOPATH)/bin/protoc-gen-go $(GOPATH)/bin/protoc-gen-micro $(GOPATH)/bin/protoc-gen-microweb $(GOPATH)/bin/protoc-gen-swagger pkg/proto/v0/example.pb.go pkg/proto/v0/example.pb.micro.go pkg/proto/v0/example.pb.web.go pkg/proto/v0/example.swagger.json
|
||||
45
ocs/README.md
Normal file
45
ocs/README.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# ownCloud Infinite Scale: OCS
|
||||
|
||||
[](https://cloud.drone.io/owncloud/ocis-ocs)
|
||||
[](https://gitter.im/cs3org/reva)
|
||||
[](https://www.codacy.com/app/owncloud/ocis-ocs?utm_source=github.com&utm_medium=referral&utm_content=owncloud/ocis-ocs&utm_campaign=Badge_Grade)
|
||||
[](http://godoc.org/github.com/owncloud/ocis-ocs)
|
||||
[](http://goreportcard.com/report/github.com/owncloud/ocis-ocs)
|
||||
[](http://microbadger.com/images/owncloud/ocis-ocs "Get your own image badge on microbadger.com")
|
||||
|
||||
**This project is under heavy development, it's not in a working state yet!**
|
||||
|
||||
## Install
|
||||
|
||||
You can download prebuilt binaries from the GitHub releases or from our [download mirrors](http://download.owncloud.com/ocis/ocs/). For instructions how to install this on your platform you should take a look at our [documentation](https://owncloud.github.io/ocis-ocs/)
|
||||
|
||||
## Development
|
||||
|
||||
Make sure you have a working Go environment, for further reference or a guide take a look at the [install instructions](http://golang.org/doc/install.html). This project requires Go >= v1.13.
|
||||
|
||||
```console
|
||||
git clone https://github.com/owncloud/ocis-ocs.git
|
||||
cd ocis-ocs
|
||||
|
||||
make generate build
|
||||
|
||||
./bin/ocis-ocs -h
|
||||
```
|
||||
|
||||
## Security
|
||||
|
||||
If you find a security issue please contact security@owncloud.com first.
|
||||
|
||||
## Contributing
|
||||
|
||||
Fork -> Patch -> Push -> Pull Request
|
||||
|
||||
## License
|
||||
|
||||
Apache-2.0
|
||||
|
||||
## Copyright
|
||||
|
||||
```console
|
||||
Copyright (c) 2019 ownCloud GmbH <https://owncloud.com>
|
||||
```
|
||||
5
ocs/changelog/0.1.0_2020-07-23/config-management.md
Normal file
5
ocs/changelog/0.1.0_2020-07-23/config-management.md
Normal file
@@ -0,0 +1,5 @@
|
||||
Enhancement: Configuration
|
||||
|
||||
Extensions should be responsible of configuring themselves. We use Viper for config loading from default paths. Environment variables **WILL** take precedence over config files.
|
||||
|
||||
https://github.com/owncloud/ocis-ocs/pull/14
|
||||
7
ocs/changelog/0.1.0_2020-07-23/initial-release.md
Normal file
7
ocs/changelog/0.1.0_2020-07-23/initial-release.md
Normal file
@@ -0,0 +1,7 @@
|
||||
Change: Initial release of basic version
|
||||
|
||||
Just prepared an initial basic version to serve OCS for the ownCloud
|
||||
Infinite Scale project. It just provides a minimal viable product to
|
||||
demonstrate the microservice pattern.
|
||||
|
||||
https://github.com/owncloud/ocis-ocs/issues/1
|
||||
7
ocs/changelog/0.1.0_2020-07-23/support-signing-key.md
Normal file
7
ocs/changelog/0.1.0_2020-07-23/support-signing-key.md
Normal file
@@ -0,0 +1,7 @@
|
||||
Enhancement: Support signing key
|
||||
|
||||
We added support for the `/v[12].php/cloud/user/signing-key` endpoint that is used by the owncloud-sdk to generate signed URLs. This allows directly downloading large files with browsers instead of using `blob://` urls, which eats memory ...
|
||||
|
||||
https://github.com/owncloud/ocis-ocs/pull/18
|
||||
https://github.com/owncloud/ocis-proxy/pull/75
|
||||
https://github.com/owncloud/owncloud-sdk/pull/504
|
||||
5
ocs/changelog/0.1.0_2020-07-23/upgrade-micro.md
Normal file
5
ocs/changelog/0.1.0_2020-07-23/upgrade-micro.md
Normal file
@@ -0,0 +1,5 @@
|
||||
Change: upgrade micro libraries
|
||||
|
||||
Updated the micro and ocis-pkg libraries to version 2.
|
||||
|
||||
https://github.com/owncloud/ocis-ocs/issues/11
|
||||
5
ocs/changelog/0.1.0_2020-07-23/use-alpine-latest.md
Normal file
5
ocs/changelog/0.1.0_2020-07-23/use-alpine-latest.md
Normal file
@@ -0,0 +1,5 @@
|
||||
Bugfix: build docker images with alpine:latest instead of alpine:edge
|
||||
|
||||
ARM builds were failing when built on alpine:edge, so we switched to alpine:latest instead.
|
||||
|
||||
https://github.com/owncloud/ocis-ocs/pull/20
|
||||
@@ -0,0 +1,7 @@
|
||||
Bugfix: Fix file descriptor leak
|
||||
|
||||
Only use a single instance of go-micro's GRPC client as it already
|
||||
does connection pooling. This prevents connection and file descriptor leaks.
|
||||
|
||||
https://github.com/owncloud/ocis-accounts/issues/79
|
||||
https://github.com/owncloud/ocis-ocs/pull/29
|
||||
5
ocs/changelog/0.2.0_2020-08-17/groups-provisioning.md
Normal file
5
ocs/changelog/0.2.0_2020-08-17/groups-provisioning.md
Normal file
@@ -0,0 +1,5 @@
|
||||
Enhancement: Add Group management for OCS Povisioning API
|
||||
|
||||
We added support for the group management related set of API calls of the provisioning API. [Reference](https://doc.owncloud.com/server/admin_manual/configuration/user/user_provisioning_api.html)
|
||||
|
||||
https://github.com/owncloud/ocis-ocs/pull/25
|
||||
5
ocs/changelog/0.2.0_2020-08-17/user-provisioning.md
Normal file
5
ocs/changelog/0.2.0_2020-08-17/user-provisioning.md
Normal file
@@ -0,0 +1,5 @@
|
||||
Enhancement: Basic Support for the User Provisioning API
|
||||
|
||||
We added support for a basic set of API calls for the user provisioning API. [Reference](https://doc.owncloud.com/server/admin_manual/configuration/user/user_provisioning_api.html)
|
||||
|
||||
https://github.com/owncloud/ocis-ocs/pull/23
|
||||
5
ocs/changelog/0.3.0_2020-08-27/add-uid-gid.md
Normal file
5
ocs/changelog/0.3.0_2020-08-27/add-uid-gid.md
Normal file
@@ -0,0 +1,5 @@
|
||||
Enhancement: Add option to create user with uidnumber and gidnumber
|
||||
|
||||
We have added an option to pass uidnumber and gidnumber to the ocis api while creating a new user
|
||||
|
||||
https://github.com/owncloud/ocis-ocs/pull/34
|
||||
@@ -0,0 +1,5 @@
|
||||
Bugfix: Mimic oc10 user enabled as string in provisioning api
|
||||
|
||||
The oc10 user provisioning API uses a string for the boolean `enabled` flag. 😭
|
||||
|
||||
https://github.com/owncloud/ocis-ocs/pull/39
|
||||
8
ocs/changelog/0.3.0_2020-08-27/signing-keys-opaqueid
Normal file
8
ocs/changelog/0.3.0_2020-08-27/signing-keys-opaqueid
Normal file
@@ -0,0 +1,8 @@
|
||||
Bugfix: Use opaque ID of a user for signing keys
|
||||
|
||||
OCIS switched from user the user's opaque ID (UUID) everywhere,
|
||||
so to keep compatible we have adjusted the signing keys endpoint
|
||||
to also use the UUID when storing and generating the keys.
|
||||
|
||||
https://github.com/owncloud/ocis/issues/436
|
||||
https://github.com/owncloud/ocis-ocs/pull/32
|
||||
8
ocs/changelog/0.3.1_2020-09-02/ocs-response-format.md
Normal file
8
ocs/changelog/0.3.1_2020-09-02/ocs-response-format.md
Normal file
@@ -0,0 +1,8 @@
|
||||
Bugfix: Add the top level response structure to json responses
|
||||
|
||||
Probably during moving the ocs code into the ocis-ocs repo the response format was changed.
|
||||
This change adds the top level response to json responses. Doing that the reponse should be compatible to the responses from OC10.
|
||||
|
||||
https://github.com/owncloud/product/issues/181
|
||||
https://github.com/owncloud/product/issues/181#issuecomment-683604168
|
||||
|
||||
5
ocs/changelog/0.3.1_2020-09-02/update-ocis-accounts.md
Normal file
5
ocs/changelog/0.3.1_2020-09-02/update-ocis-accounts.md
Normal file
@@ -0,0 +1,5 @@
|
||||
Enhancement: Update ocis-accounts
|
||||
|
||||
update ocis-accounts to v0.4.2-0.20200828150703-2ca83cf4ac20
|
||||
|
||||
https://github.com/owncloud/ocis-ocs/pull/42
|
||||
53
ocs/changelog/CHANGELOG.tmpl
Normal file
53
ocs/changelog/CHANGELOG.tmpl
Normal file
@@ -0,0 +1,53 @@
|
||||
{{ $allVersions := . }}
|
||||
{{- range $index, $changes := . }}{{ with $changes -}}
|
||||
{{ if gt (len $allVersions) 1 -}}
|
||||
# Changelog for [{{ .Version }}] ({{ .Date }})
|
||||
|
||||
The following sections list the changes in ocis-ocs {{ .Version }}.
|
||||
|
||||
{{/* creating version compare links */ -}}
|
||||
{{ $next := add1 $index -}}
|
||||
{{ if ne (len $allVersions) $next -}}
|
||||
{{ $previousVersion := (index $allVersions $next).Version -}}
|
||||
{{ if eq .Version "unreleased" -}}
|
||||
[{{ .Version }}]: https://github.com/owncloud/ocis-ocs/compare/v{{ $previousVersion }}...master
|
||||
|
||||
{{ else -}}
|
||||
[{{ .Version }}]: https://github.com/owncloud/ocis-ocs/compare/v{{ $previousVersion }}...v{{ .Version }}
|
||||
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
|
||||
{{- /* last version managed by calens, end of the loop */ -}}
|
||||
{{ if eq .Version "0.1.0" -}}
|
||||
[{{ .Version }}]: https://github.com/owncloud/ocis-ocs/compare/acd6d6e7f59d1a44bcedb4dd60564910b474c38a...v{{ .Version }}
|
||||
|
||||
{{ end -}}
|
||||
{{ else -}}
|
||||
# Changes in {{ .Version }}
|
||||
|
||||
{{ end -}}
|
||||
|
||||
## Summary
|
||||
{{ range $entry := .Entries }}{{ with $entry }}
|
||||
* {{ .Type }} - {{ .Title }}: [#{{ .PrimaryID }}]({{ .PrimaryURL }})
|
||||
{{- end }}{{ end }}
|
||||
|
||||
## Details
|
||||
{{ range $entry := .Entries }}{{ with $entry }}
|
||||
* {{ .Type }} - {{ .Title }}: [#{{ .PrimaryID }}]({{ .PrimaryURL }})
|
||||
{{ range $par := .Paragraphs }}
|
||||
{{ wrapIndent $par 80 3 }}
|
||||
{{ end -}}
|
||||
{{ range $url := .IssueURLs }}
|
||||
{{ $url -}}
|
||||
{{ end -}}
|
||||
{{ range $url := .PRURLs }}
|
||||
{{ $url -}}
|
||||
{{ end -}}
|
||||
{{ range $url := .OtherURLs }}
|
||||
{{ $url -}}
|
||||
{{ end }}
|
||||
|
||||
{{ end }}{{ end -}}
|
||||
{{ end }}{{ end -}}
|
||||
6
ocs/changelog/README.md
Normal file
6
ocs/changelog/README.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# Changelog
|
||||
|
||||
We are using [calens](https://github.com/restic/calens) to properly generate a
|
||||
changelog before we are tagging a new release. To get an idea how this could
|
||||
look like <https://github.com/restic/restic/tree/master/changelog> would be the
|
||||
best reference.
|
||||
11
ocs/changelog/TEMPLATE
Normal file
11
ocs/changelog/TEMPLATE
Normal file
@@ -0,0 +1,11 @@
|
||||
Bugfix: Fix behavior for foobar (in present tense)
|
||||
|
||||
We've fixed the behavior for foobar, a long-standing annoyance for users. The
|
||||
text should be wrapped at 80 characters length.
|
||||
|
||||
The text in the paragraphs is written in past tense. The last section is a list
|
||||
of issue URLs, PR URLs and other URLs. The first issue ID (or the first PR ID,
|
||||
in case there aren't any issue links) is used as the primary ID.
|
||||
|
||||
https://github.com/owncloud/ocis-ocs/issues/1234
|
||||
https://github.com/owncloud/ocis-ocs/pull/55555
|
||||
0
ocs/changelog/unreleased/.keep
Normal file
0
ocs/changelog/unreleased/.keep
Normal file
8
ocs/changelog/unreleased/fix-users-format.md
Normal file
8
ocs/changelog/unreleased/fix-users-format.md
Normal file
@@ -0,0 +1,8 @@
|
||||
Bugfix: match the user response to the OC10 format
|
||||
|
||||
The user response contained the field `displayname` but for certain responses
|
||||
the field `display-name` is expected. The field `display-name` was added and
|
||||
now both fields are returned to the client.
|
||||
|
||||
https://github.com/owncloud/product/issues/181
|
||||
https://github.com/owncloud/ocis-ocs/pull/61
|
||||
13
ocs/cmd/ocis-ocs/main.go
Normal file
13
ocs/cmd/ocis-ocs/main.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/owncloud/ocis-ocs/pkg/command"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := command.Execute(); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
3
ocs/config/.gitignore
vendored
Normal file
3
ocs/config/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
*
|
||||
!example.json
|
||||
!example.yml
|
||||
18
ocs/config/example.json
Normal file
18
ocs/config/example.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"debug": {
|
||||
"addr": "0.0.0.0:9114",
|
||||
"token": "",
|
||||
"pprof": false,
|
||||
"zpages": false
|
||||
},
|
||||
"http": {
|
||||
"addr": "0.0.0.0:9110"
|
||||
},
|
||||
"tracing": {
|
||||
"enabled": false,
|
||||
"type": "jaeger",
|
||||
"endpoint": "localhost:6831",
|
||||
"collector": "http://localhost:14268/api/traces",
|
||||
"service": "ocs"
|
||||
}
|
||||
}
|
||||
18
ocs/config/example.yml
Normal file
18
ocs/config/example.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
debug:
|
||||
addr: 0.0.0.0:9114
|
||||
token:
|
||||
pprof: false
|
||||
zpages: false
|
||||
|
||||
http:
|
||||
addr: 0.0.0.0:9110
|
||||
|
||||
tracing:
|
||||
enabled: false
|
||||
type: jaeger
|
||||
endpoint: localhost:6831
|
||||
collector: http://localhost:14268/api/traces
|
||||
service: ocs
|
||||
|
||||
...
|
||||
19
ocs/docker/Dockerfile.linux.amd64
Normal file
19
ocs/docker/Dockerfile.linux.amd64
Normal file
@@ -0,0 +1,19 @@
|
||||
FROM amd64/alpine:latest
|
||||
|
||||
RUN apk update && \
|
||||
apk upgrade && \
|
||||
apk add ca-certificates mailcap && \
|
||||
rm -rf /var/cache/apk/* && \
|
||||
echo 'hosts: files dns' >| /etc/nsswitch.conf
|
||||
|
||||
LABEL maintainer="ownCloud GmbH <devops@owncloud.com>" \
|
||||
org.label-schema.name="oCIS OCS" \
|
||||
org.label-schema.vendor="ownCloud GmbH" \
|
||||
org.label-schema.schema-version="1.0"
|
||||
|
||||
EXPOSE 9110 9114
|
||||
|
||||
ENTRYPOINT ["/usr/bin/ocis-ocs"]
|
||||
CMD ["server"]
|
||||
|
||||
COPY bin/ocis-ocs /usr/bin/ocis-ocs
|
||||
19
ocs/docker/Dockerfile.linux.arm
Normal file
19
ocs/docker/Dockerfile.linux.arm
Normal file
@@ -0,0 +1,19 @@
|
||||
FROM arm32v6/alpine:latest
|
||||
|
||||
RUN apk update && \
|
||||
apk upgrade && \
|
||||
apk add ca-certificates mailcap && \
|
||||
rm -rf /var/cache/apk/* && \
|
||||
echo 'hosts: files dns' >| /etc/nsswitch.conf
|
||||
|
||||
LABEL maintainer="ownCloud GmbH <devops@owncloud.com>" \
|
||||
org.label-schema.name="oCIS OCS" \
|
||||
org.label-schema.vendor="ownCloud GmbH" \
|
||||
org.label-schema.schema-version="1.0"
|
||||
|
||||
EXPOSE 9110 9114
|
||||
|
||||
ENTRYPOINT ["/usr/bin/ocis-ocs"]
|
||||
CMD ["server"]
|
||||
|
||||
COPY bin/ocis-ocs /usr/bin/ocis-ocs
|
||||
19
ocs/docker/Dockerfile.linux.arm64
Normal file
19
ocs/docker/Dockerfile.linux.arm64
Normal file
@@ -0,0 +1,19 @@
|
||||
FROM arm64v8/alpine:latest
|
||||
|
||||
RUN apk update && \
|
||||
apk upgrade && \
|
||||
apk add ca-certificates mailcap && \
|
||||
rm -rf /var/cache/apk/* && \
|
||||
echo 'hosts: files dns' >| /etc/nsswitch.conf
|
||||
|
||||
LABEL maintainer="ownCloud GmbH <devops@owncloud.com>" \
|
||||
org.label-schema.name="oCIS OCS" \
|
||||
org.label-schema.vendor="ownCloud GmbH" \
|
||||
org.label-schema.schema-version="1.0"
|
||||
|
||||
EXPOSE 9110 9114
|
||||
|
||||
ENTRYPOINT ["/usr/bin/ocis-ocs"]
|
||||
CMD ["server"]
|
||||
|
||||
COPY bin/ocis-ocs /usr/bin/ocis-ocs
|
||||
22
ocs/docker/manifest.tmpl
Normal file
22
ocs/docker/manifest.tmpl
Normal file
@@ -0,0 +1,22 @@
|
||||
image: owncloud/ocis-ocs:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
|
||||
{{#if build.tags}}
|
||||
tags:
|
||||
{{#each build.tags}}
|
||||
- {{this}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
manifests:
|
||||
- image: owncloud/ocis-ocs:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64
|
||||
platform:
|
||||
architecture: amd64
|
||||
os: linux
|
||||
- image: owncloud/ocis-ocs:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64
|
||||
platform:
|
||||
architecture: arm64
|
||||
variant: v8
|
||||
os: linux
|
||||
- image: owncloud/ocis-ocs:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm
|
||||
platform:
|
||||
architecture: arm
|
||||
variant: v6
|
||||
os: linux
|
||||
10
ocs/docs/_index.md
Normal file
10
ocs/docs/_index.md
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
title: "Ocs"
|
||||
date: 2018-05-02T00:00:00+00:00
|
||||
weight: 10
|
||||
geekdocRepo: https://github.com/owncloud/ocis-ocs
|
||||
geekdocEditPath: edit/master/docs
|
||||
geekdocFilePath: _index.md
|
||||
---
|
||||
|
||||
This service provides the OCS API which is required by some ownCloud clients.
|
||||
28
ocs/docs/building.md
Normal file
28
ocs/docs/building.md
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
title: "Building"
|
||||
date: 2018-05-02T00:00:00+00:00
|
||||
weight: 30
|
||||
geekdocRepo: https://github.com/owncloud/ocis-ocs
|
||||
geekdocEditPath: edit/master/docs
|
||||
geekdocFilePath: building.md
|
||||
---
|
||||
|
||||
{{< toc >}}
|
||||
|
||||
As this project is built with Go, so you need to install that first. The installation of Go is out of the scope of this document, please follow the official documentation for [Go](https://golang.org/doc/install), to build this project you have to install Go >= v1.12. After the installation of the required tools you need to get the sources:
|
||||
|
||||
{{< highlight txt >}}
|
||||
git clone https://github.com/owncloud/ocis-ocs.git
|
||||
cd ocis-ocs
|
||||
{{< / highlight >}}
|
||||
|
||||
All required tool besides Go itself and make are bundled or getting automatically installed within the `GOPATH`. All commands to build this project are part of our `Makefile`.
|
||||
|
||||
## Backend
|
||||
|
||||
{{< highlight txt >}}
|
||||
make generate
|
||||
make build
|
||||
{{< / highlight >}}
|
||||
|
||||
Finally you should have the binary within the `bin/` folder now, give it a try with `./bin/ocis-ocs -h` to see all available options.
|
||||
154
ocs/docs/getting-started.md
Normal file
154
ocs/docs/getting-started.md
Normal file
@@ -0,0 +1,154 @@
|
||||
---
|
||||
title: "Getting Started"
|
||||
date: 2018-05-02T00:00:00+00:00
|
||||
weight: 10
|
||||
geekdocRepo: https://github.com/owncloud/ocis-ocs
|
||||
geekdocEditPath: edit/master/docs
|
||||
geekdocFilePath: getting-started.md
|
||||
---
|
||||
|
||||
{{< toc >}}
|
||||
|
||||
## Installation
|
||||
|
||||
So far we are offering two different variants for the installation. You can choose between [Docker](https://www.docker.com/) or pre-built binaries which are stored on our download mirrors and GitHub releases. Maybe we will also provide system packages for the major distributions later if we see the need for it.
|
||||
|
||||
### Docker
|
||||
|
||||
Docker images for ocis-ocs are hosted on https://hub.docker.com/r/owncloud/ocis-ocs.
|
||||
|
||||
The `latest` tag always reflects the current master branch.
|
||||
|
||||
```console
|
||||
docker pull owncloud/ocis-ocs
|
||||
```
|
||||
|
||||
### Binaries
|
||||
|
||||
The pre-built binaries for different platforms are downloadable at https://download.owncloud.com/ocis/ocs/ . Specific releases are organized in separate folders. They are in sync which every release tag on GitHub. The binaries from the current master branch can be found in https://download.owncloud.com/ocis/ocs/testing/
|
||||
|
||||
```console
|
||||
curl https://download.owncloud.com/ocis/ocis-ocs/1.0.0-beta1/ocis-ocs-1.0.0-beta1-darwin-amd64 --output ocis-ocs
|
||||
chmod +x ocis-ocs
|
||||
./ocis-ocs server
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
The program provides a few sub-commands on execution. The available configuration methods have already been mentioned above. Generally you can always see a formated help output if you execute the binary via `ocis-ocs --help`.
|
||||
|
||||
### Server
|
||||
|
||||
The server command is used to start the http and debug server on two addresses within a single process. The http server is serving the general webservice while the debug server is used for health check, readiness check and to server the metrics mentioned below. For further help please execute:
|
||||
|
||||
{{< highlight txt >}}
|
||||
ocis-ocs server --help
|
||||
{{< / highlight >}}
|
||||
|
||||
### Health
|
||||
|
||||
The health command is used to execute a health check, if the exit code equals zero the service should be up and running, if the exist code is greater than zero the service is not in a healthy state. Generally this command is used within our Docker containers, it could also be used within Kubernetes.
|
||||
|
||||
{{< highlight txt >}}
|
||||
ocis-ocs health --help
|
||||
{{< / highlight >}}
|
||||
|
||||
## Metrics
|
||||
|
||||
This service provides some [Prometheus](https://prometheus.io/) metrics through the debug endpoint, you can optionally secure the metrics endpoint by some random token, which got to be configured through one of the flag `--debug-token` or the environment variable `OCS_DEBUG_TOKEN` mentioned above. By default the metrics endpoint is bound to `http://0.0.0.0:9114/metrics`.
|
||||
|
||||
go_gc_duration_seconds
|
||||
: A summary of the GC invocation durations
|
||||
|
||||
go_gc_duration_seconds_sum
|
||||
: A summary of the GC invocation durations
|
||||
|
||||
go_gc_duration_seconds_count
|
||||
: A summary of the GC invocation durations
|
||||
|
||||
go_goroutines
|
||||
: Number of goroutines that currently exist
|
||||
|
||||
go_info
|
||||
: Information about the Go environment
|
||||
|
||||
go_memstats_alloc_bytes
|
||||
: Number of bytes allocated and still in use
|
||||
|
||||
go_memstats_alloc_bytes_total
|
||||
: Total number of bytes allocated, even if freed
|
||||
|
||||
go_memstats_buck_hash_sys_bytes
|
||||
: Number of bytes used by the profiling bucket hash table
|
||||
|
||||
go_memstats_frees_total
|
||||
: Total number of frees
|
||||
|
||||
go_memstats_gc_cpu_fraction
|
||||
: The fraction of this program's available CPU time used by the GC since the program started
|
||||
|
||||
go_memstats_gc_sys_bytes
|
||||
: Number of bytes used for garbage collection system metadata
|
||||
|
||||
go_memstats_heap_alloc_bytes
|
||||
: Number of heap bytes allocated and still in use
|
||||
|
||||
go_memstats_heap_idle_bytes
|
||||
: Number of heap bytes waiting to be used
|
||||
|
||||
go_memstats_heap_inuse_bytes
|
||||
: Number of heap bytes that are in use
|
||||
|
||||
go_memstats_heap_objects
|
||||
: Number of allocated objects
|
||||
|
||||
go_memstats_heap_released_bytes
|
||||
: Number of heap bytes released to OS
|
||||
|
||||
go_memstats_heap_sys_bytes
|
||||
: Number of heap bytes obtained from system
|
||||
|
||||
go_memstats_last_gc_time_seconds
|
||||
: Number of seconds since 1970 of last garbage collection
|
||||
|
||||
go_memstats_lookups_total
|
||||
: Total number of pointer lookups
|
||||
|
||||
go_memstats_mallocs_total
|
||||
: Total number of mallocs
|
||||
|
||||
go_memstats_mcache_inuse_bytes
|
||||
: Number of bytes in use by mcache structures
|
||||
|
||||
go_memstats_mcache_sys_bytes
|
||||
: Number of bytes used for mcache structures obtained from system
|
||||
|
||||
go_memstats_mspan_inuse_bytes
|
||||
: Number of bytes in use by mspan structures
|
||||
|
||||
go_memstats_mspan_sys_bytes
|
||||
: Number of bytes used for mspan structures obtained from system
|
||||
|
||||
go_memstats_next_gc_bytes
|
||||
: Number of heap bytes when next garbage collection will take place
|
||||
|
||||
go_memstats_other_sys_bytes
|
||||
: Number of bytes used for other system allocations
|
||||
|
||||
go_memstats_stack_inuse_bytes
|
||||
: Number of bytes in use by the stack allocator
|
||||
|
||||
go_memstats_stack_sys_bytes
|
||||
: Number of bytes obtained from system for stack allocator
|
||||
|
||||
go_memstats_sys_bytes
|
||||
: Number of bytes obtained from system
|
||||
|
||||
go_threads
|
||||
: Number of OS threads created
|
||||
|
||||
promhttp_metric_handler_requests_in_flight
|
||||
: Current number of scrapes being served
|
||||
|
||||
promhttp_metric_handler_requests_total
|
||||
: Total number of scrapes by HTTP status code
|
||||
10
ocs/docs/license.md
Normal file
10
ocs/docs/license.md
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
title: "License"
|
||||
date: 2018-05-02T00:00:00+00:00
|
||||
weight: 40
|
||||
geekdocRepo: https://github.com/owncloud/ocis-ocs
|
||||
geekdocEditPath: edit/master/docs
|
||||
geekdocFilePath: license.md
|
||||
---
|
||||
|
||||
This project is licensed under the [Apache 2.0](https://github.com/owncloud/ocis-ocs/blob/master/LICENSE) license. For the license of the used libraries you have to check the respective sources.
|
||||
32
ocs/go.mod
Normal file
32
ocs/go.mod
Normal file
@@ -0,0 +1,32 @@
|
||||
module github.com/owncloud/ocis-ocs
|
||||
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
contrib.go.opencensus.io/exporter/jaeger v0.2.1
|
||||
contrib.go.opencensus.io/exporter/ocagent v0.7.0
|
||||
contrib.go.opencensus.io/exporter/zipkin v0.1.1
|
||||
github.com/UnnoTed/fileb0x v1.1.4
|
||||
github.com/cs3org/reva v1.1.0
|
||||
github.com/go-chi/chi v4.1.2+incompatible
|
||||
github.com/go-chi/render v1.0.1
|
||||
github.com/golang/protobuf v1.4.2
|
||||
github.com/google/uuid v1.1.2 // indirect
|
||||
github.com/micro/cli/v2 v2.1.2
|
||||
github.com/micro/go-micro/v2 v2.9.1
|
||||
github.com/oklog/run v1.1.0
|
||||
github.com/openzipkin/zipkin-go v0.2.2
|
||||
github.com/owncloud/ocis-accounts v0.4.2-0.20200828150703-2ca83cf4ac20
|
||||
github.com/owncloud/ocis-hello v0.1.0-alpha1 // indirect
|
||||
github.com/owncloud/ocis-pkg/v2 v2.4.0
|
||||
github.com/owncloud/ocis-settings v0.3.2-0.20200828130413-0cc0f5bf26fe
|
||||
github.com/owncloud/ocis-store v0.0.0-20200716140351-f9670592fb7b
|
||||
github.com/restic/calens v0.2.0
|
||||
github.com/spf13/viper v1.7.0
|
||||
github.com/stretchr/testify v1.6.1
|
||||
go.opencensus.io v0.22.4
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a // indirect
|
||||
google.golang.org/protobuf v1.25.0
|
||||
)
|
||||
|
||||
replace google.golang.org/grpc => google.golang.org/grpc v1.26.0
|
||||
1954
ocs/go.sum
Normal file
1954
ocs/go.sum
Normal file
File diff suppressed because it is too large
Load Diff
49
ocs/pkg/command/health.go
Normal file
49
ocs/pkg/command/health.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/owncloud/ocis-ocs/pkg/config"
|
||||
"github.com/owncloud/ocis-ocs/pkg/flagset"
|
||||
)
|
||||
|
||||
// Health is the entrypoint for the health command.
|
||||
func Health(cfg *config.Config) *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "health",
|
||||
Usage: "Check health status",
|
||||
Flags: flagset.HealthWithConfig(cfg),
|
||||
Action: func(c *cli.Context) error {
|
||||
logger := NewLogger(cfg)
|
||||
|
||||
resp, err := http.Get(
|
||||
fmt.Sprintf(
|
||||
"http://%s/healthz",
|
||||
cfg.Debug.Addr,
|
||||
),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
logger.Fatal().
|
||||
Err(err).
|
||||
Msg("Failed to request health check")
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
logger.Fatal().
|
||||
Int("code", resp.StatusCode).
|
||||
Msg("Health seems to be in bad state")
|
||||
}
|
||||
|
||||
logger.Debug().
|
||||
Int("code", resp.StatusCode).
|
||||
Msg("Health got a good state")
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
108
ocs/pkg/command/root.go
Normal file
108
ocs/pkg/command/root.go
Normal file
@@ -0,0 +1,108 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/owncloud/ocis-ocs/pkg/config"
|
||||
"github.com/owncloud/ocis-ocs/pkg/flagset"
|
||||
"github.com/owncloud/ocis-ocs/pkg/version"
|
||||
"github.com/owncloud/ocis-pkg/v2/log"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// Execute is the entry point for the ocis-ocs command.
|
||||
func Execute() error {
|
||||
cfg := config.New()
|
||||
|
||||
app := &cli.App{
|
||||
Name: "ocis-ocs",
|
||||
Version: version.String,
|
||||
Usage: "Serve OCS API for oCIS",
|
||||
Compiled: version.Compiled(),
|
||||
|
||||
Authors: []*cli.Author{
|
||||
{
|
||||
Name: "ownCloud GmbH",
|
||||
Email: "support@owncloud.com",
|
||||
},
|
||||
},
|
||||
|
||||
Flags: flagset.RootWithConfig(cfg),
|
||||
|
||||
Before: func(c *cli.Context) error {
|
||||
return ParseConfig(c, cfg)
|
||||
},
|
||||
|
||||
Commands: []*cli.Command{
|
||||
Server(cfg),
|
||||
Health(cfg),
|
||||
},
|
||||
}
|
||||
|
||||
cli.HelpFlag = &cli.BoolFlag{
|
||||
Name: "help,h",
|
||||
Usage: "Show the help",
|
||||
}
|
||||
|
||||
cli.VersionFlag = &cli.BoolFlag{
|
||||
Name: "version,v",
|
||||
Usage: "Print the version",
|
||||
}
|
||||
|
||||
return app.Run(os.Args)
|
||||
}
|
||||
|
||||
// NewLogger initializes a service-specific logger instance.
|
||||
func NewLogger(cfg *config.Config) log.Logger {
|
||||
return log.NewLogger(
|
||||
log.Name("ocs"),
|
||||
log.Level(cfg.Log.Level),
|
||||
log.Pretty(cfg.Log.Pretty),
|
||||
log.Color(cfg.Log.Color),
|
||||
)
|
||||
}
|
||||
|
||||
// ParseConfig reads ocs configuration from fs.
|
||||
func ParseConfig(c *cli.Context, cfg *config.Config) error {
|
||||
logger := NewLogger(cfg)
|
||||
|
||||
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||
viper.SetEnvPrefix("OCS")
|
||||
viper.AutomaticEnv()
|
||||
|
||||
if c.IsSet("config-file") {
|
||||
viper.SetConfigFile(c.String("config-file"))
|
||||
} else {
|
||||
viper.SetConfigName("ocs")
|
||||
|
||||
viper.AddConfigPath("/etc/ocis")
|
||||
viper.AddConfigPath("$HOME/.ocis")
|
||||
viper.AddConfigPath("./config")
|
||||
}
|
||||
|
||||
if err := viper.ReadInConfig(); err != nil {
|
||||
switch err.(type) {
|
||||
case viper.ConfigFileNotFoundError:
|
||||
logger.Info().
|
||||
Msg("Continue without config")
|
||||
case viper.UnsupportedConfigError:
|
||||
logger.Fatal().
|
||||
Err(err).
|
||||
Msg("Unsupported config type")
|
||||
default:
|
||||
logger.Fatal().
|
||||
Err(err).
|
||||
Msg("Failed to read config")
|
||||
}
|
||||
}
|
||||
|
||||
if err := viper.Unmarshal(&cfg); err != nil {
|
||||
logger.Fatal().
|
||||
Err(err).
|
||||
Msg("Failed to parse config")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
223
ocs/pkg/command/server.go
Normal file
223
ocs/pkg/command/server.go
Normal file
@@ -0,0 +1,223 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"contrib.go.opencensus.io/exporter/jaeger"
|
||||
"contrib.go.opencensus.io/exporter/ocagent"
|
||||
"contrib.go.opencensus.io/exporter/zipkin"
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/oklog/run"
|
||||
openzipkin "github.com/openzipkin/zipkin-go"
|
||||
zipkinhttp "github.com/openzipkin/zipkin-go/reporter/http"
|
||||
"github.com/owncloud/ocis-ocs/pkg/config"
|
||||
"github.com/owncloud/ocis-ocs/pkg/flagset"
|
||||
"github.com/owncloud/ocis-ocs/pkg/metrics"
|
||||
"github.com/owncloud/ocis-ocs/pkg/server/debug"
|
||||
"github.com/owncloud/ocis-ocs/pkg/server/http"
|
||||
"go.opencensus.io/stats/view"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
// Server is the entrypoint for the server command.
|
||||
func Server(cfg *config.Config) *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "server",
|
||||
Usage: "Start integrated server",
|
||||
Flags: flagset.ServerWithConfig(cfg),
|
||||
Before: func(c *cli.Context) error {
|
||||
if cfg.HTTP.Root != "/" {
|
||||
cfg.HTTP.Root = strings.TrimSuffix(cfg.HTTP.Root, "/")
|
||||
}
|
||||
|
||||
return ParseConfig(c, cfg)
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
logger := NewLogger(cfg)
|
||||
|
||||
if cfg.Tracing.Enabled {
|
||||
switch t := cfg.Tracing.Type; t {
|
||||
case "agent":
|
||||
exporter, err := ocagent.NewExporter(
|
||||
ocagent.WithReconnectionPeriod(5*time.Second),
|
||||
ocagent.WithAddress(cfg.Tracing.Endpoint),
|
||||
ocagent.WithServiceName(cfg.Tracing.Service),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
logger.Error().
|
||||
Err(err).
|
||||
Str("endpoint", cfg.Tracing.Endpoint).
|
||||
Str("collector", cfg.Tracing.Collector).
|
||||
Msg("Failed to create agent tracing")
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
trace.RegisterExporter(exporter)
|
||||
view.RegisterExporter(exporter)
|
||||
|
||||
case "jaeger":
|
||||
exporter, err := jaeger.NewExporter(
|
||||
jaeger.Options{
|
||||
AgentEndpoint: cfg.Tracing.Endpoint,
|
||||
CollectorEndpoint: cfg.Tracing.Collector,
|
||||
ServiceName: cfg.Tracing.Service,
|
||||
},
|
||||
)
|
||||
|
||||
logger.Info().
|
||||
Str("collector", cfg.Tracing.Collector).
|
||||
Msg("Trace collector added")
|
||||
|
||||
if err != nil {
|
||||
logger.Error().
|
||||
Err(err).
|
||||
Str("endpoint", cfg.Tracing.Endpoint).
|
||||
Str("collector", cfg.Tracing.Collector).
|
||||
Msg("Failed to create jaeger tracing")
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
trace.RegisterExporter(exporter)
|
||||
|
||||
case "zipkin":
|
||||
endpoint, err := openzipkin.NewEndpoint(
|
||||
cfg.Tracing.Service,
|
||||
cfg.Tracing.Endpoint,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
logger.Error().
|
||||
Err(err).
|
||||
Str("endpoint", cfg.Tracing.Endpoint).
|
||||
Str("collector", cfg.Tracing.Collector).
|
||||
Msg("Failed to create zipkin tracing")
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
exporter := zipkin.NewExporter(
|
||||
zipkinhttp.NewReporter(
|
||||
cfg.Tracing.Collector,
|
||||
),
|
||||
endpoint,
|
||||
)
|
||||
|
||||
trace.RegisterExporter(exporter)
|
||||
|
||||
default:
|
||||
logger.Warn().
|
||||
Str("type", t).
|
||||
Msg("Unknown tracing backend")
|
||||
}
|
||||
|
||||
trace.ApplyConfig(
|
||||
trace.Config{
|
||||
DefaultSampler: trace.AlwaysSample(),
|
||||
},
|
||||
)
|
||||
} else {
|
||||
logger.Debug().
|
||||
Msg("Tracing is not enabled")
|
||||
}
|
||||
|
||||
var (
|
||||
gr = run.Group{}
|
||||
ctx, cancel = context.WithCancel(context.Background())
|
||||
metrics = metrics.New()
|
||||
)
|
||||
|
||||
defer cancel()
|
||||
|
||||
{
|
||||
server, err := http.Server(
|
||||
http.Logger(logger),
|
||||
http.Context(ctx),
|
||||
http.Config(cfg),
|
||||
http.Metrics(metrics),
|
||||
http.Flags(flagset.RootWithConfig(config.New())),
|
||||
http.Flags(flagset.ServerWithConfig(config.New())),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
logger.Info().
|
||||
Err(err).
|
||||
Str("transport", "http").
|
||||
Msg("Failed to initialize server")
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
gr.Add(func() error {
|
||||
return server.Run()
|
||||
}, func(_ error) {
|
||||
logger.Info().
|
||||
Str("transport", "http").
|
||||
Msg("Shutting down server")
|
||||
|
||||
cancel()
|
||||
})
|
||||
}
|
||||
|
||||
{
|
||||
server, err := debug.Server(
|
||||
debug.Logger(logger),
|
||||
debug.Context(ctx),
|
||||
debug.Config(cfg),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
logger.Info().
|
||||
Err(err).
|
||||
Str("transport", "debug").
|
||||
Msg("Failed to initialize server")
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
gr.Add(func() error {
|
||||
return server.ListenAndServe()
|
||||
}, func(_ error) {
|
||||
ctx, timeout := context.WithTimeout(ctx, 5*time.Second)
|
||||
|
||||
defer timeout()
|
||||
defer cancel()
|
||||
|
||||
if err := server.Shutdown(ctx); err != nil {
|
||||
logger.Info().
|
||||
Err(err).
|
||||
Str("transport", "debug").
|
||||
Msg("Failed to shutdown server")
|
||||
} else {
|
||||
logger.Info().
|
||||
Str("transport", "debug").
|
||||
Msg("Shutting down server")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
{
|
||||
stop := make(chan os.Signal, 1)
|
||||
|
||||
gr.Add(func() error {
|
||||
signal.Notify(stop, os.Interrupt)
|
||||
|
||||
<-stop
|
||||
|
||||
return nil
|
||||
}, func(err error) {
|
||||
close(stop)
|
||||
cancel()
|
||||
})
|
||||
}
|
||||
|
||||
return gr.Run()
|
||||
},
|
||||
}
|
||||
}
|
||||
52
ocs/pkg/config/config.go
Normal file
52
ocs/pkg/config/config.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package config
|
||||
|
||||
// Log defines the available logging configuration.
|
||||
type Log struct {
|
||||
Level string
|
||||
Pretty bool
|
||||
Color bool
|
||||
}
|
||||
|
||||
// Debug defines the available debug configuration.
|
||||
type Debug struct {
|
||||
Addr string
|
||||
Token string
|
||||
Pprof bool
|
||||
Zpages bool
|
||||
}
|
||||
|
||||
// HTTP defines the available http configuration.
|
||||
type HTTP struct {
|
||||
Addr string
|
||||
Namespace string
|
||||
Root string
|
||||
}
|
||||
|
||||
// Tracing defines the available tracing configuration.
|
||||
type Tracing struct {
|
||||
Enabled bool
|
||||
Type string
|
||||
Endpoint string
|
||||
Collector string
|
||||
Service string
|
||||
}
|
||||
|
||||
// TokenManager is the config for using the reva token manager
|
||||
type TokenManager struct {
|
||||
JWTSecret string
|
||||
}
|
||||
|
||||
// Config combines all available configuration parts.
|
||||
type Config struct {
|
||||
File string
|
||||
Log Log
|
||||
Debug Debug
|
||||
HTTP HTTP
|
||||
Tracing Tracing
|
||||
TokenManager TokenManager
|
||||
}
|
||||
|
||||
// New initializes a new configuration with or without defaults.
|
||||
func New() *Config {
|
||||
return &Config{}
|
||||
}
|
||||
149
ocs/pkg/flagset/flagset.go
Normal file
149
ocs/pkg/flagset/flagset.go
Normal file
@@ -0,0 +1,149 @@
|
||||
package flagset
|
||||
|
||||
import (
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/owncloud/ocis-ocs/pkg/config"
|
||||
)
|
||||
|
||||
// RootWithConfig applies cfg to the root flagset
|
||||
func RootWithConfig(cfg *config.Config) []cli.Flag {
|
||||
return []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "config-file",
|
||||
Value: "",
|
||||
Usage: "Path to config file",
|
||||
EnvVars: []string{"OCS_CONFIG_FILE"},
|
||||
Destination: &cfg.File,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "log-level",
|
||||
Value: "info",
|
||||
Usage: "Set logging level",
|
||||
EnvVars: []string{"OCS_LOG_LEVEL"},
|
||||
Destination: &cfg.Log.Level,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "log-pretty",
|
||||
Value: true,
|
||||
Usage: "Enable pretty logging",
|
||||
EnvVars: []string{"OCS_LOG_PRETTY"},
|
||||
Destination: &cfg.Log.Pretty,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "log-color",
|
||||
Value: true,
|
||||
Usage: "Enable colored logging",
|
||||
EnvVars: []string{"OCS_LOG_COLOR"},
|
||||
Destination: &cfg.Log.Color,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// HealthWithConfig applies cfg to the root flagset
|
||||
func HealthWithConfig(cfg *config.Config) []cli.Flag {
|
||||
return []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "debug-addr",
|
||||
Value: "0.0.0.0:9114",
|
||||
Usage: "Address to debug endpoint",
|
||||
EnvVars: []string{"OCS_DEBUG_ADDR"},
|
||||
Destination: &cfg.Debug.Addr,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ServerWithConfig applies cfg to the root flagset
|
||||
func ServerWithConfig(cfg *config.Config) []cli.Flag {
|
||||
return []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "tracing-enabled",
|
||||
Value: false,
|
||||
Usage: "Enable sending traces",
|
||||
EnvVars: []string{"OCS_TRACING_ENABLED"},
|
||||
Destination: &cfg.Tracing.Enabled,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "tracing-type",
|
||||
Value: "jaeger",
|
||||
Usage: "Tracing backend type",
|
||||
EnvVars: []string{"OCS_TRACING_TYPE"},
|
||||
Destination: &cfg.Tracing.Type,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "tracing-endpoint",
|
||||
Value: "",
|
||||
Usage: "Endpoint for the agent",
|
||||
EnvVars: []string{"OCS_TRACING_ENDPOINT"},
|
||||
Destination: &cfg.Tracing.Endpoint,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "tracing-collector",
|
||||
Value: "",
|
||||
Usage: "Endpoint for the collector",
|
||||
EnvVars: []string{"OCS_TRACING_COLLECTOR"},
|
||||
Destination: &cfg.Tracing.Collector,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "tracing-service",
|
||||
Value: "ocs",
|
||||
Usage: "Service name for tracing",
|
||||
EnvVars: []string{"OCS_TRACING_SERVICE"},
|
||||
Destination: &cfg.Tracing.Service,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "debug-addr",
|
||||
Value: "0.0.0.0:9114",
|
||||
Usage: "Address to bind debug server",
|
||||
EnvVars: []string{"OCS_DEBUG_ADDR"},
|
||||
Destination: &cfg.Debug.Addr,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "debug-token",
|
||||
Value: "",
|
||||
Usage: "Token to grant metrics access",
|
||||
EnvVars: []string{"OCS_DEBUG_TOKEN"},
|
||||
Destination: &cfg.Debug.Token,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "debug-pprof",
|
||||
Usage: "Enable pprof debugging",
|
||||
EnvVars: []string{"OCS_DEBUG_PPROF"},
|
||||
Destination: &cfg.Debug.Pprof,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "debug-zpages",
|
||||
Usage: "Enable zpages debugging",
|
||||
EnvVars: []string{"OCS_DEBUG_ZPAGES"},
|
||||
Destination: &cfg.Debug.Zpages,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "http-addr",
|
||||
Value: "0.0.0.0:9110",
|
||||
Usage: "Address to bind http server",
|
||||
EnvVars: []string{"OCS_HTTP_ADDR"},
|
||||
Destination: &cfg.HTTP.Addr,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "http-namespace",
|
||||
Value: "com.owncloud.web",
|
||||
Usage: "Set the base namespace for the http namespace",
|
||||
EnvVars: []string{"OCS_NAMESPACE"},
|
||||
Destination: &cfg.HTTP.Namespace,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "http-root",
|
||||
Value: "/ocs",
|
||||
Usage: "Root path of http server",
|
||||
EnvVars: []string{"OCS_HTTP_ROOT"},
|
||||
Destination: &cfg.HTTP.Root,
|
||||
},
|
||||
|
||||
&cli.StringFlag{
|
||||
Name: "jwt-secret",
|
||||
Value: "Pive-Fumkiu4",
|
||||
Usage: "Used to dismantle the access token, should equal reva's jwt-secret",
|
||||
EnvVars: []string{"OCS_JWT_SECRET"},
|
||||
Destination: &cfg.TokenManager.JWTSecret,
|
||||
},
|
||||
}
|
||||
}
|
||||
32
ocs/pkg/metrics/metrics.go
Normal file
32
ocs/pkg/metrics/metrics.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package metrics
|
||||
|
||||
var (
|
||||
// Namespace defines the namespace for the defines metrics.
|
||||
Namespace = "ocis"
|
||||
|
||||
// Subsystem defines the subsystem for the defines metrics.
|
||||
Subsystem = "ocs"
|
||||
)
|
||||
|
||||
// Metrics defines the available metrics of this service.
|
||||
type Metrics struct {
|
||||
// Counter *prometheus.CounterVec
|
||||
}
|
||||
|
||||
// New initializes the available metrics.
|
||||
func New() *Metrics {
|
||||
m := &Metrics{
|
||||
// Counter: prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||
// Namespace: Namespace,
|
||||
// Subsystem: Subsystem,
|
||||
// Name: "greet_total",
|
||||
// Help: "How many greeting requests processed",
|
||||
// }, []string{}),
|
||||
}
|
||||
|
||||
// prometheus.Register(
|
||||
// m.Counter,
|
||||
// )
|
||||
|
||||
return m
|
||||
}
|
||||
40
ocs/pkg/middleware/accesstoken.go
Normal file
40
ocs/pkg/middleware/accesstoken.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/cs3org/reva/pkg/token/manager/jwt"
|
||||
"github.com/cs3org/reva/pkg/user"
|
||||
)
|
||||
|
||||
// AccessToken middleware is used to set the user from an x-access-token to the context
|
||||
func AccessToken(opts ...Option) func(next http.Handler) http.Handler {
|
||||
opt := newOptions(opts...)
|
||||
|
||||
return func(next http.Handler) http.Handler {
|
||||
// TODO: handle error
|
||||
tokenManager, err := jwt.New(map[string]interface{}{
|
||||
"secret": opt.TokenManagerConfig.JWTSecret,
|
||||
"expires": int64(60),
|
||||
})
|
||||
if err != nil {
|
||||
opt.Logger.Fatal().Err(err).Msgf("Could not initialize token-manager")
|
||||
}
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
token := r.Header.Get("x-access-token")
|
||||
if token != "" {
|
||||
u, err := tokenManager.DismantleToken(r.Context(), token)
|
||||
if err != nil {
|
||||
opt.Logger.Error().Err(err).Msg("could not dismantle token")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
// store user in context for request
|
||||
r = r.WithContext(user.ContextSetUser(r.Context(), u))
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
24
ocs/pkg/middleware/format.go
Normal file
24
ocs/pkg/middleware/format.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/render"
|
||||
)
|
||||
|
||||
// OCSFormatCtx middleware is used to determine the content type from
|
||||
// the format URL parameter passed in an ocs request. Defaults to XML
|
||||
func OCSFormatCtx(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Query().Get("format") {
|
||||
case "", "xml":
|
||||
r.Header.Set("Accept", "application/xml")
|
||||
r = r.WithContext(context.WithValue(r.Context(), render.ContentTypeCtxKey, render.ContentTypeXML))
|
||||
case "json":
|
||||
r.Header.Set("Accept", "application/json")
|
||||
r = r.WithContext(context.WithValue(r.Context(), render.ContentTypeCtxKey, render.ContentTypeJSON))
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
42
ocs/pkg/middleware/options.go
Normal file
42
ocs/pkg/middleware/options.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/owncloud/ocis-ocs/pkg/config"
|
||||
"github.com/owncloud/ocis-pkg/v2/log"
|
||||
)
|
||||
|
||||
// Option defines a single option function.
|
||||
type Option func(o *Options)
|
||||
|
||||
// Options defines the available options for this package.
|
||||
type Options struct {
|
||||
// Logger to use for logging, must be set
|
||||
Logger log.Logger
|
||||
// TokenManagerConfig for communicating with the reva token manager
|
||||
TokenManagerConfig config.TokenManager
|
||||
}
|
||||
|
||||
// newOptions initializes the available default options.
|
||||
func newOptions(opts ...Option) Options {
|
||||
opt := Options{}
|
||||
|
||||
for _, o := range opts {
|
||||
o(&opt)
|
||||
}
|
||||
|
||||
return opt
|
||||
}
|
||||
|
||||
// Logger provides a function to set the logger option.
|
||||
func Logger(l log.Logger) Option {
|
||||
return func(o *Options) {
|
||||
o.Logger = l
|
||||
}
|
||||
}
|
||||
|
||||
// TokenManagerConfig provides a function to set the token manger config option.
|
||||
func TokenManagerConfig(cfg config.TokenManager) Option {
|
||||
return func(o *Options) {
|
||||
o.TokenManagerConfig = cfg
|
||||
}
|
||||
}
|
||||
50
ocs/pkg/server/debug/option.go
Normal file
50
ocs/pkg/server/debug/option.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package debug
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/owncloud/ocis-ocs/pkg/config"
|
||||
"github.com/owncloud/ocis-pkg/v2/log"
|
||||
)
|
||||
|
||||
// Option defines a single option function.
|
||||
type Option func(o *Options)
|
||||
|
||||
// Options defines the available options for this package.
|
||||
type Options struct {
|
||||
Logger log.Logger
|
||||
Context context.Context
|
||||
Config *config.Config
|
||||
}
|
||||
|
||||
// newOptions initializes the available default options.
|
||||
func newOptions(opts ...Option) Options {
|
||||
opt := Options{}
|
||||
|
||||
for _, o := range opts {
|
||||
o(&opt)
|
||||
}
|
||||
|
||||
return opt
|
||||
}
|
||||
|
||||
// Logger provides a function to set the logger option.
|
||||
func Logger(val log.Logger) Option {
|
||||
return func(o *Options) {
|
||||
o.Logger = val
|
||||
}
|
||||
}
|
||||
|
||||
// Context provides a function to set the context option.
|
||||
func Context(val context.Context) Option {
|
||||
return func(o *Options) {
|
||||
o.Context = val
|
||||
}
|
||||
}
|
||||
|
||||
// Config provides a function to set the config option.
|
||||
func Config(val *config.Config) Option {
|
||||
return func(o *Options) {
|
||||
o.Config = val
|
||||
}
|
||||
}
|
||||
51
ocs/pkg/server/debug/server.go
Normal file
51
ocs/pkg/server/debug/server.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package debug
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/owncloud/ocis-ocs/pkg/config"
|
||||
"github.com/owncloud/ocis-ocs/pkg/version"
|
||||
"github.com/owncloud/ocis-pkg/v2/service/debug"
|
||||
)
|
||||
|
||||
// Server initializes the debug service and server.
|
||||
func Server(opts ...Option) (*http.Server, error) {
|
||||
options := newOptions(opts...)
|
||||
|
||||
return debug.NewService(
|
||||
debug.Logger(options.Logger),
|
||||
debug.Name("ocs"),
|
||||
debug.Version(version.String),
|
||||
debug.Address(options.Config.Debug.Addr),
|
||||
debug.Token(options.Config.Debug.Token),
|
||||
debug.Pprof(options.Config.Debug.Pprof),
|
||||
debug.Zpages(options.Config.Debug.Zpages),
|
||||
debug.Health(health(options.Config)),
|
||||
debug.Ready(ready(options.Config)),
|
||||
), nil
|
||||
}
|
||||
|
||||
// health implements the health check.
|
||||
func health(cfg *config.Config) func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
// TODO(tboerger): check if services are up and running
|
||||
|
||||
io.WriteString(w, http.StatusText(http.StatusOK))
|
||||
}
|
||||
}
|
||||
|
||||
// ready implements the ready check.
|
||||
func ready(cfg *config.Config) func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
// TODO(tboerger): check if services are up and running
|
||||
|
||||
io.WriteString(w, http.StatusText(http.StatusOK))
|
||||
}
|
||||
}
|
||||
76
ocs/pkg/server/http/option.go
Normal file
76
ocs/pkg/server/http/option.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/owncloud/ocis-ocs/pkg/config"
|
||||
"github.com/owncloud/ocis-ocs/pkg/metrics"
|
||||
"github.com/owncloud/ocis-pkg/v2/log"
|
||||
)
|
||||
|
||||
// Option defines a single option function.
|
||||
type Option func(o *Options)
|
||||
|
||||
// Options defines the available options for this package.
|
||||
type Options struct {
|
||||
Namespace string
|
||||
Logger log.Logger
|
||||
Context context.Context
|
||||
Config *config.Config
|
||||
Metrics *metrics.Metrics
|
||||
Flags []cli.Flag
|
||||
}
|
||||
|
||||
// newOptions initializes the available default options.
|
||||
func newOptions(opts ...Option) Options {
|
||||
opt := Options{}
|
||||
|
||||
for _, o := range opts {
|
||||
o(&opt)
|
||||
}
|
||||
|
||||
return opt
|
||||
}
|
||||
|
||||
// Logger provides a function to set the logger option.
|
||||
func Logger(val log.Logger) Option {
|
||||
return func(o *Options) {
|
||||
o.Logger = val
|
||||
}
|
||||
}
|
||||
|
||||
// Context provides a function to set the context option.
|
||||
func Context(val context.Context) Option {
|
||||
return func(o *Options) {
|
||||
o.Context = val
|
||||
}
|
||||
}
|
||||
|
||||
// Config provides a function to set the config option.
|
||||
func Config(val *config.Config) Option {
|
||||
return func(o *Options) {
|
||||
o.Config = val
|
||||
}
|
||||
}
|
||||
|
||||
// Metrics provides a function to set the metrics option.
|
||||
func Metrics(val *metrics.Metrics) Option {
|
||||
return func(o *Options) {
|
||||
o.Metrics = val
|
||||
}
|
||||
}
|
||||
|
||||
// Flags provides a function to set the flags option.
|
||||
func Flags(val []cli.Flag) Option {
|
||||
return func(o *Options) {
|
||||
o.Flags = append(o.Flags, val...)
|
||||
}
|
||||
}
|
||||
|
||||
// Namespace provides a function to set the Namespace option.
|
||||
func Namespace(val string) Option {
|
||||
return func(o *Options) {
|
||||
o.Namespace = val
|
||||
}
|
||||
}
|
||||
56
ocs/pkg/server/http/server.go
Normal file
56
ocs/pkg/server/http/server.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
svc "github.com/owncloud/ocis-ocs/pkg/service/v0"
|
||||
"github.com/owncloud/ocis-ocs/pkg/version"
|
||||
"github.com/owncloud/ocis-pkg/v2/middleware"
|
||||
"github.com/owncloud/ocis-pkg/v2/service/http"
|
||||
)
|
||||
|
||||
// Server initializes the http service and server.
|
||||
func Server(opts ...Option) (http.Service, error) {
|
||||
options := newOptions(opts...)
|
||||
|
||||
service := http.NewService(
|
||||
http.Logger(options.Logger),
|
||||
http.Name("ocs"),
|
||||
http.Version(version.String),
|
||||
http.Namespace(options.Config.HTTP.Namespace),
|
||||
http.Address(options.Config.HTTP.Addr),
|
||||
http.Context(options.Context),
|
||||
http.Flags(options.Flags...),
|
||||
)
|
||||
|
||||
handle := svc.NewService(
|
||||
svc.Logger(options.Logger),
|
||||
svc.Config(options.Config),
|
||||
svc.Middleware(
|
||||
middleware.RealIP,
|
||||
middleware.RequestID,
|
||||
middleware.Cache,
|
||||
middleware.Cors,
|
||||
middleware.Secure,
|
||||
middleware.Version(
|
||||
"ocs",
|
||||
version.String,
|
||||
),
|
||||
middleware.Logger(
|
||||
options.Logger,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
{
|
||||
handle = svc.NewInstrument(handle, options.Metrics)
|
||||
handle = svc.NewLogging(handle, options.Logger)
|
||||
handle = svc.NewTracing(handle)
|
||||
}
|
||||
|
||||
service.Handle(
|
||||
"/",
|
||||
handle,
|
||||
)
|
||||
|
||||
service.Init()
|
||||
return service, nil
|
||||
}
|
||||
1706
ocs/pkg/server/http/svc_test.go
Normal file
1706
ocs/pkg/server/http/svc_test.go
Normal file
File diff suppressed because it is too large
Load Diff
20
ocs/pkg/service/v0/config.go
Normal file
20
ocs/pkg/service/v0/config.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/render"
|
||||
"github.com/owncloud/ocis-ocs/pkg/service/v0/data"
|
||||
"github.com/owncloud/ocis-ocs/pkg/service/v0/response"
|
||||
)
|
||||
|
||||
// GetConfig renders the ocs config endpoint
|
||||
func (o Ocs) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
render.Render(w, r, response.DataRender(&data.ConfigData{
|
||||
Version: "1.7", // TODO get from env
|
||||
Website: "ocis", // TODO get from env
|
||||
Host: "", // TODO get from FRONTEND config
|
||||
Contact: "", // TODO get from env
|
||||
SSL: "true", // TODO get from env
|
||||
}))
|
||||
}
|
||||
171
ocs/pkg/service/v0/data/capabilities.go
Normal file
171
ocs/pkg/service/v0/data/capabilities.go
Normal file
@@ -0,0 +1,171 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
)
|
||||
|
||||
// ocsBool implements the xml/json Marshaler interface. The OCS API inconsistency require us to parse boolean values
|
||||
// as native booleans for json requests but "truthy" 0/1 values for xml requests.
|
||||
type ocsBool bool
|
||||
|
||||
func (c *ocsBool) MarshalJSON() ([]byte, error) {
|
||||
if *c {
|
||||
return []byte("true"), nil
|
||||
}
|
||||
|
||||
return []byte("false"), nil
|
||||
}
|
||||
|
||||
func (c ocsBool) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
||||
if c {
|
||||
return e.EncodeElement("1", start)
|
||||
}
|
||||
|
||||
return e.EncodeElement("0", start)
|
||||
}
|
||||
|
||||
// CapabilitiesData TODO document
|
||||
type CapabilitiesData struct {
|
||||
Capabilities *Capabilities `json:"capabilities" xml:"capabilities"`
|
||||
Version *Version `json:"version" xml:"version"`
|
||||
}
|
||||
|
||||
// Capabilities groups several capability aspects
|
||||
type Capabilities struct {
|
||||
Core *CapabilitiesCore `json:"core" xml:"core"`
|
||||
Checksums *CapabilitiesChecksums `json:"checksums" xml:"checksums"`
|
||||
Files *CapabilitiesFiles `json:"files" xml:"files" mapstructure:"files"`
|
||||
Dav *CapabilitiesDav `json:"dav" xml:"dav"`
|
||||
FilesSharing *CapabilitiesFilesSharing `json:"files_sharing" xml:"files_sharing" mapstructure:"files_sharing"`
|
||||
Notifications *CapabilitiesNotifications `json:"notifications" xml:"notifications"`
|
||||
}
|
||||
|
||||
// CapabilitiesCore holds webdav config
|
||||
type CapabilitiesCore struct {
|
||||
PollInterval int `json:"pollinterval" xml:"pollinterval" mapstructure:"poll_interval"`
|
||||
WebdavRoot string `json:"webdav-root,omitempty" xml:"webdav-root,omitempty" mapstructure:"webdav_root"`
|
||||
Status *Status `json:"status" xml:"status" mapstructure:"status"`
|
||||
SupportURLSigning ocsBool `json:"support-url-signing,omitempty" xml:"support-url-signing,omitempty" mapstructure:"support-url-signing"`
|
||||
}
|
||||
|
||||
// Status holds basic status information
|
||||
type Status struct {
|
||||
Installed ocsBool `json:"installed" xml:"installed"`
|
||||
Maintenance ocsBool `json:"maintenance" xml:"maintenance"`
|
||||
NeedsDBUpgrade ocsBool `json:"needsDbUpgrade" xml:"needsDbUpgrade"`
|
||||
Version string `json:"version" xml:"version"`
|
||||
VersionString string `json:"versionstring" xml:"versionstring"`
|
||||
Edition string `json:"edition" xml:"edition"`
|
||||
ProductName string `json:"productname" xml:"productname"`
|
||||
Hostname string `json:"hostname,omitempty" xml:"hostname,omitempty"`
|
||||
}
|
||||
|
||||
// CapabilitiesChecksums holds available hashes
|
||||
type CapabilitiesChecksums struct {
|
||||
SupportedTypes []string `json:"supportedTypes" xml:"supportedTypes>element" mapstructure:"supported_types"`
|
||||
PreferredUploadType string `json:"preferredUploadType" xml:"preferredUploadType" mapstructure:"preferred_upload_type"`
|
||||
}
|
||||
|
||||
// CapabilitiesFilesTusSupport TODO this must be a summary of storages
|
||||
type CapabilitiesFilesTusSupport struct {
|
||||
Version string `json:"version" xml:"version"`
|
||||
Resumable string `json:"resumable" xml:"resumable"`
|
||||
Extension string `json:"extension" xml:"extension"`
|
||||
MaxChunkSize int `json:"max_chunk_size" xml:"max_chunk_size" mapstructure:"max_chunk_size"`
|
||||
HTTPMethodOverride string `json:"http_method_override" xml:"http_method_override" mapstructure:"http_method_override"`
|
||||
}
|
||||
|
||||
// CapabilitiesFiles TODO this is storage specific, not global. What effect do these options have on the clients?
|
||||
type CapabilitiesFiles struct {
|
||||
PrivateLinks ocsBool `json:"privateLinks" xml:"privateLinks" mapstructure:"private_links"`
|
||||
BigFileChunking ocsBool `json:"bigfilechunking" xml:"bigfilechunking"`
|
||||
Undelete ocsBool `json:"undelete" xml:"undelete"`
|
||||
Versioning ocsBool `json:"versioning" xml:"versioning"`
|
||||
BlacklistedFiles []string `json:"blacklisted_files" xml:"blacklisted_files>element" mapstructure:"blacklisted_files"`
|
||||
TusSupport *CapabilitiesFilesTusSupport `json:"tus_support" xml:"tus_support" mapstructure:"tus_support"`
|
||||
}
|
||||
|
||||
// CapabilitiesDav holds dav endpoint config
|
||||
type CapabilitiesDav struct {
|
||||
Chunking string `json:"chunking" xml:"chunking"`
|
||||
Trashbin string `json:"trashbin" xml:"trashbin"`
|
||||
Reports []string `json:"reports" xml:"reports>element" mapstructure:"reports"`
|
||||
ChunkingParallelUploadDisabled bool `json:"chunkingParallelUploadDisabled" xml:"chunkingParallelUploadDisabled"`
|
||||
}
|
||||
|
||||
// CapabilitiesFilesSharing TODO document
|
||||
type CapabilitiesFilesSharing struct {
|
||||
APIEnabled ocsBool `json:"api_enabled" xml:"api_enabled" mapstructure:"api_enabled"`
|
||||
Resharing ocsBool `json:"resharing" xml:"resharing"`
|
||||
GroupSharing ocsBool `json:"group_sharing" xml:"group_sharing" mapstructure:"group_sharing"`
|
||||
AutoAcceptShare ocsBool `json:"auto_accept_share" xml:"auto_accept_share" mapstructure:"auto_accept_share"`
|
||||
ShareWithGroupMembersOnly ocsBool `json:"share_with_group_members_only" xml:"share_with_group_members_only" mapstructure:"share_with_group_members_only"`
|
||||
ShareWithMembershipGroupsOnly ocsBool `json:"share_with_membership_groups_only" xml:"share_with_membership_groups_only" mapstructure:"share_with_membership_groups_only"`
|
||||
SearchMinLength int `json:"search_min_length" xml:"search_min_length" mapstructure:"search_min_length"`
|
||||
DefaultPermissions int `json:"default_permissions" xml:"default_permissions" mapstructure:"default_permissions"`
|
||||
UserEnumeration *CapabilitiesFilesSharingUserEnumeration `json:"user_enumeration" xml:"user_enumeration" mapstructure:"user_enumeration"`
|
||||
Federation *CapabilitiesFilesSharingFederation `json:"federation" xml:"federation"`
|
||||
Public *CapabilitiesFilesSharingPublic `json:"public" xml:"public"`
|
||||
User *CapabilitiesFilesSharingUser `json:"user" xml:"user"`
|
||||
}
|
||||
|
||||
// CapabilitiesFilesSharingPublic TODO document
|
||||
type CapabilitiesFilesSharingPublic struct {
|
||||
Enabled ocsBool `json:"enabled" xml:"enabled"`
|
||||
SendMail ocsBool `json:"send_mail" xml:"send_mail" mapstructure:"send_mail"`
|
||||
SocialShare ocsBool `json:"social_share" xml:"social_share" mapstructure:"social_share"`
|
||||
Upload ocsBool `json:"upload" xml:"upload"`
|
||||
Multiple ocsBool `json:"multiple" xml:"multiple"`
|
||||
SupportsUploadOnly ocsBool `json:"supports_upload_only" xml:"supports_upload_only" mapstructure:"supports_upload_only"`
|
||||
Password *CapabilitiesFilesSharingPublicPassword `json:"password" xml:"password"`
|
||||
ExpireDate *CapabilitiesFilesSharingPublicExpireDate `json:"expire_date" xml:"expire_date" mapstructure:"expire_date"`
|
||||
}
|
||||
|
||||
// CapabilitiesFilesSharingPublicPassword TODO document
|
||||
type CapabilitiesFilesSharingPublicPassword struct {
|
||||
EnforcedFor *CapabilitiesFilesSharingPublicPasswordEnforcedFor `json:"enforced_for" xml:"enforced_for" mapstructure:"enforced_for"`
|
||||
Enforced ocsBool `json:"enforced" xml:"enforced"`
|
||||
}
|
||||
|
||||
// CapabilitiesFilesSharingPublicPasswordEnforcedFor TODO document
|
||||
type CapabilitiesFilesSharingPublicPasswordEnforcedFor struct {
|
||||
ReadOnly ocsBool `json:"read_only" xml:"read_only,omitempty" mapstructure:"read_only"`
|
||||
ReadWrite ocsBool `json:"read_write" xml:"read_write,omitempty" mapstructure:"read_write"`
|
||||
UploadOnly ocsBool `json:"upload_only" xml:"upload_only,omitempty" mapstructure:"upload_only"`
|
||||
}
|
||||
|
||||
// CapabilitiesFilesSharingPublicExpireDate TODO document
|
||||
type CapabilitiesFilesSharingPublicExpireDate struct {
|
||||
Enabled ocsBool `json:"enabled" xml:"enabled"`
|
||||
}
|
||||
|
||||
// CapabilitiesFilesSharingUser TODO document
|
||||
type CapabilitiesFilesSharingUser struct {
|
||||
SendMail ocsBool `json:"send_mail" xml:"send_mail" mapstructure:"send_mail"`
|
||||
}
|
||||
|
||||
// CapabilitiesFilesSharingUserEnumeration TODO document
|
||||
type CapabilitiesFilesSharingUserEnumeration struct {
|
||||
Enabled ocsBool `json:"enabled" xml:"enabled"`
|
||||
GroupMembersOnly ocsBool `json:"group_members_only" xml:"group_members_only" mapstructure:"group_members_only"`
|
||||
}
|
||||
|
||||
// CapabilitiesFilesSharingFederation holds outgoing and incoming flags
|
||||
type CapabilitiesFilesSharingFederation struct {
|
||||
Outgoing ocsBool `json:"outgoing" xml:"outgoing"`
|
||||
Incoming ocsBool `json:"incoming" xml:"incoming"`
|
||||
}
|
||||
|
||||
// CapabilitiesNotifications holds a list of notification endpoints
|
||||
type CapabilitiesNotifications struct {
|
||||
Endpoints []string `json:"ocs-endpoints" xml:"ocs-endpoints>element" mapstructure:"endpoints"`
|
||||
}
|
||||
|
||||
// Version holds version information
|
||||
type Version struct {
|
||||
Major int `json:"major" xml:"major"`
|
||||
Minor int `json:"minor" xml:"minor"`
|
||||
Micro int `json:"micro" xml:"micro"` // = patch level
|
||||
String string `json:"string" xml:"string"`
|
||||
Edition string `json:"edition" xml:"edition"`
|
||||
}
|
||||
10
ocs/pkg/service/v0/data/config.go
Normal file
10
ocs/pkg/service/v0/data/config.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package data
|
||||
|
||||
// ConfigData holds basic config
|
||||
type ConfigData struct {
|
||||
Version string `json:"version" xml:"version"`
|
||||
Website string `json:"website" xml:"website"`
|
||||
Host string `json:"host" xml:"host"`
|
||||
Contact string `json:"contact" xml:"contact"`
|
||||
SSL string `json:"ssl" xml:"ssl"`
|
||||
}
|
||||
6
ocs/pkg/service/v0/data/group.go
Normal file
6
ocs/pkg/service/v0/data/group.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package data
|
||||
|
||||
// Groups holds group ids for the groups listing
|
||||
type Groups struct {
|
||||
Groups []string `json:"groups" xml:"groups>element"`
|
||||
}
|
||||
34
ocs/pkg/service/v0/data/meta.go
Normal file
34
ocs/pkg/service/v0/data/meta.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package data
|
||||
|
||||
// Meta holds response metadata
|
||||
type Meta struct {
|
||||
Status string `json:"status" xml:"status"`
|
||||
StatusCode int `json:"statuscode" xml:"statuscode"`
|
||||
Message string `json:"message" xml:"message"`
|
||||
TotalItems string `json:"totalitems,omitempty" xml:"totalitems,omitempty"`
|
||||
ItemsPerPage string `json:"itemsperpage,omitempty" xml:"itemsperpage,omitempty"`
|
||||
}
|
||||
|
||||
// MetaOK is the default ok response with code 100
|
||||
var MetaOK = Meta{Status: "ok", StatusCode: 100, Message: "OK"}
|
||||
|
||||
// MetaFailure is a failure response with code 101
|
||||
var MetaFailure = Meta{Status: "", StatusCode: 101, Message: "Failure"}
|
||||
|
||||
// MetaInvalidInput is an error response with code 102
|
||||
var MetaInvalidInput = Meta{Status: "", StatusCode: 102, Message: "Invalid Input"}
|
||||
|
||||
// MetaBadRequest is used for unknown errors
|
||||
var MetaBadRequest = Meta{Status: "error", StatusCode: 400, Message: "Bad Request"}
|
||||
|
||||
// MetaServerError is returned on server errors
|
||||
var MetaServerError = Meta{Status: "error", StatusCode: 996, Message: "Server Error"}
|
||||
|
||||
// MetaUnauthorized is returned on unauthorized requests
|
||||
var MetaUnauthorized = Meta{Status: "error", StatusCode: 997, Message: "Unauthorised"}
|
||||
|
||||
// MetaNotFound is returned when trying to access not existing resources
|
||||
var MetaNotFound = Meta{Status: "error", StatusCode: 998, Message: "Not Found"}
|
||||
|
||||
// MetaUnknownError is used for unknown errors
|
||||
var MetaUnknownError = Meta{Status: "error", StatusCode: 999, Message: "Unknown Error"}
|
||||
35
ocs/pkg/service/v0/data/user.go
Normal file
35
ocs/pkg/service/v0/data/user.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package data
|
||||
|
||||
// Users holds user ids for the user listing
|
||||
type Users struct {
|
||||
Users []string `json:"users" xml:"users>element"`
|
||||
}
|
||||
|
||||
// User holds the payload for a GetUser response
|
||||
type User struct {
|
||||
// TODO needs better naming, clarify if we need a userid, a username or both
|
||||
Enabled string `json:"enabled" xml:"enabled"`
|
||||
UserID string `json:"id" xml:"id"`
|
||||
Username string `json:"username" xml:"username"`
|
||||
DisplayName string `json:"display-name" xml:"display-name"`
|
||||
LegacyDisplayName string `json:"displayname" xml:"displayname"`
|
||||
Email string `json:"email" xml:"email"`
|
||||
Quota *Quota `json:"quota" xml:"quota"`
|
||||
UIDNumber int64 `json:"uidnumber" xml:"uidnumber"`
|
||||
GIDNumber int64 `json:"gidnumber" xml:"gidnumber"`
|
||||
}
|
||||
|
||||
// Quota holds quota information
|
||||
type Quota struct {
|
||||
Free int64 `json:"free" xml:"free"`
|
||||
Used int64 `json:"used" xml:"used"`
|
||||
Total int64 `json:"total" xml:"total"`
|
||||
Relative float32 `json:"relative" xml:"relative"`
|
||||
Definition string `json:"definition" xml:"definition"`
|
||||
}
|
||||
|
||||
// SigningKey holds the Payload for a GetSigningKey response
|
||||
type SigningKey struct {
|
||||
User string `json:"user" xml:"user"`
|
||||
SigningKey string `json:"signing-key" xml:"signing-key"`
|
||||
}
|
||||
177
ocs/pkg/service/v0/groups.go
Normal file
177
ocs/pkg/service/v0/groups.go
Normal file
@@ -0,0 +1,177 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/go-chi/render"
|
||||
merrors "github.com/micro/go-micro/v2/errors"
|
||||
|
||||
accounts "github.com/owncloud/ocis-accounts/pkg/proto/v0"
|
||||
"github.com/owncloud/ocis-ocs/pkg/service/v0/data"
|
||||
"github.com/owncloud/ocis-ocs/pkg/service/v0/response"
|
||||
)
|
||||
|
||||
// ListUserGroups lists a users groups
|
||||
func (o Ocs) ListUserGroups(w http.ResponseWriter, r *http.Request) {
|
||||
userid := chi.URLParam(r, "userid")
|
||||
|
||||
account, err := o.getAccountService().GetAccount(r.Context(), &accounts.GetAccountRequest{Id: userid})
|
||||
|
||||
if err != nil {
|
||||
merr := merrors.FromError(err)
|
||||
if merr.Code == http.StatusNotFound {
|
||||
render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found"))
|
||||
} else {
|
||||
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
|
||||
}
|
||||
o.logger.Error().Err(err).Str("userid", userid).Msg("could not get list of user groups")
|
||||
return
|
||||
}
|
||||
|
||||
groups := []string{}
|
||||
for i := range account.MemberOf {
|
||||
groups = append(groups, account.MemberOf[i].Id)
|
||||
}
|
||||
|
||||
o.logger.Error().Err(err).Int("count", len(groups)).Str("userid", userid).Msg("listing groups for user")
|
||||
render.Render(w, r, response.DataRender(&data.Groups{Groups: groups}))
|
||||
}
|
||||
|
||||
// AddToGroup adds a user to a group
|
||||
func (o Ocs) AddToGroup(w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
userid := chi.URLParam(r, "userid")
|
||||
groupid := r.PostForm.Get("groupid")
|
||||
|
||||
if groupid == "" {
|
||||
render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "empty group assignment: unspecified group"))
|
||||
return
|
||||
}
|
||||
|
||||
_, err := o.getGroupsService().AddMember(r.Context(), &accounts.AddMemberRequest{
|
||||
AccountId: userid,
|
||||
GroupId: groupid,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
merr := merrors.FromError(err)
|
||||
if merr.Code == http.StatusNotFound {
|
||||
render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found"))
|
||||
} else {
|
||||
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
|
||||
}
|
||||
o.logger.Error().Err(err).Str("userid", userid).Str("groupid", groupid).Msg("could not add user to group")
|
||||
return
|
||||
}
|
||||
|
||||
o.logger.Debug().Str("userid", userid).Str("groupid", groupid).Msg("added user to group")
|
||||
render.Render(w, r, response.DataRender(struct{}{}))
|
||||
}
|
||||
|
||||
// RemoveFromGroup removes a user from a group
|
||||
func (o Ocs) RemoveFromGroup(w http.ResponseWriter, r *http.Request) {
|
||||
userid := chi.URLParam(r, "userid")
|
||||
groupid := r.URL.Query().Get("groupid")
|
||||
|
||||
_, err := o.getGroupsService().RemoveMember(r.Context(), &accounts.RemoveMemberRequest{
|
||||
AccountId: userid,
|
||||
GroupId: groupid,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
merr := merrors.FromError(err)
|
||||
if merr.Code == http.StatusNotFound {
|
||||
render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found"))
|
||||
} else {
|
||||
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
|
||||
}
|
||||
o.logger.Error().Err(err).Str("userid", userid).Str("groupid", groupid).Msg("could not remove user from group")
|
||||
return
|
||||
}
|
||||
|
||||
o.logger.Debug().Str("userid", userid).Str("groupid", groupid).Msg("removed user from group")
|
||||
render.Render(w, r, response.DataRender(struct{}{}))
|
||||
}
|
||||
|
||||
// ListGroups lists all groups
|
||||
func (o Ocs) ListGroups(w http.ResponseWriter, r *http.Request) {
|
||||
search := r.URL.Query().Get("search")
|
||||
query := ""
|
||||
if search != "" {
|
||||
query = fmt.Sprintf("id eq '%s' or on_premises_sam_account_name eq '%s'", escapeValue(search), escapeValue(search))
|
||||
}
|
||||
|
||||
res, err := o.getGroupsService().ListGroups(r.Context(), &accounts.ListGroupsRequest{
|
||||
Query: query,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
o.logger.Err(err).Msg("could not list users")
|
||||
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, "could not list users"))
|
||||
return
|
||||
}
|
||||
|
||||
groups := []string{}
|
||||
for i := range res.Groups {
|
||||
groups = append(groups, res.Groups[i].Id)
|
||||
}
|
||||
|
||||
render.Render(w, r, response.DataRender(&data.Groups{Groups: groups}))
|
||||
}
|
||||
|
||||
// AddGroup adds a group
|
||||
func (o Ocs) AddGroup(w http.ResponseWriter, r *http.Request) {
|
||||
render.Render(w, r, response.ErrRender(data.MetaUnknownError.StatusCode, "not implemented"))
|
||||
}
|
||||
|
||||
// DeleteGroup deletes a group
|
||||
func (o Ocs) DeleteGroup(w http.ResponseWriter, r *http.Request) {
|
||||
groupid := chi.URLParam(r, "groupid")
|
||||
|
||||
_, err := o.getGroupsService().DeleteGroup(r.Context(), &accounts.DeleteGroupRequest{
|
||||
Id: groupid,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
merr := merrors.FromError(err)
|
||||
if merr.Code == http.StatusNotFound {
|
||||
render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found"))
|
||||
} else {
|
||||
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
|
||||
}
|
||||
o.logger.Error().Err(err).Str("groupid", groupid).Msg("could not remove group")
|
||||
return
|
||||
}
|
||||
|
||||
o.logger.Debug().Str("groupid", groupid).Msg("removed group")
|
||||
render.Render(w, r, response.DataRender(struct{}{}))
|
||||
}
|
||||
|
||||
// GetGroupMembers lists all members of a group
|
||||
func (o Ocs) GetGroupMembers(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
groupid := chi.URLParam(r, "groupid")
|
||||
|
||||
res, err := o.getGroupsService().ListMembers(r.Context(), &accounts.ListMembersRequest{Id: groupid})
|
||||
|
||||
if err != nil {
|
||||
merr := merrors.FromError(err)
|
||||
if merr.Code == http.StatusNotFound {
|
||||
render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested group could not be found"))
|
||||
} else {
|
||||
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
|
||||
}
|
||||
o.logger.Error().Err(err).Str("groupid", groupid).Msg("could not get list of members")
|
||||
return
|
||||
}
|
||||
|
||||
members := []string{}
|
||||
for i := range res.Members {
|
||||
members = append(members, res.Members[i].Id)
|
||||
}
|
||||
|
||||
o.logger.Error().Err(err).Int("count", len(members)).Str("groupid", groupid).Msg("listing group members")
|
||||
render.Render(w, r, response.DataRender(&data.Users{Users: members}))
|
||||
}
|
||||
30
ocs/pkg/service/v0/instrument.go
Normal file
30
ocs/pkg/service/v0/instrument.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/owncloud/ocis-ocs/pkg/metrics"
|
||||
)
|
||||
|
||||
// NewInstrument returns a service that instruments metrics.
|
||||
func NewInstrument(next Service, metrics *metrics.Metrics) Service {
|
||||
return instrument{
|
||||
next: next,
|
||||
metrics: metrics,
|
||||
}
|
||||
}
|
||||
|
||||
type instrument struct {
|
||||
next Service
|
||||
metrics *metrics.Metrics
|
||||
}
|
||||
|
||||
// ServeHTTP implements the Service interface.
|
||||
func (i instrument) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
i.next.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// GetConfig implements the Service interface.
|
||||
func (i instrument) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
i.next.GetConfig(w, r)
|
||||
}
|
||||
30
ocs/pkg/service/v0/logging.go
Normal file
30
ocs/pkg/service/v0/logging.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/owncloud/ocis-pkg/v2/log"
|
||||
)
|
||||
|
||||
// NewLogging returns a service that logs messages.
|
||||
func NewLogging(next Service, logger log.Logger) Service {
|
||||
return logging{
|
||||
next: next,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
type logging struct {
|
||||
next Service
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
// ServeHTTP implements the Service interface.
|
||||
func (l logging) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
l.next.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// GetConfig implements the Service interface.
|
||||
func (l logging) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
l.next.GetConfig(w, r)
|
||||
}
|
||||
50
ocs/pkg/service/v0/option.go
Normal file
50
ocs/pkg/service/v0/option.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/owncloud/ocis-ocs/pkg/config"
|
||||
"github.com/owncloud/ocis-pkg/v2/log"
|
||||
)
|
||||
|
||||
// Option defines a single option function.
|
||||
type Option func(o *Options)
|
||||
|
||||
// Options defines the available options for this package.
|
||||
type Options struct {
|
||||
Logger log.Logger
|
||||
Config *config.Config
|
||||
Middleware []func(http.Handler) http.Handler
|
||||
}
|
||||
|
||||
// newOptions initializes the available default options.
|
||||
func newOptions(opts ...Option) Options {
|
||||
opt := Options{}
|
||||
|
||||
for _, o := range opts {
|
||||
o(&opt)
|
||||
}
|
||||
|
||||
return opt
|
||||
}
|
||||
|
||||
// Logger provides a function to set the logger option.
|
||||
func Logger(val log.Logger) Option {
|
||||
return func(o *Options) {
|
||||
o.Logger = val
|
||||
}
|
||||
}
|
||||
|
||||
// Config provides a function to set the config option.
|
||||
func Config(val *config.Config) Option {
|
||||
return func(o *Options) {
|
||||
o.Config = val
|
||||
}
|
||||
}
|
||||
|
||||
// Middleware provides a function to set the middleware option.
|
||||
func Middleware(val ...func(http.Handler) http.Handler) Option {
|
||||
return func(o *Options) {
|
||||
o.Middleware = val
|
||||
}
|
||||
}
|
||||
114
ocs/pkg/service/v0/response/response.go
Normal file
114
ocs/pkg/service/v0/response/response.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package response
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"net/http"
|
||||
"reflect"
|
||||
|
||||
"github.com/go-chi/render"
|
||||
"github.com/owncloud/ocis-ocs/pkg/service/v0/data"
|
||||
)
|
||||
|
||||
// Response is the top level response structure
|
||||
type Response struct {
|
||||
OCS *Payload `json:"ocs" xml:"ocs"`
|
||||
}
|
||||
|
||||
var (
|
||||
elementStartElement = xml.StartElement{Name: xml.Name{Local: "element"}}
|
||||
metaStartElement = xml.StartElement{Name: xml.Name{Local: "meta"}}
|
||||
ocsName = xml.Name{Local: "ocs"}
|
||||
dataName = xml.Name{Local: "data"}
|
||||
)
|
||||
|
||||
// Payload combines response metadata and data
|
||||
type Payload struct {
|
||||
Meta data.Meta `json:"meta" xml:"meta"`
|
||||
Data interface{} `json:"data,omitempty" xml:"data,omitempty"`
|
||||
}
|
||||
|
||||
// MarshalXML handles ocs specific wrapping of array members in 'element' tags for the data
|
||||
func (rsp Response) MarshalXML(e *xml.Encoder, start xml.StartElement) (err error) {
|
||||
// first the easy part
|
||||
// use ocs as the surrounding tag
|
||||
start.Name = ocsName
|
||||
if err = e.EncodeToken(start); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// encode the meta tag
|
||||
if err = e.EncodeElement(rsp.OCS.Meta, metaStartElement); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// we need to use reflection to determine if p.Data is an array or a slice
|
||||
rt := reflect.TypeOf(rsp.OCS.Data)
|
||||
if rt != nil && (rt.Kind() == reflect.Array || rt.Kind() == reflect.Slice) {
|
||||
// this is how to wrap the data elements in their own <element> tag
|
||||
v := reflect.ValueOf(rsp.OCS.Data)
|
||||
if err = e.EncodeToken(xml.StartElement{Name: dataName}); err != nil {
|
||||
return
|
||||
}
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
if err = e.EncodeElement(v.Index(i).Interface(), elementStartElement); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if err = e.EncodeToken(xml.EndElement{Name: dataName}); err != nil {
|
||||
return
|
||||
}
|
||||
} else if err = e.EncodeElement(rsp.OCS.Data, xml.StartElement{Name: dataName}); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// write the closing <ocs> tag
|
||||
if err = e.EncodeToken(xml.EndElement{Name: start.Name}); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Render sets the status code of the http response, taking the ocs version into account
|
||||
func (rsp *Response) Render(w http.ResponseWriter, r *http.Request) error {
|
||||
version := APIVersion(r.Context())
|
||||
m := statusCodeMapper(version)
|
||||
statusCode := m(rsp.OCS.Meta)
|
||||
render.Status(r, statusCode)
|
||||
if version == ocsVersion2 && statusCode == http.StatusOK {
|
||||
rsp.OCS.Meta.StatusCode = statusCode
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DataRender creates an OK Payload for the given data
|
||||
func DataRender(d interface{}) render.Renderer {
|
||||
return &Response{
|
||||
&Payload{
|
||||
Meta: data.MetaOK,
|
||||
Data: d,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ErrRender creates an Error Paylod with the given OCS error code and message
|
||||
// The httpcode will be determined using the API version stored in the context
|
||||
func ErrRender(c int, m string) render.Renderer {
|
||||
return &Response{
|
||||
&Payload{
|
||||
Meta: data.Meta{Status: "error", StatusCode: c, Message: m},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func statusCodeMapper(version string) func(data.Meta) int {
|
||||
var mapper func(data.Meta) int
|
||||
switch version {
|
||||
case ocsVersion1:
|
||||
mapper = OcsV1StatusCodes
|
||||
case ocsVersion2:
|
||||
mapper = OcsV2StatusCodes
|
||||
default:
|
||||
mapper = defaultStatusCodeMapper
|
||||
}
|
||||
return mapper
|
||||
}
|
||||
84
ocs/pkg/service/v0/response/version.go
Normal file
84
ocs/pkg/service/v0/response/version.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package response
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/go-chi/render"
|
||||
"github.com/owncloud/ocis-ocs/pkg/service/v0/data"
|
||||
)
|
||||
|
||||
type key int
|
||||
|
||||
const (
|
||||
apiVersionKey key = iota
|
||||
ocsVersion1 = "1"
|
||||
ocsVersion2 = "2"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultStatusCodeMapper = OcsV2StatusCodes
|
||||
)
|
||||
|
||||
// APIVersion retrieves the api version from the context.
|
||||
func APIVersion(ctx context.Context) string {
|
||||
value := ctx.Value(apiVersionKey)
|
||||
if value != nil {
|
||||
return value.(string)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// OcsV1StatusCodes returns the http status codes for the OCS API v1.
|
||||
func OcsV1StatusCodes(meta data.Meta) int {
|
||||
return http.StatusOK
|
||||
}
|
||||
|
||||
// OcsV2StatusCodes maps the OCS codes to http status codes for the ocs API v2.
|
||||
func OcsV2StatusCodes(meta data.Meta) int {
|
||||
sc := meta.StatusCode
|
||||
switch sc {
|
||||
case data.MetaNotFound.StatusCode:
|
||||
return http.StatusNotFound
|
||||
case data.MetaUnknownError.StatusCode:
|
||||
fallthrough
|
||||
case data.MetaServerError.StatusCode:
|
||||
return http.StatusInternalServerError
|
||||
case data.MetaUnauthorized.StatusCode:
|
||||
return http.StatusUnauthorized
|
||||
case 100:
|
||||
meta.StatusCode = http.StatusOK
|
||||
return http.StatusOK
|
||||
}
|
||||
// any 2xx, 4xx and 5xx will be used as is
|
||||
if sc >= 200 && sc < 600 {
|
||||
return sc
|
||||
}
|
||||
|
||||
// any error codes > 100 are treated as client errors
|
||||
if sc > 100 && sc < 200 {
|
||||
return http.StatusBadRequest
|
||||
}
|
||||
|
||||
// TODO change this status code?
|
||||
return http.StatusOK
|
||||
}
|
||||
|
||||
// VersionCtx middleware is used to determine the response mapper from
|
||||
// the URL parameters passed through as the request. In case
|
||||
// the Version is unknown, we stop here and return a 404.
|
||||
func VersionCtx(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
version := chi.URLParam(r, "version")
|
||||
if version == "" {
|
||||
render.Render(w, r, ErrRender(data.MetaBadRequest.StatusCode, "unknown ocs api version"))
|
||||
return
|
||||
}
|
||||
w.Header().Set("Ocs-Api-Version", version)
|
||||
|
||||
// store version in context so handlers can access it
|
||||
ctx := context.WithValue(r.Context(), apiVersionKey, version)
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
110
ocs/pkg/service/v0/service.go
Normal file
110
ocs/pkg/service/v0/service.go
Normal file
@@ -0,0 +1,110 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/go-chi/chi/middleware"
|
||||
"github.com/go-chi/render"
|
||||
"github.com/micro/go-micro/v2/client/grpc"
|
||||
|
||||
accounts "github.com/owncloud/ocis-accounts/pkg/proto/v0"
|
||||
"github.com/owncloud/ocis-ocs/pkg/config"
|
||||
ocsm "github.com/owncloud/ocis-ocs/pkg/middleware"
|
||||
"github.com/owncloud/ocis-ocs/pkg/service/v0/data"
|
||||
"github.com/owncloud/ocis-ocs/pkg/service/v0/response"
|
||||
"github.com/owncloud/ocis-pkg/v2/log"
|
||||
)
|
||||
|
||||
var defaultClient = grpc.NewClient()
|
||||
|
||||
// Service defines the extension handlers.
|
||||
type Service interface {
|
||||
ServeHTTP(http.ResponseWriter, *http.Request)
|
||||
GetConfig(http.ResponseWriter, *http.Request)
|
||||
}
|
||||
|
||||
// NewService returns a service implementation for Service.
|
||||
func NewService(opts ...Option) Service {
|
||||
options := newOptions(opts...)
|
||||
|
||||
m := chi.NewMux()
|
||||
m.Use(options.Middleware...)
|
||||
|
||||
svc := Ocs{
|
||||
config: options.Config,
|
||||
mux: m,
|
||||
logger: options.Logger,
|
||||
}
|
||||
|
||||
m.Route(options.Config.HTTP.Root, func(r chi.Router) {
|
||||
r.NotFound(svc.NotFound)
|
||||
r.Use(middleware.StripSlashes)
|
||||
r.Use(ocsm.AccessToken(
|
||||
ocsm.Logger(options.Logger),
|
||||
ocsm.TokenManagerConfig(options.Config.TokenManager),
|
||||
))
|
||||
r.Use(ocsm.OCSFormatCtx) // updates request Accept header according to format=(json|xml) query parameter
|
||||
r.Route("/v{version:(1|2)}.php", func(r chi.Router) {
|
||||
r.Use(response.VersionCtx) // stores version in context
|
||||
r.Route("/apps/files_sharing/api/v1", func(r chi.Router) {})
|
||||
r.Route("/apps/notifications/api/v1", func(r chi.Router) {})
|
||||
r.Route("/cloud", func(r chi.Router) {
|
||||
r.Route("/capabilities", func(r chi.Router) {})
|
||||
r.Route("/user", func(r chi.Router) {
|
||||
r.Get("/", svc.GetUser)
|
||||
r.Get("/signing-key", svc.GetSigningKey)
|
||||
})
|
||||
r.Route("/users", func(r chi.Router) {
|
||||
r.Get("/", svc.ListUsers)
|
||||
r.Post("/", svc.AddUser)
|
||||
r.Get("/{userid}", svc.GetUser)
|
||||
r.Put("/{userid}", svc.EditUser)
|
||||
r.Delete("/{userid}", svc.DeleteUser)
|
||||
|
||||
r.Route("/{userid}/groups", func(r chi.Router) {
|
||||
r.Get("/", svc.ListUserGroups)
|
||||
r.Post("/", svc.AddToGroup)
|
||||
r.Delete("/", svc.RemoveFromGroup)
|
||||
})
|
||||
})
|
||||
r.Route("/groups", func(r chi.Router) {
|
||||
r.Get("/", svc.ListGroups)
|
||||
r.Post("/", svc.AddGroup)
|
||||
r.Delete("/{groupid}", svc.DeleteGroup)
|
||||
r.Get("/{groupid}", svc.GetGroupMembers)
|
||||
})
|
||||
})
|
||||
r.Route("/config", func(r chi.Router) {
|
||||
r.Get("/", svc.GetConfig)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
return svc
|
||||
}
|
||||
|
||||
// Ocs defines implements the business logic for Service.
|
||||
type Ocs struct {
|
||||
config *config.Config
|
||||
logger log.Logger
|
||||
mux *chi.Mux
|
||||
}
|
||||
|
||||
// ServeHTTP implements the Service interface.
|
||||
func (o Ocs) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
o.mux.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// NotFound uses ErrRender to always return a proper OCS payload
|
||||
func (o Ocs) NotFound(w http.ResponseWriter, r *http.Request) {
|
||||
render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "not found"))
|
||||
}
|
||||
|
||||
func (o Ocs) getAccountService() accounts.AccountsService {
|
||||
return accounts.NewAccountsService("com.owncloud.api.accounts", defaultClient)
|
||||
}
|
||||
|
||||
func (o Ocs) getGroupsService() accounts.GroupsService {
|
||||
return accounts.NewGroupsService("com.owncloud.api.accounts", defaultClient)
|
||||
}
|
||||
28
ocs/pkg/service/v0/tracing.go
Normal file
28
ocs/pkg/service/v0/tracing.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/owncloud/ocis-pkg/v2/middleware"
|
||||
)
|
||||
|
||||
// NewTracing returns a service that instruments traces.
|
||||
func NewTracing(next Service) Service {
|
||||
return tracing{
|
||||
next: next,
|
||||
}
|
||||
}
|
||||
|
||||
type tracing struct {
|
||||
next Service
|
||||
}
|
||||
|
||||
// ServeHTTP implements the Service interface.
|
||||
func (t tracing) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
middleware.Trace(t.next).ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// GetConfig implements the Service interface.
|
||||
func (t tracing) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
t.next.GetConfig(w, r)
|
||||
}
|
||||
370
ocs/pkg/service/v0/users.go
Normal file
370
ocs/pkg/service/v0/users.go
Normal file
@@ -0,0 +1,370 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/cs3org/reva/pkg/user"
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/go-chi/render"
|
||||
"google.golang.org/protobuf/types/known/fieldmaskpb"
|
||||
|
||||
"github.com/micro/go-micro/v2/client/grpc"
|
||||
merrors "github.com/micro/go-micro/v2/errors"
|
||||
accounts "github.com/owncloud/ocis-accounts/pkg/proto/v0"
|
||||
"github.com/owncloud/ocis-ocs/pkg/service/v0/data"
|
||||
"github.com/owncloud/ocis-ocs/pkg/service/v0/response"
|
||||
storepb "github.com/owncloud/ocis-store/pkg/proto/v0"
|
||||
)
|
||||
|
||||
// GetUser returns the currently logged in user
|
||||
func (o Ocs) GetUser(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO this endpoint needs authentication using the roles and permissions
|
||||
userid := chi.URLParam(r, "userid")
|
||||
|
||||
if userid == "" {
|
||||
u, ok := user.ContextGetUser(r.Context())
|
||||
if !ok || u.Id == nil || u.Id.OpaqueId == "" {
|
||||
render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "missing user in context"))
|
||||
return
|
||||
}
|
||||
|
||||
userid = u.Id.OpaqueId
|
||||
}
|
||||
|
||||
account, err := o.getAccountService().GetAccount(r.Context(), &accounts.GetAccountRequest{
|
||||
Id: userid,
|
||||
})
|
||||
if err != nil {
|
||||
merr := merrors.FromError(err)
|
||||
if merr.Code == http.StatusNotFound {
|
||||
render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found"))
|
||||
} else {
|
||||
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
|
||||
}
|
||||
o.logger.Error().Err(err).Str("userid", userid).Msg("could not get user")
|
||||
return
|
||||
}
|
||||
|
||||
// remove password from log if it is set
|
||||
if account.PasswordProfile != nil {
|
||||
account.PasswordProfile.Password = ""
|
||||
}
|
||||
o.logger.Debug().Interface("account", account).Msg("got user")
|
||||
|
||||
// mimic the oc10 bool as string for the user enabled property
|
||||
var enabled string
|
||||
if account.AccountEnabled {
|
||||
enabled = "true"
|
||||
} else {
|
||||
enabled = "false"
|
||||
}
|
||||
|
||||
render.Render(w, r, response.DataRender(&data.User{
|
||||
UserID: account.Id, // TODO userid vs username! implications for clients if we return the userid here? -> implement graph ASAP?
|
||||
Username: account.PreferredName,
|
||||
DisplayName: account.DisplayName,
|
||||
LegacyDisplayName: account.DisplayName,
|
||||
Email: account.Mail,
|
||||
UIDNumber: account.UidNumber,
|
||||
GIDNumber: account.GidNumber,
|
||||
Enabled: enabled,
|
||||
// FIXME onlyfor users/{userid} endpoint (not /user)
|
||||
// TODO query storage registry for free space? of home storage, maybe...
|
||||
Quota: &data.Quota{
|
||||
Free: 2840756224000,
|
||||
Used: 5059416668,
|
||||
Total: 2845815640668,
|
||||
Relative: 0.18,
|
||||
Definition: "default",
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
// AddUser creates a new user account
|
||||
func (o Ocs) AddUser(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO this endpoint needs authentication using the roles and permissions
|
||||
userid := r.PostFormValue("userid")
|
||||
password := r.PostFormValue("password")
|
||||
username := r.PostFormValue("username")
|
||||
displayname := r.PostFormValue("displayname")
|
||||
email := r.PostFormValue("email")
|
||||
uid := r.PostFormValue("uidnumber")
|
||||
gid := r.PostFormValue("gidnumber")
|
||||
|
||||
var uidNumber, gidNumber int64
|
||||
var err error
|
||||
|
||||
if uid != "" {
|
||||
uidNumber, err = strconv.ParseInt(uid, 10, 64)
|
||||
if err != nil {
|
||||
render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "Cannot use the uidnumber provided"))
|
||||
o.logger.Error().Err(err).Str("userid", userid).Msg("Cannot use the uidnumber provided")
|
||||
return
|
||||
}
|
||||
}
|
||||
if gid != "" {
|
||||
gidNumber, err = strconv.ParseInt(gid, 10, 64)
|
||||
if err != nil {
|
||||
render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "Cannot use the gidnumber provided"))
|
||||
o.logger.Error().Err(err).Str("userid", userid).Msg("Cannot use the gidnumber provided")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// fallbacks
|
||||
/* TODO decide if we want to make these fallbacks. Keep in mind:
|
||||
- ocis requires a username and email
|
||||
- the username should really be different from the userid
|
||||
if username == "" {
|
||||
username = userid
|
||||
}
|
||||
if displayname == "" {
|
||||
displayname = username
|
||||
}
|
||||
*/
|
||||
|
||||
newAccount := &accounts.Account{
|
||||
DisplayName: displayname,
|
||||
PreferredName: username,
|
||||
OnPremisesSamAccountName: username,
|
||||
PasswordProfile: &accounts.PasswordProfile{
|
||||
Password: password,
|
||||
},
|
||||
Id: userid,
|
||||
Mail: email,
|
||||
AccountEnabled: true,
|
||||
}
|
||||
|
||||
if uidNumber != 0 {
|
||||
newAccount.UidNumber = uidNumber
|
||||
}
|
||||
|
||||
if gidNumber != 0 {
|
||||
newAccount.GidNumber = gidNumber
|
||||
}
|
||||
|
||||
account, err := o.getAccountService().CreateAccount(r.Context(), &accounts.CreateAccountRequest{
|
||||
Account: newAccount,
|
||||
})
|
||||
if err != nil {
|
||||
merr := merrors.FromError(err)
|
||||
if merr.Code == http.StatusBadRequest {
|
||||
render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, merr.Detail))
|
||||
} else {
|
||||
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
|
||||
}
|
||||
o.logger.Error().Err(err).Str("userid", userid).Msg("could not add user")
|
||||
// TODO check error if account already existed
|
||||
return
|
||||
}
|
||||
|
||||
// remove password from log if it is set
|
||||
if account.PasswordProfile != nil {
|
||||
account.PasswordProfile.Password = ""
|
||||
}
|
||||
o.logger.Debug().Interface("account", account).Msg("added user")
|
||||
|
||||
// mimic the oc10 bool as string for the user enabled property
|
||||
var enabled string
|
||||
if account.AccountEnabled {
|
||||
enabled = "true"
|
||||
} else {
|
||||
enabled = "false"
|
||||
}
|
||||
render.Render(w, r, response.DataRender(&data.User{
|
||||
UserID: account.Id,
|
||||
Username: account.PreferredName,
|
||||
DisplayName: account.DisplayName,
|
||||
LegacyDisplayName: account.DisplayName,
|
||||
Email: account.Mail,
|
||||
UIDNumber: account.UidNumber,
|
||||
GIDNumber: account.UidNumber,
|
||||
Enabled: enabled,
|
||||
}))
|
||||
}
|
||||
|
||||
// EditUser creates a new user account
|
||||
func (o Ocs) EditUser(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO this endpoint needs authentication
|
||||
req := accounts.UpdateAccountRequest{
|
||||
Account: &accounts.Account{
|
||||
Id: chi.URLParam(r, "userid"),
|
||||
},
|
||||
}
|
||||
key := r.PostFormValue("key")
|
||||
value := r.PostFormValue("value")
|
||||
|
||||
switch key {
|
||||
case "email":
|
||||
req.Account.Mail = value
|
||||
req.UpdateMask = &fieldmaskpb.FieldMask{Paths: []string{"Mail"}}
|
||||
case "username":
|
||||
req.Account.PreferredName = value
|
||||
req.Account.OnPremisesSamAccountName = value
|
||||
req.UpdateMask = &fieldmaskpb.FieldMask{Paths: []string{"PreferredName", "OnPremisesSamAccountName"}}
|
||||
case "password":
|
||||
req.Account.PasswordProfile = &accounts.PasswordProfile{
|
||||
Password: value,
|
||||
}
|
||||
req.UpdateMask = &fieldmaskpb.FieldMask{Paths: []string{"PasswordProfile.Password"}}
|
||||
case "displayname", "display":
|
||||
req.Account.DisplayName = value
|
||||
req.UpdateMask = &fieldmaskpb.FieldMask{Paths: []string{"DisplayName"}}
|
||||
default:
|
||||
// https://github.com/owncloud/core/blob/24b7fa1d2604a208582055309a5638dbd9bda1d1/apps/provisioning_api/lib/Users.php#L321
|
||||
render.Render(w, r, response.ErrRender(103, "unknown key '"+key+"'"))
|
||||
return
|
||||
}
|
||||
|
||||
account, err := o.getAccountService().UpdateAccount(r.Context(), &req)
|
||||
if err != nil {
|
||||
merr := merrors.FromError(err)
|
||||
switch merr.Code {
|
||||
case http.StatusNotFound:
|
||||
render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found"))
|
||||
case http.StatusBadRequest:
|
||||
render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, merr.Detail))
|
||||
default:
|
||||
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
|
||||
}
|
||||
o.logger.Error().Err(err).Str("userid", req.Account.Id).Msg("could not edit user")
|
||||
return
|
||||
}
|
||||
|
||||
// remove password from log if it is set
|
||||
if account.PasswordProfile != nil {
|
||||
account.PasswordProfile.Password = ""
|
||||
}
|
||||
|
||||
o.logger.Debug().Interface("account", account).Msg("updated user")
|
||||
render.Render(w, r, response.DataRender(struct{}{}))
|
||||
}
|
||||
|
||||
// DeleteUser deletes a user
|
||||
func (o Ocs) DeleteUser(w http.ResponseWriter, r *http.Request) {
|
||||
req := accounts.DeleteAccountRequest{
|
||||
Id: chi.URLParam(r, "userid"),
|
||||
}
|
||||
|
||||
_, err := o.getAccountService().DeleteAccount(r.Context(), &req)
|
||||
if err != nil {
|
||||
merr := merrors.FromError(err)
|
||||
if merr.Code == http.StatusNotFound {
|
||||
render.Render(w, r, response.ErrRender(data.MetaNotFound.StatusCode, "The requested user could not be found"))
|
||||
} else {
|
||||
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, err.Error()))
|
||||
}
|
||||
o.logger.Error().Err(err).Str("userid", req.Id).Msg("could not delete user")
|
||||
return
|
||||
}
|
||||
|
||||
o.logger.Debug().Str("userid", req.Id).Msg("deleted user")
|
||||
render.Render(w, r, response.DataRender(struct{}{}))
|
||||
}
|
||||
|
||||
// GetSigningKey returns the signing key for the current user. It will create it on the fly if it does not exist
|
||||
// The signing key is part of the user settings and is used by the proxy to authenticate requests
|
||||
// Currently, the username is used as the OC-Credential
|
||||
func (o Ocs) GetSigningKey(w http.ResponseWriter, r *http.Request) {
|
||||
u, ok := user.ContextGetUser(r.Context())
|
||||
if !ok {
|
||||
//o.logger.Error().Msg("missing user in context")
|
||||
render.Render(w, r, response.ErrRender(data.MetaBadRequest.StatusCode, "missing user in context"))
|
||||
return
|
||||
}
|
||||
|
||||
// use the user's UUID
|
||||
userID := u.Id.OpaqueId
|
||||
|
||||
c := storepb.NewStoreService("com.owncloud.api.store", grpc.NewClient())
|
||||
res, err := c.Read(r.Context(), &storepb.ReadRequest{
|
||||
Options: &storepb.ReadOptions{
|
||||
Database: "proxy",
|
||||
Table: "signing-keys",
|
||||
},
|
||||
Key: userID,
|
||||
})
|
||||
if err == nil && len(res.Records) > 0 {
|
||||
render.Render(w, r, response.DataRender(&data.SigningKey{
|
||||
User: userID,
|
||||
SigningKey: string(res.Records[0].Value),
|
||||
}))
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
e := merrors.Parse(err.Error())
|
||||
if e.Code == http.StatusNotFound {
|
||||
// not found is ok, so we can continue and generate the key on the fly
|
||||
} else {
|
||||
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, "error reading from store"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// try creating it
|
||||
key := make([]byte, 64)
|
||||
_, err = rand.Read(key[:])
|
||||
if err != nil {
|
||||
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, "could not generate signing key"))
|
||||
return
|
||||
}
|
||||
signingKey := hex.EncodeToString(key)
|
||||
|
||||
_, err = c.Write(r.Context(), &storepb.WriteRequest{
|
||||
Options: &storepb.WriteOptions{
|
||||
Database: "proxy",
|
||||
Table: "signing-keys",
|
||||
},
|
||||
Record: &storepb.Record{
|
||||
Key: userID,
|
||||
Value: []byte(signingKey),
|
||||
// TODO Expiry?
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
//o.logger.Error().Err(err).Msg("error writing key")
|
||||
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, "could not persist signing key"))
|
||||
return
|
||||
}
|
||||
|
||||
render.Render(w, r, response.DataRender(&data.SigningKey{
|
||||
User: userID,
|
||||
SigningKey: signingKey,
|
||||
}))
|
||||
}
|
||||
|
||||
// ListUsers lists the users
|
||||
func (o Ocs) ListUsers(w http.ResponseWriter, r *http.Request) {
|
||||
search := r.URL.Query().Get("search")
|
||||
query := ""
|
||||
if search != "" {
|
||||
query = fmt.Sprintf("id eq '%s' or on_premises_sam_account_name eq '%s'", escapeValue(search), escapeValue(search))
|
||||
}
|
||||
|
||||
res, err := o.getAccountService().ListAccounts(r.Context(), &accounts.ListAccountsRequest{
|
||||
Query: query,
|
||||
})
|
||||
if err != nil {
|
||||
o.logger.Err(err).Msg("could not list users")
|
||||
render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, "could not list users"))
|
||||
return
|
||||
}
|
||||
|
||||
users := []string{}
|
||||
for i := range res.Accounts {
|
||||
users = append(users, res.Accounts[i].Id)
|
||||
}
|
||||
|
||||
render.Render(w, r, response.DataRender(&data.Users{Users: users}))
|
||||
}
|
||||
|
||||
// escapeValue escapes all special characters in the value
|
||||
func escapeValue(value string) string {
|
||||
return strings.ReplaceAll(value, "'", "''")
|
||||
}
|
||||
19
ocs/pkg/version/version.go
Normal file
19
ocs/pkg/version/version.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package version
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
// String gets defined by the build system.
|
||||
String = "0.0.0"
|
||||
|
||||
// Date indicates the build date.
|
||||
Date = "00000000"
|
||||
)
|
||||
|
||||
// Compiled returns the compile time of this service.
|
||||
func Compiled() time.Time {
|
||||
t, _ := time.Parse("20060102", Date)
|
||||
return t
|
||||
}
|
||||
2
ocs/reflex.conf
Normal file
2
ocs/reflex.conf
Normal file
@@ -0,0 +1,2 @@
|
||||
# backend
|
||||
-r '^(cmd|pkg)/.*\.go$' -R '^node_modules/' -s -- sh -c 'make bin/ocis-ocs-debug && bin/ocis-ocs-debug --log-level debug server --debug-pprof --debug-zpages'
|
||||
8
ocs/tools.go
Normal file
8
ocs/tools.go
Normal file
@@ -0,0 +1,8 @@
|
||||
// +build tools
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "github.com/UnnoTed/fileb0x"
|
||||
_ "github.com/restic/calens"
|
||||
)
|
||||
Reference in New Issue
Block a user