Add 'ocis-pkg/' from commit '72d605ba3857d0b972ddd72e226d8a5360fb480d'

git-subtree-dir: ocis-pkg
git-subtree-mainline: 4c12bed11b
git-subtree-split: 72d605ba38
This commit is contained in:
A.Unger
2020-09-18 12:34:50 +02:00
69 changed files with 4459 additions and 0 deletions

6
ocis-pkg/.codacy.yml Normal file
View File

@@ -0,0 +1,6 @@
---
exclude_paths:
- CHANGELOG.md
- changelog/**
...

282
ocis-pkg/.drone.star Normal file
View File

@@ -0,0 +1,282 @@
def main(ctx):
stages = [
testing(ctx),
]
after = [
changelog(ctx),
publish(ctx),
]
return 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 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': 'webhippie/golang:1.13',
'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': [
'testing',
],
'trigger': {
'ref': [
'refs/heads/master',
'refs/pull/**',
],
},
}
def publish(ctx):
return {
'kind': 'pipeline',
'type': 'docker',
'name': 'publish',
'platform': {
'os': 'linux',
'arch': 'amd64',
},
'steps': [
{
'name': 'prepare',
'image': 'owncloud/alpine:latest',
'pull': 'always',
'commands': [
'mkdir -p dist',
],
},
{
'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],
],
},
{
'name': 'release',
'image': 'plugins/github-release:1',
'pull': 'always',
'settings': {
'api_key': {
'from_secret': 'github_token',
},
'files': [],
'title': ctx.build.ref.replace("refs/tags/v", ""),
'note': 'dist/CHANGELOG.md',
'overwrite': True,
'prerelease': len(ctx.build.ref.split("-")) > 1,
},
},
],
'depends_on': [
'testing',
],
'trigger': {
'ref': [
'refs/tags/**',
],
},
}

35
ocis-pkg/.editorconfig Normal file
View 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
ocis-pkg/.github/config.yml vendored Normal file
View 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-pkg/blob/master/changelog/README.md) item based on your changes.
updateDocsWhiteList:
- Tests-only
- tests-only
- Tests-Only
updateDocsTargetFiles:
- changelog/unreleased/

0
ocis-pkg/.github/issue_template.md vendored Normal file
View File

View File

98
ocis-pkg/.github/settings.yml vendored Normal file
View File

@@ -0,0 +1,98 @@
---
repository:
name: ocis-pkg
description: ':atom_symbol: Shared library for oCIS'
homepage: http://godoc.org/github.com/owncloud/ocis-pkg/
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
...

1
ocis-pkg/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
coverage.out

318
ocis-pkg/CHANGELOG.md Normal file
View File

@@ -0,0 +1,318 @@
# Changelog for [unreleased] (UNRELEASED)
The following sections list the changes in ocis-pkg unreleased.
[unreleased]: https://github.com/owncloud/ocis-pkg/compare/v2.4.0...master
## Summary
* Change - Unwrap roleIDs from access-token into metadata context: [#59](https://github.com/owncloud/ocis-pkg/pull/59)
* Change - Provide cache for roles: [#59](https://github.com/owncloud/ocis-pkg/pull/59)
* Change - Roles manager: [#60](https://github.com/owncloud/ocis-pkg/pull/60)
## Details
* Change - Unwrap roleIDs from access-token into metadata context: [#59](https://github.com/owncloud/ocis-pkg/pull/59)
We pass the RoleIDs from the access-token into the metadata context.
https://github.com/owncloud/ocis-pkg/pull/59
* Change - Provide cache for roles: [#59](https://github.com/owncloud/ocis-pkg/pull/59)
In order to work efficiently with permissions we provide a cache for roles and a middleware to
update the cache based on roleIDs from the metadata context. It can be used to check permissions
in service handlers.
https://github.com/owncloud/ocis-pkg/pull/59
* Change - Roles manager: [#60](https://github.com/owncloud/ocis-pkg/pull/60)
We combined the roles middleware and cache into a roles manager. The manager doesn't expose the
cache anymore and manages the state of the cache by fetching roles from the role service which
don't exist in the cache, yet.
https://github.com/owncloud/ocis-pkg/pull/60
# Changelog for [2.4.0] (2020-08-25)
The following sections list the changes in ocis-pkg 2.4.0.
[2.4.0]: https://github.com/owncloud/ocis-pkg/compare/v2.3.0...v2.4.0
## Summary
* Change - Use go-micro's metadata context for account id: [#56](https://github.com/owncloud/ocis-pkg/pull/56)
## Details
* Change - Use go-micro's metadata context for account id: [#56](https://github.com/owncloud/ocis-pkg/pull/56)
We switched to using go-micro's metadata context for reliably passing the AccountID in the
context across service boundaries.
https://github.com/owncloud/ocis-pkg/pull/56
# Changelog for [2.3.0] (2020-08-17)
The following sections list the changes in ocis-pkg 2.3.0.
[2.3.0]: https://github.com/owncloud/ocis-pkg/compare/v2.2.1...v2.3.0
## Summary
* Bugfix - Remove redigo 2.0.0+incompatible dependency: [#33](https://github.com/owncloud/ocis-graph/pull/33)
* Change - Add middleware for x-access-token distmantling: [#46](https://github.com/owncloud/ocis-pkg/pull/46)
* Enhancement - Add `ocis.id` and numeric id claims: [#50](https://github.com/owncloud/ocis-pkg/pull/50)
## Details
* Bugfix - Remove redigo 2.0.0+incompatible dependency: [#33](https://github.com/owncloud/ocis-graph/pull/33)
Get rid of redigo v2.0.0
https://github.com/owncloud/ocis-graph/pull/33
* Change - Add middleware for x-access-token distmantling: [#46](https://github.com/owncloud/ocis-pkg/pull/46)
We added a middleware that dismantles the `x-access-token` from the request header and makes
it available in the context.
https://github.com/owncloud/ocis-pkg/pull/46
* Enhancement - Add `ocis.id` and numeric id claims: [#50](https://github.com/owncloud/ocis-pkg/pull/50)
We added an `ocis.id` claim to the OIDC standard claims. It allows the idp to send a stable
identifier that can be exposed to the outside world (in contrast to sub, which might change
whens the IdP changes).
In addition we added `uidnumber` and `gidnumber` claims, which can be used by the IdP as well.
They will be used by storage providers that integrate with an existing LDAP server.
https://github.com/owncloud/ocis-pkg/pull/50
# Changelog for [2.2.1] (2020-04-21)
The following sections list the changes in ocis-pkg 2.2.1.
[2.2.1]: https://github.com/owncloud/ocis-pkg/compare/v2.2.0...v2.2.1
## Summary
* Bugfix - Pass flags to micro service: [#44](https://github.com/owncloud/ocis-pkg/pull/44)
## Details
* Bugfix - Pass flags to micro service: [#44](https://github.com/owncloud/ocis-pkg/pull/44)
Passed flags to the micro service to be able to pass cli arguments while starting a grpc service.
https://github.com/owncloud/ocis-pkg/pull/44
# Changelog for [2.2.0] (2020-03-30)
The following sections list the changes in ocis-pkg 2.2.0.
[2.2.0]: https://github.com/owncloud/ocis-pkg/compare/v2.1.0...v2.2.0
## Summary
* Change - Add header to cors handler: [#41](https://github.com/owncloud/ocis-pkg/issues/41)
## Details
* Change - Add header to cors handler: [#41](https://github.com/owncloud/ocis-pkg/issues/41)
The `x-requested-with` header was added to allow ajax requests.
https://github.com/owncloud/ocis-pkg/issues/41
# Changelog for [2.1.0] (2020-03-16)
The following sections list the changes in ocis-pkg 2.1.0.
[2.1.0]: https://github.com/owncloud/ocis-pkg/compare/v2.0.2...v2.1.0
## Summary
* Enhancement - Tracing middleware: [#35](https://github.com/owncloud/ocis-pkg/pull/35/)
## Details
* Enhancement - Tracing middleware: [#35](https://github.com/owncloud/ocis-pkg/pull/35/)
A new tracing middleware has been added to unpack context propagation
https://github.com/owncloud/ocis-pkg/pull/35/
# Changelog for [2.0.2] (2020-03-04)
The following sections list the changes in ocis-pkg 2.0.2.
[2.0.2]: https://github.com/owncloud/ocis-pkg/compare/v2.0.1...v2.0.2
## Summary
* Enhancement - Allow http services to register handlers: [#33](https://github.com/owncloud/ocis-pkg/pull/33)
## Details
* Enhancement - Allow http services to register handlers: [#33](https://github.com/owncloud/ocis-pkg/pull/33)
Added a handler option on http services
https://github.com/owncloud/ocis-pkg/pull/33
# Changelog for [2.0.1] (2020-02-05)
The following sections list the changes in ocis-pkg 2.0.1.
[2.0.1]: https://github.com/owncloud/ocis-pkg/compare/v2.0.0...v2.0.1
## Summary
* Bugfix - Fix Module Path: [#25](https://github.com/owncloud/ocis-pkg/pull/25)
* Bugfix - Change import paths to ocis-pkg/v2: [#27](https://github.com/owncloud/ocis-pkg/pull/27)
## Details
* Bugfix - Fix Module Path: [#25](https://github.com/owncloud/ocis-pkg/pull/25)
The module version must be in the path. See
https://github.com/golang/go/wiki/Modules#semantic-import-versioning for more
information. > If the module is version v2 or higher, the major version of the module must be
included as a /vN at the end of the module paths used in go.mod files (e.g., module
github.com/my/mod/v2, require github.com/my/mod/v2 v2.0.1) and in the package import path
(e.g., import "github.com/my/mod/v2/mypkg"). This includes the paths used in go get
commands (e.g., go get github.com/my/mod/v2@v2.0.1. Note there is both a /v2 and a @v2.0.1 in
that example. One way to think about it is that the module name now includes the /v2, so include
/v2 whenever you are using the module name).
https://github.com/owncloud/ocis-pkg/pull/25
* Bugfix - Change import paths to ocis-pkg/v2: [#27](https://github.com/owncloud/ocis-pkg/pull/27)
Changed the import paths to the current version
https://github.com/owncloud/ocis-pkg/pull/27
# Changelog for [2.0.0] (2020-02-04)
The following sections list the changes in ocis-pkg 2.0.0.
[2.0.0]: https://github.com/owncloud/ocis-pkg/compare/v1.3.0...v2.0.0
## Summary
* Change - Upgrade the micro libraries: [#22](https://github.com/owncloud/ocis-pkg/pull/22)
## Details
* Change - Upgrade the micro libraries: [#22](https://github.com/owncloud/ocis-pkg/pull/22)
Upgraded the go-micro libraries to v2.
https://github.com/owncloud/ocis-pkg/pull/22
# Changelog for [1.3.0] (2020-01-20)
The following sections list the changes in ocis-pkg 1.3.0.
[1.3.0]: https://github.com/owncloud/ocis-pkg/compare/v1.2.0...v1.3.0
## Summary
* Bugfix - Fix serving static assets: [#14](https://github.com/owncloud/ocis-pkg/pull/14)
* Change - Add TLS support for http services: [#19](https://github.com/owncloud/ocis-pkg/issues/19)
* Enhancement - Introduce OpenID Connect middleware: [#8](https://github.com/owncloud/ocis-pkg/issues/8)
## Details
* Bugfix - Fix serving static assets: [#14](https://github.com/owncloud/ocis-pkg/pull/14)
Ocis-hello used "/" as root. adding another / caused the static middleware to always fail
stripping that prefix. All requests will return 404. Setting root to `""` in the `ocis-hello`
flag does not work because Chi reports that routes need to start with `/`.
`path.Clean(root+"/")` would yield `""` for `root="/"`.
https://github.com/owncloud/ocis-pkg/pull/14
* Change - Add TLS support for http services: [#19](https://github.com/owncloud/ocis-pkg/issues/19)
`ocis-pkg` http services support TLS. The idea behind is setting the issuer on phoenix's
`config.json` to `https`. Or in other words, use https to access the Kopano extension, and
authenticate using an SSL certificate.
https://github.com/owncloud/ocis-pkg/issues/19
* Enhancement - Introduce OpenID Connect middleware: [#8](https://github.com/owncloud/ocis-pkg/issues/8)
Added an openid connect middleware that will try to authenticate users using OpenID Connect.
The claims will be added to the context of the request.
https://github.com/owncloud/ocis-pkg/issues/8
# Changelog for [1.2.0] (2019-12-09)
The following sections list the changes in ocis-pkg 1.2.0.
[1.2.0]: https://github.com/owncloud/ocis-pkg/compare/v1.1.0...v1.2.0
## Summary
* Change - Add root path to static middleware: [#9](https://github.com/owncloud/ocis-pkg/issues/9)
## Details
* Change - Add root path to static middleware: [#9](https://github.com/owncloud/ocis-pkg/issues/9)
Currently the `Static` middleware always serves from the root path, but all our HTTP handlers
accept a custom root path which also got to be applied to the static file handling.
https://github.com/owncloud/ocis-pkg/issues/9
# Changelog for [1.1.0] (2019-12-06)
The following sections list the changes in ocis-pkg 1.1.0.
[1.1.0]: https://github.com/owncloud/ocis-pkg/compare/v1.0.0...v1.1.0
## Summary
* Change - Better log level handling within micro: [#2](https://github.com/owncloud/ocis-pkg/issues/2)
## Details
* Change - Better log level handling within micro: [#2](https://github.com/owncloud/ocis-pkg/issues/2)
Currently every log message from the micro internals are logged with the info level, we really
need to respect the proper defined log level within our log wrapper package.
https://github.com/owncloud/ocis-pkg/issues/2
# Changelog for [1.0.0] (2019-12-05)
The following sections list the changes in ocis-pkg 1.0.0.
[1.0.0]: https://github.com/owncloud/ocis-pkg/compare/63fa90a673cbc3238a503ea5e6304f1db7fdf47b...v1.0.0
## Summary
* Change - Initial release of basic version: [#1](https://github.com/owncloud/ocis-pkg/issues/1)
## Details
* Change - Initial release of basic version: [#1](https://github.com/owncloud/ocis-pkg/issues/1)
Just prepared an initial basic version to have some shared functionality published which can
be used by all other ownCloud Infinite Scale extensions.
https://github.com/owncloud/ocis-pkg/issues/1

202
ocis-pkg/LICENSE Normal file
View 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.

70
ocis-pkg/Makefile Normal file
View File

@@ -0,0 +1,70 @@
SHELL := bash
NAME := ocis-pkg
IMPORT := github.com/owncloud/$(NAME)
BIN := bin
DIST := dist
ifeq ($(OS), Windows_NT)
UNAME := Windows
else
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)
GENERATE ?= $(PACKAGES)
TAGS ?=
LDFLAGS += -s -w
GCFLAGS += all=-N -l
.PHONY: all
all: build
.PHONY: sync
sync:
go mod download
.PHONY: clean
clean:
go clean -i ./...
rm -rf $(BIN) $(DIST)
.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: build
build:
$(GOBUILD) -v -tags '$(TAGS)' -ldflags '$(LDFLAGS)' ./...

45
ocis-pkg/README.md Normal file
View File

@@ -0,0 +1,45 @@
# ownCloud Infinite Scale: Pkg
[![Build Status](https://cloud.drone.io/api/badges/owncloud/ocis-pkg/status.svg)](https://cloud.drone.io/owncloud/ocis-pkg)
[![Gitter chat](https://badges.gitter.im/cs3org/reva.svg)](https://gitter.im/cs3org/reva)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/27b2cb74a61547329f9b3c56d90bd05c)](https://www.codacy.com/manual/owncloud/ocis-pkg?utm_source=github.com&utm_medium=referral&utm_content=owncloud/ocis-pkg&utm_campaign=Badge_Grade)
[![Go Doc](https://godoc.org/github.com/owncloud/ocis-pkg?status.svg)](http://godoc.org/github.com/owncloud/ocis-pkg)
[![Go Report](http://goreportcard.com/badge/github.com/owncloud/ocis-pkg)](http://goreportcard.com/report/github.com/owncloud/ocis-pkg)
This package defines some boilerplate code that reduces the code duplication within the ownCloud Infinite Scale microservice architecture. It can't be used standalone as the is a pure library. For further information about the available packages please read the source code or take a loog at [GoDoc](http://godoc.org/github.com/owncloud/ocis-pkg).
## Install
Import the required packages within your ownCloud Infinite Scale extensions and you are good to go.
## 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-pkg.git
cd ocis-pkg
make vet
make staticcheck
make lint
make test
```
## 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>
```

View File

@@ -0,0 +1,30 @@
package account
import (
"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
// JWTSecret is the jwt secret for the reva token manager
JWTSecret string
}
// Logger provides a function to set the logger option.
func Logger(l log.Logger) Option {
return func(o *Options) {
o.Logger = l
}
}
// JWTSecret provides a function to set the jwt secret option.
func JWTSecret(s string) Option {
return func(o *Options) {
o.JWTSecret = s
}
}

View File

@@ -0,0 +1,6 @@
Change: Initial release of basic version
Just prepared an initial basic version to have some shared functionality
published which can be used by all other ownCloud Infinite Scale extensions.
https://github.com/owncloud/ocis-pkg/issues/1

View File

@@ -0,0 +1,7 @@
Change: Better log level handling within micro
Currently every log message from the micro internals are logged with the info
level, we really need to respect the proper defined log level within our log
wrapper package.
https://github.com/owncloud/ocis-pkg/issues/2

View File

@@ -0,0 +1,7 @@
Change: Add root path to static middleware
Currently the `Static` middleware always serves from the root path, but all our
HTTP handlers accept a custom root path which also got to be applied to the
static file handling.
https://github.com/owncloud/ocis-pkg/issues/9

View File

@@ -0,0 +1,8 @@
Bugfix: Fix serving static assets
ocis-hello used "/" as root. adding another / caused the static middleware to
always fail stripping that prefix. All requests will return 404. Setting root
to `""` in the `ocis-hello` flag does not work because Chi reports that routes
need to start with `/`. `path.Clean(root+"/")` would yield `""` for `root="/"`.
https://github.com/owncloud/ocis-pkg/pull/14

View File

@@ -0,0 +1,6 @@
Enhancement: Introduce OpenID Connect middleware
Added an openid connect middleware that will try to authenticate users using
OpenID Connect. The claims will be added to the context of the request.
https://github.com/owncloud/ocis-pkg/issues/8

View File

@@ -0,0 +1,7 @@
Change: Add TLS support for http services
`ocis-pkg` http services support TLS. The idea behind is setting the issuer on
phoenix's `config.json` to `https`. Or in other words, use https to access the
Kopano extension, and authenticate using an SSL certificate.
https://github.com/owncloud/ocis-pkg/issues/19

View File

@@ -0,0 +1,5 @@
Change: upgrade the micro libraries
Upgraded the go-micro libraries to v2.
https://github.com/owncloud/ocis-pkg/pull/22

View File

@@ -0,0 +1,6 @@
Bugfix: Fix Module Path
The module version must be in the path. See https://github.com/golang/go/wiki/Modules#semantic-import-versioning for more information.
> If the module is version v2 or higher, the major version of the module must be included as a /vN at the end of the module paths used in go.mod files (e.g., module github.com/my/mod/v2, require github.com/my/mod/v2 v2.0.1) and in the package import path (e.g., import "github.com/my/mod/v2/mypkg"). This includes the paths used in go get commands (e.g., go get github.com/my/mod/v2@v2.0.1. Note there is both a /v2 and a @v2.0.1 in that example. One way to think about it is that the module name now includes the /v2, so include /v2 whenever you are using the module name).
https://github.com/owncloud/ocis-pkg/pull/25

View File

@@ -0,0 +1,5 @@
Bugfix: change import paths to ocis-pkg/v2
Changed the import paths to the current version
https://github.com/owncloud/ocis-pkg/pull/27

View File

@@ -0,0 +1,5 @@
Enhancement: Allow http services to register handlers
Added a handler option on http services
https://github.com/owncloud/ocis-pkg/pull/33

View File

@@ -0,0 +1,5 @@
Enhancement: Tracing middleware
A new tracing middleware has been added to unpack context propagation
https://github.com/owncloud/ocis-pkg/pull/35/

View File

@@ -0,0 +1,5 @@
Change: Add header to cors handler
The `x-requested-with` header was added to allow ajax requests.
https://github.com/owncloud/ocis-pkg/issues/41

View File

@@ -0,0 +1,5 @@
Bugfix: pass flags to micro service
Passed flags to the micro service to be able to pass cli arguments while starting a grpc service.
https://github.com/owncloud/ocis-pkg/pull/44

View File

@@ -0,0 +1,6 @@
Change: add middleware for x-access-token distmantling
We added a middleware that dismantles the `x-access-token` from the request header and makes
it available in the context.
https://github.com/owncloud/ocis-pkg/pull/46

View File

@@ -0,0 +1,7 @@
Enhancement: add `ocis.id` and numeric id claims
We added an `ocis.id` claim to the OIDC standard claims. It allows the idp to send a stable identifier that can be exposed to the outside world (in contrast to sub, which might change whens the IdP changes).
In addition we added `uidnumber` and `gidnumber` claims, which can be used by the IdP as well. They will be used by storage providers that integrate with an existing LDAP server.
https://github.com/owncloud/ocis-pkg/pull/50

View File

@@ -0,0 +1,5 @@
Bugfix: remove redigo 2.0.0+incompatible dependency
get rid of redigo v2.0.0
https://github.com/owncloud/ocis-graph/pull/33

View File

@@ -0,0 +1,6 @@
Change: use go-micro's metadata context for account id
We switched to using go-micro's metadata context for reliably passing the AccountID in the context
across service boundaries.
https://github.com/owncloud/ocis-pkg/pull/56

View 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-pkg {{ .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-pkg/compare/v{{ $previousVersion }}...master
{{ else -}}
[{{ .Version }}]: https://github.com/owncloud/ocis-pkg/compare/v{{ $previousVersion }}...v{{ .Version }}
{{ end -}}
{{ end -}}
{{- /* last version managed by calens, end of the loop */ -}}
{{ if eq .Version "1.0.0" -}}
[{{ .Version }}]: https://github.com/owncloud/ocis-pkg/compare/63fa90a673cbc3238a503ea5e6304f1db7fdf47b...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 -}}

