Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial commit for multi-tenant-configuration #37

Draft
wants to merge 79 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 49 commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
b1ccec1
Initial commit for multi-tenant-configuration
mheyen Feb 19, 2021
a597f70
Nitpicking and minor corrections
mheyen Feb 25, 2021
0c803f6
Update README of multi-tenant-configuration script
mheyen Feb 25, 2021
f40ad0d
Added basic arguments parser for command options
mheyen Feb 25, 2021
74bc708
updated config file
mheyen Feb 25, 2021
cd8fc84
re-inserted API request for testing
mheyen Feb 25, 2021
9717219
added newline
mheyen Feb 25, 2021
4617bc6
minor restructuring of the script and improved options in available c…
mheyen Feb 27, 2021
597260b
Added error handling for user creation and option to add user to all …
mheyen Mar 25, 2021
f26f524
Update REAMDE
mheyen Mar 25, 2021
2a23c78
fixed minor error while creating users for all tenants
mheyen Mar 25, 2021
417986f
included group checks and restructured code
mheyen Apr 29, 2021
947ee77
restructured code and included verbose flag and logging
mheyen Apr 29, 2021
9c18544
added function to send a PUT request to request.py
mheyen May 17, 2021
1081112
Added member and role checks for group.
mheyen May 17, 2021
97d9224
Updated User Interaction
mheyen Jun 7, 2021
cd776b0
Integrated improved User interaction for group checks
mheyen Jun 7, 2021
f94d37b
Added function to perform checks for all tenants
mheyen Jun 9, 2021
7c57358
Refactoring code
mheyen Jun 9, 2021
3966713
Refactoring code
mheyen Jun 9, 2021
c7abf80
Fixed function call get_user with the new parameters
mheyen Jun 9, 2021
078bea0
Improved args parsing
mheyen Jun 9, 2021
af8ecbe
Added improved IO to User checks and restructured code
mheyen Jun 9, 2021
9f2e64c
Added header argument to get_request in request.py
mheyen Jun 21, 2021
ef2e9ec
Updated the way permissions are stored
mheyen Jun 21, 2021
f66ba67
Added checks to configure_users.py
mheyen Jun 21, 2021
19bddc6
minor code cleanup
mheyen Jun 23, 2021
3a8f52e
removed unused imports
mheyen Jul 12, 2021
d2bab49
removed more unused imports
mheyen Jul 12, 2021
ba009f5
updated README
mheyen Jul 12, 2021
69db473
code cleanup
mheyen Jul 12, 2021
cd17f0e
Improved config parsing and code cleanup
mheyen Jul 12, 2021
482917e
minor code cleanup
mheyen Jul 14, 2021
78dcbbe
get request now allows to also use BasicAuth
mheyen Jul 14, 2021
193f1c6
added API access check, changed user role checks, and general code cl…
mheyen Jul 14, 2021
c252098
Added new Checks and more documentation
mheyen Jul 17, 2021
2060de9
Added documentation
mheyen Jul 22, 2021
da377eb
minor code restructuring
mheyen Jul 22, 2021
7fbe1ee
added function parameter types
mheyen Jul 22, 2021
5670ab6
added code documentation and improved readability
mheyen Jul 22, 2021
5429368
fixed typo and added missing documentation
mheyen Jul 22, 2021
2712647
minor code restructuring
mheyen Jul 22, 2021
265c01a
fixed update_group function calls
mheyen Jul 22, 2021
0987a53
Removed comments and added documentation
mheyen Jul 28, 2021
fd83e52
Added check for Capture Agent Accounts
mheyen Aug 3, 2021
4512e5a
Changed ID of opencast organizaion 'dummy' to 'all'
mheyen Aug 3, 2021
9e1e6ce
Adapted the group description in the configuration file to allow pyth…
mheyen Aug 3, 2021
b49fbbb
finalized group check
mheyen Aug 3, 2021
1c337e2
minor changes: added capture check, removed ToDos, and improved code
mheyen Aug 3, 2021
85766ec
updated doc string in get_request()
mheyen Aug 29, 2021
9a8981f
Added BasicLogin Wraper for Basic Authentification
mheyen Aug 29, 2021
a851a27
Changed command argument from 'tenantid' to 'tenant-id'
mheyen Aug 29, 2021
e971f73
Updated README
mheyen Aug 29, 2021
f610d00
Updated config and README
mheyen Aug 29, 2021
3659eb8
Refactored variable: base_url -> server_url
mheyen Aug 29, 2021
96e7636
Updated README: Added explanation for optional tenant_urls dictionary
mheyen Aug 29, 2021
2446129
Added explanation in config
mheyen Aug 29, 2021
c32d026
Renamed config variables
mheyen Aug 29, 2021
646f6e7
Updated config: Added explanation
mheyen Aug 29, 2021
a3cf83d
fixed json
mheyen Aug 29, 2021
7f7f517
Removed group types
mheyen Aug 29, 2021
82f3308
changed uid to username in yaml file and script
mheyen Aug 29, 2021
b044742
Updated README: Added explanation group configplaceholder
mheyen Aug 29, 2021
c1bdb2c
removed blank line
mheyen Aug 29, 2021
f4ca5ff
Changed organization config file
mheyen Aug 29, 2021
0e11c1a
Added doc strings and moved private functions
mheyen Aug 30, 2021
cde69f7
moved generic functions to lib folder
mheyen Aug 30, 2021
0b00ec9
Removed condition for verbose flag to be set to True
mheyen Aug 30, 2021
c360adf
removed abort_script() function
mheyen Aug 30, 2021
f484f77
Restructured parsing_configuration.py and added Logger class
mheyen Aug 30, 2021
c314f95
renamed file to parse_arguments.py
mheyen Aug 30, 2021
009f96b
Added config option to exclude certain tenants from checks
mheyen Aug 30, 2021
0753bdc
renamed variable
mheyen Aug 30, 2021
ca6e2fd
changed Warning to Error
mheyen Aug 30, 2021
1bd47ef
merged two check functions into one
mheyen Aug 30, 2021
140b244
removed redundant docs
mheyen Aug 30, 2021
39b2a19
moved env folder into config folder
mheyen Aug 31, 2021
4cab954
Restructuring: Iterating over organizations is now done in main.py
mheyen Sep 1, 2021
ddc70ca
Addition to former commit - Iteration is done in main.py
mheyen Sep 1, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 51 additions & 5 deletions lib/rest_requests/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
import os

