未验证 提交 976e4039 编写于 作者: A Andrey Zhavoronkov 提交者: GitHub

Kibana public/private access using IAM (#4240)

* removed djago_reproxy dependency

* fix

* added visibility option, added tests

* updated changelog

* fixed comments

* added rule for admin into .csv, regenerate analytics_test.gen.rego
Co-authored-by: NNikita Manovich <nikita.manovich@intel.com>
上级 2b50abba
......@@ -66,6 +66,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Auth token key is not returned when registering without email verification (<https://github.com/openvinotoolkit/cvat/pull/4092>)
- Error in create project from backup for standard 3D annotation (<https://github.com/openvinotoolkit/cvat/pull/4160>)
- Annotations search does not work correctly in some corner cases (when use complex properties with width, height) (<https://github.com/openvinotoolkit/cvat/pull/4198>)
- Kibana requests are not proxied due to django-revproxy incompatibility with Django >3.2.x (<https://github.com/openvinotoolkit/cvat/issues/4085>)
### Security
- Updated ELK to 6.8.23 which uses log4j 2.17.1 (<https://github.com/openvinotoolkit/cvat/pull/4206>)
......
......@@ -76,9 +76,15 @@ services:
environment:
DJANGO_LOG_SERVER_HOST: logstash
DJANGO_LOG_SERVER_PORT: 8080
CVAT_ANALYTICS: 1
traefik:
environment:
CVAT_HOST: ${CVAT_HOST:-localhost}
DJANGO_LOG_VIEWER_HOST: kibana
DJANGO_LOG_VIEWER_PORT: 5601
CVAT_ANALYTICS: 1
volumes:
- ./components/analytics/kibana_conf.yml:/etc/traefik/rules/kibana_conf.yml:ro
volumes:
cvat_events:
http:
routers:
kibana:
entryPoints:
- web
middlewares:
- analytics-auth
- strip-prefix
service: kibana
rule: Host(`{{ env "CVAT_HOST" }}`) && PathPrefix(`/analytics`)
middlewares:
analytics-auth:
forwardauth:
address: http://cvat:8080/analytics
strip-prefix:
stripprefix:
prefixes:
- /analytics
services:
kibana:
loadBalancer:
servers:
- url: http://{{ env "DJANGO_LOG_VIEWER_HOST" }}:{{ env "DJANGO_LOG_VIEWER_PORT" }}
passHostHeader: false
......@@ -252,6 +252,34 @@ class ServerPermission(OpenPolicyAgentPermission):
'share': 'list:content'
}.get(self.view.action, None)
class LogViewerPermission(OpenPolicyAgentPermission):
@classmethod
def create(cls, request, view, obj):
permissions = []
if view.basename == 'analytics':
self = cls(request, view, obj)
permissions.append(self)
return permissions
def __init__(self, request, view, obj):
super().__init__(request, view, obj)
self.url = settings.IAM_OPA_DATA_URL + '/analytics/allow'
self.payload['input']['scope'] = self.scope
self.payload['input']['resource'] = self.resource
@property
def scope(self):
return {
'list': 'view',
}.get(self.view.action, None)
@property
def resource(self):
return {
'visibility': 'public' if settings.RESTRICTIONS['analytics_visibility'] else 'private',
}
class UserPermission(OpenPolicyAgentPermission):
@classmethod
def create(cls, request, view, obj):
......
Scope,Resource,Context,Ownership,Limit,Method,URL,Privilege,Membership
view,Analytics,N/A,N/A,resource['visibility']=='public',GET,"/analytics",business,N/A
view,Analytics,N/A,N/A,,GET,"/analytics",admin,N/A
package analytics
import data.utils
# input: {
# "scope": <"view"> or null,
# "auth": {
# "user": {
# "id": <num>,
# "privilege": <"admin"|"business"|"user"|"worker"> or null
# },
# "organization": {
# "id": <num>,
# "owner": {
# "id": <num>
# },
# "user": {
# "role": <"owner"|"maintainer"|"supervisor"|"worker"> or null
# }
# } or null,
# },
# "resource": {
# "visibility": <"public"|"private"> or null,
# }
# }
default allow = false
allow {
utils.is_admin
}
allow {
input.resource.visibility == utils.PUBLIC
input.scope == utils.VIEW
utils.has_perm(utils.BUSINESS)
}
此差异已折叠。
......@@ -2,4 +2,4 @@ from django.apps import AppConfig
class LogViewerConfig(AppConfig):
name = 'log_viewer'
name = 'cvat.apps.log_viewer'
# Copyright (C) 2018-2020 Intel Corporation
# Copyright (C) 2018-2022 Intel Corporation
#
# SPDX-License-Identifier: MIT
from django.urls import path
from rest_framework import routers
from . import views
urlpatterns = [
path('<path:path>', views.LogViewerProxy.as_view())
]
router = routers.DefaultRouter(trailing_slash=False)
router.register('analytics', views.LogViewerAccessViewSet, basename='analytics')
urlpatterns = router.urls
# Copyright (C) 2018-2020 Intel Corporation
# Copyright (C) 2018-2022 Intel Corporation
#
# SPDX-License-Identifier: MIT
import os
from revproxy.views import ProxyView
from django.utils.decorators import method_decorator
from django.conf import settings
from rules.contrib.views import PermissionRequiredMixin
from cvat.apps.iam import login_required
@method_decorator(login_required, name='dispatch')
class LogViewerProxy(PermissionRequiredMixin, ProxyView):
permission_required = settings.RESTRICTIONS['analytics_access']
upstream = 'http://{}:{}'.format(os.getenv('DJANGO_LOG_VIEWER_HOST'),
os.getenv('DJANGO_LOG_VIEWER_PORT'))
add_remote_user = True
def get_request_headers(self):
headers = super().get_request_headers()
headers['X-Forwarded-User'] = headers['REMOTE_USER']
return headers
# Returns True if the user has any of the specified permissions
def has_permission(self):
perms = self.get_permission_required()
return any(self.request.user.has_perm(perm) for perm in perms)
from django.http import HttpResponsePermanentRedirect
from rest_framework import status, viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
class LogViewerAccessViewSet(viewsets.ViewSet):
serializer_class = None
def list(self, request):
return Response(status=status.HTTP_200_OK)
# All log view requests are proxied by Traefik in production mode which is not available in debug mode,
# In order not to duplicate settings, let's just redirect to the default page in debug mode
@action(detail=False, url_path='app/kibana')
def redirect(self, request):
if settings.DEBUG:
return HttpResponsePermanentRedirect('http://localhost:5601/app/kibana')
......@@ -21,7 +21,6 @@ sqlparse==0.4.2
django-sendfile2==0.6.1
dj-pagination==2.5.0
python-logstash-async==2.2.0
django-revproxy==0.10.0
rules==2.2
GitPython==3.1.8
coreapi==2.3.3
......
......@@ -21,6 +21,7 @@ import shutil
import subprocess
import mimetypes
from corsheaders.defaults import default_headers
from distutils.util import strtobool
mimetypes.add_type("application/wasm", ".wasm", True)
......@@ -108,7 +109,6 @@ INSTALLED_APPS = [
'compressor',
'django_sendfile',
'dj_pagination',
'revproxy',
'rest_framework',
'rest_framework.authtoken',
'django_filters',
......@@ -127,7 +127,7 @@ INSTALLED_APPS = [
'cvat.apps.dataset_repo',
'cvat.apps.restrictions',
'cvat.apps.lambda_manager',
'cvat.apps.opencv'
'cvat.apps.opencv',
]
SITE_ID = 1
......@@ -185,7 +185,7 @@ REST_AUTH_SERIALIZERS = {
'PASSWORD_RESET_SERIALIZER': 'cvat.apps.iam.serializers.PasswordResetSerializerEx',
}
if os.getenv('DJANGO_LOG_VIEWER_HOST'):
if strtobool(os.getenv('CVAT_ANALYTICS', '0')):
INSTALLED_APPS += ['cvat.apps.log_viewer']
MIDDLEWARE = [
......@@ -420,11 +420,6 @@ LOGGING = {
'handlers': [],
'level': os.getenv('DJANGO_LOG_LEVEL', 'DEBUG'),
},
'revproxy': {
'handlers': ['console', 'server_file'],
'level': os.getenv('DJANGO_LOG_LEVEL', 'DEBUG')
},
'django': {
'handlers': ['console', 'server_file'],
'level': 'INFO',
......@@ -454,13 +449,9 @@ RESTRICTIONS = {
# this setting reduces task visibility to owner and assignee only
'reduce_task_visibility': False,
# allow access to analytics component to users with the following roles
'analytics_access': (
'engine.role.observer',
'engine.role.annotator',
'engine.role.user',
'engine.role.admin',
),
# allow access to analytics component to users with business role
# otherwise, only the administrator has access
'analytics_visibility': True,
}
# http://www.grantjenks.com/docs/diskcache/tutorial.html#djangocache
......
......@@ -32,7 +32,7 @@ if apps.is_installed('cvat.apps.dataset_repo'):
urlpatterns.append(path('git/repository/', include('cvat.apps.dataset_repo.urls')))
if apps.is_installed('cvat.apps.log_viewer'):
urlpatterns.append(path('analytics/', include('cvat.apps.log_viewer.urls')))
urlpatterns.append(path('', include('cvat.apps.log_viewer.urls')))
if apps.is_installed('cvat.apps.lambda_manager'):
urlpatterns.append(path('', include('cvat.apps.lambda_manager.urls')))
......
......@@ -44,7 +44,7 @@ services:
- traefik.enable=true
- traefik.http.services.cvat.loadbalancer.server.port=8080
- traefik.http.routers.cvat.rule=Host(`${CVAT_HOST:-localhost}`) &&
PathPrefix(`/api/`, `/git/`, `/opencv/`, `/analytics/`, `/static/`, `/admin`, `/documentation/`, `/django-rq`)
PathPrefix(`/api/`, `/git/`, `/opencv/`, `/static/`, `/admin`, `/documentation/`, `/django-rq`)
- traefik.http.routers.cvat.entrypoints=web
volumes:
- cvat_data:/home/django/data
......@@ -75,6 +75,7 @@ services:
- '--providers.docker.exposedByDefault=false'
- '--providers.docker.network=cvat'
- '--entryPoints.web.address=:8080'
- '--providers.file.directory=/etc/traefik/rules'
# Uncomment to get Traefik dashboard
# - "--entryPoints.dashboard.address=:8090"
# - "--api.dashboard=true"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册