View File

@@ -0,0 +1,10 @@
# 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.
We're using kebab case for naming the changelog files:
`kebab-case-looks-like-this`

View 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-pkg/issues/1234
https://github.com/owncloud/ocis-pkg/pull/55555

View File

View File

@@ -0,0 +1,5 @@
Change: Unwrap roleIDs from access-token into metadata context
We pass the RoleIDs from the access-token into the metadata context.
https://github.com/owncloud/ocis-pkg/pull/59

View File

@@ -0,0 +1,7 @@
Change: Provide cache for roles
In order to work efficiently with permissions we provide a cache for roles and a
middleware to update the cache based on roleIDs from the metadata context. It can be
used to check permissions in service handlers.
https://github.com/owncloud/ocis-pkg/pull/59

View File

@@ -0,0 +1,6 @@
Change: Roles manager
We combined the roles middleware and cache into a roles manager. The manager doesn't expose the cache anymore and manages
the state of the cache by fetching roles from the role service which don't exist in the cache, yet.
https://github.com/owncloud/ocis-pkg/pull/60

View File

@@ -0,0 +1,14 @@
package conversions
import "strings"
// StringToSliceString splits a string into a slice string according to separator
func StringToSliceString(src string, sep string) []string {
var parts []string
parsed := strings.Split(src, sep)
for _, v := range parsed {
parts = append(parts, strings.TrimSpace(v))
}
return parts
}