import requests
from requests.auth import HTTPDigestAuth
from requests.auth import HTTPDigestAuth, HTTPBasicAuth
from requests_toolbelt import MultipartEncoder

from rest_requests.request_error import RequestError


def get_request(url, digest_login, element_description, asset_type_description=None, asset_description=None,
stream=False):
def get_request(url, login, element_description, asset_type_description=None, asset_description=None,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pls also rename in doc string! Change type to something like Login

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

stream=False, headers=None, use_digest=True):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use_digest is missing in doc string

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

"""
Make a get request to the given url with the given digest login. If the request fails with an error or a status
code != 200, a Request Error with the error message /status code and the given descriptions is thrown.
Expand All @@ -28,13 +28,21 @@ def get_request(url, digest_login, element_description, asset_type_description=N
:type asset_description: str
:param stream: Whether to stream response
:type stream: bool
:param headers: The headers to include in the request
:type headers: dict
:return: response
:raise RequestError:
"""

headers = headers if headers else {}
if use_digest:
auth = HTTPDigestAuth(login.user, login.password)
headers["X-Requested-Auth"] = "Digest"
else:
auth = HTTPBasicAuth(login['user'], login['password'])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The structure of login should ideally stay the same. Maybe create a BasicLogin wrapper similar to DigestLogin?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.
I added a very small Wrapper BasicLogin


try:
response = requests.get(url, auth=HTTPDigestAuth(digest_login.user, digest_login.password),
headers={"X-Requested-Auth": "Digest"}, stream=stream)
response = requests.get(url, auth=auth, headers=headers, stream=stream)
except Exception as e:
raise RequestError.with_error(url, str(e), element_description, asset_type_description, asset_description)