View File

@@ -0,0 +1,35 @@
package conversions
import "testing"
var scenarios = []struct {
name string
input string
separator string
out []string
}{
{
"comma separated input",
"a, b, c, d",
",",
[]string{"a", "b", "c", "d"},
}, {
"space separated input",
"a b c d",
" ",
[]string{"a", "b", "c", "d"},
},
}
func TestStringToSliceString(t *testing.T) {
for _, tt := range scenarios {
t.Run(tt.name, func(t *testing.T) {
s := StringToSliceString(tt.input, tt.separator)
for i, v := range tt.out {
if tt.out[i] != v {
t.Errorf("got %q, want %q", s, tt.out)
}
}
})
}
}

26
ocis-pkg/go.mod Normal file
View File

@@ -0,0 +1,26 @@
module github.com/owncloud/ocis-pkg/v2
go 1.13
require (
github.com/ascarter/requestid v0.0.0-20170313220838-5b76ab3d4aee
github.com/coreos/go-oidc v2.2.1+incompatible
github.com/cs3org/reva v1.1.0
github.com/go-chi/chi v4.1.2+incompatible
github.com/haya14busa/goverage v0.0.0-20180129164344-eec3514a20b5
github.com/justinas/alice v1.2.0
github.com/micro/cli/v2 v2.1.2
github.com/micro/go-micro/v2 v2.9.1
github.com/micro/go-plugins/wrapper/trace/opencensus/v2 v2.9.1
github.com/owncloud/ocis-settings v0.3.2-0.20200828091056-47af10a0e872
github.com/prometheus/client_golang v1.7.1
github.com/restic/calens v0.2.0
github.com/rs/zerolog v1.19.0
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce
go.opencensus.io v0.22.4
golang.org/x/lint v0.0.0-20200302205851-738671d3881b
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
honnef.co/go/tools v0.0.1-2020.1.5
)
replace google.golang.org/grpc => google.golang.org/grpc v1.26.0

1405
ocis-pkg/go.sum Normal file

File diff suppressed because it is too large Load Diff

172
ocis-pkg/log/log.go Normal file
View File

@@ -0,0 +1,172 @@
package log
import (
"fmt"
"os"
"strings"
"time"
mdlog "github.com/micro/go-micro/v2/debug/log"
mlog "github.com/micro/go-micro/v2/util/log"
"github.com/micro/go-micro/v2/util/ring"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
// Logger simply wraps the zerolog logger.
type Logger struct {
zerolog.Logger
}
// NewLogger initializes a new logger instance.
func NewLogger(opts ...Option) Logger {
options := newOptions(opts...)
switch strings.ToLower(options.Level) {
case "panic":
zerolog.SetGlobalLevel(zerolog.PanicLevel)
mlog.SetLevel(mlog.LevelFatal)
case "fatal":
zerolog.SetGlobalLevel(zerolog.FatalLevel)
mlog.SetLevel(mlog.LevelFatal)
case "error":
zerolog.SetGlobalLevel(zerolog.ErrorLevel)
mlog.SetLevel(mlog.LevelError)
case "warn":
zerolog.SetGlobalLevel(zerolog.WarnLevel)
mlog.SetLevel(mlog.LevelWarn)
case "info":
zerolog.SetGlobalLevel(zerolog.InfoLevel)
mlog.SetLevel(mlog.LevelInfo)
case "debug":
zerolog.SetGlobalLevel(zerolog.DebugLevel)
mlog.SetLevel(mlog.LevelDebug)
case "trace":
zerolog.SetGlobalLevel(zerolog.DebugLevel)
mlog.SetLevel(mlog.LevelTrace)
default:
zerolog.SetGlobalLevel(zerolog.InfoLevel)
mlog.SetLevel(mlog.LevelInfo)
}
var logger zerolog.Logger
if options.Pretty {
logger = log.Output(
zerolog.NewConsoleWriter(
func(w *zerolog.ConsoleWriter) {
w.TimeFormat = time.RFC3339
w.Out = os.Stderr
w.NoColor = !options.Color
},
),
)
} else {
logger = zerolog.New(os.Stderr)
}
logger = logger.With().
Str("service", options.Name).
Timestamp().
Logger()
mlog.SetLogger(
microZerolog{
logger: logger,
buffer: ring.New(mdlog.DefaultSize),
},
)
return Logger{
logger,
}
}
// microZerolog implements the required interface for the go-micro logger.
type microZerolog struct {
logger zerolog.Logger
buffer *ring.Buffer
}
func (mz microZerolog) Read(opts ...mdlog.ReadOption) ([]mdlog.Record, error) {
options := mdlog.ReadOptions{}
for _, o := range opts {
o(&options)
}
var entries []*ring.Entry
if !options.Since.IsZero() {
entries = mz.buffer.Since(options.Since)
}
if options.Count > 0 {
switch len(entries) > 0 {
case true:
if options.Count > len(entries) {
entries = entries[0:options.Count]
}
default:
entries = mz.buffer.Get(options.Count)
}
}
records := make([]mdlog.Record, 0, len(entries))
for _, entry := range entries {
record := mdlog.Record{
Timestamp: entry.Timestamp,
Message: entry.Value,
}
records = append(records, record)
}
return records, nil
}
func (mz microZerolog) Write(record mdlog.Record) error {
level := record.Metadata["level"]
mz.log(level, fmt.Sprint(record.Message))
mz.buffer.Put(record.Message)
return nil
}
func (mz microZerolog) Stream() (mdlog.Stream, error) {
stream, stop := mz.buffer.Stream()
records := make(chan mdlog.Record, 128)
last10 := mz.buffer.Get(10)
go func() {
for _, entry := range last10 {
records <- mdlog.Record{
Timestamp: entry.Timestamp,
Message: entry.Value,
Metadata: make(map[string]string),
}
}
for entry := range stream {
records <- mdlog.Record{
Timestamp: entry.Timestamp,
Message: entry.Value,
Metadata: make(map[string]string),
}
}
}()
return &logStream{
stream: records,
stop: stop,
}, nil
}
func (mz microZerolog) log(level string, msg string) {
l, err := zerolog.ParseLevel(level)
if err != nil {
l = zerolog.InfoLevel
}
mz.logger.WithLevel(l).Msg(msg)
// Invoke os.Exit because unlike zerolog.Logger.Fatal zerolog.Logger.WithLevel won't stop the execution.
if l == zerolog.FatalLevel {
os.Exit(1)
}
}

56
ocis-pkg/log/option.go Normal file
View File

@@ -0,0 +1,56 @@
package log
// Option defines a single option function.
type Option func(o *Options)
// Options defines the available options for this package.
type Options struct {
Name string
Level string
Pretty bool
Color bool
}
// newOptions initializes the available default options.
func newOptions(opts ...Option) Options {
opt := Options{
Name: "ocis",
Level: "info",
Pretty: true,
Color: true,
}
for _, o := range opts {
o(&opt)
}
return opt
}
// Name provides a function to set the name option.
func Name(val string) Option {
return func(o *Options) {
o.Name = val
}
}
// Level provides a function to set the level option.
func Level(val string) Option {
return func(o *Options) {
o.Level = val
}
}
// Pretty provides a function to set the pretty option.
func Pretty(val bool) Option {
return func(o *Options) {
o.Pretty = val
}
}
// Color provides a function to set the color option.
func Color(val bool) Option {
return func(o *Options) {
o.Color = val
}
}

24
ocis-pkg/log/stream.go Normal file
View File

@@ -0,0 +1,24 @@
package log
import (
mdlog "github.com/micro/go-micro/v2/debug/log"
)
type logStream struct {
stream <-chan mdlog.Record
stop chan bool
}
func (l *logStream) Chan() <-chan mdlog.Record {
return l.stream
}
func (l *logStream) Stop() error {
select {
case <-l.stop:
return nil
default:
close(l.stop)
}
return nil
}

View File

@@ -0,0 +1,65 @@
package middleware
import (
"context"
"net/http"
"github.com/cs3org/reva/pkg/token/manager/jwt"
"github.com/micro/go-micro/v2/metadata"
"github.com/owncloud/ocis-pkg/v2/account"
)
// newAccountOptions initializes the available default options.
func newAccountOptions(opts ...account.Option) account.Options {
opt := account.Options{}
for _, o := range opts {
o(&opt)
}
return opt
}
// AccountID serves as key for the account uuid in the context
const AccountID string = "Account-Id"
// RoleIDs serves as key for the roles in the context
const RoleIDs string = "Role-Ids"
// UUIDKey serves as key for the account uuid in the context
// Deprecated: UUIDKey exists for compatibility reasons. Use AccountID instead.
var UUIDKey struct{}
// ExtractAccountUUID provides a middleware to extract the account uuid from the x-access-token header value
// and write it to the context. If there is no x-access-token the middleware is omitted.
func ExtractAccountUUID(opts ...account.Option) func(http.Handler) http.Handler {
opt := newAccountOptions(opts...)
tokenManager, err := jwt.New(map[string]interface{}{
"secret": opt.JWTSecret,
"expires": int64(60),
})
if err != nil {
opt.Logger.Fatal().Err(err).Msgf("Could not initialize token-manager")
}
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("x-access-token")
if len(token) == 0 {
next.ServeHTTP(w, r)
return
}
user, err := tokenManager.DismantleToken(r.Context(), token)
if err != nil {
opt.Logger.Error().Err(err)
return
}
// Important: user.Id.OpaqueId is the AccountUUID. Set this way in the account uuid middleware in ocis-proxy.
// https://github.com/owncloud/ocis-proxy/blob/ea254d6036592cf9469d757d1295e0c4309d1e63/pkg/middleware/account_uuid.go#L109
ctx := context.WithValue(r.Context(), UUIDKey, user.Id.OpaqueId)
// TODO: implement token manager in cs3org/reva that uses generic metadata instead of access token from header.
ctx = metadata.Set(ctx, AccountID, user.Id.OpaqueId)
ctx = metadata.Set(ctx, RoleIDs, string(user.Opaque.Map["roles"].Value))
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}

View File

@@ -0,0 +1,49 @@
package middleware
import (
"net/http"
"time"
)
// Cache writes required cache headers to all requests.
func Cache(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate, value")
w.Header().Set("Expires", "Thu, 01 Jan 1970 00:00:00 GMT")
w.Header().Set("Last-Modified", time.Now().UTC().Format(http.TimeFormat))
next.ServeHTTP(w, r)
})
}
// Cors writes required cors headers to all requests.
func Cors(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != "OPTIONS" {
next.ServeHTTP(w, r)
} else {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "authorization, origin, content-type, accept, x-requested-with")
w.Header().Set("Allow", "HEAD, GET, POST, PUT, PATCH, DELETE, OPTIONS")
w.WriteHeader(http.StatusOK)
}
})
}
// Secure writes required access headers to all requests.
func Secure(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("X-Frame-Options", "DENY")
w.Header().Set("X-Content-Type-Options", "nosniff")
w.Header().Set("X-XSS-Protection", "1; mode=block")
if r.TLS != nil {
w.Header().Set("Strict-Transport-Security", "max-age=31536000")
}
next.ServeHTTP(w, r)
})
}