Expand Down Expand Up @@ -129,3 +137,41 @@ def big_post_request(url, digest_login, element_description, asset_type_descript
raise RequestError.with_status_code(url, str(response.status_code), element_description, asset_type_description,
asset_description)
return response


def put_request(url, digest_login, element_description, asset_type_description=None, asset_description=None,
data=None, files=None):
"""
Make a put request to the given url with the given digest login. If the request fails with an error or a status
code != 200, a Request Error with the error message /status code and the given descriptions is thrown.

:param url: URL to make put request to
:type url: str
:param digest_login: The login credentials for digest authentication
:type digest_login: DigestLogin
:param element_description: Element description in case of errors, e.g. 'event', 'series', 'tenants'
:type element_description: str
:param asset_type_description: Asset type type description in case of errors, e.g. 'series', 'episode'
:type asset_type_description: str
:param asset_description: Asset description in case of errors, e.g. 'Dublin Core catalogs', 'ACL'
:type asset_description: str
:param data: Any data to attach to the request
:type data: dict
:param files: Any files to attach to the request
:type files: dict
:return: response
:raise RequestError:
"""

auth = HTTPDigestAuth(digest_login.user, digest_login.password)
headers = {"X-Requested-Auth": "Digest"}

try:
response = requests.put(url, auth=auth, headers=headers, data=data, files=files)
except Exception as e:
raise RequestError.with_error(url, str(e), element_description, asset_type_description, asset_description)

if response.status_code < 200 or response.status_code > 299:
raise RequestError.with_status_code(url, str(response.status_code), element_description, asset_type_description,
asset_description)
return response
62 changes: 62 additions & 0 deletions multi-tenant-configuration/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Multi-tenants User configuration scripts for Opencast
**ToDo**
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still some work left to do here, I see. :D

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hope I improved that a bit :D


This script ...

Currently this script does not ...

## How to Use

### Configuration

The script is configured by editing the values in `config.py`:

| Configuration Key | Description | Default/Example |
| :---------------- | :---------------------------------------- | :--------------------------- |
| `url` | The URL of the global admin node ? | https://tenant1.opencast.com |
| `tenant_url_pattern` | The URL pattern of the target tenants | https://tenant2.opencast.com |
| `tenant_urls` | A dictioanry of server URLs of the target tenants | https://tenant2.opencast.com |
| `digest_user` | The user name of the digest user | `opencast_system_account` |
| `digest_pw` | The password of the digest user | `CHANGE_ME` |
| `env_path` | The id of the workflow to start on ingest | reimport-workflow |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This table is not consistent yet, but I guess you're aware.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated table.


**TODo**: check the below ...

_The configured digest user needs to exist on both tenants and have the same password for both of them. This is because
the script ingests the assets via URL, which is faster, but the user needs to be able to access the source tenant from
the target tenant for this to work. Additionally the user currently needs to have ROLE_ADMIN to be able to use
`/assets/{episodeid}`._

_For the future, Basic Authentication and the use of an endpoint that doesn't require the Admin role (e.g.
`api/events/{id}`) would be preferable, so you can simply add a frontend user with the necessary rights (ingest,
access to the events/series) and the same password to both tenants._
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copied over from another README, I assume - not really relevant here :D

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed that part :)


#### group config:
The names in the group config file must be unique per Tenant!

### Usage

The script can be called with the following command (all parameters in brackets are optional):

`python main.py -e ENVIRONMENT [-t TENANT_ID] [-c CHECK] [-v True]`

| Param | Description |
| :---: | :---------- |
| `-e` / `--environment` | The environment where to find the configuration file (either `staging` or `production`) |
| `-t` / `--tenantid` | The id of the target tenant to be configured |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change long argument to --tenant-id?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

| `-c` / `--check` | checks to be performed (`users`, `groups`, `cast` or `capture`) (default: `all`) |
| `-v` / `--verbose` | enables logging to be prompted if set to `True` |

#### example:

`python main.py -e staging -t tenant1 -c groups -v True`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-v option should not need a value - just is existence should be enough to trigger verbose mode.


## Requirements

This script was written for Python 3.8. You can install the necessary packages with

**ToDo check the requirements file**

`pip install -r requirements.txt`

Additionally, this script uses modules contained in the _lib_ directory.
26 changes: 26 additions & 0 deletions multi-tenant-configuration/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Configuration

# Set this to your global admin node
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There can only be one admin node right now, so "global" is kinda unnecessary. But it is important that it's not a tenant-specific URL.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed "global" .

base_url = "http://localhost:8080"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know I used this in the past, but now I'd prefer server_url.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed that variable.


# If you have multiple tenants, use an URL pattern.
# example:
# tenant_url_pattern = "https://{}.example.org"
tenant_url_pattern = "http://{}:8080"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe explain that the pattern will be filled with the tenant id.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.


# You can also define a dictionary of tenant URLs, which will be prioritized over the URL pattern:
# # examples:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe explain why you'd want to do this: If the tenant id is not an exact part of the URL or the URLs don't follow a common pattern. Can also go into the README.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty much copied your point and added to README.

# tenant_urls = {
# 'tenant1': 'http://tenant1:8080',
# 'tenant2': 'http://tenant2:8080'
# }
# tenant_urls = {'tenant1': 'https://develop.opencast.org'}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One example is enough, no?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed the second example.


# Digest User login
digest_user = "opencast_system_account"
digest_pw = "CHANGE_ME"

# path to environment configuration file
env_path = "environment/{}/opencast-organizations.yml"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe explain that the placeholder will be filled with the environment variable that's passed as an argument.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

# path to group configuration file
group_path = "configurations/group_configuration.yaml"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

org_configand group_config?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renamed to org_config_path and group_config_path .

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe make it possible to use environment variable here, too? People might want to have separate group configs. So basically, replace if there's a placeholder here, otherwise just don't.

Copy link
Author

@mheyen mheyen Aug 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So far, the script did not allow for this kind of flexibility.
I think this is also not really needed here. One just need to change the path in the config file to point to another group config file.
For now, I just left this as it is.