View File

@@ -0,0 +1,30 @@
package middleware
import (
"net/http"
"time"
"github.com/go-chi/chi/middleware"
"github.com/owncloud/ocis-pkg/v2/log"
)
// Logger is a middleware to log http requests.
func Logger(logger log.Logger) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
wrap := middleware.NewWrapResponseWriter(w, r.ProtoMajor)
next.ServeHTTP(wrap, r)
logger.Debug().
Str("request", r.Header.Get("X-Request-ID")).
Str("proto", r.Proto).
Str("method", r.Method).
Int("status", wrap.Status()).
Str("path", r.URL.Path).
Dur("duration", time.Since(start)).
Int("bytes", wrap.BytesWritten()).
Msg("")
})
}
}

View File

@@ -0,0 +1,107 @@
package middleware
import (
"context"
"crypto/tls"
"fmt"
"net/http"
"strings"
"time"
oidc "github.com/coreos/go-oidc"
ocisoidc "github.com/owncloud/ocis-pkg/v2/oidc"
"golang.org/x/oauth2"
)
// newOIDCOptions initializes the available default options.
func newOIDCOptions(opts ...ocisoidc.Option) ocisoidc.Options {
opt := ocisoidc.Options{}
for _, o := range opts {
o(&opt)
}
return opt
}
// OpenIDConnect provides a middleware to check access secured by a static token.
func OpenIDConnect(opts ...ocisoidc.Option) func(http.Handler) http.Handler {
opt := newOIDCOptions(opts...)
// set defaults
if opt.Realm == "" {
opt.Realm = opt.Endpoint
}
if len(opt.SigningAlgs) < 1 {
opt.SigningAlgs = []string{"RS256", "PS256"}
}
var oidcProvider *oidc.Provider
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
header := r.Header.Get("Authorization")
if header == "" || !strings.HasPrefix(header, "Bearer ") {
w.Header().Add("WWW-Authenticate", fmt.Sprintf(`Bearer realm="%s"`, opt.Realm))
http.Error(w, ErrInvalidToken.Error(), http.StatusUnauthorized)
return
}
token := header[7:]
tr := &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: opt.Insecure,
},
}
customHTTPClient := &http.Client{
Transport: tr,
Timeout: time.Second * 10,
}
customCtx := context.WithValue(r.Context(), oauth2.HTTPClient, customHTTPClient)
// use cached provider
if oidcProvider == nil {
// Initialize a provider by specifying the issuer URL.
// provider needs to be cached as when it is created
// it will fetch the keys from the issuer using the .well-known
// endpoint
provider, err := oidc.NewProvider(customCtx, opt.Endpoint)
if err != nil {
opt.Logger.Error().Err(err).Msg("could not initialize oidc provider")
w.WriteHeader(http.StatusInternalServerError)
return
}
oidcProvider = provider
}
// The claims we want to have
var claims ocisoidc.StandardClaims
// TODO cache userinfo for access token if we can determine the expiry (which works in case it is a jwt based access token)
oauth2Token := &oauth2.Token{
AccessToken: token,
}
userInfo, err := oidcProvider.UserInfo(customCtx, oauth2.StaticTokenSource(oauth2Token))
if err != nil {
opt.Logger.Error().Err(err).Str("token", string(token)).Msg("Failed to get userinfo")
http.Error(w, ErrInvalidToken.Error(), http.StatusUnauthorized)
return
}
// parse claims
if err := userInfo.Claims(&claims); err != nil {
opt.Logger.Error().Err(err).Interface("userinfo", userInfo).Msg("failed to unmarshal userinfo claims")
w.WriteHeader(http.StatusInternalServerError)
return
}
opt.Logger.Debug().Interface("claims", claims).Interface("userInfo", userInfo).Msg("unmarshalled userinfo")
// store claims in context
// uses the original context, not the one with probably reduced security
nr := r.WithContext(ocisoidc.NewContext(r.Context(), &claims))
next.ServeHTTP(w, nr)
})
}
}

View File

@@ -0,0 +1,18 @@
package middleware
import (
"net/http"
"github.com/tomasen/realip"
)
// RealIP is a middleware that sets a http.Request RemoteAddr.
func RealIP(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if ip := realip.RealIP(r); ip != "" {
r.RemoteAddr = ip
}
next.ServeHTTP(w, r)
})
}

View File

@@ -0,0 +1,12 @@
package middleware
import (
"net/http"
"github.com/ascarter/requestid"
)
// RequestID is a convenient middleware to inject a request id.
func RequestID(next http.Handler) http.Handler {
return requestid.RequestIDHandler(next)
}

View File

@@ -0,0 +1,35 @@
package middleware
import (
"net/http"
"path"
"strings"
)
// Static is a middleware that serves static assets.
func Static(root string, fs http.FileSystem) func(http.Handler) http.Handler {
if !strings.HasSuffix(root, "/") {
root = root + "/"
}
static := http.StripPrefix(
root,
http.FileServer(
fs,
),
)
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if strings.HasPrefix(r.URL.Path, path.Join(root, "api")) {
next.ServeHTTP(w, r)
} else {
if strings.HasSuffix(r.URL.Path, "/") {
http.NotFound(w, r)
} else {
static.ServeHTTP(w, r)
}
}
})
}
}

View File

@@ -0,0 +1,37 @@
package middleware
import (
"errors"
"net/http"
)
var (
// ErrInvalidToken is returned when the request token is invalid.
ErrInvalidToken = errors.New("invalid or missing token")
)
// Token provides a middleware to check access secured by a static token.
func Token(token string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if token == "" {
next.ServeHTTP(w, r)
return
}
header := r.Header.Get("Authorization")
if header == "" {
http.Error(w, ErrInvalidToken.Error(), http.StatusUnauthorized)
return
}
if header != "Bearer "+token {
http.Error(w, ErrInvalidToken.Error(), http.StatusUnauthorized)
return
}
next.ServeHTTP(w, r)
})
}
}

View File

@@ -0,0 +1,31 @@
package middleware
import (
"context"
"net/http"
"go.opencensus.io/plugin/ochttp/propagation/tracecontext"
"go.opencensus.io/trace"
)
// Trace unpacks the request context looking for an existing trace id.
func Trace(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var ctx context.Context
var span *trace.Span
tc := tracecontext.HTTPFormat{}
// reconstruct span context from request
if sc, ok := tc.SpanContextFromRequest(r); ok {
// if there is one, add it to the new span
ctx, span = trace.StartSpanWithRemoteParent(r.Context(), r.URL.String(), sc)
defer span.End()
} else {
// create a new span if there is no context
ctx, span = trace.StartSpan(r.Context(), r.URL.String())
defer span.End()
}
next.ServeHTTP(w, r.WithContext(ctx))
})
}

View File

@@ -0,0 +1,21 @@
package middleware
import (
"fmt"
"net/http"
"strings"
)
// Version writes the current version to the headers.
func Version(name, version string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set(
fmt.Sprintf("X-%s-VERSION", strings.ToUpper(name)),
version,
)
next.ServeHTTP(w, r)
})
}
}

183
ocis-pkg/oidc/claims.go Normal file
View File

@@ -0,0 +1,183 @@
package oidc
// The ProviderMetadata describes an idp.
// see https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
type ProviderMetadata struct {
AuthorizationEndpoint string `json:"authorization_endpoint,omitempty"`
//claims_parameter_supported
ClaimsSupported []string `json:"claims_supported,omitempty"`
//grant_types_supported
IDTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported,omitempty"`
Issuer string `json:"issuer,omitempty"`
JwksURI string `json:"jwks_uri,omitempty"`
//registration_endpoint
//request_object_signing_alg_values_supported
//request_parameter_supported
//request_uri_parameter_supported
//require_request_uri_registration
//response_modes_supported
ResponseTypesSupported []string `json:"response_types_supported,omitempty"`
ScopesSupported []string `json:"scopes_supported,omitempty"`
SubjectTypesSupported []string `json:"subject_types_supported,omitempty"`
TokenEndpoint string `json:"token_endpoint,omitempty"`
//token_endpoint_auth_methods_supported
//token_endpoint_auth_signing_alg_values_supported
UserinfoEndpoint string `json:"userinfo_endpoint,omitempty"`
//userinfo_signing_alg_values_supported
//code_challenge_methods_supported
IntrospectionEndpoint string `json:"introspection_endpoint,omitempty"`
//introspection_endpoint_auth_methods_supported
//introspection_endpoint_auth_signing_alg_values_supported
RevocationEndpoint string `json:"revocation_endpoint,omitempty"`
//revocation_endpoint_auth_methods_supported
//revocation_endpoint_auth_signing_alg_values_supported
//id_token_encryption_alg_values_supported
//id_token_encryption_enc_values_supported
//userinfo_encryption_alg_values_supported
//userinfo_encryption_enc_values_supported
//request_object_encryption_alg_values_supported
//request_object_encryption_enc_values_supported
CheckSessionIframe string `json:"check_session_iframe,omitempty"`
EndSessionEndpoint string `json:"end_session_endpoint,omitempty"`
//claim_types_supported
}
// StandardClaims will be stored in the context to be consumed by the oidc user manager
// They can be requested to be returned either in the UserInfo Response, per
// Section 5.3.2, or in the ID Token, per Section 2.
// see https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
type StandardClaims struct {
// Time the End-User's information was last updated. Its value is a
// JSON number representing the number of seconds from 1970-01-01T0:0:0Z
// as measured in UTC until the date/time.
UpdatedAt int64 `json:"updated_at,omitempty"`
// True if the End-User's e-mail address has been verified; otherwise false.
// When this Claim Value is true, this means that the OP took affirmative
// steps to ensure that this e-mail address was controlled by the End-User
// at the time the verification was performed. The means by which an e-mail
// address is verified is context-specific, and dependent upon the trust
// framework or contractual agreements within which the parties are operating.
EmailVerified bool `json:"email_verified,omitempty"`
// True if the End-User's phone number has been verified; otherwise false.
// When this Claim Value is true, this means that the OP took affirmative
// steps to ensure that this phone number was controlled by the End-User
// at the time the verification was performed. The means by which a phone
// number is verified is context-specific, and dependent upon the trust
// framework or contractual agreements within which the parties are
// operating. When true, the phone_number Claim MUST be in E.164 format
// and any extensions MUST be represented in RFC 3966 format.
PhoneNumberVerified bool `json:"phone_number_verified,omitempty"`
Iss string `json:"iss"`
// Subject - Identifier for the End-User at the Issuer.
Sub string `json:"sub,omitempty"`
// End-User's full name in displayable form including all name parts, possibly
// including titles and suffixes, ordered according to the End-User's locale
// and preferences.
Name string `json:"name,omitempty"`
// Given name(s) or first name(s) of the End-User. Note that in some cultures,
// people can have multiple given names; all can be present, with the names
// being separated by space characters.
GivenName string `json:"given_name,omitempty"`
// Surname(s) or last name(s) of the End-User. Note that in some cultures,
// people can have multiple family names or no family name; all can be present,
// with the names being separated by space characters.
FamilyName string `json:"family_name,omitempty"`
// Middle name(s) of the End-User. Note that in some cultures, people can have
// multiple middle names; all can be present, with the names being separated by
// space characters. Also note that in some cultures, middle names are not used.
MiddleName string `json:"middle_name,omitempty"`
// Casual name of the End-User that may or may not be the same as the given_name.
// For instance, a nickname value of Mike might be returned alongside a given_name
// value of Michael.
Nickname string `json:"nickname,omitempty"`
// Shorthand name by which the End-User wishes to be referred to at the RP, such
// as janedoe or j.doe. This value MAY be any valid JSON string including special
// characters such as @, /, or whitespace. The RP MUST NOT rely upon this value
// being unique, as discussed in Section 5.7.
PreferredUsername string `json:"preferred_username,omitempty"`
// URL of the End-User's profile page. The contents of this Web page SHOULD be
// about the End-User.
Profile string `json:"profile,omitempty"`
// URL of the End-User's profile picture. This URL MUST refer to an image file
// (for example, a PNG, JPEG, or GIF image file), rather than to a Web page
// containing an image. Note that this URL SHOULD specifically reference a
// profile photo of the End-User suitable for displaying when describing the
// End-User, rather than an arbitrary photo taken by the End-User.
Picture string `json:"picture,omitempty"`
// URL of the End-User's Web page or blog. This Web page SHOULD contain
// information published by the End-User or an organization that the End-User
// is affiliated with.
Website string `json:"website,omitempty"`
// End-User's preferred e-mail address. Its value MUST conform to the RFC 5322
// addr-spec syntax. The RP MUST NOT rely upon this value being unique, as
// discussed in Section 5.7.
Email string `json:"email,omitempty"`
// End-User's gender. Values defined by this specification are female and male.
// Other values MAY be used when neither of the defined values are applicable.
Gender string `json:"gender,omitempty"`
// End-User's birthday, represented as an ISO 8601:2004 YYYY-MM-DD format.
// The year MAY be 0000, indicating that it is omitted. To represent only the
// year, YYYY format is allowed. Note that depending on the underlying
// platform's date related function, providing just year can result in
// varying month and day, so the implementers need to take this factor into
// account to correctly process the dates.
Birthdate string `json:"birthdate,omitempty"`
// String from zoneinfo time zone database representing the End-User's time
// zone. For example, Europe/Paris or America/Los_Angeles.
Zoneinfo string `json:"zoneinfo,omitempty"`
// End-User's locale, represented as a BCP47 [RFC5646] language tag.
// This is typically an ISO 639-1 Alpha-2 [ISO6391] language code in
// lowercase and an ISO 3166-1 Alpha-2 [ISO31661] country code in
// uppercase, separated by a dash. For example, en-US or fr-CA. As a
// compatibility note, some implementations have used an underscore as
// the separator rather than a dash, for example, en_US; Relying Parties
// MAY choose to accept this locale syntax as well.
Locale string `json:"locale,omitempty"`
// End-User's preferred telephone number. E.164 [E.164] is RECOMMENDED
// as the format of this Claim, for example, +1 (425) 555-1212 or
// +56 (2) 687 2400. If the phone number contains an extension, it is
// RECOMMENDED that the extension be represented using the RFC 3966
// extension syntax, for example, +1 (604) 555-1234;ext=5678.
PhoneNumber string `json:"phone_number,omitempty"`
// TODO Name is the correct one, does kopano use display name? -> double check and report bug
DisplayName string `json:"display_name,omitempty"`
Groups []string `json:"groups,omitempty"`
// End-User's preferred postal address. The value of the address member
// is a JSON [RFC4627] structure containing some or all of the members
// defined in Section 5.1.1.
// TODO add address claim https://openid.net/specs/openid-connect-core-1_0.html#AddressClaim
Address map[string]interface{} `json:"address,omitempty"`
KCIdentity map[string]string `json:"kc.identity,omitempty"`
// To integrate with an existing LDAP server the IdP can send the numeric user and group id:
// UIDNumber is a unique numerical id that will be used when setting acls on a storage that integrates with the OS/LDAP
UIDNumber string `json:"uidnumber,omitempty"`
// GIDNumber is a unique numerical id that will be used when setting acls on a storage that integrates with the OS/LDAP
GIDNumber string `json:"gidnumber,omitempty"`
// OcisID is a unique, persistent, non reassignable user id
OcisID string `json:"ocis.id,omitempty"`
}

17
ocis-pkg/oidc/context.go Normal file
View File