113 changes: 113 additions & 0 deletions multi-tenant-configuration/configurations/group_configuration.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
{
KatrinIhler marked this conversation as resolved.
Show resolved Hide resolved
"groups" : [
{
"name": "System Administrators",
"description": "System Administrators",
"tenants": "all",
"type": "closed",
"members": [
{
"name": "Guy 1",
"email": "[email protected]",
"reason": "Operations partner",
"uid": "guy-1",
"tenants": "all"
},
{
"name": "Guy 2",
"email": "[email protected]",
"reason": "Operations partner",
"uid": "guy-2",
"tenants": "tenant1"
}
],
"inactive_members": [ ],
"permissions": [
{
"tenants": "all",
"roles": ["ROLE_ADMIN", "ROLE_SUDO"]
}
]
},
{
"name": "Organization Administrators",
"description": "Organization administrators have full access to all content of ${name}",
"tenants": "all",
"type": "open",
"members": [],
"inactive_members": [],
"permissions": [
{
"tenants": "all",
"roles": [
"ROLE_ADMIN_UI",
"ROLE_ORG_ADMIN"
]
},
{
"tenants" : "tenant2",
"roles": {
"add": [
"ROLE_UI_EVENTS_DETAILS_ACL_VIEW",
"ROLE_UI_EVENTS_DETAILS_ACL_EDIT"
],
"remove": []
}
}
]
},
{
"name": "Producers",
"description": "Producers have limited access to content and functionality",
"tenants": "all",
"type": "open",
"members": [],
"inactive_members": [],
"permissions": [
{
"tenants": "all",
"roles": [
"ROLE_ADMIN_UI",
"ROLE_UI_EVENTS_CREATE"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indentation is off here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haven't looked at the json file in a while :D

]
},
{
"tenants" : "tenant1",
"roles": {
"add": [
"ROLE_UI_EVENTS_COUNTERS_VIEW"
],
"remove": [
"ROLE_UI_EVENTS_CREATE"
]
}
},
{
"tenants" : "tenant2",
"roles": {
"add": [
"ROLE_ORG_ADMIN"
],
"remove": []
}
}
]
},
{
"name": "Tenant2 Producers",
"description": "Tenant2 Producers have limited access to content and functionality",
"tenants": "tenant2",
"type": "open",
"members": [],
"inactive_members": [],
"permissions": [
{
"tenants": "all",
"roles": [
"ROLE_ADMIN_UI"
]
}
]
}
]
}
83 changes: 83 additions & 0 deletions multi-tenant-configuration/configurations/group_configuration.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
---
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NICE!


groups:
- name: System Administrators
description: System Administrators
tenants: all
type: closed
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed, the group types can go away.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the group types from the yaml file.

members:
- name: Guy 1
email: [email protected]
reason: Operations partner
uid: guy1
tenants: all
- name: Guy 2
email: [email protected]
reason: Operations partner
uid: guy2
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename uid to username since I think that's what it's called in Opencast?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right: I changed that in the yaml file and in the script.

tenants: tenant1
inactive_members: []
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens with those anyway? Oo

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume they are being ignored Oo

To be honest, I did not know what to specifically do with these members.
What is their purpose anyway?

permissions:
- tenants: all
roles:
- ROLE_ADMIN
- ROLE_SUDO
- name: Organization Administrators
description: Organization administrators have full access to all content of {tenant_id}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe document which placeholders can be used here?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added to README.

tenants: all
type: open
members: []
inactive_members: []
permissions:
- tenants: all
roles:
- ROLE_ADMIN_UI
- ROLE_ORG_ADMIN
- tenants: tenant2
roles:
add:
- ROLE_UI_EVENTS_DETAILS_ACL_VIEW
- ROLE_UI_EVENTS_DETAILS_ACL_EDIT
remove: []
- name: Producers
description: Producers have limited access to content and functionality
tenants: all
type: open
members: []
inactive_members: []
permissions:
- tenants: all
roles:
- ROLE_ADMIN_UI
- ROLE_UI_EVENTS_CREATE
- tenants: tenant1
roles:
add:
- ROLE_UI_EVENTS_COUNTERS_VIEW
remove:
- ROLE_UI_EVENTS_CREATE
- tenants: tenant2
roles:
add:
- ROLE_ORG_ADMIN
remove: []
- name: Tenant1 Producers
description: Tenant1 Producers have limited access to content and functionality
tenants: tenant1
type: open
members:
- name: Guy X
email: [email protected]
reason: Operations partner
uid: guyx
tenants: all
- name: Guy 2
email: [email protected]
reason: Operations partner
uid: guy2
tenants: tenant1
inactive_members: []
permissions:
- tenants: all
roles:
- ROLE_ADMIN_UI
Loading