@@ -0,0 +1,17 @@
package oidc
import "context"
// contextKey is the key for oidc claims in a context
type contextKey struct{}
// NewContext makes a new context that contains the OpenID Connect claims.
func NewContext(parent context.Context, c *StandardClaims) context.Context {
return context.WithValue(parent, contextKey{}, c)
}
// FromContext returns the StandardClaims stored in a context, or nil if there isn't one.
func FromContext(ctx context.Context) *StandardClaims {
s, _ := ctx.Value(contextKey{}).(*StandardClaims)
return s
}

57
ocis-pkg/oidc/option.go Normal file
View File

@@ -0,0 +1,57 @@
package oidc
import (
"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
// Endpoint is the OpenID Connect provider URL
Endpoint string
// Realm to use in the WWW-Authenticate header, defaults to Endpoint
Realm string
// SigningAlgs to use when verifying jwt signatures, defaults to "RS256" & "PS256"
SigningAlgs []string
// Insecure can be used to disable http certificate checks
Insecure bool
}
// Logger provides a function to set the logger option.
func Logger(l log.Logger) Option {
return func(o *Options) {
o.Logger = l
}
}
// Endpoint provides a function to set the endpoint option.
func Endpoint(e string) Option {
return func(o *Options) {
o.Endpoint = e
}
}
// Realm provides a function to set the realm option.
func Realm(r string) Option {
return func(o *Options) {
o.Realm = r
}
}
// SigningAlgs provides a function to set the signing algorithms option.
func SigningAlgs(sa []string) Option {
return func(o *Options) {
o.SigningAlgs = sa
}
}
// Insecure provides a function to set the insecure option.
func Insecure(i bool) Option {
return func(o *Options) {
o.Insecure = i
}
}

71
ocis-pkg/roles/cache.go Normal file
View File

@@ -0,0 +1,71 @@
package roles
import (
"sync"
"time"
settings "github.com/owncloud/ocis-settings/pkg/proto/v0"
)
// entry extends a bundle and adds a TTL
type entry struct {
*settings.Bundle
inserted time.Time
}
// cache is a cache implementation for roles, keyed by roleIDs.
type cache struct {
entries map[string]entry
size int
ttl time.Duration
m sync.Mutex
}
// newCache returns a new instance of Cache.
func newCache(size int, ttl time.Duration) cache {
return cache{
size: size,
ttl: ttl,
entries: map[string]entry{},
}
}
// get gets a role-bundle by a given `roleID`.
func (c *cache) get(roleID string) *settings.Bundle {
c.m.Lock()
defer c.m.Unlock()
if _, ok := c.entries[roleID]; ok {
return c.entries[roleID].Bundle
}
return nil
}
// set sets a roleID / role-bundle.
func (c *cache) set(roleID string, value *settings.Bundle) {
c.m.Lock()
defer c.m.Unlock()
if !c.fits() {
c.evict()
}
c.entries[roleID] = entry{
value,
time.Now(),
}
}
// evict frees memory from the cache by removing entries that exceeded the cache TTL.
func (c *cache) evict() {
for i := range c.entries {
if c.entries[i].inserted.Add(c.ttl).Before(time.Now()) {
delete(c.entries, i)
}
}
}
// fits returns whether the cache fits more entries.
func (c *cache) fits() bool {
return c.size > len(c.entries)
}

69
ocis-pkg/roles/manager.go Normal file
View File

@@ -0,0 +1,69 @@
package roles
import (
"context"
"github.com/owncloud/ocis-pkg/v2/log"
settings "github.com/owncloud/ocis-settings/pkg/proto/v0"
)
// Manager manages a cache of roles by fetching unknown roles from the settings.RoleService.
type Manager struct {
logger log.Logger
cache cache
roleService settings.RoleService
}
// NewManager returns a new instance of Manager.
func NewManager(o ...Option) Manager {
opts := newOptions(o...)
return Manager{
cache: newCache(opts.size, opts.ttl),
roleService: opts.roleService,
}
}
// List returns all roles that match the given roleIDs.
func (m *Manager) List(ctx context.Context, roleIDs []string) []*settings.Bundle {
// get from cache
result := make([]*settings.Bundle, 0)
lookup := make([]string, 0)
for _, roleID := range roleIDs {
if hit := m.cache.get(roleID); hit == nil {
lookup = append(lookup, roleID)
} else {
result = append(result, hit)
}
}
// if there are roles missing, fetch them from the RoleService
if len(lookup) > 0 {
request := &settings.ListBundlesRequest{
BundleIds: lookup,
}
res, err := m.roleService.ListRoles(ctx, request)
if err != nil {
m.logger.Debug().Err(err).Msg("failed to fetch roles by roleIDs")
}
for _, role := range res.Bundles {
m.cache.set(role.Id, role)
result = append(result, role)
}
}
return result
}
// FindPermissionByID searches for a permission-setting by the permissionID, but limited to the given roleIDs
func (m *Manager) FindPermissionByID(ctx context.Context, roleIDs []string, permissionID string) *settings.Setting {
for _, role := range m.List(ctx, roleIDs) {
for _, setting := range role.Settings {
if setting.Id == permissionID {
return setting
}
}
}
return nil
}

57
ocis-pkg/roles/option.go Normal file
View File

@@ -0,0 +1,57 @@
package roles
import (
"time"
"github.com/owncloud/ocis-pkg/v2/log"
settings "github.com/owncloud/ocis-settings/pkg/proto/v0"
)
// Options are all the possible options.
type Options struct {
size int
ttl time.Duration
logger log.Logger
roleService settings.RoleService
}
// Option mutates option
type Option func(*Options)
// CacheSize configures the size of the cache in items.
func CacheSize(s int) Option {
return func(o *Options) {
o.size = s
}
}
// CacheTTL rebuilds the cache after the configured duration.
func CacheTTL(ttl time.Duration) Option {
return func(o *Options) {
o.ttl = ttl
}
}
// Logger sets a preconfigured logger
func Logger(logger log.Logger) Option {
return func(o *Options) {
o.logger = logger
}
}
// RoleService provides endpoints for fetching roles.
func RoleService(rs settings.RoleService) Option {
return func(o *Options) {
o.roleService = rs
}
}
func newOptions(opts ...Option) Options {
o := Options{}
for _, v := range opts {
v(&o)
}
return o
}

22
ocis-pkg/roles/util.go Normal file
View File

@@ -0,0 +1,22 @@
package roles
import (
"context"
"encoding/json"
"github.com/micro/go-micro/v2/metadata"
"github.com/owncloud/ocis-pkg/v2/middleware"
)
// ReadRoleIDsFromContext extracts roleIDs from the metadata context and returns them as []string
func ReadRoleIDsFromContext(ctx context.Context) (roleIDs []string, ok bool) {
roleIDsJSON, ok := metadata.Get(ctx, middleware.RoleIDs)
if !ok {
return nil, false
}
err := json.Unmarshal([]byte(roleIDsJSON), &roleIDs)
if err != nil {
return nil, false
}
return roleIDs, true
}

View File

@@ -0,0 +1,97 @@
package debug
import (
"net/http"
"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
Name string
Version string
Address string
Token string
Pprof bool
Zpages bool
Health func(http.ResponseWriter, *http.Request)
Ready func(http.ResponseWriter, *http.Request)
}
// 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
}
}
// Name provides a function to set the name option.
func Name(n string) Option {
return func(o *Options) {
o.Name = n
}
}
// Version provides a function to set the version option.
func Version(v string) Option {
return func(o *Options) {
o.Version = v
}
}
// Address provides a function to set the address option.
func Address(a string) Option {
return func(o *Options) {
o.Address = a
}
}
// Token provides a function to set the token option.
func Token(t string) Option {
return func(o *Options) {
o.Token = t
}
}
// Pprof provides a function to set the pprof option.
func Pprof(p bool) Option {
return func(o *Options) {
o.Pprof = p
}
}
// Zpages provides a function to set the zpages option.
func Zpages(z bool) Option {
return func(o *Options) {
o.Zpages = z
}
}
// Health provides a function to set the health option.
func Health(h func(http.ResponseWriter, *http.Request)) Option {
return func(o *Options) {
o.Health = h
}
}
// Ready provides a function to set the ready option.
func Ready(r func(http.ResponseWriter, *http.Request)) Option {
return func(o *Options) {
o.Ready = r
}
}

View File

@@ -0,0 +1,63 @@
package debug
import (
"net/http"
"net/http/pprof"
"github.com/justinas/alice"
"github.com/owncloud/ocis-pkg/v2/middleware"
"github.com/prometheus/client_golang/prometheus/promhttp"
"go.opencensus.io/zpages"
)
// NewService initializes a new debug service.
func NewService(opts ...Option) *http.Server {
dopts := newOptions(opts...)
dopts.Logger.Info().
Str("transport", "debug").
Str("addr", dopts.Address).
Msg("Starting server")
mux := http.NewServeMux()
mux.Handle("/metrics", alice.New(
middleware.Token(
dopts.Token,
),
).Then(
promhttp.Handler(),
))
mux.HandleFunc("/healthz", dopts.Health)
mux.HandleFunc("/readyz", dopts.Ready)
if dopts.Pprof {
mux.HandleFunc("/debug/pprof/", pprof.Index)
mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
}
if dopts.Zpages {
zpages.Handle(mux, "/debug")
}
return &http.Server{
Addr: dopts.Address,
Handler: alice.New(
middleware.RealIP,
middleware.RequestID,
middleware.Cache,
middleware.Cors,
middleware.Secure,
middleware.Version(
dopts.Name,
dopts.Version,
),
).Then(
mux,
),
}
}

View File

@@ -0,0 +1,84 @@
package grpc
import (
"context"
"github.com/micro/cli/v2"
"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
Namespace string
Name string
Version string
Address string
Context context.Context
Flags []cli.Flag
}
// newOptions initializes the available default options.
func newOptions(opts ...Option) Options {
opt := Options{
Namespace: "go.micro.api",
}
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
}
}
// Namespace provides a function to set the namespace option.
func Namespace(n string) Option {
return func(o *Options) {
o.Namespace = n
}
}
// Name provides a function to set the name option.
func Name(n string) Option {
return func(o *Options) {
o.Name = n
}
}
// Version provides a function to set the version option.
func Version(v string) Option {
return func(o *Options) {
o.Version = v
}
}
// Address provides a function to set the address option.
func Address(a string) Option {
return func(o *Options) {
o.Address = a
}
}
// Context provides a function to set the context option.
func Context(ctx context.Context) Option {
return func(o *Options) {
o.Context = ctx
}
}
// Flags provides a function to set the flags option.
func Flags(flags ...cli.Flag) Option {
return func(o *Options) {
o.Flags = append(o.Flags, flags...)
}
}

View File

@@ -0,0 +1,53 @@
package grpc
import (
"strings"
"time"
"github.com/micro/go-micro/v2"
"github.com/micro/go-plugins/wrapper/trace/opencensus/v2"
"github.com/owncloud/ocis-pkg/v2/wrapper/prometheus"
)
// Service simply wraps the go-micro grpc service.
type Service struct {
micro.Service
}
// NewService initializes a new grpc service.
func NewService(opts ...Option) Service {
sopts := newOptions(opts...)
sopts.Logger.Info().
Str("transport", "grpc").
Str("addr", sopts.Address).
Msg("Starting server")
mopts := []micro.Option{
micro.Name(
strings.Join(
[]string{
sopts.Namespace,
sopts.Name,
},
".",
),
),
micro.Version(sopts.Version),
micro.Address(sopts.Address),
micro.WrapHandler(prometheus.NewHandlerWrapper()),
micro.WrapClient(opencensus.NewClientWrapper()),
micro.WrapHandler(opencensus.NewHandlerWrapper()),
micro.WrapSubscriber(opencensus.NewSubscriberWrapper()),
micro.RegisterTTL(time.Second * 30),
micro.RegisterInterval(time.Second * 10),
micro.Context(sopts.Context),
micro.Flags(sopts.Flags...),
}
return Service{
micro.NewService(
mopts...,
),
}
}

View File

@@ -0,0 +1,102 @@
package http
import (
"context"
"crypto/tls"
"net/http"
"github.com/micro/cli/v2"
"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
TLSConfig *tls.Config
Namespace string
Name string
Version string
Address string
Handler http.Handler
Context context.Context
Flags []cli.Flag
}
// newOptions initializes the available default options.
func newOptions(opts ...Option) Options {
opt := Options{
Namespace: "go.micro.web",
}
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
}
}
// Namespace provides a function to set the namespace option.
func Namespace(n string) Option {
return func(o *Options) {
o.Namespace = n
}
}
// Name provides a function to set the name option.
func Name(n string) Option {
return func(o *Options) {
o.Name = n
}
}
// Version provides a function to set the version option.
func Version(v string) Option {
return func(o *Options) {
o.Version = v
}
}
// Address provides a function to set the address option.
func Address(a string) Option {
return func(o *Options) {
o.Address = a
}
}
// Context provides a function to set the context option.
func Context(ctx context.Context) Option {
return func(o *Options) {
o.Context = ctx
}
}
// Flags provides a function to set the flags option.
func Flags(flags ...cli.Flag) Option {
return func(o *Options) {
o.Flags = append(o.Flags, flags...)
}
}
// TLSConfig provides a function to set the TLSConfig option.
func TLSConfig(config *tls.Config) Option {
return func(o *Options) {
o.TLSConfig = config
}
}
// Handler sets the http handler for the service
func Handler(h http.Handler) Option {
return func(o *Options) {
o.Handler = h
}
}

View File

@@ -0,0 +1,57 @@
package http
import (
"crypto/tls"
"strings"
"time"
"github.com/micro/go-micro/v2/web"
)
// Service simply wraps the go-micro web service.
type Service struct {
web.Service
}
// NewService initializes a new http service.
func NewService(opts ...Option) Service {
sopts := newOptions(opts...)
sopts.Logger.Info().
Str("transport", transport(sopts.TLSConfig)).
Str("addr", sopts.Address).
Msg("Starting server")
wopts := []web.Option{
web.Name(
strings.Join(
[]string{
sopts.Namespace,
sopts.Name,
},
".",
),
),
web.Version(sopts.Version),
web.Address(sopts.Address),
web.RegisterTTL(time.Second * 30),
web.RegisterInterval(time.Second * 10),
web.Context(sopts.Context),
web.TLSConfig(sopts.TLSConfig),
web.Handler(sopts.Handler),
web.Flags(sopts.Flags...),
}
return Service{
web.NewService(
wopts...,
),
}
}
func transport(secure *tls.Config) string {
if secure != nil {
return "https"
}
return "http"
}

10
ocis-pkg/tools.go Normal file
View File

@@ -0,0 +1,10 @@
// +build tools
package main
import (
_ "github.com/haya14busa/goverage"
_ "github.com/restic/calens"
_ "golang.org/x/lint/golint"
_ "honnef.co/go/tools/cmd/staticcheck"
)

View File

@@ -0,0 +1,88 @@
package prometheus
import (
"context"
"github.com/micro/go-micro/v2/server"
"github.com/prometheus/client_golang/prometheus"
)
var (
// Namespace defines the namespace of the defined metrics.
Namespace = "ocis"
)
// NewHandlerWrapper initializes the prometheus handler wrapper.
func NewHandlerWrapper(opts ...server.Option) server.HandlerWrapper {
counter := prometheus.NewCounterVec(
prometheus.CounterOpts{
Namespace: Namespace,
Name: "request_total",
Help: "How many service requests processed",
},
[]string{"method", "status"},
)
latency := prometheus.NewSummaryVec(
prometheus.SummaryOpts{
Namespace: Namespace,
Name: "upstream_latency_microseconds",
Help: "Service method latencies in microseconds",
},
[]string{"method"},
)
duration := prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Namespace: Namespace,
Name: "request_duration_seconds",
Help: "Service method request time in seconds",
},
[]string{"method"},
)
prometheus.Register(
prometheus.NewProcessCollector(prometheus.ProcessCollectorOpts{}),
)
prometheus.Register(
prometheus.NewGoCollector(),
)
prometheus.Register(
counter,
)
prometheus.Register(
latency,
)
prometheus.Register(
duration,
)
return func(fn server.HandlerFunc) server.HandlerFunc {
return func(ctx context.Context, req server.Request, rsp interface{}) error {
name := req.Endpoint()
timer := prometheus.NewTimer(prometheus.ObserverFunc(func(v float64) {
us := v * 1000000
latency.WithLabelValues(name).Observe(us)
duration.WithLabelValues(name).Observe(v)
}))
defer timer.ObserveDuration()
err := fn(ctx, req, rsp)
if err == nil {
counter.WithLabelValues(name, "success").Inc()
} else {
counter.WithLabelValues(name, "fail").Inc()
}
return err
}
}
}