Compare commits
34 Commits
1.13.1
...
8d071c68df
Author | SHA1 | Date | |
---|---|---|---|
8d071c68df | |||
3b1691ff62 | |||
a77c43d213 | |||
086959b431 | |||
3f117f9d83 | |||
6a23845a4f | |||
b9c6d04e04 | |||
32c0027ecf | |||
dae52e8646 | |||
890a5651a0 | |||
4cb37f8347 | |||
a2010b9517 | |||
c95736ce14 | |||
b46c61954c | |||
afe4266600 | |||
0c8d1c8da1 | |||
eac998b5cc | |||
5914782252 | |||
73d875c4ac | |||
8f439f0675 | |||
0f102c6aaf | |||
4852c6caeb | |||
3fffba2eba | |||
a1293984ea | |||
4876db50c1 | |||
425cc066af | |||
1086f517fa | |||
2fdbf87ddd | |||
86228836c7 | |||
a6e6c948a5 | |||
dcdfa8feb7 | |||
8388d2e695 | |||
29f269050f | |||
93c4fc2009 |
2
.cz.yaml
2
.cz.yaml
@ -17,5 +17,5 @@ commitizen:
|
||||
prerelease_offset: 1
|
||||
tag_format: $version
|
||||
update_changelog_on_bump: false
|
||||
version: 1.13.1
|
||||
version: 1.0.0-b14
|
||||
version_scheme: semver
|
||||
|
90
.github/ISSUE_TEMPLATE/new_model.md
vendored
90
.github/ISSUE_TEMPLATE/new_model.md
vendored
@ -1,90 +0,0 @@
|
||||
---
|
||||
name: New Database Model
|
||||
about: Use when creating a new database model.
|
||||
title: "New Model - <model table name>"
|
||||
type: Task
|
||||
labels: task::feature, triage, type::task
|
||||
---
|
||||
|
||||
<!-- Add an intro -->
|
||||
|
||||
|
||||
<!-- describe a use case if not covered in intro -->
|
||||
|
||||
|
||||
## 📝 Details
|
||||
<!--
|
||||
|
||||
Describe in detail the following:
|
||||
|
||||
- New model field
|
||||
- if foreign key field, what it's name will be or if it's not to be linked ensure specified and coded with `related_name = '+' to disable the link`.
|
||||
- How the UI will work, be layed out, new ui features etc
|
||||
- custom permissions if required
|
||||
|
||||
-->
|
||||
|
||||
|
||||
### 🚧 Tasks
|
||||
|
||||
<!-- Don't remove tasks strike them out. use `~~` before and after the item. i.e. `- ~~[ ] Model Created~~` note: don't include the list dash-->
|
||||
|
||||
- [ ] 🆕 Model Created
|
||||
|
||||
- [ ] 🛠️ Migrations added
|
||||
|
||||
- [ ] 🏷️ Model tag added to `app/core/lib/slash_commands/linked_model.CommandLinkedModel.get_model()` function
|
||||
|
||||
- [ ] 📘 Tag updated in the [docs](https://nofusscomputing.com/projects/centurion_erp/user/core/markdown/#model-reference)
|
||||
- [ ] tag added to `app/core/models/ticket/ticket_linked_items.TicketLinkedItem.__str__()`
|
||||
- [ ] tag added to `app/core/lib/slash_commands/linked_model.CommandLinkedModel.get_model()`
|
||||
- [ ] ⚒️ Migration _Ticket Linked Item item_type choices update_
|
||||
|
||||
>[!note]
|
||||
> Ensure that when creating the tag the following is adhered to:
|
||||
> - Two words are not to contain a space char, `\s`. It is to be replaced with an underscore `_`
|
||||
> - As much as practical, keep the tag as close to the model name as possible
|
||||
|
||||
- [ ] 📝 New [History model](https://nofusscomputing.com/projects/centurion_erp/development/core/model_history/) created
|
||||
|
||||
- [ ] 📓 New [Notes model](https://nofusscomputing.com/projects/centurion_erp/development/core/model_notes/) created
|
||||
- [ ] 🆕 Model Created
|
||||
- [ ] 🛠️ Migrations added
|
||||
- [ ] Add `app_label` to KB Models `app/assistance/models/model_knowledge_base_article.all_models().model_apps`
|
||||
- [ ] _(Notes not used/required) - _ Add `model_name` to KB Models `app/assistance/models/model_knowledge_base_article.all_models().excluded_models`
|
||||
- [ ] 🧪 [Unit tested](https://nofusscomputing.com/projects/centurion_erp/development/core/model_notes/#testing)
|
||||
- [ ] 🧪 [Functional tested](https://nofusscomputing.com/projects/centurion_erp/development/core/model_notes/#testing)
|
||||
|
||||
- [ ] Admin Documentation added/updated _if applicable_
|
||||
- [ ] Developer Documentation added/updated _if applicable_
|
||||
- [ ] User Documentation added/updated
|
||||
|
||||
---
|
||||
|
||||
<!-- Add additional tasks here and as a check box list -->
|
||||
|
||||
|
||||
|
||||
#### 🧪 Tests
|
||||
|
||||
- [ ] Unit Test Model
|
||||
- [ ] Unit Test Tenancy Object
|
||||
- [ ] Unit Test Serializer
|
||||
- [ ] Unit Test ViewSet
|
||||
- [ ] Function Test ViewSet
|
||||
- [ ] Function Test API Metadata
|
||||
- [ ] Function Test API Permissions
|
||||
- [ ] Function Test API Render (fields)
|
||||
- [ ] Function Test History Entries
|
||||
- [ ] Function Test History API Render (fields)
|
||||
|
||||
|
||||
### ✅ Requirements
|
||||
|
||||
A Requirement is a must have. In addition will also be tested.
|
||||
|
||||
- [ ] Must have a [model_tag](https://nofusscomputing.com/projects/centurion_erp/user/core/markdown/#model-reference)
|
||||
|
||||
---
|
||||
|
||||
<!-- Add additional requirement here and as a check box list -->
|
5
.github/pull_request_template.md
vendored
5
.github/pull_request_template.md
vendored
@ -20,9 +20,6 @@
|
||||
|
||||
<!-- dont remove tasks below strike through including the checkbox by enclosing in double tidle '~~' -->
|
||||
|
||||
- [ ] **Feature Release ONLY** :red_square: Squash migration files :red_square:
|
||||
_Multiple migration files created as part of this release are to be sqauashed into a few files as possible so as to limit the number of migrations_
|
||||
|
||||
- [ ] :firecracker: Contains breaking-change Any Breaking change(s)?
|
||||
|
||||
_Breaking Change must also be notated in the commit that introduces it and in [Conventional Commit Format](https://www.conventionalcommits.org/en/v1.0.0/)._
|
||||
@ -35,8 +32,6 @@
|
||||
|
||||
- [ ] :checkered_flag: Milestone assigned
|
||||
|
||||
- [ ] :gear: :test_tube: [Functional Test(s) Written](https://nofusscomputing.com/projects/centurion_erp/development/testing/)
|
||||
|
||||
- [ ] :test_tube: [Unit Test(s) Written](https://nofusscomputing.com/projects/centurion_erp/development/testing/)
|
||||
|
||||
_ensure test coverage delta is not less than zero_
|
||||
|
11
.github/workflows/ci.yaml
vendored
11
.github/workflows/ci.yaml
vendored
@ -16,17 +16,6 @@ env:
|
||||
jobs:
|
||||
|
||||
|
||||
mkdocs:
|
||||
name: 'MKDocs'
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: write
|
||||
statuses: write
|
||||
checks: write
|
||||
actions: write
|
||||
uses: nofusscomputing/action_mkdocs/.github/workflows/reusable_mkdocs.yaml@development
|
||||
|
||||
|
||||
docker:
|
||||
name: 'Docker'
|
||||
uses: nofusscomputing/action_docker/.github/workflows/docker.yaml@development
|
||||
|
7
.gitignore
vendored
7
.gitignore
vendored
@ -9,10 +9,3 @@ artifacts/
|
||||
volumes/
|
||||
build/
|
||||
pages/
|
||||
node_modules/
|
||||
.markdownlint-cli2.jsonc
|
||||
.markdownlint.json
|
||||
package-lock.json
|
||||
package.json
|
||||
**.junit.xml
|
||||
feature_flags.json
|
||||
|
1
.vscode/extensions.json
vendored
1
.vscode/extensions.json
vendored
@ -7,6 +7,5 @@
|
||||
"streetsidesoftware.code-spell-checker",
|
||||
"qwtel.sqlite-viewer",
|
||||
"jebbs.markdown-extended",
|
||||
"william-voyek.vscode-nginx",
|
||||
]
|
||||
}
|
54
.vscode/launch.json
vendored
54
.vscode/launch.json
vendored
@ -4,9 +4,8 @@
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
|
||||
{
|
||||
"name": "Centurion",
|
||||
"name": "Debug: Django",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
@ -17,58 +16,9 @@
|
||||
"autoStartBrowser": false,
|
||||
"program": "${workspaceFolder}/app/manage.py"
|
||||
},
|
||||
{
|
||||
|
||||
"name": "Debug: Gunicorn",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"module": "gunicorn",
|
||||
"args": [
|
||||
"--config=../includes/etc/gunicorn.conf.py",
|
||||
"--access-logfile",
|
||||
"-",
|
||||
"--workers",
|
||||
"3",
|
||||
"--bind",
|
||||
"0.0.0.0:8002",
|
||||
"app.wsgi:application",
|
||||
|
||||
],
|
||||
"django": true,
|
||||
"autoStartBrowser": false,
|
||||
"cwd": "${workspaceFolder}/app",
|
||||
"env": {
|
||||
"PROMETHEUS_MULTIPROC_DIR": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Centurion Feature Flag (Management Command)",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"feature_flag",
|
||||
// "0.0.0.0:8002"
|
||||
],
|
||||
"django": true,
|
||||
"autoStartBrowser": false,
|
||||
"program": "${workspaceFolder}/app/manage.py"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "Migrate",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"migrate"
|
||||
],
|
||||
"django": true,
|
||||
"autoStartBrowser": false,
|
||||
"program": "${workspaceFolder}/app/manage.py"
|
||||
|
||||
},
|
||||
{
|
||||
"name": "Debug: Celery",
|
||||
"type": "debugpy",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"module": "celery",
|
||||
"console": "integratedTerminal",
|
||||
|
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@ -8,8 +8,7 @@
|
||||
// "-v",
|
||||
// "--cov",
|
||||
// "--cov-report xml",
|
||||
"-s",
|
||||
"app",
|
||||
"app"
|
||||
],
|
||||
"python.testing.unittestEnabled": false,
|
||||
"python.testing.pytestEnabled": true,
|
||||
@ -18,6 +17,4 @@
|
||||
"ITSM"
|
||||
],
|
||||
"cSpell.language": "en-AU",
|
||||
"jest.enable": false,
|
||||
"pylint.enabled": true,
|
||||
}
|
2229
CHANGELOG.md
2229
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
125
CONTRIBUTING.md
125
CONTRIBUTING.md
@ -1,131 +1,6 @@
|
||||
# Contribution Guide
|
||||
|
||||
|
||||
Development of this project has been setup to be done from VSCodium. The following additional requirements need to be met:
|
||||
|
||||
- npm has been installed. _required for `markdown` linting_
|
||||
|
||||
`sudo apt install -y --no-install-recommends npm`
|
||||
|
||||
- setup of other requirements can be done with `make prepare`
|
||||
|
||||
- **ALL** Linting must pass for Merge to be conducted.
|
||||
|
||||
_`make lint`_
|
||||
|
||||
## TL;DR
|
||||
|
||||
|
||||
from the root of the project to start a test server use:
|
||||
|
||||
``` bash
|
||||
|
||||
# activate python venv
|
||||
source /tmp/centurion_erp/bin/activate
|
||||
|
||||
# enter app dir
|
||||
cd app
|
||||
|
||||
# Start dev server can be viewed at http://127.0.0.1:8002
|
||||
python manage.py runserver 8002
|
||||
|
||||
# Run any migrations, if required
|
||||
python manage.py migrate
|
||||
|
||||
# Create a super suer if required
|
||||
python manage.py createsuperuser
|
||||
|
||||
```
|
||||
|
||||
## Makefile
|
||||
|
||||
!!! tip "TL;DR"
|
||||
Common make commands are `make prepare` then `make docs` and `make lint`
|
||||
|
||||
Included within the root of the repository is a makefile that can be used during development to check/run different items as is required during development. The following make targets are available:
|
||||
|
||||
- `prepare`
|
||||
|
||||
_prepare the repository. init's all git submodules and sets up a python virtual env and other make targets_
|
||||
|
||||
- `docs`
|
||||
|
||||
_builds the docs and places them within a directory called build, which can be viewed within a web browser_
|
||||
|
||||
- `lint`
|
||||
|
||||
_conducts all required linting_
|
||||
|
||||
- `docs-lint`
|
||||
|
||||
_lints the markdown documents within the docs directory for formatting errors that MKDocs may/will have an issue with._
|
||||
|
||||
- `clean`
|
||||
|
||||
_cleans up build artifacts and removes the python virtual environment_
|
||||
|
||||
|
||||
> this doc is yet to receive a re-write
|
||||
|
||||
|
||||
## Docker Container
|
||||
|
||||
within the `deploy/` directory there is a docker compose file. running `docker compose up` from this directory will launch a full stack deployment locally containing Centurion API, User Interface, a worker and a RabbitMQ server. once launched you can navigate to `http://127.0.0.1/` to start browsing the site.
|
||||
|
||||
You may need to run migrations if your not mounting your own DB. to do this run `docker exec -ti centurion-erp python manage.py migrate`
|
||||
|
||||
## Page speed tests
|
||||
|
||||
to run page speed tests (requires a working prometheus and grafa setup). use the following
|
||||
|
||||
|
||||
``` bash
|
||||
|
||||
clear; \
|
||||
K6_PROMETHEUS_RW_TREND_STATS="p(99),p(95),p(90),max,min" \
|
||||
K6_PROMETHEUS_RW_SERVER_URL=http://<prometheus url>:9090/api/v1/write \
|
||||
BASE_URL="http://127.0.0.1:8002" \
|
||||
AUTH_TOKEN="< api token of superuser>" \
|
||||
k6 run \
|
||||
-o experimental-prometheus-rw \
|
||||
--tag "commit=$(git rev-parse HEAD)" \
|
||||
--tag "testid=<name of test for ref>" \
|
||||
test/page_speed.js
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Tips / Handy info
|
||||
|
||||
- To obtain a list of models _(in in the same order as the file system)_ using the db shell `python3 manage.py dbshell` run the following sql command:
|
||||
|
||||
``` sql
|
||||
|
||||
SELECT model FROM django_content_type ORDER BY app_label ASC, model ASC;
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Old working docs
|
||||
|
||||
|
||||
## Dev Environment
|
||||
|
||||
It's advised to setup a python virtual env for development. this can be done with the following commands.
|
||||
|
13
README.md
13
README.md
@ -32,14 +32,9 @@ This project is hosted on [Github](https://github.com/NofussComputing/centurion_
|
||||
|
||||
**Stable Branch**
|
||||
|
||||
 
|
||||
|
||||

|
||||
  
|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
|
||||
----
|
||||
@ -48,13 +43,9 @@ This project is hosted on [Github](https://github.com/NofussComputing/centurion_
|
||||
|
||||
|
||||
|
||||
 
|
||||
|
||||

|
||||
  
|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
----
|
||||
<br>
|
||||
|
133
Release-Notes.md
133
Release-Notes.md
@ -1,136 +1,7 @@
|
||||
## Version 1.13.0
|
||||
|
||||
- DevOps Module added.
|
||||
|
||||
- Feature Flagging Component added as par of the DevOps module.
|
||||
|
||||
|
||||
## Version 1.11.0
|
||||
|
||||
**Note:** Migrations should be performed offline. **Failing to perform** an online migration, the option provided below will not be available if the migration crashes. Running the below commands to reset the database for the migrations to re-run will cause data loss if users are making changes to Centurion.
|
||||
|
||||
- History views removed from original Centurion interface.
|
||||
|
||||
- History views removed from API v1.
|
||||
|
||||
- A migration exists that will move the history from the old tables to the new ones.
|
||||
|
||||
if for some reason the migration crashes enter the following commands in the dbshell `python manage.py dbshell` and restart the migrations
|
||||
|
||||
``` sql
|
||||
|
||||
delete from access_organization_history;
|
||||
delete from access_team_history;
|
||||
|
||||
delete from assistance_knowledge_base_history;
|
||||
delete from assistance_knowledge_base_category_history;
|
||||
|
||||
delete from config_management_configgroups_history;
|
||||
delete from config_management_configgroupsoftware_history;
|
||||
delete from config_management_configgrouphosts_history;
|
||||
|
||||
delete from core_manufacturer_history;
|
||||
delete from core_ticketcategory_history;
|
||||
delete from core_ticketcommentcategory_history;
|
||||
|
||||
delete from itam_device_history;
|
||||
delete from itam_devicemodel_history;
|
||||
delete from itam_devicetype_history;
|
||||
delete from itam_deviceoperatingsystem_history;
|
||||
delete from itam_devicesoftware_history;
|
||||
delete from itam_operatingsystem_history;
|
||||
delete from itam_operatingsystemversion_history;
|
||||
delete from itam_software_history;
|
||||
delete from itam_softwareversion_history;
|
||||
delete from itam_softwarecategory_history;
|
||||
|
||||
delete from itim_cluster_history;
|
||||
delete from itim_clustertype_history;
|
||||
delete from itim_port_history;
|
||||
delete from itim_service_history;
|
||||
|
||||
delete from project_management_project_history;
|
||||
delete from project_management_projectmilestone_history;
|
||||
delete from project_management_projectstate_history;
|
||||
delete from project_management_projecttype_history;
|
||||
delete from settings_externallink_history;
|
||||
|
||||
delete from core_model_history;
|
||||
|
||||
```
|
||||
|
||||
The above commands truncate the data from the new history tables so the migration can run again.
|
||||
|
||||
|
||||
## Version 1.10.0
|
||||
|
||||
- Nothing significant to report
|
||||
|
||||
|
||||
## Version 1.9.0
|
||||
|
||||
- Nothing significant to report
|
||||
|
||||
|
||||
## Version 1.8.0
|
||||
|
||||
- Prometheus exporter added. To enable metrics for the database you will have to update the database backend. see the [docs](https://nofusscomputing.com/projects/centurion_erp/administration/monitoring/#django-exporter-setup) for further information.
|
||||
|
||||
|
||||
## Version 1.5.0
|
||||
|
||||
- When v1.4.0 was release the migrations were not merged. As part of the work conducted on this release the v1.4 migrations have been squashed. This should not have any effect on any system that when they updated to v1.4, they ran the migrations and they **completed successfully**. Upgrading from <1.4.0 to this release should also have no difficulties as the migrations required still exist. There are less of them, however with more work per migration.
|
||||
|
||||
!!! Note
|
||||
If you require the previously squashed migrations for what ever reason. Clone the repo and go to commit 17f47040d6737905a1769eee5c45d9d15339fdbf, which is the commit prior to the squashing which is commit ca2da06d2cd393cabb7e172ad47dfb2dd922d952.
|
||||
|
||||
|
||||
## Version 1.4.0
|
||||
|
||||
API redesign in preparation for moving the UI out of centurion to it's [own project](https://github.com/nofusscomputing/centurion_erp_ui). This release introduces a **Feature freeze** to the current UI. Only bug fixes will be done for the current UI.
|
||||
API v2 is a beta release and is subject to change. On completion of the new UI, API v2 will more likely than not be set as stable.
|
||||
|
||||
- A large emphasis is being placed upon API stability. This is being achieved by ensuring the following:
|
||||
|
||||
- Actions can only be carried out by users whom have the correct permissions
|
||||
|
||||
- fields are of the correct type and visible when required as part of the API response
|
||||
|
||||
- Data validations work and notify the user of any issue
|
||||
|
||||
We are make the above possible by ensuring a more stringent test policy.
|
||||
|
||||
- New API will be at path `api/v2`.
|
||||
|
||||
- API v1 is now **Feature frozen** with only bug fixes being completed. It's recommended that you move to and start using API v2 as this has feature parity with API v1.
|
||||
|
||||
- API v1 is **depreciated**
|
||||
|
||||
- Depreciation of **ALL** API urls. API v1 Will be [removed in v2.0.0](https://github.com/nofusscomputing/centurion_erp/issues/343) release of Centurion.
|
||||
|
||||
|
||||
# Version 1.3.0
|
||||
|
||||
!!! danger "Security"
|
||||
As is currently the recommended method of deployment, the Centurion Container must be deployed behind a reverse proxy the conducts the SSL termination.
|
||||
|
||||
This release updates the docker container to be a production setup for deployment of Centurion. Prior to this version Centurion ERP was using a development setup for the webserver.
|
||||
|
||||
- Docker now uses SupervisorD for container
|
||||
|
||||
- Gunicorn WSGI setup for Centurion with NginX as the webserver.
|
||||
|
||||
- Container now has a health check.
|
||||
|
||||
- To setup container as "Worker", set `IS_WORKER='True'` environmental variable within container. _**Note:** You can still use command `celery -A app worker -l INFO`, although **not** recommended as the container health check will not be functioning_
|
||||
|
||||
|
||||
## Version 1.0.0
|
||||
|
||||
# Version 1.0.0
|
||||
|
||||
Initial Release of Centurion ERP.
|
||||
|
||||
|
||||
### Breaking changes
|
||||
## Breaking changes
|
||||
|
||||
- Nil
|
||||
|
@ -1,11 +1,7 @@
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth.models import Group, User
|
||||
from django.contrib.auth.admin import UserAdmin
|
||||
from django.contrib.auth.models import Group
|
||||
|
||||
|
||||
from access.models.organization import Organization
|
||||
from access.models.team import Team
|
||||
from access.models.team_user import TeamUsers
|
||||
from .models import *
|
||||
|
||||
admin.site.unregister(Group)
|
||||
|
||||
@ -29,40 +25,6 @@ class OrganizationAdmin(admin.ModelAdmin):
|
||||
list_filter = ["created"]
|
||||
search_fields = ["team_name"]
|
||||
|
||||
|
||||
admin.site.register(Organization,OrganizationAdmin)
|
||||
|
||||
|
||||
class TeamUserInline(admin.TabularInline):
|
||||
model = TeamUsers
|
||||
extra = 0
|
||||
|
||||
readonly_fields = ['created', 'modified']
|
||||
fields = ['team']
|
||||
|
||||
fk_name = 'user'
|
||||
|
||||
|
||||
admin.site.unregister(User)
|
||||
class UsrAdmin(UserAdmin):
|
||||
|
||||
fieldsets = (
|
||||
(None, {"fields": ("username", "password")}),
|
||||
("Personal info", {"fields": ("first_name", "last_name", "email")}),
|
||||
(
|
||||
"Permissions",
|
||||
{
|
||||
"fields": (
|
||||
"is_active",
|
||||
"is_staff",
|
||||
"is_superuser",
|
||||
),
|
||||
|
||||
},
|
||||
),
|
||||
("Important dates", {"fields": ("last_login", "date_joined")}),
|
||||
)
|
||||
|
||||
inlines = [TeamUserInline]
|
||||
|
||||
admin.site.register(User,UsrAdmin)
|
||||
|
||||
|
@ -11,20 +11,12 @@ class AutoCreatedField(models.DateTimeField):
|
||||
|
||||
"""
|
||||
|
||||
help_text = 'Date and time of creation'
|
||||
|
||||
verbose_name = 'Created'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
kwargs.setdefault("editable", False)
|
||||
|
||||
kwargs.setdefault("default", now)
|
||||
|
||||
kwargs.setdefault("help_text", self.help_text)
|
||||
|
||||
kwargs.setdefault("verbose_name", self.verbose_name)
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
@ -36,18 +28,6 @@ class AutoLastModifiedField(AutoCreatedField):
|
||||
|
||||
"""
|
||||
|
||||
help_text = 'Date and time of last modification'
|
||||
|
||||
verbose_name = 'Modified'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
kwargs.setdefault("help_text", self.help_text)
|
||||
|
||||
kwargs.setdefault("verbose_name", self.verbose_name)
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def pre_save(self, model_instance, add):
|
||||
|
||||
value = now()
|
||||
@ -65,20 +45,6 @@ class AutoSlugField(models.SlugField):
|
||||
|
||||
"""
|
||||
|
||||
help_text = 'slug for this field'
|
||||
|
||||
verbose_name = 'Slug'
|
||||
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
kwargs.setdefault("help_text", self.help_text)
|
||||
|
||||
kwargs.setdefault("verbose_name", self.verbose_name)
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
def pre_save(self, model_instance, add):
|
||||
|
||||
if not model_instance.slug or model_instance.slug == '_':
|
||||
|
@ -3,7 +3,7 @@ from django.db.models import Q
|
||||
|
||||
from app import settings
|
||||
|
||||
from access.models.organization import Organization
|
||||
from access.models import Organization
|
||||
|
||||
from core.forms.common import CommonModelForm
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
from django import forms
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.db.models import Q
|
||||
from django.forms import inlineformset_factory
|
||||
|
||||
from app import settings
|
||||
|
||||
from .team_users import TeamUsersForm, TeamUsers
|
||||
|
||||
from access.models.team import Team
|
||||
from access.functions import permissions
|
||||
|
||||
from app import settings
|
||||
from access.models import Team
|
||||
|
||||
from core.forms.common import CommonModelForm
|
||||
|
||||
@ -66,4 +66,38 @@ class TeamForm(CommonModelForm):
|
||||
|
||||
self.fields['permissions'].widget.attrs = {'style': "height: 200px;"}
|
||||
|
||||
self.fields['permissions'].queryset = permissions.permission_queryset()
|
||||
apps = [
|
||||
'access',
|
||||
'assistance',
|
||||
'config_management',
|
||||
'core',
|
||||
'django_celery_results',
|
||||
'itam',
|
||||
'settings',
|
||||
]
|
||||
|
||||
exclude_models = [
|
||||
'appsettings',
|
||||
'chordcounter',
|
||||
'groupresult',
|
||||
'organization'
|
||||
'settings',
|
||||
'usersettings',
|
||||
]
|
||||
|
||||
exclude_permissions = [
|
||||
'add_organization',
|
||||
'add_taskresult',
|
||||
'change_organization',
|
||||
'change_taskresult',
|
||||
'delete_organization',
|
||||
'delete_taskresult',
|
||||
]
|
||||
|
||||
self.fields['permissions'].queryset = Permission.objects.filter(
|
||||
content_type__app_label__in=apps,
|
||||
).exclude(
|
||||
content_type__model__in=exclude_models
|
||||
).exclude(
|
||||
codename__in = exclude_permissions
|
||||
)
|
||||
|
@ -2,7 +2,7 @@ from django.db.models import Q
|
||||
|
||||
from app import settings
|
||||
|
||||
from access.models.team_user import TeamUsers
|
||||
from access.models import TeamUsers
|
||||
|
||||
from core.forms.common import CommonModelForm
|
||||
|
||||
|
@ -1,56 +0,0 @@
|
||||
from django.contrib.auth.models import Permission
|
||||
|
||||
def permission_queryset():
|
||||
"""Filter Permissions to those used within the application
|
||||
|
||||
Returns:
|
||||
list: Filtered queryset that only contains the used permissions
|
||||
"""
|
||||
|
||||
apps = [
|
||||
'access',
|
||||
'assistance',
|
||||
'config_management',
|
||||
'core',
|
||||
'devops',
|
||||
'django_celery_results',
|
||||
'itam',
|
||||
'itim',
|
||||
'project_management',
|
||||
'settings',
|
||||
]
|
||||
|
||||
exclude_models = [
|
||||
'appsettings',
|
||||
'chordcounter',
|
||||
'comment',
|
||||
'groupresult',
|
||||
'history',
|
||||
'modelnotes',
|
||||
'usersettings',
|
||||
]
|
||||
|
||||
exclude_permissions = [
|
||||
'add_checkin',
|
||||
'add_history',
|
||||
'add_organization',
|
||||
'add_taskresult',
|
||||
'change_checkin',
|
||||
'change_history',
|
||||
'change_organization',
|
||||
'change_taskresult',
|
||||
'delete_checkin',
|
||||
'delete_history',
|
||||
'delete_organization',
|
||||
'delete_taskresult',
|
||||
'view_checkin',
|
||||
'view_history',
|
||||
]
|
||||
|
||||
return Permission.objects.filter(
|
||||
content_type__app_label__in=apps,
|
||||
).exclude(
|
||||
content_type__model__in=exclude_models
|
||||
).exclude(
|
||||
codename__in = exclude_permissions
|
||||
)
|
@ -1,161 +0,0 @@
|
||||
from django.contrib.auth.middleware import (
|
||||
AuthenticationMiddleware,
|
||||
SimpleLazyObject,
|
||||
partial,
|
||||
)
|
||||
from django.contrib.auth.models import User, Group
|
||||
from django.utils.deprecation import MiddlewareMixin
|
||||
|
||||
|
||||
from access.models.organization import Organization
|
||||
from access.models.team import Team
|
||||
|
||||
|
||||
from settings.models.app_settings import AppSettings
|
||||
|
||||
|
||||
class RequestTenancy(MiddlewareMixin):
|
||||
"""Access Middleware
|
||||
|
||||
Serves the purpose of adding the users tenancy details to rhe request
|
||||
object.
|
||||
"""
|
||||
|
||||
|
||||
def process_request(self, request):
|
||||
|
||||
request.app_settings = AppSettings.objects.select_related('global_organization').get(
|
||||
owner_organization = None
|
||||
)
|
||||
|
||||
request.tenancy = Tenancy(user = request.user, app_settings = request.app_settings)
|
||||
|
||||
|
||||
|
||||
class Tenancy:
|
||||
|
||||
user: User = None
|
||||
|
||||
groups: list([Group]) = None
|
||||
|
||||
_app_settings: AppSettings = None
|
||||
|
||||
|
||||
_user_organizations: list([Organization]) = None
|
||||
"""Cached User Organizations"""
|
||||
|
||||
_user_teams: list([Team]) = None
|
||||
"""Cached User Teams"""
|
||||
|
||||
|
||||
_user_permissions: list([str]) = None
|
||||
"""Cached User User Permissions"""
|
||||
|
||||
|
||||
|
||||
def __init__(self, user: User, app_settings: AppSettings):
|
||||
|
||||
self.user = user
|
||||
|
||||
self. _app_settings = app_settings
|
||||
|
||||
self.groups = user.groups.select_related('team', 'team__organization').prefetch_related('team__permissions__content_type')
|
||||
|
||||
self._user_organizations = []
|
||||
|
||||
self._user_groups = []
|
||||
|
||||
self._user_teams = []
|
||||
|
||||
self._user_permissions = []
|
||||
|
||||
|
||||
for group in self.groups:
|
||||
|
||||
if group.team not in self._user_teams:
|
||||
|
||||
self._user_teams += [ group.team ]
|
||||
|
||||
for permission in group.team.permissions.all():
|
||||
|
||||
permission_value = str( permission.content_type.app_label + '.' + permission.codename )
|
||||
|
||||
if permission_value not in self._user_permissions:
|
||||
|
||||
self._user_permissions += [ permission_value ]
|
||||
|
||||
|
||||
if group.team.organization not in self._user_organizations:
|
||||
|
||||
self._user_organizations += [ group.team.organization ]
|
||||
|
||||
|
||||
|
||||
def is_member(self, organization: Organization) -> bool:
|
||||
"""Returns true if the current user is a member of the organization
|
||||
|
||||
iterates over the user_organizations list and returns true if the user is a member
|
||||
|
||||
Returns:
|
||||
bool: _description_
|
||||
"""
|
||||
|
||||
is_member: bool = False
|
||||
|
||||
if organization is None:
|
||||
|
||||
return False
|
||||
|
||||
if int(organization) in self._user_organizations:
|
||||
|
||||
is_member = True
|
||||
|
||||
return is_member
|
||||
|
||||
|
||||
|
||||
def has_organization_permission(self, organization: Organization, permissions_required: str) -> bool:
|
||||
""" Check if user has permission within organization.
|
||||
|
||||
Args:
|
||||
organization (int): Organization to check.
|
||||
permissions_required (list): if doing object level permissions, pass in required permission.
|
||||
|
||||
Returns:
|
||||
bool: True for yes.
|
||||
"""
|
||||
|
||||
has_permission: bool = False
|
||||
|
||||
if type(organization) is not Organization:
|
||||
|
||||
raise TypeError('Organization must be of type Organization')
|
||||
|
||||
|
||||
if type(permissions_required) is not str:
|
||||
|
||||
raise TypeError('permissions_required must be of type str')
|
||||
|
||||
|
||||
if not organization:
|
||||
|
||||
return has_permission
|
||||
|
||||
|
||||
for team in self._user_teams:
|
||||
|
||||
if(
|
||||
team.organization.id == int(organization)
|
||||
or getattr(self._app_settings.global_organization, 'id', 0) == int(organization)
|
||||
):
|
||||
|
||||
for permission in team.permissions.all():
|
||||
|
||||
assembled_permission = str(permission.content_type.app_label) + '.' + str( permission.codename )
|
||||
|
||||
if assembled_permission == permissions_required:
|
||||
|
||||
has_permission = True
|
||||
|
||||
|
||||
return has_permission
|
@ -44,7 +44,7 @@ class Migration(migrations.Migration):
|
||||
('team_name', models.CharField(default='', max_length=50, verbose_name='Name')),
|
||||
('created', access.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False)),
|
||||
('modified', access.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False)),
|
||||
('organization', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists])),
|
||||
('organization', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists])),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'Teams',
|
||||
|
@ -1,89 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-12-06 06:47
|
||||
|
||||
import access.models
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('access', '0001_initial'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='organization',
|
||||
options={'ordering': ['name'], 'verbose_name': 'Organization', 'verbose_name_plural': 'Organizations'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='team',
|
||||
options={'ordering': ['team_name'], 'verbose_name': 'Team', 'verbose_name_plural': 'Teams'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='teamusers',
|
||||
options={'ordering': ['user'], 'verbose_name': 'Team User', 'verbose_name_plural': 'Team Users'},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='organization',
|
||||
name='id',
|
||||
field=models.AutoField(help_text='ID of this item', primary_key=True, serialize=False, unique=True, verbose_name='ID'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='organization',
|
||||
name='manager',
|
||||
field=models.ForeignKey(help_text='Manager for this organization', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='Manager'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='organization',
|
||||
name='model_notes',
|
||||
field=models.TextField(blank=True, default=None, help_text='Tid bits of information', null=True, verbose_name='Notes'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='organization',
|
||||
name='name',
|
||||
field=models.CharField(help_text='Name of this Organization', max_length=50, unique=True, verbose_name='Name'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='team',
|
||||
name='is_global',
|
||||
field=models.BooleanField(default=False, help_text='Is this a global object?', verbose_name='Global Object'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='team',
|
||||
name='model_notes',
|
||||
field=models.TextField(blank=True, default=None, help_text='Tid bits of information', null=True, verbose_name='Notes'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='team',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='team',
|
||||
name='team_name',
|
||||
field=models.CharField(help_text='Name to give this team', max_length=50, verbose_name='Name'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='teamusers',
|
||||
name='id',
|
||||
field=models.AutoField(help_text='ID of this Team User', primary_key=True, serialize=False, unique=True, verbose_name='ID'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='teamusers',
|
||||
name='manager',
|
||||
field=models.BooleanField(blank=True, default=False, help_text='Is this user to be a manager of this team', verbose_name='manager'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='teamusers',
|
||||
name='team',
|
||||
field=models.ForeignKey(help_text='Team user belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='team', to='access.team', verbose_name='Team'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='teamusers',
|
||||
name='user',
|
||||
field=models.ForeignKey(help_text='User who will be added to the team', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='User'),
|
||||
),
|
||||
]
|
@ -1,49 +0,0 @@
|
||||
# Generated by Django 5.1.5 on 2025-02-09 11:07
|
||||
|
||||
import access.models
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('access', '0002_alter_organization_options_alter_team_options_and_more'),
|
||||
('core', '0012_modelnotes'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='team',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.team.Team.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='OrganizationNotes',
|
||||
fields=[
|
||||
('modelnotes_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.modelnotes')),
|
||||
('model', models.ForeignKey(help_text='Model this note belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='notes', to='access.organization', verbose_name='Model')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Organization Note',
|
||||
'verbose_name_plural': 'Organization Notes',
|
||||
'db_table': 'access_organization_notes',
|
||||
'ordering': ['-created'],
|
||||
},
|
||||
bases=('core.modelnotes',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='TeamNotes',
|
||||
fields=[
|
||||
('modelnotes_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.modelnotes')),
|
||||
('model', models.ForeignKey(help_text='Model this note belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='notes', to='access.team', verbose_name='Model')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Team Note',
|
||||
'verbose_name_plural': 'Team Notes',
|
||||
'db_table': 'access_team_notes',
|
||||
'ordering': ['-created'],
|
||||
},
|
||||
bases=('core.modelnotes',),
|
||||
),
|
||||
]
|
@ -1,43 +0,0 @@
|
||||
# Generated by Django 5.1.5 on 2025-02-20 13:25
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('access', '0003_alter_team_organization_organizationnotes_teamnotes'),
|
||||
('core', '0015_modelhistory_manufacturerhistory_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='OrganizationHistory',
|
||||
fields=[
|
||||
('modelhistory_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.modelhistory')),
|
||||
('model', models.ForeignKey(help_text='Model this note belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='history', to='access.organization', verbose_name='Model')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Organization History',
|
||||
'verbose_name_plural': 'Organization History',
|
||||
'db_table': 'access_organization_history',
|
||||
'ordering': ['-created'],
|
||||
},
|
||||
bases=('core.modelhistory',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='TeamHistory',
|
||||
fields=[
|
||||
('modelhistory_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.modelhistory')),
|
||||
('model', models.ForeignKey(help_text='Model this note belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='history', to='access.team', verbose_name='Model')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Team History',
|
||||
'verbose_name_plural': 'Team History',
|
||||
'db_table': 'access_team_history',
|
||||
'ordering': ['-created'],
|
||||
},
|
||||
bases=('core.modelhistory',),
|
||||
),
|
||||
]
|
@ -4,27 +4,12 @@ from django.contrib.auth.models import Group
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.utils.functional import cached_property
|
||||
|
||||
from access.models.organization import Organization
|
||||
from access.models.team import Team
|
||||
|
||||
from .models import Organization, Team
|
||||
|
||||
|
||||
class OrganizationMixin():
|
||||
"""Base Organization class"""
|
||||
|
||||
parent_model: str = None
|
||||
""" Parent Model
|
||||
|
||||
This attribute defines the parent model for the model in question. The parent model when defined
|
||||
will be used as the object to obtain the permissions from.
|
||||
"""
|
||||
|
||||
parent_model_pk_kwarg: str = 'pk'
|
||||
"""Parent Model kwarg
|
||||
|
||||
This value is used to define the kwarg that is used as the parent objects primary key (pk).
|
||||
"""
|
||||
|
||||
request = None
|
||||
|
||||
user_groups = []
|
||||
@ -41,24 +26,20 @@ class OrganizationMixin():
|
||||
parent_model (Model): with PK from kwargs['pk']
|
||||
"""
|
||||
|
||||
return self.parent_model.objects.get(pk=self.kwargs[self.parent_model_pk_kwarg])
|
||||
return self.parent_model.objects.get(pk=self.kwargs['pk'])
|
||||
|
||||
|
||||
def object_organization(self) -> int:
|
||||
|
||||
id = None
|
||||
|
||||
if hasattr(self, '_object_organization'):
|
||||
|
||||
return int(self._object_organization)
|
||||
|
||||
try:
|
||||
|
||||
if hasattr(self, 'get_queryset'):
|
||||
self.get_queryset()
|
||||
|
||||
|
||||
if self.parent_model:
|
||||
if hasattr(self, 'parent_model'):
|
||||
obj = self.get_parent_obj()
|
||||
|
||||
id = obj.get_organization().id
|
||||
@ -80,10 +61,6 @@ class OrganizationMixin():
|
||||
|
||||
id = 0
|
||||
|
||||
if hasattr(self, 'instance') and id is None: # Form Instance
|
||||
|
||||
id = self.instance.get_organization()
|
||||
|
||||
|
||||
except AttributeError:
|
||||
|
||||
@ -107,10 +84,6 @@ class OrganizationMixin():
|
||||
|
||||
pass
|
||||
|
||||
if id is not None:
|
||||
|
||||
self._object_organization = id
|
||||
|
||||
|
||||
return id
|
||||
|
||||
@ -126,13 +99,9 @@ class OrganizationMixin():
|
||||
|
||||
is_member = False
|
||||
|
||||
if organization is None:
|
||||
if organization in self.user_organizations():
|
||||
|
||||
return False
|
||||
|
||||
if int(organization) in self.user_organizations():
|
||||
|
||||
is_member = True
|
||||
return True
|
||||
|
||||
return is_member
|
||||
|
||||
@ -142,10 +111,6 @@ class OrganizationMixin():
|
||||
Override of 'PermissionRequiredMixin' method so that this mixin can obtain the required permission.
|
||||
"""
|
||||
|
||||
if not hasattr(self, 'permission_required'):
|
||||
|
||||
return []
|
||||
|
||||
if self.permission_required is None:
|
||||
raise ImproperlyConfigured(
|
||||
f"{self.__class__.__name__} is missing the "
|
||||
@ -182,10 +147,6 @@ class OrganizationMixin():
|
||||
|
||||
user_organizations = []
|
||||
|
||||
if hasattr(self, '_user_organizations'):
|
||||
|
||||
return self._user_organizations
|
||||
|
||||
teams = Team.objects
|
||||
|
||||
for group in self.request.user.groups.all():
|
||||
@ -196,41 +157,18 @@ class OrganizationMixin():
|
||||
|
||||
user_organizations = user_organizations + [team.organization.id]
|
||||
|
||||
if len(user_organizations) > 0:
|
||||
|
||||
self._user_organizations = user_organizations
|
||||
|
||||
|
||||
return user_organizations
|
||||
|
||||
|
||||
# ToDo: Ensure that the group has access to item
|
||||
def has_organization_permission(self, organization: int = None, permissions_required: list = None) -> bool:
|
||||
""" Check if user has permission within organization.
|
||||
|
||||
Args:
|
||||
organization (int, optional): Organization to check. Defaults to None.
|
||||
permissions_required (list, optional): if doing object level permissions, pass in required permission. Defaults to None.
|
||||
|
||||
Returns:
|
||||
bool: True for yes.
|
||||
"""
|
||||
def has_organization_permission(self, organization: int=None) -> bool:
|
||||
|
||||
has_permission = False
|
||||
|
||||
if permissions_required is None:
|
||||
|
||||
permissions_required = self.get_permission_required()
|
||||
|
||||
if not organization:
|
||||
|
||||
organization = self.object_organization()
|
||||
|
||||
else:
|
||||
|
||||
organization = int(organization)
|
||||
|
||||
|
||||
if self.is_member(organization) or organization == 0:
|
||||
|
||||
groups = Group.objects.filter(pk__in=self.user_groups)
|
||||
@ -244,7 +182,7 @@ class OrganizationMixin():
|
||||
|
||||
assembled_permission = str(permission["content_type__app_label"]) + '.' + str(permission["codename"])
|
||||
|
||||
if assembled_permission in permissions_required and (team['organization_id'] == organization or organization == 0):
|
||||
if assembled_permission in self.get_permission_required() and (team['organization_id'] == organization or organization == 0):
|
||||
|
||||
return True
|
||||
|
||||
@ -304,23 +242,15 @@ class OrganizationMixin():
|
||||
|
||||
return True
|
||||
|
||||
if permissions_required:
|
||||
perms = self.get_permission_required()
|
||||
|
||||
perms = permissions_required
|
||||
|
||||
else:
|
||||
|
||||
perms = self.get_permission_required()
|
||||
|
||||
if self.has_organization_permission(permissions_required = perms):
|
||||
if self.has_organization_permission():
|
||||
|
||||
return True
|
||||
|
||||
if self.request.user.has_perms(perms) and str(self.request.method).lower() == 'get':
|
||||
if self.request.user.has_perms(perms) and len(self.kwargs) == 0 and str(self.request.method).lower() == 'get':
|
||||
|
||||
if len(self.kwargs) == 0 or (len(self.kwargs) == 1 and 'ticket_type' in self.kwargs):
|
||||
|
||||
return True
|
||||
return True
|
||||
|
||||
for required_permission in self.permission_required:
|
||||
|
||||
@ -397,12 +327,6 @@ class OrganizationPermission(AccessMixin, OrganizationMixin):
|
||||
|
||||
if not request.user.is_authenticated:
|
||||
return self.handle_no_permission()
|
||||
|
||||
if len(self.permission_required) == 0:
|
||||
|
||||
if hasattr(self, 'get_dynamic_permissions'):
|
||||
|
||||
self.permission_required = self.get_dynamic_permissions()
|
||||
|
||||
if len(self.permission_required) > 0:
|
||||
|
||||
|
@ -1,272 +0,0 @@
|
||||
from django.contrib.auth.models import User, Group
|
||||
from django.db import models
|
||||
|
||||
from access.models.organization import Organization
|
||||
from access.models.team import Team
|
||||
|
||||
|
||||
|
||||
class OrganizationMixin:
|
||||
"""Organization Tenancy Mixin
|
||||
|
||||
This class is intended to be included in **ALL** View / Viewset classes as
|
||||
it contains the functions/methods required to conduct the permission
|
||||
checking.
|
||||
"""
|
||||
|
||||
|
||||
_obj_organization: int = None
|
||||
"""Cached Object Organization"""
|
||||
|
||||
def get_obj_organization(self, obj = None, request = None) -> Organization:
|
||||
"""Fetch the objects Organization
|
||||
|
||||
Args:
|
||||
obj (Model): Model of object
|
||||
|
||||
Raises:
|
||||
ValueError: When `obj` and `request` are both missing
|
||||
|
||||
Returns:
|
||||
Organization: Organization the object is from
|
||||
None: No Organization was found
|
||||
"""
|
||||
|
||||
if obj is None and request is None:
|
||||
|
||||
raise ValueError('Missing Parameter. obj or request must be supplied')
|
||||
|
||||
|
||||
if self._obj_organization:
|
||||
|
||||
return self._obj_organization
|
||||
|
||||
|
||||
if obj:
|
||||
|
||||
self._obj_organization = getattr(obj, 'organization', None)
|
||||
|
||||
|
||||
if not self._obj_organization:
|
||||
|
||||
self._obj_organization = getattr(obj, 'get_organization', lambda: None)()
|
||||
|
||||
elif (
|
||||
request
|
||||
and not self.kwargs.get('pk', None)
|
||||
):
|
||||
|
||||
if getattr(request.stream, 'method', '') != 'DELETE':
|
||||
|
||||
data = getattr(request, 'data', None)
|
||||
|
||||
if data:
|
||||
|
||||
data_organization = self.kwargs.get('organization_id', None)
|
||||
|
||||
if not data_organization:
|
||||
|
||||
data_organization = request.data.get('organization_id', None)
|
||||
|
||||
|
||||
if not data_organization:
|
||||
|
||||
data_organization = request.data.get('organization', None)
|
||||
|
||||
|
||||
if data_organization:
|
||||
|
||||
self._obj_organization = Organization.objects.get(
|
||||
pk = int( data_organization )
|
||||
)
|
||||
|
||||
elif self.kwargs.get('pk', None):
|
||||
|
||||
|
||||
obj = self.model.objects.get( pk = self.kwargs.get('pk', None) )
|
||||
|
||||
if getattr(obj, 'organization', None):
|
||||
|
||||
self._obj_organization = obj.organization
|
||||
|
||||
elif str(self.model._meta.verbose_name).lower() == 'organization':
|
||||
|
||||
self._obj_organization = obj
|
||||
|
||||
|
||||
if self.get_parent_model(): # if defined is to overwrite object organization
|
||||
|
||||
parent_obj = self.get_parent_obj()
|
||||
|
||||
self._obj_organization = parent_obj.get_organization()
|
||||
|
||||
|
||||
return self._obj_organization
|
||||
|
||||
|
||||
|
||||
def get_parent_model(self):
|
||||
"""Get the Parent Model
|
||||
|
||||
This function exists so that dynamic parent models can be defined.
|
||||
They are defined by overriding this method.
|
||||
|
||||
Returns:
|
||||
Model: Parent Model
|
||||
"""
|
||||
|
||||
return self.parent_model
|
||||
|
||||
|
||||
|
||||
def get_parent_obj(self):
|
||||
""" Get the Parent Model Object
|
||||
|
||||
Use in views where the the model has no organization and the organization should be fetched from the parent model.
|
||||
|
||||
Requires attribute `parent_model` within the view with the value of the parent's model class
|
||||
|
||||
Returns:
|
||||
parent_model (Model): with PK from kwargs['pk']
|
||||
"""
|
||||
|
||||
return self.parent_model.objects.get(pk=self.kwargs[self.parent_model_pk_kwarg])
|
||||
|
||||
|
||||
|
||||
def get_permission_organizations(self, permission: str ) -> list([ int ]):
|
||||
"""Return Organization(s) the permission belongs to
|
||||
|
||||
Searches the users organizations for the required permission, if found
|
||||
the organization is added to the list to return.
|
||||
|
||||
Args:
|
||||
permission (str): Permission to search users organizations for
|
||||
|
||||
Returns:
|
||||
Organizations (list): All Organizations where the permission was found.
|
||||
"""
|
||||
|
||||
_permission_organizations: list = []
|
||||
|
||||
for team in self.request.tenancy._user_teams:
|
||||
|
||||
for team_permission in team.permissions.all():
|
||||
|
||||
permission_value = str( team_permission.content_type.app_label + '.' + team_permission.codename )
|
||||
|
||||
if permission_value == permission:
|
||||
|
||||
_permission_organizations += [ team.organization.id ]
|
||||
|
||||
|
||||
return _permission_organizations
|
||||
|
||||
|
||||
_permission_required: str = None
|
||||
"""Cached Permissions required"""
|
||||
|
||||
|
||||
def get_permission_required(self) -> str:
|
||||
""" Get / Generate Permission Required
|
||||
|
||||
If there is a requirement that there be custom/dynamic permissions,
|
||||
this function can be safely overridden.
|
||||
|
||||
Raises:
|
||||
ValueError: Unable to determin the view action
|
||||
|
||||
Returns:
|
||||
str: Permission in format `<app_name>.<action>_<model_name>`
|
||||
"""
|
||||
|
||||
if self._permission_required:
|
||||
|
||||
return self._permission_required
|
||||
|
||||
|
||||
if hasattr(self, 'get_dynamic_permissions'):
|
||||
|
||||
self._permission_required = self.get_dynamic_permissions()
|
||||
|
||||
if type(self._permission_required) is list:
|
||||
|
||||
self._permission_required = self._permission_required[0]
|
||||
|
||||
return self._permission_required
|
||||
|
||||
|
||||
view_action: str = None
|
||||
|
||||
if(
|
||||
self.action == 'create'
|
||||
or getattr(self.request._stream, 'method', '') == 'POST'
|
||||
):
|
||||
|
||||
view_action = 'add'
|
||||
|
||||
elif (
|
||||
self.action == 'partial_update'
|
||||
or self.action == 'update'
|
||||
or getattr(self.request._stream, 'method', '') == 'PATCH'
|
||||
or getattr(self.request._stream, 'method', '') == 'PUT'
|
||||
):
|
||||
|
||||
view_action = 'change'
|
||||
|
||||
elif(
|
||||
self.action == 'destroy'
|
||||
or getattr(self.request._stream, 'method', '') == 'DELETE'
|
||||
):
|
||||
|
||||
view_action = 'delete'
|
||||
|
||||
elif (
|
||||
self.action == 'list'
|
||||
):
|
||||
|
||||
view_action = 'view'
|
||||
|
||||
elif self.action == 'retrieve':
|
||||
|
||||
view_action = 'view'
|
||||
|
||||
elif self.action == 'metadata':
|
||||
|
||||
view_action = 'view'
|
||||
|
||||
elif self.action is None:
|
||||
|
||||
return False
|
||||
|
||||
|
||||
|
||||
if view_action is None:
|
||||
|
||||
raise ValueError('view_action could not be defined.')
|
||||
|
||||
|
||||
permission = self.model._meta.app_label + '.' + view_action + '_' + self.model._meta.model_name
|
||||
|
||||
permission_required = permission
|
||||
|
||||
|
||||
self._permission_required = permission_required
|
||||
|
||||
return self._permission_required
|
||||
|
||||
|
||||
|
||||
parent_model: models.Model = None
|
||||
""" Parent Model
|
||||
|
||||
This attribute defines the parent model for the model in question. The parent model when defined
|
||||
will be used as the object to obtain the permissions from.
|
||||
"""
|
||||
|
||||
|
||||
parent_model_pk_kwarg: str = 'pk'
|
||||
"""Parent Model kwarg
|
||||
|
||||
This value is used to define the kwarg that is used as the parent objects primary key (pk).
|
||||
"""
|
@ -1,322 +0,0 @@
|
||||
import traceback
|
||||
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
|
||||
from rest_framework import exceptions
|
||||
from rest_framework.permissions import DjangoObjectPermissions
|
||||
|
||||
from access.models.tenancy import Organization, TenancyObject
|
||||
|
||||
from core import exceptions as centurion_exceptions
|
||||
|
||||
|
||||
|
||||
class OrganizationPermissionMixin(
|
||||
DjangoObjectPermissions,
|
||||
):
|
||||
"""Organization Permission Mixin
|
||||
|
||||
This class is to be used as the permission class for API `Views`/`ViewSets`.
|
||||
In combination with the `OrganizationPermissionsMixin`, permission checking
|
||||
will be done to ensure the user has the correct permissions to perform the
|
||||
CRUD operation.
|
||||
|
||||
**Note:** If the user is not authenticated, they will be denied access
|
||||
globally.
|
||||
|
||||
Permissions are broken down into two areas:
|
||||
|
||||
- `Tenancy` Objects
|
||||
|
||||
This object requires that the user have the correct permission and that
|
||||
permission be assigned within the organiztion the object belongs to.
|
||||
|
||||
- `Non-Tenancy` Objects.
|
||||
|
||||
This object requires the the use have the correct permission assigned,
|
||||
regardless of the organization the object is from. This includes objects
|
||||
that have no organization.
|
||||
|
||||
"""
|
||||
|
||||
_is_tenancy_model: bool = None
|
||||
|
||||
def is_tenancy_model(self, view) -> bool:
|
||||
"""Determin if the Model is a `Tenancy` Model
|
||||
|
||||
Will look at the model defined within the view unless a parent
|
||||
model is found. If the latter is true, the parent_model will be used to
|
||||
determin if the model is a `Tenancy` model
|
||||
|
||||
Args:
|
||||
view (object): The View the HTTP request was mad to
|
||||
|
||||
Returns:
|
||||
True (bool): Model is a Tenancy Model.
|
||||
False (bool): Model is not a Tenancy model.
|
||||
"""
|
||||
|
||||
if not self._is_tenancy_model:
|
||||
|
||||
if hasattr(view, 'model'):
|
||||
|
||||
self._is_tenancy_model = issubclass(view.model, TenancyObject)
|
||||
|
||||
if view.get_parent_model():
|
||||
|
||||
self._is_tenancy_model = issubclass(view.get_parent_model(), TenancyObject)
|
||||
|
||||
return self._is_tenancy_model
|
||||
|
||||
|
||||
|
||||
def has_permission(self, request, view):
|
||||
""" Check if user has the required permission
|
||||
|
||||
Permission flow is as follows:
|
||||
|
||||
- Un-authenticated users. Access Denied
|
||||
|
||||
- Authenticated user whom make a request using wrong method. Access
|
||||
Denied
|
||||
|
||||
- Authenticated user who is not in same organization as object. Access
|
||||
Denied
|
||||
|
||||
- Authenticated user who is in same organization as object, however is
|
||||
missing the correct permission. Access Denied
|
||||
|
||||
Depending upon user type, they will recieve different feedback. In order
|
||||
they are:
|
||||
|
||||
- Non-authenticated users will **always** recieve HTTP/401
|
||||
|
||||
- Authenticated users who use an unsupported method, HTTP/405
|
||||
|
||||
- Authenticated users missing the correct permission recieve HTTP/403
|
||||
|
||||
Args:
|
||||
request (object): The HTTP Request Object
|
||||
view (_type_): The View/Viewset Object the request was made to
|
||||
|
||||
Raises:
|
||||
PermissionDenied: User does not have the required permission.
|
||||
NotAuthenticated: User is not logged into Centurion.
|
||||
ValueError: Could not determin the view action.
|
||||
|
||||
Returns:
|
||||
True (bool): User has the required permission.
|
||||
False (bool): User does not have the required permission
|
||||
"""
|
||||
|
||||
if request.user.is_anonymous:
|
||||
|
||||
raise centurion_exceptions.NotAuthenticated()
|
||||
|
||||
try:
|
||||
|
||||
if (
|
||||
(
|
||||
view.model.__name__ == 'UserSettings'
|
||||
and request._user.id == int(view.kwargs.get('pk', 0))
|
||||
)
|
||||
or (
|
||||
view.model.__name__ == 'AuthToken'
|
||||
and request._user.id == int(view.kwargs.get('model_id', 0))
|
||||
)
|
||||
):
|
||||
|
||||
return True
|
||||
|
||||
elif (
|
||||
(
|
||||
view.model.__name__ == 'UserSettings'
|
||||
and request._user.id != int(view.kwargs.get('pk', 0))
|
||||
)
|
||||
or (
|
||||
view.model.__name__ == 'AuthToken'
|
||||
and request._user.id != int(view.kwargs.get('model_id', 0))
|
||||
)
|
||||
):
|
||||
|
||||
|
||||
return False
|
||||
|
||||
|
||||
has_permission_required: bool = False
|
||||
|
||||
user_permissions = request.tenancy._user_permissions
|
||||
|
||||
permission_required = view.get_permission_required()
|
||||
|
||||
if permission_required and user_permissions:
|
||||
# No permission_required couldnt get permissions
|
||||
# No user_permissions, user missing the required permission
|
||||
|
||||
has_permission_required: bool = permission_required in user_permissions
|
||||
|
||||
|
||||
if request.method not in view.allowed_methods:
|
||||
|
||||
raise centurion_exceptions.MethodNotAllowed(method = request.method)
|
||||
|
||||
|
||||
elif not has_permission_required and not request.user.is_superuser:
|
||||
|
||||
raise centurion_exceptions.PermissionDenied()
|
||||
|
||||
|
||||
obj_organization: Organization = view.get_obj_organization(
|
||||
request = request
|
||||
)
|
||||
|
||||
view_action: str = None
|
||||
|
||||
if(
|
||||
view.action == 'create'
|
||||
and request.method == 'POST'
|
||||
):
|
||||
|
||||
view_action = 'add'
|
||||
|
||||
elif(
|
||||
view.action == 'destroy'
|
||||
and request.method == 'DELETE'
|
||||
):
|
||||
|
||||
view_action = 'delete'
|
||||
|
||||
elif (
|
||||
view.action == 'list'
|
||||
):
|
||||
|
||||
view_action = 'view'
|
||||
|
||||
elif (
|
||||
view.action == 'partial_update'
|
||||
and request.method == 'PATCH'
|
||||
):
|
||||
|
||||
view_action = 'change'
|
||||
|
||||
elif (
|
||||
view.action == 'update'
|
||||
and request.method == 'PUT'
|
||||
):
|
||||
|
||||
view_action = 'change'
|
||||
|
||||
elif(
|
||||
view.action == 'retrieve'
|
||||
and request.method == 'GET'
|
||||
):
|
||||
|
||||
view_action = 'view'
|
||||
|
||||
elif(
|
||||
view.action == 'metadata'
|
||||
and request.method == 'OPTIONS'
|
||||
):
|
||||
|
||||
return True
|
||||
|
||||
|
||||
if view_action is None:
|
||||
|
||||
raise ValueError('view_action could not be defined.')
|
||||
|
||||
|
||||
if obj_organization is None or request.user.is_superuser:
|
||||
|
||||
return True
|
||||
|
||||
elif obj_organization is not None:
|
||||
|
||||
if request.tenancy.has_organization_permission(
|
||||
organization = obj_organization,
|
||||
permissions_required = view.get_permission_required()
|
||||
):
|
||||
|
||||
return True
|
||||
|
||||
|
||||
except ValueError as e:
|
||||
|
||||
# ToDo: This exception could be used in traces as it provides
|
||||
# information as to dodgy requests. This exception is raised
|
||||
# when the method does not match the view action.
|
||||
|
||||
print(traceback.format_exc())
|
||||
|
||||
except centurion_exceptions.Http404 as e:
|
||||
# This exception genrally means that the user is not in the same
|
||||
# organization as the object as objects are filtered to users
|
||||
# organizations ONLY.
|
||||
|
||||
pass
|
||||
|
||||
except centurion_exceptions.ObjectDoesNotExist as e:
|
||||
# This exception genrally means that the user is not in the same
|
||||
# organization as the object as objects are filtered to users
|
||||
# organizations ONLY.
|
||||
|
||||
pass
|
||||
|
||||
except centurion_exceptions.PermissionDenied as e:
|
||||
# This Exception will be raised after this function has returned
|
||||
# False.
|
||||
|
||||
pass
|
||||
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def has_object_permission(self, request, view, obj):
|
||||
|
||||
try:
|
||||
|
||||
|
||||
if request.user.is_anonymous:
|
||||
|
||||
return False
|
||||
|
||||
|
||||
if (
|
||||
(
|
||||
view.model.__name__ == 'UserSettings'
|
||||
and request._user.id == int(view.kwargs.get('pk', 0))
|
||||
)
|
||||
or (
|
||||
view.model.__name__ == 'AuthToken'
|
||||
and request._user.id == int(view.kwargs.get('model_id', 0))
|
||||
)
|
||||
):
|
||||
|
||||
return True
|
||||
|
||||
|
||||
object_organization = view._obj_organization
|
||||
|
||||
if object_organization:
|
||||
|
||||
if(
|
||||
int(object_organization)
|
||||
in view.get_permission_organizations( view.get_permission_required() )
|
||||
or request.user.is_superuser
|
||||
or getattr(request.app_settings.global_organization, 'id', 0) == int(object_organization)
|
||||
):
|
||||
|
||||
return True
|
||||
|
||||
|
||||
elif not self.is_tenancy_model( view ) or request.user.is_superuser:
|
||||
|
||||
return True
|
||||
|
||||
|
||||
except Exception as e:
|
||||
|
||||
print(traceback.format_exc())
|
||||
|
||||
return False
|
324
app/access/models.py
Normal file
324
app/access/models.py
Normal file
@ -0,0 +1,324 @@
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User, Group, Permission
|
||||
from django.forms import ValidationError
|
||||
|
||||
from .fields import *
|
||||
|
||||
from core.middleware.get_request import get_request
|
||||
from core.mixin.history_save import SaveHistory
|
||||
|
||||
|
||||
class Organization(SaveHistory):
|
||||
|
||||
class Meta:
|
||||
verbose_name_plural = "Organizations"
|
||||
ordering = ['name']
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
|
||||
if self.slug == '_':
|
||||
self.slug = self.name.lower().replace(' ', '_')
|
||||
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
id = models.AutoField(
|
||||
primary_key=True,
|
||||
unique=True,
|
||||
blank=False
|
||||
)
|
||||
|
||||
name = models.CharField(
|
||||
blank = False,
|
||||
max_length = 50,
|
||||
unique = True,
|
||||
)
|
||||
|
||||
manager = models.ForeignKey(
|
||||
User,
|
||||
on_delete=models.SET_NULL,
|
||||
blank = False,
|
||||
null = True,
|
||||
help_text = 'Organization Manager'
|
||||
)
|
||||
|
||||
model_notes = models.TextField(
|
||||
blank = True,
|
||||
default = None,
|
||||
null= True,
|
||||
verbose_name = 'Notes',
|
||||
)
|
||||
|
||||
slug = AutoSlugField()
|
||||
|
||||
created = AutoCreatedField()
|
||||
|
||||
modified = AutoLastModifiedField()
|
||||
|
||||
|
||||
def get_organization(self):
|
||||
return self
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
|
||||
class TenancyManager(models.Manager):
|
||||
"""Multi-Tennant Object Manager
|
||||
|
||||
This manager specifically caters for the multi-tenancy features of Centurion ERP.
|
||||
"""
|
||||
|
||||
|
||||
def get_queryset(self):
|
||||
""" Fetch the data
|
||||
|
||||
This function filters the data fetched from the database to that which is from the organizations
|
||||
the user is a part of.
|
||||
|
||||
!!! danger "Requirement"
|
||||
This method may be overridden however must still be called from the overriding function. i.e. `super().get_queryset()`
|
||||
|
||||
## Workflow
|
||||
|
||||
This functions workflow is as follows:
|
||||
|
||||
- Fetch the user from the request
|
||||
|
||||
- Check if the user is authenticated
|
||||
|
||||
- Iterate over the users teams
|
||||
|
||||
- Store unique organizations from users teams
|
||||
|
||||
- return results
|
||||
|
||||
Returns:
|
||||
(queryset): **super user**: return unfiltered data.
|
||||
(queryset): **not super user**: return data from the stored unique organizations.
|
||||
"""
|
||||
|
||||
request = get_request()
|
||||
|
||||
user_organizations: list(str()) = []
|
||||
|
||||
|
||||
if request:
|
||||
|
||||
user = request.user._wrapped if hasattr(request.user,'_wrapped') else request.user
|
||||
|
||||
|
||||
if user.is_authenticated:
|
||||
|
||||
for team_user in TeamUsers.objects.filter(user=user):
|
||||
|
||||
|
||||
if team_user.team.organization.name not in user_organizations:
|
||||
|
||||
|
||||
if not user_organizations:
|
||||
|
||||
self.user_organizations = []
|
||||
|
||||
user_organizations += [ team_user.team.organization.id ]
|
||||
|
||||
|
||||
if len(user_organizations) > 0 and not user.is_superuser:
|
||||
|
||||
return super().get_queryset().filter(
|
||||
models.Q(organization__in=user_organizations)
|
||||
|
|
||||
models.Q(is_global = True)
|
||||
)
|
||||
|
||||
return super().get_queryset()
|
||||
|
||||
|
||||
|
||||
class TenancyObject(SaveHistory):
|
||||
""" Tenancy Model Abstrct class.
|
||||
|
||||
This class is for inclusion wihtin **every** model within Centurion ERP.
|
||||
Provides the required fields, functions and methods for multi tennant objects.
|
||||
Unless otherwise stated, **no** object within this class may be overridden.
|
||||
|
||||
Raises:
|
||||
ValidationError: User failed to supply organization
|
||||
"""
|
||||
|
||||
objects = TenancyManager()
|
||||
""" Multi-Tenanant Objects """
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
||||
def validatate_organization_exists(self):
|
||||
"""Ensure that the user did provide an organization
|
||||
|
||||
Raises:
|
||||
ValidationError: User failed to supply organization.
|
||||
"""
|
||||
|
||||
if not self:
|
||||
raise ValidationError('You must provide an organization')
|
||||
|
||||
|
||||
organization = models.ForeignKey(
|
||||
Organization,
|
||||
on_delete=models.CASCADE,
|
||||
blank = False,
|
||||
null = True,
|
||||
validators = [validatate_organization_exists],
|
||||
)
|
||||
|
||||
is_global = models.BooleanField(
|
||||
default = False,
|
||||
blank = False
|
||||
)
|
||||
|
||||
model_notes = models.TextField(
|
||||
blank = True,
|
||||
default = None,
|
||||
null= True,
|
||||
verbose_name = 'Notes',
|
||||
)
|
||||
|
||||
def get_organization(self) -> Organization:
|
||||
return self.organization
|
||||
|
||||
|
||||
|
||||
class Team(Group, TenancyObject):
|
||||
class Meta:
|
||||
# proxy = True
|
||||
verbose_name_plural = "Teams"
|
||||
ordering = ['team_name']
|
||||
|
||||
|
||||
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
|
||||
|
||||
self.name = self.organization.name.lower().replace(' ', '_') + '_' + self.team_name.lower().replace(' ', '_')
|
||||
|
||||
super().save(force_insert=force_insert, force_update=force_update, using=using, update_fields=update_fields)
|
||||
|
||||
|
||||
team_name = models.CharField(
|
||||
verbose_name = 'Name',
|
||||
blank = False,
|
||||
max_length = 50,
|
||||
unique = False,
|
||||
default = ''
|
||||
)
|
||||
|
||||
created = AutoCreatedField()
|
||||
|
||||
modified = AutoLastModifiedField()
|
||||
|
||||
|
||||
@property
|
||||
def parent_object(self):
|
||||
""" Fetch the parent object """
|
||||
|
||||
return self.organization
|
||||
|
||||
|
||||
def permission_list(self) -> list:
|
||||
|
||||
permission_list = []
|
||||
|
||||
for permission in self.permissions.all():
|
||||
|
||||
if str(permission.content_type.app_label + '.' + permission.codename) in permission_list:
|
||||
continue
|
||||
|
||||
permission_list += [ str(permission.content_type.app_label + '.' + permission.codename) ]
|
||||
|
||||
return [permission_list, self.permissions.all()]
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return self.team_name
|
||||
|
||||
|
||||
|
||||
class TeamUsers(SaveHistory):
|
||||
|
||||
class Meta:
|
||||
# proxy = True
|
||||
verbose_name_plural = "Team Users"
|
||||
ordering = ['user']
|
||||
|
||||
id = models.AutoField(
|
||||
primary_key=True,
|
||||
unique=True,
|
||||
blank=False
|
||||
)
|
||||
|
||||
team = models.ForeignKey(
|
||||
Team,
|
||||
related_name="team",
|
||||
on_delete=models.CASCADE)
|
||||
|
||||
user = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE
|
||||
)
|
||||
|
||||
manager = models.BooleanField(
|
||||
verbose_name='manager',
|
||||
default=False,
|
||||
blank=True
|
||||
)
|
||||
|
||||
created = AutoCreatedField()
|
||||
|
||||
modified = AutoLastModifiedField()
|
||||
|
||||
|
||||
def delete(self, using=None, keep_parents=False):
|
||||
""" Delete Team
|
||||
|
||||
Overrides, post-action
|
||||
As teams are an extension of Groups, remove the user to the team.
|
||||
"""
|
||||
|
||||
super().delete(using=using, keep_parents=keep_parents)
|
||||
|
||||
group = Group.objects.get(pk=self.team.id)
|
||||
|
||||
user = User.objects.get(pk=self.user_id)
|
||||
|
||||
user.groups.remove(group)
|
||||
|
||||
|
||||
def get_organization(self) -> Organization:
|
||||
return self.team.organization
|
||||
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
""" Save Team
|
||||
|
||||
Overrides, post-action
|
||||
As teams are an extension of groups, add the user to the matching group.
|
||||
"""
|
||||
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
group = Group.objects.get(pk=self.team.id)
|
||||
|
||||
user = User.objects.get(pk=self.user_id)
|
||||
|
||||
user.groups.add(group)
|
||||
|
||||
|
||||
@property
|
||||
def parent_object(self):
|
||||
""" Fetch the parent object """
|
||||
|
||||
return self.team
|
||||
|
||||
def __str__(self):
|
||||
return self.user.username
|
||||
|
@ -1,155 +0,0 @@
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from rest_framework.reverse import reverse
|
||||
|
||||
from access.fields import (
|
||||
AutoCreatedField,
|
||||
AutoLastModifiedField,
|
||||
AutoSlugField
|
||||
)
|
||||
|
||||
from core.mixin.history_save import SaveHistory
|
||||
|
||||
|
||||
|
||||
class Organization(SaveHistory):
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Organization"
|
||||
verbose_name_plural = "Organizations"
|
||||
ordering = ['name']
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
|
||||
if self.slug == '_':
|
||||
self.slug = self.name.lower().replace(' ', '_')
|
||||
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
id = models.AutoField(
|
||||
blank=False,
|
||||
help_text = 'ID of this item',
|
||||
primary_key=True,
|
||||
unique=True,
|
||||
verbose_name = 'ID'
|
||||
)
|
||||
|
||||
name = models.CharField(
|
||||
blank = False,
|
||||
help_text = 'Name of this Organization',
|
||||
max_length = 50,
|
||||
unique = True,
|
||||
verbose_name = 'Name'
|
||||
)
|
||||
|
||||
manager = models.ForeignKey(
|
||||
User,
|
||||
blank = False,
|
||||
help_text = 'Manager for this organization',
|
||||
null = True,
|
||||
on_delete=models.SET_NULL,
|
||||
verbose_name = 'Manager'
|
||||
)
|
||||
|
||||
model_notes = models.TextField(
|
||||
blank = True,
|
||||
default = None,
|
||||
help_text = 'Tid bits of information',
|
||||
null= True,
|
||||
verbose_name = 'Notes',
|
||||
)
|
||||
|
||||
slug = AutoSlugField()
|
||||
|
||||
created = AutoCreatedField()
|
||||
|
||||
modified = AutoLastModifiedField()
|
||||
|
||||
|
||||
def get_organization(self):
|
||||
return self
|
||||
|
||||
def __int__(self):
|
||||
|
||||
return self.id
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
table_fields: list = [
|
||||
'nbsp',
|
||||
'name',
|
||||
'created',
|
||||
'modified',
|
||||
'nbsp'
|
||||
]
|
||||
|
||||
page_layout: list = [
|
||||
{
|
||||
"name": "Details",
|
||||
"slug": "details",
|
||||
"sections": [
|
||||
{
|
||||
"layout": "double",
|
||||
"left": [
|
||||
'name',
|
||||
'manager',
|
||||
'created',
|
||||
'modified',
|
||||
],
|
||||
"right": [
|
||||
'model_notes',
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Teams",
|
||||
"slug": "teams",
|
||||
"sections": [
|
||||
{
|
||||
"layout": "table",
|
||||
"field": "teams"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Knowledge Base",
|
||||
"slug": "kb_articles",
|
||||
"sections": [
|
||||
{
|
||||
"layout": "table",
|
||||
"field": "knowledge_base",
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Notes",
|
||||
"slug": "notes",
|
||||
"sections": []
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def get_url( self, request = None ) -> str:
|
||||
|
||||
if request:
|
||||
|
||||
return reverse("v2:_api_v2_organization-detail", request=request, kwargs={'pk': self.id})
|
||||
|
||||
return reverse("v2:_api_v2_organization-detail", kwargs={'pk': self.id})
|
||||
|
||||
|
||||
def save_history(self, before: dict, after: dict) -> bool:
|
||||
|
||||
from access.models.organization_history import OrganizationHistory
|
||||
|
||||
history = super().save_history(
|
||||
before = before,
|
||||
after = after,
|
||||
history_model = OrganizationHistory
|
||||
)
|
||||
|
||||
|
||||
return history
|
@ -1,53 +0,0 @@
|
||||
from django.db import models
|
||||
|
||||
from core.models.model_history import ModelHistory
|
||||
|
||||
from access.models.organization import Organization
|
||||
|
||||
|
||||
|
||||
class OrganizationHistory(
|
||||
ModelHistory
|
||||
):
|
||||
|
||||
|
||||
class Meta:
|
||||
|
||||
db_table = 'access_organization_history'
|
||||
|
||||
ordering = ModelHistory._meta.ordering
|
||||
|
||||
verbose_name = 'Organization History'
|
||||
|
||||
verbose_name_plural = 'Organization History'
|
||||
|
||||
|
||||
model = models.ForeignKey(
|
||||
Organization,
|
||||
blank = False,
|
||||
help_text = 'Model this note belongs to',
|
||||
null = False,
|
||||
on_delete = models.CASCADE,
|
||||
related_name = 'history',
|
||||
verbose_name = 'Model',
|
||||
)
|
||||
|
||||
table_fields: list = []
|
||||
|
||||
page_layout: dict = []
|
||||
|
||||
|
||||
def get_object(self):
|
||||
|
||||
return self
|
||||
|
||||
|
||||
def get_serialized_model(self, serializer_context):
|
||||
|
||||
model = None
|
||||
|
||||
from access.serializers.organization import OrganizationBaseSerializer
|
||||
|
||||
model = OrganizationBaseSerializer(self.model, context = serializer_context)
|
||||
|
||||
return model
|
@ -1,45 +0,0 @@
|
||||
from django.db import models
|
||||
|
||||
from access.models.organization import Organization
|
||||
|
||||
from core.models.model_notes import ModelNotes
|
||||
|
||||
|
||||
|
||||
class OrganizationNotes(
|
||||
ModelNotes
|
||||
):
|
||||
|
||||
|
||||
class Meta:
|
||||
|
||||
db_table = 'access_organization_notes'
|
||||
|
||||
ordering = ModelNotes._meta.ordering
|
||||
|
||||
verbose_name = 'Organization Note'
|
||||
|
||||
verbose_name_plural = 'Organization Notes'
|
||||
|
||||
|
||||
model = models.ForeignKey(
|
||||
Organization,
|
||||
blank = False,
|
||||
help_text = 'Model this note belongs to',
|
||||
null = False,
|
||||
on_delete = models.CASCADE,
|
||||
related_name = 'notes',
|
||||
verbose_name = 'Model',
|
||||
)
|
||||
|
||||
table_fields: list = []
|
||||
|
||||
page_layout: dict = []
|
||||
|
||||
|
||||
def get_url_kwargs(self) -> dict:
|
||||
|
||||
return {
|
||||
'model_id': self.model.pk,
|
||||
'pk': self.pk
|
||||
}
|
@ -1,189 +0,0 @@
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import Group
|
||||
|
||||
from rest_framework.reverse import reverse
|
||||
|
||||
from access.fields import (
|
||||
AutoCreatedField,
|
||||
AutoLastModifiedField
|
||||
)
|
||||
|
||||
from access.models.organization import Organization
|
||||
from access.models.tenancy import TenancyObject
|
||||
|
||||
from core import exceptions as centurion_exceptions
|
||||
|
||||
|
||||
class Team(Group, TenancyObject):
|
||||
|
||||
class Meta:
|
||||
|
||||
ordering = [ 'team_name' ]
|
||||
|
||||
verbose_name = 'Team'
|
||||
|
||||
verbose_name_plural = "Teams"
|
||||
|
||||
|
||||
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
|
||||
|
||||
self.name = self.organization.name.lower().replace(' ', '_') + '_' + self.team_name.lower().replace(' ', '_')
|
||||
|
||||
super().save(force_insert=force_insert, force_update=force_update, using=using, update_fields=update_fields)
|
||||
|
||||
|
||||
def validatate_organization_exists(self):
|
||||
"""Ensure that the user did provide an organization
|
||||
|
||||
Raises:
|
||||
ValidationError: User failed to supply organization.
|
||||
"""
|
||||
|
||||
if not self:
|
||||
raise centurion_exceptions.ValidationError('You must provide an organization')
|
||||
|
||||
|
||||
|
||||
team_name = models.CharField(
|
||||
blank = False,
|
||||
help_text = 'Name to give this team',
|
||||
max_length = 50,
|
||||
unique = False,
|
||||
verbose_name = 'Name',
|
||||
)
|
||||
|
||||
organization = models.ForeignKey(
|
||||
Organization,
|
||||
blank = False,
|
||||
help_text = 'Organization this belongs to',
|
||||
null = False,
|
||||
on_delete = models.CASCADE,
|
||||
validators = [validatate_organization_exists],
|
||||
verbose_name = 'Organization'
|
||||
)
|
||||
|
||||
created = AutoCreatedField()
|
||||
|
||||
modified = AutoLastModifiedField()
|
||||
|
||||
page_layout: dict = [
|
||||
{
|
||||
"name": "Details",
|
||||
"slug": "details",
|
||||
"sections": [
|
||||
{
|
||||
"layout": "double",
|
||||
"left": [
|
||||
'organization',
|
||||
'team_name',
|
||||
'created',
|
||||
'modified',
|
||||
],
|
||||
"right": [
|
||||
'model_notes',
|
||||
]
|
||||
},
|
||||
{
|
||||
"layout": "table",
|
||||
"name": "Users",
|
||||
"field": "users",
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Knowledge Base",
|
||||
"slug": "kb_articles",
|
||||
"sections": [
|
||||
{
|
||||
"layout": "table",
|
||||
"field": "knowledge_base",
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Notes",
|
||||
"slug": "notes",
|
||||
"sections": []
|
||||
},
|
||||
]
|
||||
|
||||
table_fields: list = [
|
||||
'team_name',
|
||||
'modified',
|
||||
'created',
|
||||
]
|
||||
|
||||
|
||||
def get_url( self, request = None ) -> str:
|
||||
|
||||
if request:
|
||||
|
||||
return reverse(f"v2:_api_v2_organization_team-detail", request=request, kwargs = self.get_url_kwargs() )
|
||||
|
||||
return reverse(f"v2:_api_v2_organization_team-detail", kwargs = self.get_url_kwargs() )
|
||||
|
||||
|
||||
def get_url_kwargs(self) -> dict:
|
||||
"""Fetch the URL kwargs
|
||||
|
||||
Returns:
|
||||
dict: kwargs required for generating the URL with `reverse`
|
||||
"""
|
||||
|
||||
return {
|
||||
'organization_id': self.organization.id,
|
||||
'pk': self.id
|
||||
}
|
||||
|
||||
|
||||
def get_url_kwargs_notes(self) -> dict:
|
||||
"""Fetch the URL kwargs for model notes
|
||||
|
||||
Returns:
|
||||
dict: notes kwargs required for generating the URL with `reverse`
|
||||
"""
|
||||
|
||||
return {
|
||||
'organization_id': self.organization.id,
|
||||
'model_id': self.id
|
||||
}
|
||||
|
||||
|
||||
|
||||
# @property
|
||||
# def parent_object(self):
|
||||
# """ Fetch the parent object """
|
||||
|
||||
# return self.organization
|
||||
|
||||
|
||||
def permission_list(self) -> list:
|
||||
|
||||
permission_list = []
|
||||
|
||||
for permission in self.permissions.all():
|
||||
|
||||
if str(permission.content_type.app_label + '.' + permission.codename) in permission_list:
|
||||
continue
|
||||
|
||||
permission_list += [ str(permission.content_type.app_label + '.' + permission.codename) ]
|
||||
|
||||
return [permission_list, self.permissions.all()]
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return self.organization.name + ', ' + self.team_name
|
||||
|
||||
|
||||
def save_history(self, before: dict, after: dict) -> bool:
|
||||
|
||||
from access.models.team_history import TeamHistory
|
||||
|
||||
history = super().save_history(
|
||||
before = before,
|
||||
after = after,
|
||||
history_model = TeamHistory
|
||||
)
|
||||
|
||||
|
||||
return history
|
@ -1,53 +0,0 @@
|
||||
from django.db import models
|
||||
|
||||
from core.models.model_history import ModelHistory
|
||||
|
||||
from access.models.team import Team
|
||||
|
||||
|
||||
|
||||
class TeamHistory(
|
||||
ModelHistory
|
||||
):
|
||||
|
||||
|
||||
class Meta:
|
||||
|
||||
db_table = 'access_team_history'
|
||||
|
||||
ordering = ModelHistory._meta.ordering
|
||||
|
||||
verbose_name = 'Team History'
|
||||
|
||||
verbose_name_plural = 'Team History'
|
||||
|
||||
|
||||
model = models.ForeignKey(
|
||||
Team,
|
||||
blank = False,
|
||||
help_text = 'Model this note belongs to',
|
||||
null = False,
|
||||
on_delete = models.CASCADE,
|
||||
related_name = 'history',
|
||||
verbose_name = 'Model',
|
||||
)
|
||||
|
||||
table_fields: list = []
|
||||
|
||||
page_layout: dict = []
|
||||
|
||||
|
||||
def get_object(self):
|
||||
|
||||
return self
|
||||
|
||||
|
||||
def get_serialized_model(self, serializer_context):
|
||||
|
||||
model = None
|
||||
|
||||
from access.serializers.teams import TeamBaseSerializer
|
||||
|
||||
model = TeamBaseSerializer(self.model, context = serializer_context)
|
||||
|
||||
return model
|
@ -1,54 +0,0 @@
|
||||
from django.db import models
|
||||
|
||||
from rest_framework.reverse import reverse
|
||||
|
||||
from access.models.team import Team
|
||||
|
||||
from core.models.model_notes import ModelNotes
|
||||
|
||||
|
||||
|
||||
class TeamNotes(
|
||||
ModelNotes
|
||||
):
|
||||
|
||||
|
||||
class Meta:
|
||||
|
||||
db_table = 'access_team_notes'
|
||||
|
||||
ordering = ModelNotes._meta.ordering
|
||||
|
||||
verbose_name = 'Team Note'
|
||||
|
||||
verbose_name_plural = 'Team Notes'
|
||||
|
||||
|
||||
model = models.ForeignKey(
|
||||
Team,
|
||||
blank = False,
|
||||
help_text = 'Model this note belongs to',
|
||||
null = False,
|
||||
on_delete = models.CASCADE,
|
||||
related_name = 'notes',
|
||||
verbose_name = 'Model',
|
||||
)
|
||||
|
||||
table_fields: list = []
|
||||
|
||||
page_layout: dict = []
|
||||
|
||||
|
||||
def get_url( self, request = None ) -> str:
|
||||
|
||||
kwargs = {
|
||||
'organization_id': self.organization.pk,
|
||||
'model_id': self.model.pk,
|
||||
'pk': self.pk
|
||||
}
|
||||
|
||||
if request:
|
||||
|
||||
return reverse("v2:_api_v2_team_note-detail", request=request, kwargs = kwargs )
|
||||
|
||||
return reverse("v2:_api_v2_team_note-detail", kwargs = kwargs )
|
@ -1,143 +0,0 @@
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User, Group
|
||||
|
||||
from rest_framework.reverse import reverse
|
||||
|
||||
from access.fields import (
|
||||
AutoCreatedField,
|
||||
AutoLastModifiedField
|
||||
)
|
||||
|
||||
from access.models.organization import Organization
|
||||
from access.models.team import Team
|
||||
|
||||
from core.lib.feature_not_used import FeatureNotUsed
|
||||
from core.mixin.history_save import SaveHistory
|
||||
|
||||
|
||||
|
||||
class TeamUsers(SaveHistory):
|
||||
|
||||
class Meta:
|
||||
|
||||
ordering = ['user']
|
||||
|
||||
verbose_name = "Team User"
|
||||
|
||||
verbose_name_plural = "Team Users"
|
||||
|
||||
|
||||
id = models.AutoField(
|
||||
blank=False,
|
||||
help_text = 'ID of this Team User',
|
||||
primary_key=True,
|
||||
unique=True,
|
||||
verbose_name = 'ID'
|
||||
)
|
||||
|
||||
team = models.ForeignKey(
|
||||
Team,
|
||||
blank = False,
|
||||
help_text = 'Team user belongs to',
|
||||
null = False,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="team",
|
||||
verbose_name = 'Team'
|
||||
)
|
||||
|
||||
user = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
blank = False,
|
||||
help_text = 'User who will be added to the team',
|
||||
null = False,
|
||||
on_delete=models.CASCADE,
|
||||
verbose_name = 'User'
|
||||
)
|
||||
|
||||
manager = models.BooleanField(
|
||||
blank=True,
|
||||
default=False,
|
||||
help_text = 'Is this user to be a manager of this team',
|
||||
verbose_name='manager',
|
||||
)
|
||||
|
||||
created = AutoCreatedField()
|
||||
|
||||
modified = AutoLastModifiedField()
|
||||
|
||||
page_layout: list = []
|
||||
|
||||
table_fields: list = [
|
||||
'user',
|
||||
'manager'
|
||||
]
|
||||
|
||||
|
||||
def delete(self, using=None, keep_parents=False):
|
||||
""" Delete Team
|
||||
|
||||
Overrides, post-action
|
||||
As teams are an extension of Groups, remove the user to the team.
|
||||
"""
|
||||
|
||||
super().delete(using=using, keep_parents=keep_parents)
|
||||
|
||||
group = Group.objects.get(pk=self.team.id)
|
||||
|
||||
user = User.objects.get(pk=self.user_id)
|
||||
|
||||
user.groups.remove(group)
|
||||
|
||||
|
||||
def get_organization(self) -> Organization:
|
||||
return self.team.organization
|
||||
|
||||
|
||||
def get_url( self, request = None ) -> str:
|
||||
|
||||
url_kwargs: dict = {
|
||||
'organization_id': self.team.organization.id,
|
||||
'team_id': self.team.id,
|
||||
'pk': self.id
|
||||
}
|
||||
|
||||
print(f'url kwargs are: {url_kwargs}')
|
||||
|
||||
|
||||
if request:
|
||||
|
||||
return reverse(f"v2:_api_v2_organization_team_user-detail", request=request, kwargs = url_kwargs )
|
||||
|
||||
return reverse(f"v2:_api_v2_organization_team_user-detail", kwargs = url_kwargs )
|
||||
|
||||
|
||||
def get_url_kwargs_notes(self):
|
||||
|
||||
return FeatureNotUsed
|
||||
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
""" Save Team
|
||||
|
||||
Overrides, post-action
|
||||
As teams are an extension of groups, add the user to the matching group.
|
||||
"""
|
||||
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
group = Group.objects.get(pk=self.team.id)
|
||||
|
||||
user = User.objects.get(pk=self.user_id)
|
||||
|
||||
user.groups.add(group)
|
||||
|
||||
|
||||
@property
|
||||
def parent_object(self):
|
||||
""" Fetch the parent object """
|
||||
|
||||
return self.team
|
||||
|
||||
def __str__(self):
|
||||
return self.user.username
|
@ -1,254 +0,0 @@
|
||||
# from django.conf import settings
|
||||
from django.db import models
|
||||
# from django.contrib.auth.models import User, Group
|
||||
|
||||
from rest_framework.reverse import reverse
|
||||
|
||||
# from .fields import *
|
||||
|
||||
from access.models.organization import Organization
|
||||
|
||||
from core import exceptions as centurion_exceptions
|
||||
from core.middleware.get_request import get_request
|
||||
from core.mixin.history_save import SaveHistory
|
||||
|
||||
|
||||
|
||||
class TenancyManager(models.Manager):
|
||||
"""Multi-Tennant Object Manager
|
||||
|
||||
This manager specifically caters for the multi-tenancy features of Centurion ERP.
|
||||
"""
|
||||
|
||||
|
||||
def get_queryset(self):
|
||||
""" Fetch the data
|
||||
|
||||
This function filters the data fetched from the database to that which is from the organizations
|
||||
the user is a part of.
|
||||
|
||||
!!! danger "Requirement"
|
||||
This method may be overridden however must still be called from the overriding function. i.e. `super().get_queryset()`
|
||||
|
||||
## Workflow
|
||||
|
||||
This functions workflow is as follows:
|
||||
|
||||
- Fetch the user from the request
|
||||
|
||||
- Check if the user is authenticated
|
||||
|
||||
- Iterate over the users teams
|
||||
|
||||
- Store unique organizations from users teams
|
||||
|
||||
- return results
|
||||
|
||||
Returns:
|
||||
(queryset): **super user**: return unfiltered data.
|
||||
(queryset): **not super user**: return data from the stored unique organizations.
|
||||
"""
|
||||
|
||||
request = get_request()
|
||||
|
||||
user_organizations: list(str()) = []
|
||||
|
||||
|
||||
if request:
|
||||
|
||||
if request.app_settings.global_organization:
|
||||
|
||||
user_organizations += [ request.app_settings.global_organization.id ]
|
||||
|
||||
|
||||
user = request.user
|
||||
|
||||
|
||||
if user.is_authenticated:
|
||||
|
||||
for team in request.tenancy._user_teams:
|
||||
|
||||
|
||||
if team.organization.id not in user_organizations:
|
||||
|
||||
if not user_organizations:
|
||||
|
||||
self.user_organizations = []
|
||||
|
||||
user_organizations += [ team.organization.id ]
|
||||
|
||||
|
||||
# if len(user_organizations) > 0 and not user.is_superuser and self.model.is_global is not None:
|
||||
if len(user_organizations) > 0 and not user.is_superuser:
|
||||
|
||||
if getattr(self.model, 'is_global', False) is True:
|
||||
|
||||
return super().get_queryset().filter(
|
||||
models.Q(organization__in=user_organizations)
|
||||
|
|
||||
models.Q(is_global = True)
|
||||
)
|
||||
|
||||
else:
|
||||
|
||||
return super().get_queryset().filter(
|
||||
models.Q(organization__in=user_organizations)
|
||||
)
|
||||
|
||||
return super().get_queryset()
|
||||
|
||||
|
||||
|
||||
class TenancyObject(SaveHistory):
|
||||
""" Tenancy Model Abstrct class.
|
||||
|
||||
This class is for inclusion wihtin **every** model within Centurion ERP.
|
||||
Provides the required fields, functions and methods for multi tennant objects.
|
||||
Unless otherwise stated, **no** object within this class may be overridden.
|
||||
|
||||
Raises:
|
||||
ValidationError: User failed to supply organization
|
||||
"""
|
||||
|
||||
objects = TenancyManager()
|
||||
""" Multi-Tenanant Objects """
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
||||
def validatate_organization_exists(self):
|
||||
"""Ensure that the user did provide an organization
|
||||
|
||||
Raises:
|
||||
ValidationError: User failed to supply organization.
|
||||
"""
|
||||
|
||||
if not self:
|
||||
raise centurion_exceptions.ValidationError('You must provide an organization')
|
||||
|
||||
|
||||
id = models.AutoField(
|
||||
blank=False,
|
||||
help_text = 'ID of the item',
|
||||
primary_key=True,
|
||||
unique=True,
|
||||
verbose_name = 'ID'
|
||||
)
|
||||
|
||||
organization = models.ForeignKey(
|
||||
Organization,
|
||||
blank = False,
|
||||
help_text = 'Organization this belongs to',
|
||||
null = False,
|
||||
on_delete = models.CASCADE,
|
||||
related_name = '+',
|
||||
validators = [validatate_organization_exists],
|
||||
verbose_name = 'Organization'
|
||||
)
|
||||
|
||||
is_global = models.BooleanField(
|
||||
blank = False,
|
||||
default = False,
|
||||
help_text = 'Is this a global object?',
|
||||
verbose_name = 'Global Object'
|
||||
)
|
||||
|
||||
model_notes = models.TextField(
|
||||
blank = True,
|
||||
default = None,
|
||||
help_text = 'Tid bits of information',
|
||||
null = True,
|
||||
verbose_name = 'Notes',
|
||||
)
|
||||
|
||||
def get_organization(self) -> Organization:
|
||||
return self.organization
|
||||
|
||||
app_namespace: str = None
|
||||
"""Application namespace.
|
||||
|
||||
Specify the applications namespace i.e. `devops`, without including
|
||||
the API version, i.e. `v2:devops`.
|
||||
"""
|
||||
|
||||
def get_app_namespace(self) -> str:
|
||||
"""Fetch the Application namespace if specified.
|
||||
|
||||
Returns:
|
||||
str: Application namespace suffixed with colin `:`
|
||||
None: No application namespace found.
|
||||
"""
|
||||
|
||||
app_namespace = ''
|
||||
|
||||
if self.app_namespace:
|
||||
|
||||
app_namespace = self.app_namespace + ':'
|
||||
|
||||
return str(app_namespace)
|
||||
|
||||
|
||||
def get_url( self, request = None ) -> str:
|
||||
"""Fetch the models URL
|
||||
|
||||
If URL kwargs are required to generate the URL, define a `get_url_kwargs` that returns them.
|
||||
|
||||
Args:
|
||||
request (object, optional): The request object that was made by the end user. Defaults to None.
|
||||
|
||||
Returns:
|
||||
str: Canonical URL of the model if the `request` object was provided. Otherwise the relative URL.
|
||||
"""
|
||||
|
||||
model_name = str(self._meta.verbose_name.lower()).replace(' ', '_')
|
||||
|
||||
|
||||
if request:
|
||||
|
||||
return reverse(f"v2:" + self.get_app_namespace() + f"_api_v2_{model_name}-detail", request=request, kwargs = self.get_url_kwargs() )
|
||||
|
||||
return reverse(f"v2:" + self.get_app_namespace() + f"_api_v2_{model_name}-detail", kwargs = self.get_url_kwargs() )
|
||||
|
||||
|
||||
def get_url_kwargs(self) -> dict:
|
||||
"""Fetch the URL kwargs
|
||||
|
||||
Returns:
|
||||
dict: kwargs required for generating the URL with `reverse`
|
||||
"""
|
||||
|
||||
return {
|
||||
'pk': self.id
|
||||
}
|
||||
|
||||
|
||||
def get_url_kwargs_notes(self) -> dict:
|
||||
"""Fetch the URL kwargs for model notes
|
||||
|
||||
Returns:
|
||||
dict: notes kwargs required for generating the URL with `reverse`
|
||||
"""
|
||||
|
||||
return {
|
||||
'model_id': self.id
|
||||
}
|
||||
|
||||
|
||||
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
|
||||
|
||||
self.clean()
|
||||
|
||||
if(
|
||||
not getattr(self, 'organization', None)
|
||||
and self._meta.model_name !='appsettingshistory' # App Settings for
|
||||
):
|
||||
|
||||
raise centurion_exceptions.ValidationError(
|
||||
detail = {
|
||||
'organization': 'Organization is required'
|
||||
},
|
||||
code = 'required'
|
||||
)
|
||||
|
||||
super().save(force_insert=force_insert, force_update=force_update, using=using, update_fields=update_fields)
|
@ -1,104 +0,0 @@
|
||||
from rest_framework.reverse import reverse
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
from access.models.organization import Organization
|
||||
|
||||
from app.serializers.user import UserBaseSerializer
|
||||
|
||||
from core import fields as centurion_field
|
||||
|
||||
|
||||
|
||||
class OrganizationBaseSerializer(serializers.ModelSerializer):
|
||||
|
||||
display_name = serializers.SerializerMethodField('get_display_name')
|
||||
|
||||
def get_display_name(self, item) -> str:
|
||||
|
||||
return str( item )
|
||||
|
||||
url = serializers.HyperlinkedIdentityField(
|
||||
view_name="v2:_api_v2_organization-detail", format="html"
|
||||
)
|
||||
|
||||
class Meta:
|
||||
|
||||
model = Organization
|
||||
|
||||
fields = [
|
||||
'id',
|
||||
'display_name',
|
||||
'name',
|
||||
'url',
|
||||
]
|
||||
|
||||
read_only_fields = [
|
||||
'id',
|
||||
'display_name',
|
||||
'name',
|
||||
'url',
|
||||
]
|
||||
|
||||
|
||||
|
||||
class OrganizationModelSerializer(
|
||||
OrganizationBaseSerializer
|
||||
):
|
||||
|
||||
_urls = serializers.SerializerMethodField('get_url')
|
||||
|
||||
def get_url(self, item) -> dict:
|
||||
|
||||
return {
|
||||
'_self': item.get_url( request = self._context['view'].request ),
|
||||
'knowledge_base': reverse(
|
||||
"v2:_api_v2_model_kb-list",
|
||||
request=self._context['view'].request,
|
||||
kwargs={
|
||||
'model': self.Meta.model._meta.model_name,
|
||||
'model_pk': item.pk
|
||||
}
|
||||
),
|
||||
'notes': reverse(
|
||||
"v2:_api_v2_organization_note-list",
|
||||
request=self._context['view'].request,
|
||||
kwargs={
|
||||
'model_id': item.pk
|
||||
}
|
||||
),
|
||||
'teams': reverse("v2:_api_v2_organization_team-list", request=self._context['view'].request, kwargs={'organization_id': item.pk}),
|
||||
}
|
||||
|
||||
model_notes = centurion_field.MarkdownField( required = False )
|
||||
|
||||
class Meta:
|
||||
|
||||
model = Organization
|
||||
|
||||
fields = '__all__'
|
||||
|
||||
fields = [
|
||||
'id',
|
||||
'display_name',
|
||||
'name',
|
||||
'model_notes',
|
||||
'manager',
|
||||
'created',
|
||||
'modified',
|
||||
'_urls',
|
||||
]
|
||||
|
||||
read_only_fields = [
|
||||
'id',
|
||||
'display_name',
|
||||
'created',
|
||||
'modified',
|
||||
'_urls',
|
||||
]
|
||||
|
||||
|
||||
class OrganizationViewSerializer(OrganizationModelSerializer):
|
||||
pass
|
||||
|
||||
manager = UserBaseSerializer(many=False, read_only = True)
|
@ -1,48 +0,0 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from access.models.organization_notes import OrganizationNotes
|
||||
|
||||
from api.serializers import common
|
||||
|
||||
from app.serializers.user import UserBaseSerializer
|
||||
|
||||
from core.serializers.model_notes import (
|
||||
ModelNotes,
|
||||
ModelNoteBaseSerializer,
|
||||
ModelNoteModelSerializer,
|
||||
ModelNoteViewSerializer
|
||||
)
|
||||
|
||||
|
||||
|
||||
class OrganizationNoteBaseSerializer(ModelNoteBaseSerializer):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class OrganizationNoteModelSerializer(
|
||||
ModelNoteModelSerializer
|
||||
):
|
||||
|
||||
|
||||
class Meta:
|
||||
|
||||
model = OrganizationNotes
|
||||
|
||||
fields = ModelNoteModelSerializer.Meta.fields + [
|
||||
'model',
|
||||
]
|
||||
|
||||
read_only_fields = ModelNoteModelSerializer.Meta.read_only_fields + [
|
||||
'model',
|
||||
'content_type',
|
||||
]
|
||||
|
||||
|
||||
|
||||
class OrganizationNoteViewSerializer(
|
||||
ModelNoteViewSerializer,
|
||||
OrganizationNoteModelSerializer,
|
||||
):
|
||||
|
||||
pass
|
@ -1,48 +0,0 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from access.models.team_notes import TeamNotes
|
||||
|
||||
from api.serializers import common
|
||||
|
||||
from app.serializers.user import UserBaseSerializer
|
||||
|
||||
from core.serializers.model_notes import (
|
||||
ModelNotes,
|
||||
ModelNoteBaseSerializer,
|
||||
ModelNoteModelSerializer,
|
||||
ModelNoteViewSerializer
|
||||
)
|
||||
|
||||
|
||||
|
||||
class TeamNoteBaseSerializer(ModelNoteBaseSerializer):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class TeamNoteModelSerializer(
|
||||
ModelNoteModelSerializer
|
||||
):
|
||||
|
||||
|
||||
class Meta:
|
||||
|
||||
model = TeamNotes
|
||||
|
||||
fields = ModelNoteModelSerializer.Meta.fields + [
|
||||
'model',
|
||||
]
|
||||
|
||||
read_only_fields = ModelNoteModelSerializer.Meta.read_only_fields + [
|
||||
'model',
|
||||
'content_type',
|
||||
]
|
||||
|
||||
|
||||
|
||||
class TeamNoteViewSerializer(
|
||||
ModelNoteViewSerializer,
|
||||
TeamNoteModelSerializer,
|
||||
):
|
||||
|
||||
pass
|
@ -1,103 +0,0 @@
|
||||
from rest_framework.reverse import reverse
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
from access.models.team_user import TeamUsers
|
||||
|
||||
from api.serializers import common
|
||||
|
||||
from app.serializers.user import UserBaseSerializer
|
||||
|
||||
|
||||
|
||||
class TeamUserBaseSerializer(serializers.ModelSerializer):
|
||||
|
||||
|
||||
display_name = serializers.SerializerMethodField('get_display_name')
|
||||
|
||||
def get_display_name(self, item) -> str:
|
||||
|
||||
return str( item )
|
||||
|
||||
url = serializers.SerializerMethodField('get_url')
|
||||
|
||||
def get_url(self, item) -> str:
|
||||
|
||||
return item.get_url( request = self.context['view'].request )
|
||||
|
||||
|
||||
class Meta:
|
||||
|
||||
model = TeamUsers
|
||||
|
||||
fields = [
|
||||
'id',
|
||||
'display_name',
|
||||
'url',
|
||||
]
|
||||
|
||||
read_only_fields = [
|
||||
'id',
|
||||
'display_name',
|
||||
'url',
|
||||
]
|
||||
|
||||
|
||||
|
||||
class TeamUserModelSerializer(
|
||||
common.CommonModelSerializer,
|
||||
TeamUserBaseSerializer
|
||||
):
|
||||
|
||||
_urls = serializers.SerializerMethodField('get_url')
|
||||
|
||||
def get_url(self, item) -> dict:
|
||||
|
||||
get_url = super().get_url( item = item )
|
||||
|
||||
del get_url['history']
|
||||
|
||||
del get_url['knowledge_base']
|
||||
|
||||
return get_url
|
||||
|
||||
|
||||
class Meta:
|
||||
|
||||
model = TeamUsers
|
||||
|
||||
fields = [
|
||||
'id',
|
||||
'display_name',
|
||||
'manager',
|
||||
'user',
|
||||
'created',
|
||||
'modified',
|
||||
'_urls',
|
||||
]
|
||||
|
||||
read_only_fields = [
|
||||
'id',
|
||||
'display_name',
|
||||
'created',
|
||||
'modified',
|
||||
'_urls',
|
||||
]
|
||||
|
||||
|
||||
|
||||
def is_valid(self, *, raise_exception=True) -> bool:
|
||||
|
||||
is_valid = False
|
||||
|
||||
is_valid = super().is_valid(raise_exception=raise_exception)
|
||||
|
||||
self.validated_data['team_id'] = int(self._context['view'].kwargs['team_id'])
|
||||
|
||||
return is_valid
|
||||
|
||||
|
||||
|
||||
class TeamUserViewSerializer(TeamUserModelSerializer):
|
||||
|
||||
user = UserBaseSerializer(read_only = True)
|
@ -1,132 +0,0 @@
|
||||
from rest_framework.reverse import reverse
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
from access.models.team import Team
|
||||
|
||||
from api.serializers import common
|
||||
|
||||
from access.functions.permissions import permission_queryset
|
||||
from access.serializers.organization import OrganizationBaseSerializer
|
||||
|
||||
from app.serializers.permission import Permission, PermissionBaseSerializer
|
||||
|
||||
from core import fields as centurion_field
|
||||
|
||||
|
||||
|
||||
class TeamBaseSerializer(serializers.ModelSerializer):
|
||||
|
||||
|
||||
display_name = serializers.SerializerMethodField('get_display_name')
|
||||
|
||||
def get_display_name(self, item) -> str:
|
||||
|
||||
return str( item )
|
||||
|
||||
url = serializers.SerializerMethodField('get_url')
|
||||
|
||||
def get_url(self, item) -> str:
|
||||
|
||||
return item.get_url( request = self.context['view'].request )
|
||||
|
||||
|
||||
class Meta:
|
||||
|
||||
model = Team
|
||||
|
||||
fields = [
|
||||
'id',
|
||||
'display_name',
|
||||
'team_name',
|
||||
'url',
|
||||
]
|
||||
|
||||
read_only_fields = [
|
||||
'id',
|
||||
'display_name',
|
||||
'team_name',
|
||||
'url',
|
||||
]
|
||||
|
||||
|
||||
|
||||
class TeamModelSerializer(
|
||||
common.CommonModelSerializer,
|
||||
TeamBaseSerializer
|
||||
):
|
||||
|
||||
|
||||
_urls = serializers.SerializerMethodField('get_url')
|
||||
|
||||
def get_url(self, item) -> dict:
|
||||
|
||||
get_url = super().get_url( item = item )
|
||||
|
||||
get_url.update({
|
||||
'users': reverse(
|
||||
'v2:_api_v2_organization_team_user-list',
|
||||
request=self.context['view'].request,
|
||||
kwargs={
|
||||
'organization_id': item.organization.id,
|
||||
'team_id': item.pk
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
return get_url
|
||||
|
||||
|
||||
team_name = centurion_field.CharField( autolink = True )
|
||||
|
||||
permissions = serializers.PrimaryKeyRelatedField(many = True, queryset=permission_queryset(), required = False)
|
||||
|
||||
class Meta:
|
||||
|
||||
model = Team
|
||||
|
||||
fields = '__all__'
|
||||
|
||||
fields = [
|
||||
'id',
|
||||
'display_name',
|
||||
'team_name',
|
||||
'model_notes',
|
||||
'permissions',
|
||||
'organization',
|
||||
'is_global',
|
||||
'created',
|
||||
'modified',
|
||||
'_urls',
|
||||
]
|
||||
|
||||
read_only_fields = [
|
||||
'id',
|
||||
'display_name',
|
||||
'name',
|
||||
'organization',
|
||||
'created',
|
||||
'modified',
|
||||
'_urls',
|
||||
]
|
||||
|
||||
|
||||
|
||||
def is_valid(self, *, raise_exception=True) -> bool:
|
||||
|
||||
is_valid = False
|
||||
|
||||
is_valid = super().is_valid(raise_exception=raise_exception)
|
||||
|
||||
self.validated_data['organization_id'] = int(self._context['view'].kwargs['organization_id'])
|
||||
|
||||
|
||||
return is_valid
|
||||
|
||||
|
||||
|
||||
class TeamViewSerializer(TeamModelSerializer):
|
||||
|
||||
organization = OrganizationBaseSerializer(many=False, read_only=True)
|
||||
|
||||
permissions = PermissionBaseSerializer(many = True)
|
@ -1,7 +1,7 @@
|
||||
import pytest
|
||||
import unittest
|
||||
|
||||
from access.models.tenancy import TenancyManager
|
||||
from access.models import TenancyManager
|
||||
|
||||
|
||||
|
||||
@ -11,19 +11,6 @@ class TenancyObject:
|
||||
model = None
|
||||
""" Model to be tested """
|
||||
|
||||
should_model_history_be_saved: bool = True
|
||||
""" Should model history be saved.
|
||||
|
||||
By default this should always be 'True', however in special
|
||||
circumstances, this may not be desired.
|
||||
"""
|
||||
|
||||
|
||||
def test_history_save(self):
|
||||
"""Confirm the desired intent for saving model history."""
|
||||
|
||||
assert self.model.save_model_history == self.should_model_history_be_saved
|
||||
|
||||
|
||||
def test_has_attr_get_organization(self):
|
||||
""" TenancyObject attribute check
|
||||
|
@ -1,32 +0,0 @@
|
||||
from django.test import TestCase
|
||||
|
||||
from access.models.organization_history import Organization, OrganizationHistory
|
||||
|
||||
from core.tests.abstract.test_functional_history import HistoryEntriesCommon
|
||||
|
||||
|
||||
|
||||
class History(
|
||||
HistoryEntriesCommon,
|
||||
TestCase,
|
||||
):
|
||||
|
||||
model = Organization
|
||||
|
||||
history_model = OrganizationHistory
|
||||
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
|
||||
super().setUpTestData()
|
||||
|
||||
self.obj = self.model.objects.create(
|
||||
name = self.field_value_original,
|
||||
)
|
||||
|
||||
self.obj_delete = self.model.objects.create(
|
||||
name = self.field_value_delete,
|
||||
)
|
||||
|
||||
self.call_the_banners()
|
@ -1,92 +0,0 @@
|
||||
import pytest
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.test import TestCase
|
||||
|
||||
from rest_framework.exceptions import ValidationError
|
||||
|
||||
from access.serializers.organization import (
|
||||
Organization,
|
||||
OrganizationModelSerializer
|
||||
)
|
||||
|
||||
|
||||
|
||||
class OrganizationValidationAPI(
|
||||
TestCase,
|
||||
):
|
||||
|
||||
model = Organization
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. Create an org
|
||||
2. Create an item
|
||||
"""
|
||||
|
||||
self.user = User.objects.create(username = 'org_user', password='random password')
|
||||
|
||||
self.valid_data = {
|
||||
'name': 'valid_org_data',
|
||||
'manager': self.user.id
|
||||
}
|
||||
|
||||
self.item = self.model.objects.create(
|
||||
name = 'random title',
|
||||
)
|
||||
|
||||
|
||||
|
||||
def test_serializer_valid_data(self):
|
||||
"""Serializer Validation Check
|
||||
|
||||
Ensure that if creating and no name is provided a validation error occurs
|
||||
"""
|
||||
|
||||
serializer = OrganizationModelSerializer(
|
||||
data = self.valid_data
|
||||
)
|
||||
|
||||
assert serializer.is_valid(raise_exception = True)
|
||||
|
||||
|
||||
|
||||
def test_serializer_validation_no_name(self):
|
||||
"""Serializer Validation Check
|
||||
|
||||
Ensure that if creating and no name is provided a validation error occurs
|
||||
"""
|
||||
|
||||
data = self.valid_data.copy()
|
||||
|
||||
del data['name']
|
||||
|
||||
with pytest.raises(ValidationError) as err:
|
||||
|
||||
serializer = OrganizationModelSerializer(
|
||||
data = data
|
||||
)
|
||||
|
||||
serializer.is_valid(raise_exception = True)
|
||||
|
||||
assert err.value.get_codes()['name'][0] == 'required'
|
||||
|
||||
|
||||
|
||||
def test_serializer_validation_manager_optional(self):
|
||||
"""Serializer Validation Check
|
||||
|
||||
Ensure that if creating and no name is provided a validation error occurs
|
||||
"""
|
||||
|
||||
data = self.valid_data.copy()
|
||||
|
||||
del data['manager']
|
||||
|
||||
serializer = OrganizationModelSerializer(
|
||||
data = data
|
||||
)
|
||||
|
||||
assert serializer.is_valid(raise_exception = True)
|
@ -1,318 +0,0 @@
|
||||
import pytest
|
||||
import unittest
|
||||
import requests
|
||||
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import AnonymousUser, Permission, User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.shortcuts import reverse
|
||||
from django.test import Client, TestCase
|
||||
|
||||
from access.models.organization import Organization
|
||||
from access.models.team import Team
|
||||
from access.models.team_user import TeamUsers
|
||||
|
||||
from api.tests.abstract.api_permissions_viewset import APIPermissions
|
||||
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
|
||||
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional, MetaDataNavigationEntriesFunctional
|
||||
|
||||
|
||||
|
||||
class ViewSetBase:
|
||||
|
||||
model = Organization
|
||||
|
||||
app_namespace = 'v2'
|
||||
|
||||
url_name = '_api_v2_organization'
|
||||
|
||||
change_data = {'name': 'device'}
|
||||
|
||||
delete_data = {}
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. Create an organization for user and item
|
||||
. create an organization that is different to item
|
||||
2. Create a team
|
||||
3. create teams with each permission: view, add, change, delete
|
||||
4. create a user per team
|
||||
"""
|
||||
|
||||
organization = Organization.objects.create(name='test_org')
|
||||
|
||||
self.organization = organization
|
||||
|
||||
self.item = organization
|
||||
|
||||
different_organization = Organization.objects.create(name='test_different_organization')
|
||||
|
||||
self.different_organization = different_organization
|
||||
|
||||
self.other_org_item = organization
|
||||
|
||||
view_permissions = Permission.objects.get(
|
||||
codename = 'view_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
view_team = Team.objects.create(
|
||||
team_name = 'view_team',
|
||||
organization = organization,
|
||||
)
|
||||
|
||||
view_team_b = Team.objects.create(
|
||||
team_name = 'view_team',
|
||||
organization = different_organization,
|
||||
)
|
||||
|
||||
view_team.permissions.set([view_permissions])
|
||||
|
||||
view_team_b.permissions.set([view_permissions])
|
||||
|
||||
|
||||
|
||||
add_permissions = Permission.objects.get(
|
||||
codename = 'add_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
add_team = Team.objects.create(
|
||||
team_name = 'add_team',
|
||||
organization = organization,
|
||||
)
|
||||
|
||||
add_team.permissions.set([add_permissions])
|
||||
|
||||
|
||||
|
||||
change_permissions = Permission.objects.get(
|
||||
codename = 'change_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
change_team = Team.objects.create(
|
||||
team_name = 'change_team',
|
||||
organization = organization,
|
||||
)
|
||||
|
||||
change_team.permissions.set([change_permissions])
|
||||
|
||||
|
||||
|
||||
delete_permissions = Permission.objects.get(
|
||||
codename = 'delete_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
delete_team = Team.objects.create(
|
||||
team_name = 'delete_team',
|
||||
organization = organization,
|
||||
)
|
||||
|
||||
delete_team.permissions.set([delete_permissions])
|
||||
|
||||
|
||||
self.no_permissions_user = User.objects.create_user(username="test_no_permissions", password="password")
|
||||
|
||||
|
||||
self.view_user = User.objects.create_user(username="test_user_view", password="password")
|
||||
teamuser = TeamUsers.objects.create(
|
||||
team = view_team,
|
||||
user = self.view_user
|
||||
)
|
||||
|
||||
self.view_user_b = User.objects.create_user(username="test_user_view_b", password="password")
|
||||
teamuser = TeamUsers.objects.create(
|
||||
team = view_team_b,
|
||||
user = self.view_user_b
|
||||
)
|
||||
|
||||
|
||||
self.url_view_kwargs = { 'pk': self.item.id }
|
||||
|
||||
self.add_data = {
|
||||
'name': 'team_post',
|
||||
}
|
||||
|
||||
|
||||
self.super_add_user = User.objects.create_user(username="test_user_add_super", password="password", is_superuser = True)
|
||||
|
||||
self.add_user = User.objects.create_user(username="test_user_add", password="password")
|
||||
teamuser = TeamUsers.objects.create(
|
||||
team = add_team,
|
||||
user = self.add_user
|
||||
)
|
||||
|
||||
self.change_user = User.objects.create_user(username="test_user_change", password="password")
|
||||
teamuser = TeamUsers.objects.create(
|
||||
team = change_team,
|
||||
user = self.change_user
|
||||
)
|
||||
|
||||
self.delete_user = User.objects.create_user(username="test_user_delete", password="password")
|
||||
teamuser = TeamUsers.objects.create(
|
||||
team = delete_team,
|
||||
user = self.delete_user
|
||||
)
|
||||
|
||||
|
||||
self.different_organization_user = User.objects.create_user(username="test_different_organization_user", password="password")
|
||||
|
||||
|
||||
different_organization_team = Team.objects.create(
|
||||
team_name = 'different_organization_team',
|
||||
organization = different_organization,
|
||||
)
|
||||
|
||||
different_organization_team.permissions.set([
|
||||
view_permissions,
|
||||
add_permissions,
|
||||
change_permissions,
|
||||
delete_permissions,
|
||||
])
|
||||
|
||||
TeamUsers.objects.create(
|
||||
team = different_organization_team,
|
||||
user = self.different_organization_user
|
||||
)
|
||||
|
||||
|
||||
|
||||
class OrganizationPermissionsAPI(
|
||||
ViewSetBase,
|
||||
APIPermissions,
|
||||
TestCase
|
||||
):
|
||||
|
||||
def test_returned_data_from_user_and_global_organizations_only(self):
|
||||
"""Check items returned
|
||||
|
||||
This test case is a over-ride of a test case with the same name.
|
||||
This model is not a tenancy model making this test not-applicable.
|
||||
|
||||
Items returned from the query Must be from the users organization and
|
||||
global ONLY!
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
|
||||
def test_add_has_permission(self):
|
||||
""" Check correct permission for add
|
||||
|
||||
Attempt to add as user with permission
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
|
||||
if self.url_kwargs:
|
||||
|
||||
url = reverse( self.app_namespace + ':' + self.url_name + '-list', kwargs = self.url_kwargs )
|
||||
|
||||
else:
|
||||
|
||||
url = reverse( self.app_namespace + ':' + self.url_name + '-list' )
|
||||
|
||||
|
||||
client.force_login( self.add_user )
|
||||
|
||||
response = client.post( url, data = self.add_data )
|
||||
|
||||
assert response.status_code == 201
|
||||
|
||||
|
||||
|
||||
def test_returned_results_only_user_orgs(self):
|
||||
"""Returned results check
|
||||
|
||||
This test case is an override of a test of the same name.
|
||||
organizations are not tenancy objects and therefor are supposed to
|
||||
return all items when a user queries them.
|
||||
|
||||
Ensure that a query to the viewset endpoint does not return
|
||||
items that are not part of the users organizations.
|
||||
"""
|
||||
|
||||
|
||||
# Ensure the other org item exists, without test not able to function
|
||||
print('Check that the different organization item has been defined')
|
||||
assert hasattr(self, 'other_org_item')
|
||||
|
||||
# ensure that the variables for the two orgs are different orgs
|
||||
print('checking that the different and user oganizations are different')
|
||||
assert self.different_organization.id != self.organization.id
|
||||
|
||||
|
||||
client = Client()
|
||||
|
||||
if self.url_kwargs:
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-list', kwargs = self.url_kwargs)
|
||||
|
||||
else:
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-list')
|
||||
|
||||
|
||||
client.force_login(self.view_user)
|
||||
response = client.get(url)
|
||||
|
||||
contains_different_org: bool = False
|
||||
|
||||
# for item in response.data['results']:
|
||||
|
||||
# if int(item['id']) != self.organization.id:
|
||||
|
||||
# contains_different_org = True
|
||||
|
||||
assert len(response.data['results']) == 2
|
||||
|
||||
|
||||
def test_add_different_organization_denied(self):
|
||||
""" Check correct permission for add
|
||||
|
||||
This test is a duplicate of a test case with the same name.
|
||||
Organizations are not tenancy models so this test does nothing of value
|
||||
|
||||
attempt to add as user from different organization
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class OrganizationViewSet(
|
||||
ViewSetBase,
|
||||
SerializersTestCases,
|
||||
TestCase
|
||||
):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class OrganizationMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctional,
|
||||
MetaDataNavigationEntriesFunctional,
|
||||
TestCase
|
||||
):
|
||||
|
||||
menu_id = 'access'
|
||||
|
||||
menu_entry_id = 'organization'
|
@ -1,116 +0,0 @@
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.test import TestCase
|
||||
|
||||
from access.viewsets.organization_notes import ViewSet
|
||||
|
||||
from core.tests.abstract.test_functional_notes_viewset import (
|
||||
ModelNotesViewSetBase,
|
||||
ModelNotesMetadata,
|
||||
ModelNotesPermissionsAPI,
|
||||
ModelNotesSerializer
|
||||
)
|
||||
|
||||
|
||||
|
||||
class ViewSetBase(
|
||||
ModelNotesViewSetBase
|
||||
):
|
||||
|
||||
viewset = ViewSet
|
||||
|
||||
url_name = '_api_v2_organization_note'
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
|
||||
|
||||
super().setUpTestData()
|
||||
|
||||
self.item = self.viewset.model.objects.create(
|
||||
organization = self.organization,
|
||||
content = 'a random comment',
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = str(self.model._meta.app_label).lower(),
|
||||
model = str(self.model.model.field.related_model.__name__).replace(' ', '').lower(),
|
||||
),
|
||||
model = self.organization,
|
||||
created_by = self.view_user,
|
||||
modified_by = self.view_user,
|
||||
)
|
||||
|
||||
self.other_org_item = self.viewset.model.objects.create(
|
||||
organization = self.different_organization,
|
||||
content = 'a random comment',
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = str(self.model._meta.app_label).lower(),
|
||||
model = str(self.model.model.field.related_model.__name__).replace(' ', '').lower(),
|
||||
),
|
||||
model = self.different_organization,
|
||||
created_by = self.view_user,
|
||||
modified_by = self.view_user,
|
||||
)
|
||||
|
||||
|
||||
self.global_org_item = self.viewset.model.objects.create(
|
||||
organization = self.global_organization,
|
||||
content = 'a random comment global_organization',
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = str(self.model._meta.app_label).lower(),
|
||||
model = str(self.model.model.field.related_model.__name__).replace(' ', '').lower(),
|
||||
),
|
||||
model = self.global_organization,
|
||||
created_by = self.view_user,
|
||||
modified_by = self.view_user,
|
||||
)
|
||||
|
||||
self.url_kwargs = {
|
||||
'model_id': self.item.model.pk,
|
||||
}
|
||||
|
||||
self.url_view_kwargs = {
|
||||
'model_id': self.item.model.pk,
|
||||
'pk': self.item.id
|
||||
}
|
||||
|
||||
|
||||
|
||||
class OrganizationModelNotesPermissionsAPI(
|
||||
ViewSetBase,
|
||||
ModelNotesPermissionsAPI,
|
||||
TestCase,
|
||||
):
|
||||
|
||||
|
||||
def test_returned_data_from_user_and_global_organizations_only(self):
|
||||
"""Check items returned
|
||||
|
||||
This test case is a over-ride of a test case with the same name.
|
||||
This model is not a global model making this test not-applicable.
|
||||
|
||||
Items returned from the query Must be from the users organization and
|
||||
global ONLY!
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class OrganizationModelNotesSerializer(
|
||||
ViewSetBase,
|
||||
ModelNotesSerializer,
|
||||
TestCase,
|
||||
):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class OrganizationModelNotesMetadata(
|
||||
ViewSetBase,
|
||||
ModelNotesMetadata,
|
||||
TestCase,
|
||||
|
||||
):
|
||||
|
||||
pass
|
@ -1,53 +0,0 @@
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.test import TestCase
|
||||
|
||||
from core.tests.abstract.model_notes_api_fields import ModelNotesNotesAPIFields
|
||||
|
||||
from access.models.organization import Organization
|
||||
from access.models.organization_notes import OrganizationNotes
|
||||
|
||||
|
||||
class OrganizationNotesAPI(
|
||||
ModelNotesNotesAPIFields,
|
||||
TestCase,
|
||||
):
|
||||
|
||||
model = OrganizationNotes
|
||||
|
||||
view_name: str = '_api_v2_organization_note'
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. Call parent setup
|
||||
2. Create a model note
|
||||
3. add url kwargs
|
||||
4. make the API request
|
||||
|
||||
"""
|
||||
|
||||
super().setUpTestData()
|
||||
|
||||
|
||||
self.item = self.model.objects.create(
|
||||
organization = self.organization,
|
||||
content = 'a random comment',
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = str(self.model._meta.app_label).lower(),
|
||||
model = str(self.model.model.field.related_model.__name__).replace(' ', '').lower(),
|
||||
),
|
||||
model = Organization.objects.create(
|
||||
name = 'dev'
|
||||
),
|
||||
created_by = self.view_user,
|
||||
modified_by = self.view_user,
|
||||
)
|
||||
|
||||
|
||||
self.url_view_kwargs = {
|
||||
'model_id': self.item.model.pk,
|
||||
'pk': self.item.pk
|
||||
}
|
||||
|
||||
self.make_request()
|
@ -1,37 +0,0 @@
|
||||
from django.test import TestCase
|
||||
|
||||
from access.models.team_history import Team, TeamHistory
|
||||
|
||||
from core.tests.abstract.test_functional_history import HistoryEntriesCommon
|
||||
|
||||
|
||||
|
||||
class History(
|
||||
HistoryEntriesCommon,
|
||||
TestCase,
|
||||
):
|
||||
|
||||
model = Team
|
||||
|
||||
history_model = TeamHistory
|
||||
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
|
||||
super().setUpTestData()
|
||||
|
||||
self.field_name = 'team_name'
|
||||
|
||||
self.obj = self.model.objects.create(
|
||||
organization = self.organization,
|
||||
# name = self.field_value_original,
|
||||
team_name = self.field_value_original
|
||||
)
|
||||
|
||||
self.obj_delete = self.model.objects.create(
|
||||
organization = self.organization,
|
||||
name = self.field_value_delete,
|
||||
)
|
||||
|
||||
self.call_the_banners()
|
@ -1,314 +0,0 @@
|
||||
import pytest
|
||||
import unittest
|
||||
import requests
|
||||
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import AnonymousUser, Permission, User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.test import Client, TestCase
|
||||
|
||||
from rest_framework.reverse import reverse
|
||||
|
||||
from access.models.organization import Organization
|
||||
from access.models.team import Team
|
||||
from access.models.team_user import TeamUsers
|
||||
|
||||
|
||||
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional
|
||||
from api.tests.abstract.api_permissions_viewset import APIPermissions
|
||||
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
|
||||
|
||||
|
||||
|
||||
class ViewSetBase:
|
||||
|
||||
model = Team
|
||||
|
||||
app_namespace = 'API'
|
||||
|
||||
url_name = '_api_v2_organization_team'
|
||||
|
||||
change_data = {'name': 'device'}
|
||||
|
||||
delete_data = {'device': 'device'}
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. Create an organization for user and item
|
||||
. create an organization that is different to item
|
||||
2. Create a team
|
||||
3. create teams with each permission: view, add, change, delete
|
||||
4. create a user per team
|
||||
"""
|
||||
|
||||
organization = Organization.objects.create(name='test_org')
|
||||
|
||||
self.organization = organization
|
||||
|
||||
different_organization = Organization.objects.create(name='test_different_organization')
|
||||
|
||||
self.different_organization = different_organization
|
||||
|
||||
|
||||
self.item = self.model.objects.create(
|
||||
organization=organization,
|
||||
name = 'teamone'
|
||||
)
|
||||
|
||||
self.other_org_item = self.model.objects.create(
|
||||
organization=different_organization,
|
||||
name = 'teamtwo'
|
||||
)
|
||||
|
||||
|
||||
self.url_kwargs = {'organization_id': self.organization.id}
|
||||
|
||||
self.url_view_kwargs = {'organization_id': self.organization.id, 'pk': self.item.id}
|
||||
|
||||
self.add_data = {'team_name': 'team_post'}
|
||||
|
||||
|
||||
view_permissions = Permission.objects.get(
|
||||
codename = 'view_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
view_team = Team.objects.create(
|
||||
team_name = 'view_team',
|
||||
organization = organization,
|
||||
)
|
||||
|
||||
view_team.permissions.set([view_permissions])
|
||||
|
||||
|
||||
|
||||
add_permissions = Permission.objects.get(
|
||||
codename = 'add_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
add_team = Team.objects.create(
|
||||
team_name = 'add_team',
|
||||
organization = organization,
|
||||
)
|
||||
|
||||
add_team.permissions.set([add_permissions])
|
||||
|
||||
|
||||
|
||||
change_permissions = Permission.objects.get(
|
||||
codename = 'change_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
change_team = Team.objects.create(
|
||||
team_name = 'change_team',
|
||||
organization = organization,
|
||||
)
|
||||
|
||||
change_team.permissions.set([change_permissions])
|
||||
|
||||
|
||||
|
||||
delete_permissions = Permission.objects.get(
|
||||
codename = 'delete_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
delete_team = Team.objects.create(
|
||||
team_name = 'delete_team',
|
||||
organization = organization,
|
||||
)
|
||||
|
||||
delete_team.permissions.set([delete_permissions])
|
||||
|
||||
|
||||
self.no_permissions_user = User.objects.create_user(username="test_no_permissions", password="password")
|
||||
|
||||
|
||||
self.view_user = User.objects.create_user(username="test_user_view", password="password")
|
||||
teamuser = TeamUsers.objects.create(
|
||||
team = view_team,
|
||||
user = self.view_user
|
||||
)
|
||||
|
||||
self.add_user = User.objects.create_user(username="test_user_add", password="password")
|
||||
teamuser = TeamUsers.objects.create(
|
||||
team = add_team,
|
||||
user = self.add_user
|
||||
)
|
||||
|
||||
self.change_user = User.objects.create_user(username="test_user_change", password="password")
|
||||
teamuser = TeamUsers.objects.create(
|
||||
team = change_team,
|
||||
user = self.change_user
|
||||
)
|
||||
|
||||
self.delete_user = User.objects.create_user(username="test_user_delete", password="password")
|
||||
teamuser = TeamUsers.objects.create(
|
||||
team = delete_team,
|
||||
user = self.delete_user
|
||||
)
|
||||
|
||||
|
||||
self.different_organization_user = User.objects.create_user(username="test_different_organization_user", password="password")
|
||||
|
||||
|
||||
different_organization_team = Team.objects.create(
|
||||
team_name = 'different_organization_team',
|
||||
organization = different_organization,
|
||||
)
|
||||
|
||||
different_organization_team.permissions.set([
|
||||
view_permissions,
|
||||
add_permissions,
|
||||
change_permissions,
|
||||
delete_permissions,
|
||||
])
|
||||
|
||||
TeamUsers.objects.create(
|
||||
team = different_organization_team,
|
||||
user = self.different_organization_user
|
||||
)
|
||||
|
||||
|
||||
|
||||
class TeamPermissionsAPI(
|
||||
ViewSetBase,
|
||||
APIPermissions,
|
||||
TestCase,
|
||||
):
|
||||
|
||||
|
||||
def test_returned_data_from_user_and_global_organizations_only(self):
|
||||
"""Check items returned
|
||||
|
||||
This test case is a over-ride of a test case with the same name.
|
||||
This model is not a tenancy model making this test not-applicable.
|
||||
|
||||
Items returned from the query Must be from the users organization and
|
||||
global ONLY!
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class TeamViewSet(
|
||||
ViewSetBase,
|
||||
SerializersTestCases,
|
||||
TestCase,
|
||||
):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class TeamMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctional,
|
||||
TestCase,
|
||||
|
||||
):
|
||||
|
||||
|
||||
|
||||
def test_method_options_request_detail_data_has_key_urls_back(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request data returned has key `urls.back`
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
response = client.options(
|
||||
reverse(
|
||||
self.app_namespace + ':' + self.url_name + '-detail',
|
||||
kwargs=self.url_view_kwargs
|
||||
),
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
assert 'back' in response.data['urls']
|
||||
|
||||
|
||||
def test_method_options_request_detail_data_key_urls_back_is_str(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request data key `urls.back` is str
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
response = client.options(
|
||||
reverse(
|
||||
self.app_namespace + ':' + self.url_name + '-detail',
|
||||
kwargs=self.url_view_kwargs
|
||||
),
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
assert type(response.data['urls']['back']) is str
|
||||
|
||||
|
||||
|
||||
def test_method_options_request_list_data_has_key_urls_return_url(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request data returned has key `urls.return_url`
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
if self.url_kwargs:
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-list', kwargs = self.url_kwargs)
|
||||
|
||||
else:
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-list')
|
||||
|
||||
response = client.options( url, content_type='application/json' )
|
||||
|
||||
assert 'return_url' in response.data['urls']
|
||||
|
||||
|
||||
def test_method_options_request_list_data_key_urls_return_url_is_str(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request data key `urls.return_url` is str
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
if self.url_kwargs:
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-list', kwargs = self.url_kwargs)
|
||||
|
||||
else:
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-list')
|
||||
|
||||
response = client.options( url, content_type='application/json' )
|
||||
|
||||
assert type(response.data['urls']['return_url']) is str
|
||||
|
||||
|
@ -1,208 +0,0 @@
|
||||
import pytest
|
||||
|
||||
from django.contrib.auth.models import Permission, User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.test import TestCase
|
||||
|
||||
from rest_framework.exceptions import ValidationError
|
||||
|
||||
from access.middleware.request import Tenancy
|
||||
|
||||
from access.models.organization import Organization
|
||||
|
||||
from access.serializers.teams import (
|
||||
Team,
|
||||
TeamModelSerializer
|
||||
)
|
||||
|
||||
from settings.models.app_settings import AppSettings
|
||||
|
||||
|
||||
|
||||
class MockView:
|
||||
|
||||
action: str = None
|
||||
|
||||
kwargs: dict = {}
|
||||
|
||||
request = None
|
||||
|
||||
|
||||
def __init__(self, user: User):
|
||||
|
||||
app_settings = AppSettings.objects.select_related('global_organization').get(
|
||||
owner_organization = None
|
||||
)
|
||||
|
||||
self.request = MockRequest( user = user, app_settings = app_settings)
|
||||
|
||||
|
||||
|
||||
class MockRequest:
|
||||
|
||||
tenancy: Tenancy = None
|
||||
|
||||
user = None
|
||||
|
||||
def __init__(self, user: User, app_settings):
|
||||
|
||||
self.user = user
|
||||
|
||||
self.app_settings = app_settings
|
||||
|
||||
self.tenancy = Tenancy(
|
||||
user = user,
|
||||
app_settings = app_settings
|
||||
)
|
||||
|
||||
|
||||
|
||||
class TeamValidationAPI(
|
||||
TestCase,
|
||||
):
|
||||
|
||||
model = Team
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. Create an org
|
||||
2. Create an item
|
||||
"""
|
||||
|
||||
self.organization = Organization.objects.create(
|
||||
name = 'team org serializer test'
|
||||
)
|
||||
|
||||
self.user = User.objects.create(username = 'org_user', password='random password')
|
||||
|
||||
|
||||
view_permissions = Permission.objects.get(
|
||||
codename = 'view_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
self.valid_data = {
|
||||
'organization': self.organization.id,
|
||||
'team_name': 'valid_org_data',
|
||||
'permissions': [
|
||||
view_permissions.id,
|
||||
]
|
||||
}
|
||||
|
||||
self.item = self.model.objects.create(
|
||||
organization = self.organization,
|
||||
name = 'random team title',
|
||||
)
|
||||
|
||||
|
||||
|
||||
def test_serializer_valid_data(self):
|
||||
"""Serializer Validation Check
|
||||
|
||||
Ensure that if creating an item supplied valid data
|
||||
creates an item.
|
||||
"""
|
||||
|
||||
|
||||
mock_view = MockView( user = self.user )
|
||||
mock_view.action = 'create'
|
||||
mock_view.kwargs: dict = {
|
||||
'organization_id': self.organization.id
|
||||
}
|
||||
|
||||
# mock_request = MockRequest()
|
||||
# mock_request.user = self.user
|
||||
|
||||
# mock_view.request = mock_request
|
||||
|
||||
|
||||
serializer = TeamModelSerializer(
|
||||
context = {
|
||||
'request': mock_view.request,
|
||||
'view': mock_view,
|
||||
},
|
||||
data = self.valid_data
|
||||
)
|
||||
|
||||
assert serializer.is_valid(raise_exception = True)
|
||||
|
||||
|
||||
|
||||
def test_serializer_validation_no_name(self):
|
||||
"""Serializer Validation Check
|
||||
|
||||
Ensure that if creating and no name is provided a validation error occurs
|
||||
"""
|
||||
|
||||
|
||||
mock_view = MockView( user = self.user)
|
||||
mock_view.action = 'create'
|
||||
mock_view.kwargs: dict = {
|
||||
'organization_id': self.organization.id
|
||||
}
|
||||
|
||||
# mock_request = MockRequest()
|
||||
# mock_request.user = self.user
|
||||
|
||||
# mock_view.request = mock_request
|
||||
|
||||
|
||||
data = self.valid_data.copy()
|
||||
|
||||
del data['team_name']
|
||||
|
||||
with pytest.raises(ValidationError) as err:
|
||||
|
||||
serializer = TeamModelSerializer(
|
||||
context = {
|
||||
'request': mock_view.request,
|
||||
'view': mock_view,
|
||||
},
|
||||
data = data
|
||||
)
|
||||
|
||||
serializer.is_valid(raise_exception = True)
|
||||
|
||||
assert err.value.get_codes()['team_name'][0] == 'required'
|
||||
|
||||
|
||||
|
||||
def test_serializer_validation_permissions_optional(self):
|
||||
"""Serializer Validation Check
|
||||
|
||||
Ensure that if creating and permissions are not supplied, the item is
|
||||
still created.
|
||||
"""
|
||||
|
||||
|
||||
mock_view = MockView( user = self.user)
|
||||
mock_view.action = 'create'
|
||||
mock_view.kwargs: dict = {
|
||||
'organization_id': self.organization.id
|
||||
}
|
||||
|
||||
# mock_request = MockRequest()
|
||||
# mock_request.user = self.user
|
||||
|
||||
# mock_view.request = mock_request
|
||||
|
||||
|
||||
data = self.valid_data.copy()
|
||||
|
||||
del data['permissions']
|
||||
|
||||
serializer = TeamModelSerializer(
|
||||
context = {
|
||||
'request': mock_view.request,
|
||||
'view': mock_view,
|
||||
},
|
||||
data = data
|
||||
)
|
||||
|
||||
assert serializer.is_valid(raise_exception = True)
|
@ -1,127 +0,0 @@
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.test import TestCase
|
||||
|
||||
from access.viewsets.team_notes import ViewSet
|
||||
|
||||
from core.tests.abstract.test_functional_notes_viewset import (
|
||||
ModelNotesViewSetBase,
|
||||
ModelNotesMetadata,
|
||||
ModelNotesPermissionsAPI,
|
||||
ModelNotesSerializer
|
||||
)
|
||||
|
||||
|
||||
|
||||
class ViewSetBase(
|
||||
ModelNotesViewSetBase
|
||||
):
|
||||
|
||||
viewset = ViewSet
|
||||
|
||||
url_name = '_api_v2_team_note'
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
|
||||
|
||||
super().setUpTestData()
|
||||
|
||||
self.item = self.viewset.model.objects.create(
|
||||
organization = self.organization,
|
||||
content = 'a random comment',
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = str(self.model._meta.app_label).lower(),
|
||||
model = str(self.model.model.field.related_model.__name__).replace(' ', '').lower(),
|
||||
),
|
||||
model = self.viewset.model.model.field.related_model.objects.create(
|
||||
organization = self.organization,
|
||||
name = 'note model'
|
||||
),
|
||||
created_by = self.view_user,
|
||||
modified_by = self.view_user,
|
||||
)
|
||||
|
||||
self.other_org_item = self.viewset.model.objects.create(
|
||||
organization = self.different_organization,
|
||||
content = 'a random comment',
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = str(self.model._meta.app_label).lower(),
|
||||
model = str(self.model.model.field.related_model.__name__).replace(' ', '').lower(),
|
||||
),
|
||||
model = self.viewset.model.model.field.related_model.objects.create(
|
||||
organization = self.different_organization,
|
||||
name = 'note model'
|
||||
),
|
||||
created_by = self.view_user,
|
||||
modified_by = self.view_user,
|
||||
)
|
||||
|
||||
|
||||
self.global_org_item = self.viewset.model.objects.create(
|
||||
organization = self.global_organization,
|
||||
content = 'a random comment global_organization',
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = str(self.model._meta.app_label).lower(),
|
||||
model = str(self.model.model.field.related_model.__name__).replace(' ', '').lower(),
|
||||
),
|
||||
model = self.viewset.model.model.field.related_model.objects.create(
|
||||
organization = self.global_organization,
|
||||
name = 'note model global_organization'
|
||||
),
|
||||
created_by = self.view_user,
|
||||
modified_by = self.view_user,
|
||||
)
|
||||
|
||||
self.url_kwargs = {
|
||||
'organization_id': self.organization.id,
|
||||
'model_id': self.item.model.pk,
|
||||
}
|
||||
|
||||
self.url_view_kwargs = {
|
||||
'organization_id': self.organization.id,
|
||||
'model_id': self.item.model.pk,
|
||||
'pk': self.item.id
|
||||
}
|
||||
|
||||
|
||||
|
||||
class TeamModelNotesPermissionsAPI(
|
||||
ViewSetBase,
|
||||
ModelNotesPermissionsAPI,
|
||||
TestCase,
|
||||
):
|
||||
|
||||
|
||||
def test_returned_data_from_user_and_global_organizations_only(self):
|
||||
"""Check items returned
|
||||
|
||||
This test case is a over-ride of a test case with the same name.
|
||||
This model is not a global model making this test not-applicable.
|
||||
|
||||
Items returned from the query Must be from the users organization and
|
||||
global ONLY!
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class TeamModelNotesSerializer(
|
||||
ViewSetBase,
|
||||
ModelNotesSerializer,
|
||||
TestCase,
|
||||
):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class TeamModelNotesMetadata(
|
||||
ViewSetBase,
|
||||
ModelNotesMetadata,
|
||||
TestCase,
|
||||
|
||||
):
|
||||
|
||||
pass
|
@ -1,56 +0,0 @@
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.test import TestCase
|
||||
|
||||
from core.tests.abstract.model_notes_api_fields import ModelNotesNotesAPIFields
|
||||
|
||||
from access.models.team import Team
|
||||
from access.models.team_notes import TeamNotes
|
||||
|
||||
|
||||
|
||||
class TeamNotesAPI(
|
||||
ModelNotesNotesAPIFields,
|
||||
TestCase,
|
||||
):
|
||||
|
||||
model = TeamNotes
|
||||
|
||||
view_name: str = '_api_v2_team_note'
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. Call parent setup
|
||||
2. Create a model note
|
||||
3. add url kwargs
|
||||
4. make the API request
|
||||
|
||||
"""
|
||||
|
||||
super().setUpTestData()
|
||||
|
||||
|
||||
self.item = self.model.objects.create(
|
||||
organization = self.organization,
|
||||
content = 'a random comment',
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = str(self.model._meta.app_label).lower(),
|
||||
model = str(self.model.model.field.related_model.__name__).replace(' ', '').lower(),
|
||||
),
|
||||
model = Team.objects.create(
|
||||
organization = self.organization,
|
||||
name = 'note model'
|
||||
),
|
||||
created_by = self.view_user,
|
||||
modified_by = self.view_user,
|
||||
)
|
||||
|
||||
|
||||
self.url_view_kwargs = {
|
||||
'organization_id': self.organization.pk,
|
||||
'model_id': self.item.model.pk,
|
||||
'pk': self.item.pk
|
||||
}
|
||||
|
||||
self.make_request()
|
@ -1,326 +0,0 @@
|
||||
import pytest
|
||||
import unittest
|
||||
import requests
|
||||
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import AnonymousUser, Permission, User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.test import Client, TestCase
|
||||
|
||||
from rest_framework.reverse import reverse
|
||||
|
||||
from access.models.organization import Organization
|
||||
from access.models.team import Team
|
||||
from access.models.team_user import TeamUsers
|
||||
|
||||
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional
|
||||
from api.tests.abstract.api_permissions_viewset import APIPermissions
|
||||
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
|
||||
|
||||
|
||||
|
||||
class ViewSetBase:
|
||||
|
||||
model = TeamUsers
|
||||
|
||||
app_namespace = 'API'
|
||||
|
||||
url_name = '_api_v2_organization_team_user'
|
||||
|
||||
change_data = {'name': 'device'}
|
||||
|
||||
delete_data = {'device': 'device'}
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. Create an organization for user and item
|
||||
2. Create a team
|
||||
3. create teams with each permission: view, add, change, delete
|
||||
4. create a user per team
|
||||
"""
|
||||
|
||||
organization = Organization.objects.create(name='test_org')
|
||||
|
||||
self.organization = organization
|
||||
|
||||
different_organization = Organization.objects.create(name='test_different_organization')
|
||||
|
||||
self.different_organization = different_organization
|
||||
|
||||
|
||||
view_permissions = Permission.objects.get(
|
||||
codename = 'view_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
view_team = Team.objects.create(
|
||||
team_name = 'view_team',
|
||||
organization = organization,
|
||||
)
|
||||
|
||||
view_team_b = Team.objects.create(
|
||||
team_name = 'view_team',
|
||||
organization = different_organization,
|
||||
)
|
||||
|
||||
view_team.permissions.set([view_permissions])
|
||||
|
||||
view_team_b.permissions.set([view_permissions])
|
||||
|
||||
|
||||
|
||||
add_permissions = Permission.objects.get(
|
||||
codename = 'add_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
add_team = Team.objects.create(
|
||||
team_name = 'add_team',
|
||||
organization = organization,
|
||||
)
|
||||
|
||||
add_team.permissions.set([add_permissions])
|
||||
|
||||
|
||||
|
||||
change_permissions = Permission.objects.get(
|
||||
codename = 'change_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
change_team = Team.objects.create(
|
||||
team_name = 'change_team',
|
||||
organization = organization,
|
||||
)
|
||||
|
||||
change_team.permissions.set([change_permissions])
|
||||
|
||||
|
||||
|
||||
delete_permissions = Permission.objects.get(
|
||||
codename = 'delete_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
delete_team = Team.objects.create(
|
||||
team_name = 'delete_team',
|
||||
organization = organization,
|
||||
)
|
||||
|
||||
delete_team.permissions.set([delete_permissions])
|
||||
|
||||
|
||||
self.no_permissions_user = User.objects.create_user(username="test_no_permissions", password="password")
|
||||
|
||||
|
||||
self.view_user = User.objects.create_user(username="test_user_view", password="password")
|
||||
|
||||
self.view_user_b = User.objects.create_user(username="test_user_view_b", password="password")
|
||||
|
||||
|
||||
self.item = TeamUsers.objects.create(
|
||||
team = view_team,
|
||||
user = self.view_user
|
||||
)
|
||||
|
||||
self.other_org_item = TeamUsers.objects.create(
|
||||
team = view_team_b,
|
||||
user = self.view_user_b
|
||||
)
|
||||
|
||||
self.url_view_kwargs = {'organization_id': self.organization.id, 'team_id': view_team.id, 'pk': self.item.id}
|
||||
|
||||
self.url_kwargs = {'organization_id': self.organization.id, 'team_id': view_team.id}
|
||||
|
||||
|
||||
random_user = User.objects.create_user(username="random_user", password="password")
|
||||
self.add_data = {'user': random_user.id}
|
||||
|
||||
|
||||
self.add_user = User.objects.create_user(username="test_user_add", password="password")
|
||||
teamuser = TeamUsers.objects.create(
|
||||
team = add_team,
|
||||
user = self.add_user
|
||||
)
|
||||
|
||||
self.change_user = User.objects.create_user(username="test_user_change", password="password")
|
||||
teamuser = TeamUsers.objects.create(
|
||||
team = change_team,
|
||||
user = self.change_user
|
||||
)
|
||||
|
||||
self.delete_user = User.objects.create_user(username="test_user_delete", password="password")
|
||||
teamuser = TeamUsers.objects.create(
|
||||
team = delete_team,
|
||||
user = self.delete_user
|
||||
)
|
||||
|
||||
|
||||
self.different_organization_user = User.objects.create_user(username="test_different_organization_user", password="password")
|
||||
|
||||
|
||||
different_organization_team = Team.objects.create(
|
||||
team_name = 'different_organization_team',
|
||||
organization = different_organization,
|
||||
)
|
||||
|
||||
different_organization_team.permissions.set([
|
||||
view_permissions,
|
||||
add_permissions,
|
||||
change_permissions,
|
||||
delete_permissions,
|
||||
])
|
||||
|
||||
TeamUsers.objects.create(
|
||||
team = different_organization_team,
|
||||
user = self.different_organization_user
|
||||
)
|
||||
|
||||
|
||||
|
||||
class TeamUserPermissionsAPI(
|
||||
ViewSetBase,
|
||||
APIPermissions,
|
||||
TestCase
|
||||
):
|
||||
|
||||
|
||||
def test_returned_results_only_user_orgs(self):
|
||||
"""This test is not applicable for team_user as users are not tenancy objects
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
|
||||
def test_returned_data_from_user_and_global_organizations_only(self):
|
||||
"""Check items returned
|
||||
|
||||
This test case is a over-ride of a test case with the same name.
|
||||
This model is not a tenancy model making this test not-applicable.
|
||||
|
||||
Items returned from the query Must be from the users organization and
|
||||
global ONLY!
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class TeamUserViewSet(
|
||||
ViewSetBase,
|
||||
SerializersTestCases,
|
||||
TestCase
|
||||
):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class TeamUserMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctional,
|
||||
TestCase,
|
||||
|
||||
):
|
||||
|
||||
|
||||
|
||||
def test_method_options_request_detail_data_has_key_urls_back(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request data returned has key `urls.back`
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
response = client.options(
|
||||
reverse(
|
||||
self.app_namespace + ':' + self.url_name + '-detail',
|
||||
kwargs=self.url_view_kwargs
|
||||
),
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
assert 'back' in response.data['urls']
|
||||
|
||||
|
||||
def test_method_options_request_detail_data_key_urls_back_is_str(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request data key `urls.back` is str
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
response = client.options(
|
||||
reverse(
|
||||
self.app_namespace + ':' + self.url_name + '-detail',
|
||||
kwargs=self.url_view_kwargs
|
||||
),
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
assert type(response.data['urls']['back']) is str
|
||||
|
||||
|
||||
|
||||
def test_method_options_request_list_data_has_key_urls_return_url(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request data returned has key `urls.return_url`
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
if self.url_kwargs:
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-list', kwargs = self.url_kwargs)
|
||||
|
||||
else:
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-list')
|
||||
|
||||
response = client.options( url, content_type='application/json' )
|
||||
|
||||
assert 'return_url' in response.data['urls']
|
||||
|
||||
|
||||
def test_method_options_request_list_data_key_urls_return_url_is_str(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request data key `urls.return_url` is str
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
if self.url_kwargs:
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-list', kwargs = self.url_kwargs)
|
||||
|
||||
else:
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-list')
|
||||
|
||||
response = client.options( url, content_type='application/json' )
|
||||
|
||||
assert type(response.data['urls']['return_url']) is str
|
@ -1,187 +0,0 @@
|
||||
import pytest
|
||||
|
||||
from django.contrib.auth.models import Permission, User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.test import TestCase
|
||||
|
||||
from rest_framework.exceptions import ValidationError
|
||||
|
||||
from access.models.organization import Organization
|
||||
from access.models.team import Team
|
||||
|
||||
from access.serializers.team_user import (
|
||||
TeamUsers,
|
||||
TeamUserModelSerializer
|
||||
)
|
||||
|
||||
|
||||
|
||||
class MockView:
|
||||
|
||||
action: str = None
|
||||
|
||||
kwargs: dict = {}
|
||||
|
||||
|
||||
|
||||
class MockRequest:
|
||||
|
||||
user = None
|
||||
|
||||
|
||||
|
||||
class TeamValidationAPI(
|
||||
TestCase,
|
||||
):
|
||||
|
||||
model = TeamUsers
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. Create an org
|
||||
2. Create an item
|
||||
"""
|
||||
|
||||
self.organization = Organization.objects.create(
|
||||
name = 'team org serializer test'
|
||||
)
|
||||
|
||||
self.user = User.objects.create(username = 'org_user', password='random password')
|
||||
|
||||
|
||||
view_permissions = Permission.objects.get(
|
||||
codename = 'view_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
self.team = Team.objects.create(
|
||||
organization = self.organization,
|
||||
name = 'random team title',
|
||||
)
|
||||
|
||||
|
||||
self.valid_data = {
|
||||
'team': self.team.id,
|
||||
'user': self.user.id
|
||||
}
|
||||
|
||||
|
||||
self.item = self.model.objects.create(
|
||||
team = self.team,
|
||||
user = self.user,
|
||||
)
|
||||
|
||||
|
||||
|
||||
def test_serializer_valid_data(self):
|
||||
"""Serializer Validation Check
|
||||
|
||||
Ensure that if creating an item supplied valid data
|
||||
creates an item.
|
||||
"""
|
||||
|
||||
|
||||
mock_view = MockView()
|
||||
mock_view.action = 'create'
|
||||
mock_view.kwargs: dict = {
|
||||
'organization_id': self.organization.id,
|
||||
'team_id': self.team.id
|
||||
}
|
||||
|
||||
mock_request = MockRequest()
|
||||
mock_request.user = self.user
|
||||
|
||||
mock_view.request = mock_request
|
||||
|
||||
|
||||
serializer = TeamUserModelSerializer(
|
||||
context = {
|
||||
'request': mock_request,
|
||||
'view': mock_view,
|
||||
},
|
||||
data = self.valid_data
|
||||
)
|
||||
|
||||
assert serializer.is_valid(raise_exception = True)
|
||||
|
||||
|
||||
|
||||
def test_serializer_validation_no_team_creates(self):
|
||||
"""Serializer Validation Check
|
||||
|
||||
Ensure that if creating and no team is provided no validation
|
||||
error occurs as the team id is collected from the view
|
||||
"""
|
||||
|
||||
|
||||
mock_view = MockView()
|
||||
mock_view.action = 'create'
|
||||
mock_view.kwargs: dict = {
|
||||
'organization_id': self.organization.id,
|
||||
'team_id': self.team.id
|
||||
}
|
||||
|
||||
mock_request = MockRequest()
|
||||
mock_request.user = self.user
|
||||
|
||||
mock_view.request = mock_request
|
||||
|
||||
|
||||
data = self.valid_data.copy()
|
||||
|
||||
del data['team']
|
||||
|
||||
serializer = TeamUserModelSerializer(
|
||||
context = {
|
||||
'request': mock_request,
|
||||
'view': mock_view,
|
||||
},
|
||||
data = data
|
||||
)
|
||||
|
||||
assert serializer.is_valid(raise_exception = True)
|
||||
|
||||
|
||||
|
||||
def test_serializer_validation_no_user(self):
|
||||
"""Serializer Validation Check
|
||||
|
||||
Ensure that if creating and no user is provided a validation error occurs
|
||||
"""
|
||||
|
||||
|
||||
mock_view = MockView()
|
||||
mock_view.action = 'create'
|
||||
mock_view.kwargs: dict = {
|
||||
'organization_id': self.organization.id,
|
||||
'team_id': self.team.id
|
||||
}
|
||||
|
||||
mock_request = MockRequest()
|
||||
mock_request.user = self.user
|
||||
|
||||
mock_view.request = mock_request
|
||||
|
||||
|
||||
data = self.valid_data.copy()
|
||||
|
||||
del data['user']
|
||||
|
||||
with pytest.raises(ValidationError) as err:
|
||||
|
||||
serializer = TeamUserModelSerializer(
|
||||
context = {
|
||||
'request': mock_request,
|
||||
'view': mock_view,
|
||||
},
|
||||
data = data
|
||||
)
|
||||
|
||||
serializer.is_valid(raise_exception = True)
|
||||
|
||||
assert err.value.get_codes()['user'][0] == 'required'
|
File diff suppressed because it is too large
Load Diff
@ -2,17 +2,14 @@ import pytest
|
||||
import unittest
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import AnonymousUser, Permission, User
|
||||
from django.contrib.auth.models import AnonymousUser, User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.shortcuts import reverse
|
||||
from django.test import Client, TestCase
|
||||
|
||||
from rest_framework.relations import Hyperlink
|
||||
|
||||
from access.models.organization import Organization
|
||||
from access.models.team import Team
|
||||
from access.models.team_user import TeamUsers
|
||||
|
||||
from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
|
||||
|
||||
@ -20,7 +17,7 @@ class OrganizationAPI(TestCase):
|
||||
|
||||
model = Organization
|
||||
|
||||
app_namespace = 'v1'
|
||||
app_namespace = 'API'
|
||||
|
||||
url_name = '_api_organization'
|
||||
|
||||
|
@ -1,195 +0,0 @@
|
||||
import pytest
|
||||
import unittest
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import AnonymousUser, Permission, User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.shortcuts import reverse
|
||||
from django.test import Client, TestCase
|
||||
|
||||
from rest_framework.relations import Hyperlink
|
||||
|
||||
from access.models.organization import Organization
|
||||
from access.models.team import Team
|
||||
from access.models.team_user import TeamUsers
|
||||
|
||||
|
||||
from api.tests.abstract.api_fields import APICommonFields
|
||||
|
||||
|
||||
class OrganizationAPI(
|
||||
TestCase,
|
||||
APICommonFields
|
||||
):
|
||||
|
||||
model = Organization
|
||||
|
||||
app_namespace = 'v2'
|
||||
|
||||
url_name = '_api_v2_organization'
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. Create the object
|
||||
2. create view user
|
||||
3. add user as org manager
|
||||
4. make api request
|
||||
"""
|
||||
|
||||
organization = Organization.objects.create(name='test_org', model_notes='random text')
|
||||
|
||||
self.organization = organization
|
||||
|
||||
|
||||
self.item = organization
|
||||
|
||||
self.url_view_kwargs = {'pk': self.item.id}
|
||||
|
||||
view_permissions = Permission.objects.get(
|
||||
codename = 'view_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
view_team = Team.objects.create(
|
||||
team_name = 'view_team',
|
||||
organization = organization,
|
||||
)
|
||||
|
||||
view_team.permissions.set([view_permissions])
|
||||
|
||||
|
||||
self.view_user = User.objects.create_user(username="test_user_view", password="password")
|
||||
teamuser = TeamUsers.objects.create(
|
||||
team = view_team,
|
||||
user = self.view_user
|
||||
)
|
||||
|
||||
organization.manager = self.view_user
|
||||
|
||||
organization.save()
|
||||
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-detail', kwargs=self.url_view_kwargs)
|
||||
|
||||
|
||||
client.force_login(self.view_user)
|
||||
response = client.get(url)
|
||||
|
||||
self.api_data = response.data
|
||||
|
||||
|
||||
|
||||
def test_api_field_exists_name(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
name field must exist
|
||||
"""
|
||||
|
||||
assert 'name' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_name(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
name field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['name']) is str
|
||||
|
||||
|
||||
|
||||
def test_api_field_exists_manager(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
manager field must exist
|
||||
"""
|
||||
|
||||
assert 'manager' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_manager(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
manager field must be dict
|
||||
"""
|
||||
|
||||
assert type(self.api_data['manager']) is dict
|
||||
|
||||
|
||||
def test_api_field_exists_manager_id(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
manager.id field must exist
|
||||
"""
|
||||
|
||||
assert 'id' in self.api_data['manager']
|
||||
|
||||
|
||||
def test_api_field_type_manager_id(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
manager.id field must be int
|
||||
"""
|
||||
|
||||
assert type(self.api_data['manager']['id']) is int
|
||||
|
||||
|
||||
def test_api_field_exists_manager_display_name(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
manager.display_name field must exist
|
||||
"""
|
||||
|
||||
assert 'display_name' in self.api_data['manager']
|
||||
|
||||
|
||||
def test_api_field_type_manager_display_name(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
manager.display_name field must be int
|
||||
"""
|
||||
|
||||
assert type(self.api_data['manager']['display_name']) is str
|
||||
|
||||
|
||||
def test_api_field_exists_manager_url(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
manager.display_name field must exist
|
||||
"""
|
||||
|
||||
assert 'url' in self.api_data['manager']
|
||||
|
||||
|
||||
def test_api_field_type_manager_url(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
manager.url field must be Hyperlink
|
||||
"""
|
||||
|
||||
assert type(self.api_data['manager']['url']) is Hyperlink
|
||||
|
||||
|
||||
|
||||
def test_api_field_exists_url_teams(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
_urls.teams field must exist
|
||||
"""
|
||||
|
||||
assert 'teams' in self.api_data['_urls']
|
||||
|
||||
|
||||
def test_api_field_type_url_teams(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
_urls.teams field must be Hyperlink
|
||||
"""
|
||||
|
||||
assert type(self.api_data['_urls']['teams']) is str
|
@ -1,6 +1,6 @@
|
||||
# from django.conf import settings
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import AnonymousUser, Permission, User
|
||||
from django.contrib.auth.models import AnonymousUser, User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.shortcuts import reverse
|
||||
from django.test import TestCase, Client
|
||||
@ -9,9 +9,7 @@ import pytest
|
||||
import unittest
|
||||
import requests
|
||||
|
||||
from access.models.organization import Organization
|
||||
from access.models.team import Team
|
||||
from access.models.team_user import TeamUsers
|
||||
from access.models import Organization, Team, TeamUsers, Permission
|
||||
from access.tests.abstract.model_permissions_organization_manager import OrganizationManagerModelPermissionChange, OrganizationManagerModelPermissionView
|
||||
|
||||
from app.tests.abstract.model_permissions import ModelPermissionsView, ModelPermissionsChange
|
||||
|
@ -2,14 +2,12 @@ import pytest
|
||||
import unittest
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import AnonymousUser, Permission, User
|
||||
from django.contrib.auth.models import AnonymousUser, User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.shortcuts import reverse
|
||||
from django.test import Client, TestCase
|
||||
|
||||
from access.models.organization import Organization
|
||||
from access.models.team import Team
|
||||
from access.models.team_user import TeamUsers
|
||||
from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from api.tests.abstract.api_permissions import APIPermissionChange, APIPermissionView
|
||||
|
||||
@ -22,7 +20,7 @@ class OrganizationPermissionsAPI(TestCase, APIPermissionChange, APIPermissionVie
|
||||
model_name = 'organization'
|
||||
app_label = 'access'
|
||||
|
||||
app_namespace = 'v1'
|
||||
app_namespace = 'API'
|
||||
|
||||
url_name = '_api_organization'
|
||||
|
||||
|
@ -6,8 +6,5 @@ import unittest
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
from access.models.organization import Organization
|
||||
from access.models.team import Team
|
||||
|
||||
from access.models import Organization, Team
|
||||
|
||||
|
@ -0,0 +1,187 @@
|
||||
|
||||
import pytest
|
||||
import unittest
|
||||
import requests
|
||||
|
||||
from django.test import TestCase, Client
|
||||
|
||||
from access.models import Organization
|
||||
|
||||
from core.models.history import History
|
||||
|
||||
from access.models import Organization
|
||||
|
||||
|
||||
|
||||
class OrganizationHistory(TestCase):
|
||||
|
||||
model = Organization
|
||||
|
||||
model_name = 'organization'
|
||||
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
""" Setup Test """
|
||||
|
||||
organization = Organization.objects.create(name='test_org')
|
||||
|
||||
self.organization = organization
|
||||
|
||||
self.item_create = self.model.objects.create(
|
||||
name = 'test_item_' + self.model_name,
|
||||
)
|
||||
|
||||
|
||||
self.history_create = History.objects.get(
|
||||
action = History.Actions.ADD[0],
|
||||
item_pk = self.item_create.pk,
|
||||
item_class = self.model._meta.model_name,
|
||||
)
|
||||
|
||||
self.item_change = self.item_create
|
||||
self.item_change.name = 'test_item_' + self.model_name + '_changed'
|
||||
self.item_change.save()
|
||||
|
||||
self.history_change = History.objects.get(
|
||||
action = History.Actions.UPDATE[0],
|
||||
item_pk = self.item_change.pk,
|
||||
item_class = self.model._meta.model_name,
|
||||
)
|
||||
|
||||
self.item_delete = self.model.objects.create(
|
||||
name = 'test_item_delete_' + self.model_name,
|
||||
)
|
||||
|
||||
self.item_delete.delete()
|
||||
|
||||
self.history_delete = History.objects.filter(
|
||||
item_pk = self.item_delete.pk,
|
||||
item_class = self.model._meta.model_name,
|
||||
)
|
||||
|
||||
self.history_delete_children = History.objects.filter(
|
||||
item_parent_pk = self.item_delete.pk,
|
||||
item_parent_class = self.model._meta.model_name,
|
||||
)
|
||||
|
||||
|
||||
|
||||
def test_history_entry_item_add_field_action(self):
|
||||
""" Ensure action is "add" for item creation """
|
||||
|
||||
history = self.history_create.__dict__
|
||||
|
||||
assert history['action'] == int(History.Actions.ADD[0])
|
||||
# assert type(history['action']) is int
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="to be written")
|
||||
def test_history_entry_item_add_field_after(self):
|
||||
""" Ensure after field contains correct value """
|
||||
|
||||
history = self.history_create.__dict__
|
||||
|
||||
assert history['after'] == str('{}')
|
||||
# assert type(history['after']) is str
|
||||
|
||||
|
||||
def test_history_entry_item_add_field_before(self):
|
||||
""" Ensure before field is an empty JSON string for create """
|
||||
|
||||
history = self.history_create.__dict__
|
||||
|
||||
assert history['before'] == str('{}')
|
||||
# assert type(history['before']) is str
|
||||
|
||||
|
||||
def test_history_entry_item_add_field_item_pk(self):
|
||||
""" Ensure history entry field item_pk is the created items pk """
|
||||
|
||||
history = self.history_create.__dict__
|
||||
|
||||
assert history['item_pk'] == self.item_create.pk
|
||||
# assert type(history['item_pk']) is int
|
||||
|
||||
|
||||
def test_history_entry_item_add_field_item_class(self):
|
||||
""" Ensure history entry field item_class is the model name """
|
||||
|
||||
history = self.history_create.__dict__
|
||||
|
||||
assert history['item_class'] == self.model._meta.model_name
|
||||
# assert type(history['item_class']) is str
|
||||
|
||||
|
||||
|
||||
|
||||
################################## Change ##################################
|
||||
|
||||
|
||||
|
||||
|
||||
def test_history_entry_item_change_field_action(self):
|
||||
""" Ensure action is "add" for item creation """
|
||||
|
||||
history = self.history_change.__dict__
|
||||
|
||||
assert history['action'] == int(History.Actions.UPDATE[0])
|
||||
# assert type(history['action']) is int
|
||||
|
||||
|
||||
def test_history_entry_item_change_field_after(self):
|
||||
""" Ensure after field contains correct value """
|
||||
|
||||
history = self.history_change.__dict__
|
||||
|
||||
assert history['after'] == str('{"name": "test_item_' + self.model_name + '_changed"}')
|
||||
# assert type(history['after']) is str
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="to be written")
|
||||
def test_history_entry_item_change_field_before(self):
|
||||
""" Ensure before field is an empty JSON string for create """
|
||||
|
||||
history = self.history_change.__dict__
|
||||
|
||||
assert history['before'] == str('{}')
|
||||
# assert type(history['before']) is str
|
||||
|
||||
|
||||
def test_history_entry_item_change_field_item_pk(self):
|
||||
""" Ensure history entry field item_pk is the created items pk """
|
||||
|
||||
history = self.history_change.__dict__
|
||||
|
||||
assert history['item_pk'] == self.item_create.pk
|
||||
# assert type(history['item_pk']) is int
|
||||
|
||||
|
||||
def test_history_entry_item_change_field_item_class(self):
|
||||
""" Ensure history entry field item_class is the model name """
|
||||
|
||||
history = self.history_change.__dict__
|
||||
|
||||
assert history['item_class'] == self.model._meta.model_name
|
||||
# assert type(history['item_class']) is str
|
||||
|
||||
|
||||
|
||||
|
||||
################################## Delete ##################################
|
||||
|
||||
|
||||
|
||||
|
||||
def test_device_history_entry_delete(self):
|
||||
""" When an item is deleted, it's history entries must be removed """
|
||||
|
||||
assert self.history_delete.exists() is False
|
||||
|
||||
|
||||
def test_device_history_entry_children_delete(self):
|
||||
""" When an item is deleted, it's history entries must be removed """
|
||||
|
||||
assert self.history_delete_children.exists() is False
|
||||
|
||||
|
@ -0,0 +1,165 @@
|
||||
# from django.conf import settings
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import AnonymousUser, User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.shortcuts import reverse
|
||||
from django.test import TestCase, Client
|
||||
|
||||
import pytest
|
||||
import unittest
|
||||
import requests
|
||||
|
||||
from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from core.models.history import History
|
||||
|
||||
|
||||
class OrganizationHistoryPermissions(TestCase):
|
||||
|
||||
|
||||
item_model = Organization
|
||||
|
||||
|
||||
model = History
|
||||
|
||||
model_name = 'history'
|
||||
|
||||
app_label = 'core'
|
||||
|
||||
namespace = ''
|
||||
|
||||
name_view = '_history'
|
||||
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. Create an organization for user and item
|
||||
2. create an organization that is different to item
|
||||
3. Create a device
|
||||
4. Add history device history entry as item
|
||||
5. create a user
|
||||
6. create user in different organization (with the required permission)
|
||||
"""
|
||||
|
||||
organization = Organization.objects.create(name='test_org')
|
||||
|
||||
self.organization = organization
|
||||
|
||||
different_organization = Organization.objects.create(name='test_different_organization')
|
||||
|
||||
self.item = self.organization
|
||||
|
||||
self.history_model_name = self.item._meta.model_name
|
||||
|
||||
self.history = self.model.objects.get(
|
||||
item_pk = self.item.id,
|
||||
item_class = self.item._meta.model_name,
|
||||
action = self.model.Actions.ADD,
|
||||
)
|
||||
|
||||
view_permissions = Permission.objects.get(
|
||||
codename = 'view_' + self.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.app_label,
|
||||
model = self.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
view_team = Team.objects.create(
|
||||
team_name = 'view_team',
|
||||
organization = organization,
|
||||
)
|
||||
|
||||
view_team.permissions.set([view_permissions])
|
||||
|
||||
|
||||
self.no_permissions_user = User.objects.create_user(username="test_no_permissions", password="password")
|
||||
|
||||
|
||||
self.view_user = User.objects.create_user(username="test_user_view", password="password")
|
||||
teamuser = TeamUsers.objects.create(
|
||||
team = view_team,
|
||||
user = self.view_user
|
||||
)
|
||||
|
||||
self.different_organization_user = User.objects.create_user(username="test_different_organization_user", password="password")
|
||||
|
||||
|
||||
different_organization_team = Team.objects.create(
|
||||
team_name = 'different_organization_team',
|
||||
organization = different_organization,
|
||||
)
|
||||
|
||||
different_organization_team.permissions.set([
|
||||
view_permissions,
|
||||
])
|
||||
|
||||
TeamUsers.objects.create(
|
||||
team = different_organization_team,
|
||||
user = self.different_organization_user
|
||||
)
|
||||
|
||||
|
||||
|
||||
def test_auth_view_history_user_anon_denied(self):
|
||||
""" Check correct permission for view
|
||||
|
||||
Attempt to view as anon user
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.namespace + self.name_view, kwargs={'model_name': self.history_model_name, 'model_pk': self.item.id})
|
||||
|
||||
response = client.get(url)
|
||||
|
||||
assert response.status_code == 302 and response.url.startswith('/account/login')
|
||||
|
||||
|
||||
def test_auth_view_history_no_permission_denied(self):
|
||||
""" Check correct permission for view
|
||||
|
||||
Attempt to view with user missing permission
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.namespace + self.name_view, kwargs={'model_name': self.history_model_name, 'model_pk': self.item.id})
|
||||
|
||||
|
||||
client.force_login(self.no_permissions_user)
|
||||
response = client.get(url)
|
||||
|
||||
assert response.status_code == 403
|
||||
|
||||
|
||||
def test_auth_view_history_different_organizaiton_denied(self):
|
||||
""" Check correct permission for view
|
||||
|
||||
Attempt to view with user from different organization
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.namespace + self.name_view, kwargs={'model_name': self.history_model_name, 'model_pk': self.item.id})
|
||||
|
||||
|
||||
client.force_login(self.different_organization_user)
|
||||
response = client.get(url)
|
||||
|
||||
assert response.status_code == 403
|
||||
|
||||
|
||||
def test_auth_view_history_has_permission(self):
|
||||
""" Check correct permission for view
|
||||
|
||||
Attempt to view as user with view permission
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.namespace + self.name_view, kwargs={'model_name': self.history_model_name, 'model_pk': self.item.id})
|
||||
|
||||
|
||||
client.force_login(self.view_user)
|
||||
response = client.get(url)
|
||||
|
||||
assert response.status_code == 200
|
@ -1,66 +0,0 @@
|
||||
import pytest
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.test import Client, TestCase
|
||||
|
||||
from rest_framework.reverse import reverse
|
||||
|
||||
|
||||
from access.models.organization import Organization
|
||||
from access.viewsets.organization import ViewSet
|
||||
|
||||
from api.tests.abstract.viewsets import ViewSetModel
|
||||
|
||||
|
||||
|
||||
class ViewsetCommon(
|
||||
ViewSetModel,
|
||||
):
|
||||
|
||||
viewset = ViewSet
|
||||
|
||||
route_name = 'API:_api_v2_organization'
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. Create an organization
|
||||
3. create super user
|
||||
"""
|
||||
|
||||
organization = Organization.objects.create(name='test_org')
|
||||
|
||||
self.organization = organization
|
||||
|
||||
self.view_user = User.objects.create_user(username="test_view_user", password="password", is_superuser=True)
|
||||
|
||||
self.kwargs = {}
|
||||
|
||||
|
||||
class OrganizationViewsetList(
|
||||
ViewsetCommon,
|
||||
TestCase,
|
||||
):
|
||||
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. make list request
|
||||
"""
|
||||
|
||||
|
||||
super().setUpTestData()
|
||||
|
||||
|
||||
client = Client()
|
||||
|
||||
url = reverse(
|
||||
self.route_name + '-list'
|
||||
)
|
||||
|
||||
client.force_login(self.view_user)
|
||||
|
||||
self.http_options_response_list = client.options(url)
|
@ -1,40 +0,0 @@
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.test import TestCase
|
||||
|
||||
from core.tests.abstract.test_unit_model_history_api_v2 import PrimaryModelHistoryAPI
|
||||
|
||||
from access.models.organization_history import Organization, OrganizationHistory
|
||||
|
||||
|
||||
|
||||
class ModelHistoryAPI(
|
||||
PrimaryModelHistoryAPI,
|
||||
TestCase,
|
||||
):
|
||||
|
||||
audit_model = Organization
|
||||
|
||||
model = OrganizationHistory
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
|
||||
super().setUpTestData()
|
||||
|
||||
self.audit_object = self.organization
|
||||
|
||||
self.history_entry = self.model.objects.create(
|
||||
organization = self.audit_object,
|
||||
action = self.model.Actions.ADD,
|
||||
user = self.view_user,
|
||||
before = {},
|
||||
after = {},
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.audit_object._meta.app_label,
|
||||
model = self.audit_object._meta.model_name,
|
||||
),
|
||||
model = self.audit_object,
|
||||
)
|
||||
|
||||
|
||||
self.make_request()
|
@ -1,36 +0,0 @@
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.test import TestCase
|
||||
|
||||
from core.tests.abstract.test_unit_model_notes_model import ModelNotesModel
|
||||
|
||||
from access.models.organization_notes import OrganizationNotes
|
||||
|
||||
|
||||
|
||||
class OrganizationNotesModel(
|
||||
ModelNotesModel,
|
||||
TestCase,
|
||||
):
|
||||
|
||||
model = OrganizationNotes
|
||||
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test"""
|
||||
|
||||
super().setUpTestData()
|
||||
|
||||
|
||||
self.item = self.model.objects.create(
|
||||
organization = self.organization,
|
||||
content = 'a random comment for an exiting item',
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = str(self.model._meta.app_label).lower(),
|
||||
model = str(self.model.model.field.related_model.__name__).replace(' ', '').lower(),
|
||||
),
|
||||
model = self.model.model.field.related_model.objects.create(
|
||||
name = 'note model existing item',
|
||||
),
|
||||
created_by = self.user,
|
||||
)
|
@ -1,57 +0,0 @@
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.test import TestCase
|
||||
|
||||
from core.tests.abstract.test_unit_model_notes_serializer import ModelNotesSerializerTestCases
|
||||
|
||||
from access.serializers.organization_notes import OrganizationNotes, OrganizationNoteModelSerializer
|
||||
|
||||
|
||||
|
||||
class OrganizationNotesSerializer(
|
||||
ModelNotesSerializerTestCases,
|
||||
TestCase,
|
||||
):
|
||||
|
||||
model = OrganizationNotes
|
||||
|
||||
model_serializer = OrganizationNoteModelSerializer
|
||||
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test"""
|
||||
|
||||
super().setUpTestData()
|
||||
|
||||
|
||||
self.note_model = self.model.model.field.related_model.objects.create(
|
||||
name = 'note model',
|
||||
)
|
||||
|
||||
self.note_model_two = self.model.model.field.related_model.objects.create(
|
||||
name = 'note model two',
|
||||
)
|
||||
|
||||
|
||||
self.item = self.model.objects.create(
|
||||
organization = self.organization,
|
||||
content = 'a random comment for an exiting item',
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = str(self.model._meta.app_label).lower(),
|
||||
model = str(self.model.model.field.related_model.__name__).replace(' ', '').lower(),
|
||||
),
|
||||
model = self.model.model.field.related_model.objects.create(
|
||||
name = 'note model existing item',
|
||||
),
|
||||
created_by = self.user_two,
|
||||
)
|
||||
|
||||
|
||||
self.valid_data = {
|
||||
'organization': self.organization_two.id,
|
||||
'content': 'a random comment',
|
||||
'content_type': self.content_type_two.id,
|
||||
'model': self.note_model_two.id,
|
||||
'created_by': self.user_two.id,
|
||||
'modified_by': self.user_two.id,
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
import pytest
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.test import Client, TestCase
|
||||
|
||||
from rest_framework.reverse import reverse
|
||||
|
||||
from access.models.organization import Organization
|
||||
from access.viewsets.team_notes import ViewSet
|
||||
|
||||
from api.tests.abstract.viewsets import ViewSetModel
|
||||
|
||||
|
||||
|
||||
class ViewsetCommon(
|
||||
ViewSetModel,
|
||||
):
|
||||
|
||||
viewset = ViewSet
|
||||
|
||||
route_name = 'v2:_api_v2_organization_note'
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. Create an organization
|
||||
3. create super user
|
||||
"""
|
||||
|
||||
organization = Organization.objects.create(name='test_org')
|
||||
|
||||
self.organization = organization
|
||||
|
||||
self.view_user = User.objects.create_user(username="test_view_user", password="password", is_superuser=True)
|
||||
|
||||
|
||||
|
||||
|
||||
class OrganizationNotesViewsetList(
|
||||
ViewsetCommon,
|
||||
TestCase,
|
||||
):
|
||||
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. Create object that is to be tested against
|
||||
2. add kwargs
|
||||
3. make list request
|
||||
"""
|
||||
|
||||
|
||||
super().setUpTestData()
|
||||
|
||||
self.kwargs = {
|
||||
'model_id': self.organization.id,
|
||||
}
|
||||
|
||||
|
||||
client = Client()
|
||||
|
||||
url = reverse(
|
||||
self.route_name + '-list',
|
||||
kwargs = self.kwargs
|
||||
)
|
||||
|
||||
client.force_login(self.view_user)
|
||||
|
||||
self.http_options_response_list = client.options(url)
|
@ -1,12 +1,9 @@
|
||||
import pytest
|
||||
import unittest
|
||||
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.test import TestCase, Client
|
||||
|
||||
from access.models.organization import Organization
|
||||
from access.models.team import Team
|
||||
from access.models.team_user import TeamUsers
|
||||
from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from app.tests.abstract.models import TenancyModel
|
||||
|
||||
@ -37,22 +34,22 @@ class TeamModel(
|
||||
)
|
||||
|
||||
|
||||
# def test_model_has_property_parent_object(self):
|
||||
# """ Check if model contains 'parent_object'
|
||||
def test_model_has_property_parent_object(self):
|
||||
""" Check if model contains 'parent_object'
|
||||
|
||||
# This is a required property for all models that have a parent
|
||||
# """
|
||||
This is a required property for all models that have a parent
|
||||
"""
|
||||
|
||||
# assert hasattr(self.model, 'parent_object')
|
||||
assert hasattr(self.model, 'parent_object')
|
||||
|
||||
|
||||
# def test_model_property_parent_object_returns_object(self):
|
||||
# """ Check if model contains 'parent_object'
|
||||
def test_model_property_parent_object_returns_object(self):
|
||||
""" Check if model contains 'parent_object'
|
||||
|
||||
# This is a required property for all models that have a parent
|
||||
# """
|
||||
This is a required property for all models that have a parent
|
||||
"""
|
||||
|
||||
# assert self.item.parent_object is self.parent_item
|
||||
assert self.item.parent_object is self.parent_item
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="to be written")
|
||||
@ -70,74 +67,4 @@ class TeamModel(
|
||||
|
||||
@pytest.mark.skip(reason="uses Django group manager")
|
||||
def test_model_class_tenancy_manager_function_get_queryset_called(self):
|
||||
pass
|
||||
|
||||
|
||||
def test_model_fields_parameter_not_empty_help_text(self):
|
||||
"""Test Field called with Parameter
|
||||
|
||||
This is a custom test of a test derived of the samae name. It's required
|
||||
as the team model extends the Group model.
|
||||
|
||||
During field creation, paramater `help_text` must not be `None` or empty ('')
|
||||
"""
|
||||
|
||||
group_mode_fields_to_ignore: list = [
|
||||
'id',
|
||||
'name',
|
||||
'group_ptr_id'
|
||||
]
|
||||
|
||||
fields_have_test_value: bool = True
|
||||
|
||||
for field in self.model._meta.fields:
|
||||
|
||||
if field.attname in group_mode_fields_to_ignore:
|
||||
|
||||
continue
|
||||
|
||||
print(f'Checking field {field.attname} is not empty')
|
||||
|
||||
if (
|
||||
field.help_text is None
|
||||
or field.help_text == ''
|
||||
):
|
||||
|
||||
print(f' Failure on field {field.attname}')
|
||||
|
||||
fields_have_test_value = False
|
||||
|
||||
|
||||
assert fields_have_test_value
|
||||
|
||||
def test_model_fields_parameter_type_verbose_name(self):
|
||||
"""Test Field called with Parameter
|
||||
|
||||
This is a custom test of a test derived of the samae name. It's required
|
||||
as the team model extends the Group model.
|
||||
|
||||
During field creation, paramater `verbose_name` must be of type str
|
||||
"""
|
||||
|
||||
group_mode_fields_to_ignore: list = [
|
||||
'name',
|
||||
]
|
||||
|
||||
fields_have_test_value: bool = True
|
||||
|
||||
for field in self.model._meta.fields:
|
||||
|
||||
if field.attname in group_mode_fields_to_ignore:
|
||||
|
||||
continue
|
||||
|
||||
print(f'Checking field {field.attname} is of type str')
|
||||
|
||||
if not type(field.verbose_name) is str:
|
||||
|
||||
print(f' Failure on field {field.attname}')
|
||||
|
||||
fields_have_test_value = False
|
||||
|
||||
|
||||
assert fields_have_test_value
|
||||
pass
|
@ -4,16 +4,14 @@ import requests
|
||||
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import AnonymousUser, Permission, User
|
||||
from django.contrib.auth.models import AnonymousUser, User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.shortcuts import reverse
|
||||
from django.test import Client, TestCase
|
||||
|
||||
from rest_framework.relations import Hyperlink
|
||||
|
||||
from access.models.organization import Organization
|
||||
from access.models.team import Team
|
||||
from access.models.team_user import TeamUsers
|
||||
from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
# from api.tests.abstract.api_permissions import APIPermissions
|
||||
|
||||
@ -23,7 +21,7 @@ class TeamAPI(TestCase):
|
||||
|
||||
model = Team
|
||||
|
||||
app_namespace = 'v1'
|
||||
app_namespace = 'API'
|
||||
|
||||
url_name = '_api_team'
|
||||
|
||||
|
@ -1,176 +0,0 @@
|
||||
import pytest
|
||||
|
||||
from django.contrib.auth.models import Permission, User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.shortcuts import reverse
|
||||
from django.test import Client, TestCase
|
||||
|
||||
from rest_framework.relations import Hyperlink
|
||||
|
||||
from access.models.organization import Organization
|
||||
from access.models.team import Team
|
||||
from access.models.team_user import TeamUsers
|
||||
|
||||
from api.tests.abstract.api_fields import APITenancyObject
|
||||
|
||||
|
||||
|
||||
class TeamAPI(
|
||||
TestCase,
|
||||
APITenancyObject
|
||||
):
|
||||
|
||||
model = Team
|
||||
|
||||
app_namespace = 'v2'
|
||||
|
||||
url_name = '_api_v2_organization_team'
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. Create the object
|
||||
2. create view user
|
||||
3. add user as org manager
|
||||
4. make api request
|
||||
"""
|
||||
|
||||
organization = Organization.objects.create(name='test_org')
|
||||
|
||||
self.organization = organization
|
||||
|
||||
different_organization = Organization.objects.create(name='test_different_organization')
|
||||
|
||||
|
||||
self.item = self.model.objects.create(
|
||||
organization=organization,
|
||||
team_name = 'teamone',
|
||||
model_notes = 'random note'
|
||||
)
|
||||
|
||||
self.url_view_kwargs = {'organization_id': self.organization.id, 'pk': self.item.id}
|
||||
|
||||
view_permissions = Permission.objects.get(
|
||||
codename = 'view_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
self.item.permissions.set([view_permissions])
|
||||
|
||||
self.view_user = User.objects.create_user(username="test_user_view", password="password")
|
||||
teamuser = TeamUsers.objects.create(
|
||||
team = self.item,
|
||||
user = self.view_user
|
||||
)
|
||||
|
||||
organization.manager = self.view_user
|
||||
|
||||
organization.save()
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-detail', kwargs=self.url_view_kwargs)
|
||||
|
||||
|
||||
client.force_login(self.view_user)
|
||||
response = client.get(url)
|
||||
|
||||
self.api_data = response.data
|
||||
|
||||
|
||||
|
||||
|
||||
def test_api_field_exists_team_name(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
team_name field must exist
|
||||
"""
|
||||
|
||||
assert 'team_name' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_team_name(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
team_name field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['team_name']) is str
|
||||
|
||||
|
||||
|
||||
def test_api_field_exists_permissions(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
permissions field must exist
|
||||
"""
|
||||
|
||||
assert 'permissions' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_permissions(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
url field must be list
|
||||
"""
|
||||
|
||||
assert type(self.api_data['permissions']) is list
|
||||
|
||||
|
||||
|
||||
def test_api_field_exists_permissions_id(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
permissions.id field must exist
|
||||
"""
|
||||
|
||||
assert 'id' in self.api_data['permissions'][0]
|
||||
|
||||
|
||||
def test_api_field_type_permissions_id(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
permissions.id field must be int
|
||||
"""
|
||||
|
||||
assert type(self.api_data['permissions'][0]['id']) is int
|
||||
|
||||
|
||||
def test_api_field_exists_permissions_display_name(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
permissions.display_name field must exist
|
||||
"""
|
||||
|
||||
assert 'display_name' in self.api_data['permissions'][0]
|
||||
|
||||
|
||||
def test_api_field_type_permissions_display_name(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
permissions.display_name field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['permissions'][0]['display_name']) is str
|
||||
|
||||
|
||||
|
||||
def test_api_field_exists_permissions_url(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
permissions.url field must exist
|
||||
"""
|
||||
|
||||
assert 'url' in self.api_data['permissions'][0]
|
||||
|
||||
|
||||
def test_api_field_type_permissions_url(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
permissions.url field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['permissions'][0]['url']) is Hyperlink
|
79
app/access/tests/unit/team/test_team_core_history.py
Normal file
79
app/access/tests/unit/team/test_team_core_history.py
Normal file
@ -0,0 +1,79 @@
|
||||
|
||||
import pytest
|
||||
import unittest
|
||||
import requests
|
||||
|
||||
from django.test import TestCase, Client
|
||||
|
||||
from access.models import Organization
|
||||
|
||||
from core.models.history import History
|
||||
from core.tests.abstract.history_entry import HistoryEntry
|
||||
from core.tests.abstract.history_entry_child_model import HistoryEntryChildItem
|
||||
|
||||
from access.models import Team
|
||||
|
||||
from django.contrib.auth.models import Group
|
||||
|
||||
|
||||
class TeamHistory(TestCase, HistoryEntry, HistoryEntryChildItem):
|
||||
|
||||
|
||||
model = Team
|
||||
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
""" Setup Test """
|
||||
|
||||
organization = Organization.objects.create(name='test_org')
|
||||
|
||||
self.organization = organization
|
||||
|
||||
self.item_parent = organization
|
||||
|
||||
self.item_create = self.model.objects.create(
|
||||
team_name = 'test_item_' + self.model._meta.model_name,
|
||||
organization = self.organization
|
||||
)
|
||||
|
||||
|
||||
self.history_create = History.objects.get(
|
||||
action = History.Actions.ADD[0],
|
||||
item_pk = self.item_create.pk,
|
||||
item_class = self.model._meta.model_name,
|
||||
)
|
||||
|
||||
self.item_change = self.item_create
|
||||
self.item_change.team_name = 'test_item_' + self.model._meta.model_name + '_changed'
|
||||
self.item_change.save()
|
||||
|
||||
self.field_after_expected_value = '{"name": "test_org_' + self.item_change.team_name + '", "team_name": "' + self.item_change.team_name + '"}'
|
||||
|
||||
self.history_change = History.objects.get(
|
||||
action = History.Actions.UPDATE[0],
|
||||
item_pk = self.item_change.pk,
|
||||
item_class = self.model._meta.model_name,
|
||||
)
|
||||
|
||||
debug = Group.objects.all()
|
||||
|
||||
self.item_delete = self.model.objects.create(
|
||||
team_name = 'test_item_delete_' + self.model._meta.model_name,
|
||||
organization = self.organization
|
||||
)
|
||||
|
||||
self.deleted_pk = self.item_delete.pk
|
||||
|
||||
self.item_delete.delete()
|
||||
|
||||
self.history_delete = History.objects.get(
|
||||
action = History.Actions.DELETE[0],
|
||||
item_pk = self.deleted_pk,
|
||||
item_class = self.model._meta.model_name,
|
||||
)
|
||||
|
||||
self.history_delete_children = History.objects.filter(
|
||||
item_parent_pk = self.deleted_pk,
|
||||
item_parent_class = self.item_parent._meta.model_name,
|
||||
)
|
168
app/access/tests/unit/team/test_team_history_permission.py
Normal file
168
app/access/tests/unit/team/test_team_history_permission.py
Normal file
@ -0,0 +1,168 @@
|
||||
# from django.conf import settings
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import AnonymousUser, User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.shortcuts import reverse
|
||||
from django.test import TestCase, Client
|
||||
|
||||
import pytest
|
||||
import unittest
|
||||
import requests
|
||||
|
||||
from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from core.models.history import History
|
||||
|
||||
|
||||
class TeamHistoryPermissions(TestCase):
|
||||
|
||||
|
||||
item_model = Team
|
||||
|
||||
|
||||
model = History
|
||||
|
||||
model_name = 'history'
|
||||
|
||||
app_label = 'core'
|
||||
|
||||
namespace = ''
|
||||
|
||||
name_view = '_history'
|
||||
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. Create an organization for user and item
|
||||
2. create an organization that is different to item
|
||||
3. Create a device
|
||||
4. Add history device history entry as item
|
||||
5. create a user
|
||||
6. create user in different organization (with the required permission)
|
||||
"""
|
||||
|
||||
organization = Organization.objects.create(name='test_org')
|
||||
|
||||
self.organization = organization
|
||||
|
||||
different_organization = Organization.objects.create(name='test_different_organization')
|
||||
|
||||
self.item = self.item_model.objects.create(
|
||||
organization=organization,
|
||||
name = 'deviceone'
|
||||
)
|
||||
|
||||
self.history_model_name = self.item._meta.model_name
|
||||
|
||||
self.history = self.model.objects.get(
|
||||
item_pk = self.item.id,
|
||||
item_class = self.item._meta.model_name,
|
||||
action = self.model.Actions.ADD,
|
||||
)
|
||||
|
||||
view_permissions = Permission.objects.get(
|
||||
codename = 'view_' + self.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.app_label,
|
||||
model = self.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
view_team = Team.objects.create(
|
||||
team_name = 'view_team',
|
||||
organization = organization,
|
||||
)
|
||||
|
||||
view_team.permissions.set([view_permissions])
|
||||
|
||||
|
||||
self.no_permissions_user = User.objects.create_user(username="test_no_permissions", password="password")
|
||||
|
||||
|
||||
self.view_user = User.objects.create_user(username="test_user_view", password="password")
|
||||
teamuser = TeamUsers.objects.create(
|
||||
team = view_team,
|
||||
user = self.view_user
|
||||
)
|
||||
|
||||
self.different_organization_user = User.objects.create_user(username="test_different_organization_user", password="password")
|
||||
|
||||
|
||||
different_organization_team = Team.objects.create(
|
||||
team_name = 'different_organization_team',
|
||||
organization = different_organization,
|
||||
)
|
||||
|
||||
different_organization_team.permissions.set([
|
||||
view_permissions,
|
||||
])
|
||||
|
||||
TeamUsers.objects.create(
|
||||
team = different_organization_team,
|
||||
user = self.different_organization_user
|
||||
)
|
||||
|
||||
|
||||
|
||||
def test_auth_view_history_user_anon_denied(self):
|
||||
""" Check correct permission for view
|
||||
|
||||
Attempt to view as anon user
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.namespace + self.name_view, kwargs={'model_name': self.history_model_name, 'model_pk': self.item.id})
|
||||
|
||||
response = client.get(url)
|
||||
|
||||
assert response.status_code == 302 and response.url.startswith('/account/login')
|
||||
|
||||
|
||||
def test_auth_view_history_no_permission_denied(self):
|
||||
""" Check correct permission for view
|
||||
|
||||
Attempt to view with user missing permission
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.namespace + self.name_view, kwargs={'model_name': self.history_model_name, 'model_pk': self.item.id})
|
||||
|
||||
|
||||
client.force_login(self.no_permissions_user)
|
||||
response = client.get(url)
|
||||
|
||||
assert response.status_code == 403
|
||||
|
||||
|
||||
def test_auth_view_history_different_organizaiton_denied(self):
|
||||
""" Check correct permission for view
|
||||
|
||||
Attempt to view with user from different organization
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.namespace + self.name_view, kwargs={'model_name': self.history_model_name, 'model_pk': self.item.id})
|
||||
|
||||
|
||||
client.force_login(self.different_organization_user)
|
||||
response = client.get(url)
|
||||
|
||||
assert response.status_code == 403
|
||||
|
||||
|
||||
def test_auth_view_history_has_permission(self):
|
||||
""" Check correct permission for view
|
||||
|
||||
Attempt to view as user with view permission
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.namespace + self.name_view, kwargs={'model_name': self.history_model_name, 'model_pk': self.item.id})
|
||||
|
||||
|
||||
client.force_login(self.view_user)
|
||||
response = client.get(url)
|
||||
|
||||
assert response.status_code == 200
|
@ -1,6 +1,6 @@
|
||||
# from django.conf import settings
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import AnonymousUser, Permission, User
|
||||
from django.contrib.auth.models import AnonymousUser, User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.shortcuts import reverse
|
||||
from django.test import TestCase, Client
|
||||
@ -9,9 +9,7 @@ import pytest
|
||||
import unittest
|
||||
import requests
|
||||
|
||||
from access.models.organization import Organization
|
||||
from access.models.team import Team
|
||||
from access.models.team_user import TeamUsers
|
||||
from access.models import Organization, Team, TeamUsers, Permission
|
||||
from access.tests.abstract.model_permissions_organization_manager import OrganizationManagerModelPermissions
|
||||
|
||||
from app.tests.abstract.model_permissions import ModelPermissions
|
||||
|
@ -4,13 +4,11 @@ import requests
|
||||
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import AnonymousUser, Permission, User
|
||||
from django.contrib.auth.models import AnonymousUser, User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.test import TestCase
|
||||
|
||||
from access.models.organization import Organization
|
||||
from access.models.team import Team
|
||||
from access.models.team_user import TeamUsers
|
||||
from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from api.tests.abstract.api_permissions import APIPermissions
|
||||
|
||||
@ -20,7 +18,7 @@ class TeamPermissionsAPI(TestCase, APIPermissions):
|
||||
|
||||
model = Team
|
||||
|
||||
app_namespace = 'v1'
|
||||
app_namespace = 'API'
|
||||
|
||||
url_name = '_api_team'
|
||||
|
||||
|
@ -1,43 +0,0 @@
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.test import TestCase
|
||||
|
||||
from core.tests.abstract.test_unit_model_history_api_v2 import PrimaryModelHistoryAPI
|
||||
|
||||
from access.models.team_history import Team, TeamHistory
|
||||
|
||||
|
||||
|
||||
class ModelHistoryAPI(
|
||||
PrimaryModelHistoryAPI,
|
||||
TestCase,
|
||||
):
|
||||
|
||||
audit_model = Team
|
||||
|
||||
model = TeamHistory
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
|
||||
super().setUpTestData()
|
||||
|
||||
self.audit_object = self.audit_model.objects.create(
|
||||
organization = self.organization,
|
||||
name = 'one',
|
||||
)
|
||||
|
||||
self.history_entry = self.model.objects.create(
|
||||
organization = self.audit_object.organization,
|
||||
action = self.model.Actions.ADD,
|
||||
user = self.view_user,
|
||||
before = {},
|
||||
after = {},
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.audit_object._meta.app_label,
|
||||
model = self.audit_object._meta.model_name,
|
||||
),
|
||||
model = self.audit_object,
|
||||
)
|
||||
|
||||
|
||||
self.make_request()
|
@ -1,67 +0,0 @@
|
||||
import pytest
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.test import Client, TestCase
|
||||
|
||||
from rest_framework.reverse import reverse
|
||||
|
||||
from access.models.organization import Organization
|
||||
from access.viewsets.team import ViewSet
|
||||
|
||||
from api.tests.abstract.viewsets import ViewSetModel
|
||||
|
||||
|
||||
|
||||
class ViewsetCommon(
|
||||
ViewSetModel,
|
||||
):
|
||||
|
||||
viewset = ViewSet
|
||||
|
||||
route_name = 'API:_api_v2_organization_team'
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. Create an organization
|
||||
3. create super user
|
||||
"""
|
||||
|
||||
organization = Organization.objects.create(name='test_org')
|
||||
|
||||
self.organization = organization
|
||||
|
||||
self.view_user = User.objects.create_user(username="test_view_user", password="password", is_superuser=True)
|
||||
|
||||
self.kwargs = { 'organization_id': self.organization.id }
|
||||
|
||||
|
||||
|
||||
class TeamViewsetList(
|
||||
ViewsetCommon,
|
||||
TestCase,
|
||||
):
|
||||
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. make list request
|
||||
"""
|
||||
|
||||
|
||||
super().setUpTestData()
|
||||
|
||||
|
||||
client = Client()
|
||||
|
||||
url = reverse(
|
||||
self.route_name + '-list',
|
||||
kwargs = self.kwargs
|
||||
)
|
||||
|
||||
client.force_login(self.view_user)
|
||||
|
||||
self.http_options_response_list = client.options(url)
|
@ -1,37 +0,0 @@
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.test import TestCase
|
||||
|
||||
from core.tests.abstract.test_unit_model_notes_model import ModelNotesModel
|
||||
|
||||
from access.models.team_notes import TeamNotes
|
||||
|
||||
|
||||
|
||||
class TeamNotesModel(
|
||||
ModelNotesModel,
|
||||
TestCase,
|
||||
):
|
||||
|
||||
model = TeamNotes
|
||||
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test"""
|
||||
|
||||
super().setUpTestData()
|
||||
|
||||
|
||||
self.item = self.model.objects.create(
|
||||
organization = self.organization,
|
||||
content = 'a random comment for an exiting item',
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = str(self.model._meta.app_label).lower(),
|
||||
model = str(self.model.model.field.related_model.__name__).replace(' ', '').lower(),
|
||||
),
|
||||
model = self.model.model.field.related_model.objects.create(
|
||||
organization = self.organization,
|
||||
name = 'note model existing item',
|
||||
),
|
||||
created_by = self.user,
|
||||
)
|
@ -1,60 +0,0 @@
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.test import TestCase
|
||||
|
||||
from core.tests.abstract.test_unit_model_notes_serializer import ModelNotesSerializerTestCases
|
||||
|
||||
from access.serializers.team_notes import TeamNotes, TeamNoteModelSerializer
|
||||
|
||||
|
||||
|
||||
class TeamNotesSerializer(
|
||||
ModelNotesSerializerTestCases,
|
||||
TestCase,
|
||||
):
|
||||
|
||||
model = TeamNotes
|
||||
|
||||
model_serializer = TeamNoteModelSerializer
|
||||
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test"""
|
||||
|
||||
super().setUpTestData()
|
||||
|
||||
|
||||
self.note_model = self.model.model.field.related_model.objects.create(
|
||||
organization = self.organization,
|
||||
team_name = 'note model',
|
||||
)
|
||||
|
||||
self.note_model_two = self.model.model.field.related_model.objects.create(
|
||||
organization = self.organization,
|
||||
team_name = 'note model two',
|
||||
)
|
||||
|
||||
|
||||
self.item = self.model.objects.create(
|
||||
organization = self.organization,
|
||||
content = 'a random comment for an exiting item',
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = str(self.model._meta.app_label).lower(),
|
||||
model = str(self.model.model.field.related_model.__name__).replace(' ', '').lower(),
|
||||
),
|
||||
model = self.model.model.field.related_model.objects.create(
|
||||
organization = self.organization,
|
||||
team_name = 'note model existing item',
|
||||
),
|
||||
created_by = self.user_two,
|
||||
)
|
||||
|
||||
|
||||
self.valid_data = {
|
||||
'organization': self.organization_two.id,
|
||||
'content': 'a random comment',
|
||||
'content_type': self.content_type_two.id,
|
||||
'model': self.note_model_two.id,
|
||||
'created_by': self.user_two.id,
|
||||
'modified_by': self.user_two.id,
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
import pytest
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.test import Client, TestCase
|
||||
|
||||
from rest_framework.reverse import reverse
|
||||
|
||||
from access.models.organization import Organization
|
||||
from access.viewsets.team_notes import ViewSet
|
||||
|
||||
from api.tests.abstract.viewsets import ViewSetModel
|
||||
|
||||
|
||||
|
||||
class ViewsetCommon(
|
||||
ViewSetModel,
|
||||
):
|
||||
|
||||
viewset = ViewSet
|
||||
|
||||
route_name = 'v2:_api_v2_team_note'
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. Create an organization
|
||||
3. create super user
|
||||
"""
|
||||
|
||||
organization = Organization.objects.create(name='test_org')
|
||||
|
||||
self.organization = organization
|
||||
|
||||
self.view_user = User.objects.create_user(username="test_view_user", password="password", is_superuser=True)
|
||||
|
||||
|
||||
|
||||
|
||||
class TeamNotesViewsetList(
|
||||
ViewsetCommon,
|
||||
TestCase,
|
||||
):
|
||||
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. Create object that is to be tested against
|
||||
2. add kwargs
|
||||
3. make list request
|
||||
"""
|
||||
|
||||
|
||||
super().setUpTestData()
|
||||
|
||||
self.note_model = self.viewset.model.model.field.related_model.objects.create(
|
||||
organization = self.organization,
|
||||
name = 'note model'
|
||||
)
|
||||
|
||||
self.kwargs = {
|
||||
'organization_id': self.organization.id,
|
||||
'model_id': self.note_model.pk,
|
||||
}
|
||||
|
||||
|
||||
client = Client()
|
||||
|
||||
url = reverse(
|
||||
self.route_name + '-list',
|
||||
kwargs = self.kwargs
|
||||
)
|
||||
|
||||
client.force_login(self.view_user)
|
||||
|
||||
self.http_options_response_list = client.options(url)
|
@ -2,22 +2,13 @@ import pytest
|
||||
import unittest
|
||||
|
||||
from django.test import TestCase, Client
|
||||
from django.contrib.auth.models import Permission, User
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from access.models.organization import Organization
|
||||
from access.models.team import Team
|
||||
from access.models.team_user import TeamUsers
|
||||
|
||||
from app.tests.abstract.models import BaseModel
|
||||
|
||||
from core.mixin.history_save import SaveHistory
|
||||
from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
|
||||
|
||||
class TeamUsersModel(
|
||||
TestCase,
|
||||
BaseModel
|
||||
):
|
||||
class TeamUsersModel(TestCase):
|
||||
|
||||
model = TeamUsers
|
||||
|
||||
@ -63,14 +54,3 @@ class TeamUsersModel(
|
||||
"""
|
||||
|
||||
assert self.item.parent_object == self.parent_item
|
||||
|
||||
|
||||
|
||||
@pytest.mark.skip( reason = 'TeamUsers is not a tenancy object' )
|
||||
def test_class_inherits_tenancy_objecy(self):
|
||||
""" Confirm class inheritence
|
||||
|
||||
TenancyObject must inherit TenancyObject
|
||||
"""
|
||||
|
||||
pass
|
||||
|
@ -1,216 +0,0 @@
|
||||
import pytest
|
||||
|
||||
from django.contrib.auth.models import Permission, User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.shortcuts import reverse
|
||||
from django.test import Client, TestCase
|
||||
|
||||
from rest_framework.relations import Hyperlink
|
||||
|
||||
from access.models.organization import Organization
|
||||
from access.models.team import Team
|
||||
from access.models.team_user import TeamUsers
|
||||
|
||||
from api.tests.abstract.api_fields import APICommonFields
|
||||
|
||||
|
||||
|
||||
class TeamUserAPI(
|
||||
TestCase,
|
||||
APICommonFields
|
||||
):
|
||||
|
||||
model = TeamUsers
|
||||
|
||||
app_namespace = 'v2'
|
||||
|
||||
url_name = '_api_v2_organization_team_user'
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. Create the object
|
||||
2. create view user
|
||||
3. add user as org manager
|
||||
4. make api request
|
||||
"""
|
||||
|
||||
organization = Organization.objects.create(name='test_org')
|
||||
|
||||
self.organization = organization
|
||||
|
||||
different_organization = Organization.objects.create(name='test_different_organization')
|
||||
|
||||
|
||||
view_permissions = Permission.objects.get(
|
||||
codename = 'view_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
view_team = Team.objects.create(
|
||||
team_name = 'view_team',
|
||||
organization = organization,
|
||||
)
|
||||
|
||||
view_team.permissions.set([view_permissions])
|
||||
|
||||
|
||||
self.view_user = User.objects.create_user(username="test_user_view", password="password")
|
||||
|
||||
|
||||
self.item = TeamUsers.objects.create(
|
||||
team = view_team,
|
||||
user = self.view_user
|
||||
)
|
||||
|
||||
self.url_view_kwargs = {'organization_id': self.organization.id, 'team_id': view_team.id, 'pk': self.item.id}
|
||||
|
||||
self.url_kwargs = {'organization_id': self.organization.id, 'team_id': view_team.id}
|
||||
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-detail', kwargs=self.url_view_kwargs)
|
||||
|
||||
|
||||
client.force_login(self.view_user)
|
||||
response = client.get(url)
|
||||
|
||||
self.api_data = response.data
|
||||
|
||||
|
||||
|
||||
|
||||
def test_api_field_exists_manager(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
manager field must exist
|
||||
"""
|
||||
|
||||
assert 'manager' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_manager(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
manager field must be bool
|
||||
"""
|
||||
|
||||
assert type(self.api_data['manager']) is bool
|
||||
|
||||
|
||||
|
||||
def test_api_field_exists_created(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
created field must exist
|
||||
"""
|
||||
|
||||
assert 'created' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_created(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
created field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['created']) is str
|
||||
|
||||
|
||||
|
||||
def test_api_field_exists_modified(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
modified field must exist
|
||||
"""
|
||||
|
||||
assert 'modified' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_modified(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
modified field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['modified']) is str
|
||||
|
||||
|
||||
|
||||
|
||||
# def test_api_field_exists_permissions(self):
|
||||
# """ Test for existance of API Field
|
||||
|
||||
# permissions field must exist
|
||||
# """
|
||||
|
||||
# assert 'permissions' in self.api_data
|
||||
|
||||
|
||||
# def test_api_field_type_permissions(self):
|
||||
# """ Test for type for API Field
|
||||
|
||||
# url field must be list
|
||||
# """
|
||||
|
||||
# assert type(self.api_data['permissions']) is list
|
||||
|
||||
|
||||
|
||||
# def test_api_field_exists_permissions_id(self):
|
||||
# """ Test for existance of API Field
|
||||
|
||||
# permissions.id field must exist
|
||||
# """
|
||||
|
||||
# assert 'id' in self.api_data['permissions'][0]
|
||||
|
||||
|
||||
# def test_api_field_type_permissions_id(self):
|
||||
# """ Test for type for API Field
|
||||
|
||||
# permissions.id field must be int
|
||||
# """
|
||||
|
||||
# assert type(self.api_data['permissions'][0]['id']) is int
|
||||
|
||||
|
||||
# def test_api_field_exists_permissions_display_name(self):
|
||||
# """ Test for existance of API Field
|
||||
|
||||
# permissions.display_name field must exist
|
||||
# """
|
||||
|
||||
# assert 'display_name' in self.api_data['permissions'][0]
|
||||
|
||||
|
||||
# def test_api_field_type_permissions_display_name(self):
|
||||
# """ Test for type for API Field
|
||||
|
||||
# permissions.display_name field must be str
|
||||
# """
|
||||
|
||||
# assert type(self.api_data['permissions'][0]['display_name']) is str
|
||||
|
||||
|
||||
|
||||
# def test_api_field_exists_permissions_url(self):
|
||||
# """ Test for existance of API Field
|
||||
|
||||
# permissions.url field must exist
|
||||
# """
|
||||
|
||||
# assert 'url' in self.api_data['permissions'][0]
|
||||
|
||||
|
||||
# def test_api_field_type_permissions_url(self):
|
||||
# """ Test for type for API Field
|
||||
|
||||
# permissions.url field must be str
|
||||
# """
|
||||
|
||||
# assert type(self.api_data['permissions'][0]['url']) is Hyperlink
|
@ -0,0 +1,92 @@
|
||||
|
||||
import pytest
|
||||
import unittest
|
||||
import requests
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.test import TestCase, Client
|
||||
|
||||
from access.models import Organization
|
||||
|
||||
from core.models.history import History
|
||||
from core.tests.abstract.history_entry import HistoryEntry
|
||||
from core.tests.abstract.history_entry_child_model import HistoryEntryChildItem
|
||||
|
||||
from access.models import Team, TeamUsers
|
||||
|
||||
|
||||
|
||||
class TeamUsersHistory(TestCase, HistoryEntry, HistoryEntryChildItem):
|
||||
|
||||
model = TeamUsers
|
||||
|
||||
model_name = 'teamusers'
|
||||
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
""" Setup Test """
|
||||
|
||||
organization = Organization.objects.create(name='test_org')
|
||||
|
||||
self.organization = organization
|
||||
|
||||
self.item_parent = Team.objects.create(
|
||||
team_name = 'test_item_' + self.model._meta.model_name,
|
||||
organization = self.organization
|
||||
)
|
||||
|
||||
self.user = User.objects.create(
|
||||
username = 'test_item_' + self.model._meta.model_name,
|
||||
password = 'a random password'
|
||||
)
|
||||
|
||||
self.item_create = self.model.objects.create(
|
||||
user = self.user,
|
||||
team = self.item_parent
|
||||
)
|
||||
|
||||
|
||||
self.history_create = History.objects.get(
|
||||
action = History.Actions.ADD[0],
|
||||
item_pk = self.item_create.pk,
|
||||
item_class = self.model._meta.model_name,
|
||||
)
|
||||
|
||||
self.item_change = self.item_create
|
||||
self.item_change.manager = True
|
||||
self.item_change.save()
|
||||
|
||||
self.field_after_expected_value = '{"manager": true}'
|
||||
|
||||
self.history_change = History.objects.get(
|
||||
action = History.Actions.UPDATE[0],
|
||||
item_pk = self.item_change.pk,
|
||||
item_class = self.model._meta.model_name,
|
||||
)
|
||||
|
||||
|
||||
self.user_delete = User.objects.create(
|
||||
username = 'test_item_delete' + self.model._meta.model_name,
|
||||
password = 'a random password'
|
||||
)
|
||||
|
||||
self.item_delete = self.model.objects.create(
|
||||
user = self.user_delete,
|
||||
team = self.item_parent
|
||||
)
|
||||
|
||||
self.deleted_pk = self.item_delete.pk
|
||||
|
||||
self.item_delete.delete()
|
||||
|
||||
self.history_delete = History.objects.get(
|
||||
action = History.Actions.DELETE[0],
|
||||
item_pk = self.deleted_pk,
|
||||
item_class = self.model._meta.model_name,
|
||||
)
|
||||
|
||||
self.history_delete_children = History.objects.filter(
|
||||
item_parent_pk = self.deleted_pk,
|
||||
item_parent_class = self.item_parent._meta.model_name,
|
||||
)
|
@ -6,9 +6,7 @@ import unittest
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
from access.models.organization import Organization
|
||||
from access.models.team import Team
|
||||
from access.models import Organization, Team
|
||||
|
||||
|
||||
# @pytest.fixture
|
||||
|
@ -1,6 +1,6 @@
|
||||
# from django.conf import settings
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import AnonymousUser, Permission, User
|
||||
from django.contrib.auth.models import AnonymousUser, User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.shortcuts import reverse
|
||||
from django.test import TestCase, Client
|
||||
@ -9,10 +9,7 @@ import pytest
|
||||
import unittest
|
||||
import requests
|
||||
|
||||
from access.models.organization import Organization
|
||||
from access.models.team import Team
|
||||
from access.models.team_user import TeamUsers
|
||||
|
||||
from access.models import Organization, Team, TeamUsers, Permission
|
||||
from access.tests.abstract.model_permissions_organization_manager import OrganizationManagerModelPermissionAdd, OrganizationManagerModelPermissionDelete
|
||||
|
||||
from app.tests.abstract.model_permissions import ModelPermissionsAdd, ModelPermissionsChange, ModelPermissionsDelete
|
||||
|
@ -1,76 +0,0 @@
|
||||
import pytest
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.test import Client, TestCase
|
||||
|
||||
from rest_framework.reverse import reverse
|
||||
|
||||
from access.models.organization import Organization
|
||||
from access.models.team import Team
|
||||
from access.viewsets.team_user import ViewSet
|
||||
|
||||
from api.tests.abstract.viewsets import ViewSetModel
|
||||
|
||||
|
||||
|
||||
class ViewsetCommon(
|
||||
ViewSetModel,
|
||||
):
|
||||
|
||||
viewset = ViewSet
|
||||
|
||||
route_name = 'API:_api_v2_organization_team_user'
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. Create an organization
|
||||
3. create super user
|
||||
"""
|
||||
|
||||
organization = Organization.objects.create(name='test_org')
|
||||
|
||||
self.organization = organization
|
||||
|
||||
self.team = Team.objects.create(
|
||||
organization = self.organization,
|
||||
name = 'team'
|
||||
)
|
||||
|
||||
self.view_user = User.objects.create_user(username="test_view_user", password="password", is_superuser=True)
|
||||
|
||||
self.kwargs = {
|
||||
'organization_id': self.organization.id,
|
||||
'team_id': self.team.id
|
||||
}
|
||||
|
||||
|
||||
|
||||
class TeamUserViewsetList(
|
||||
ViewsetCommon,
|
||||
TestCase,
|
||||
):
|
||||
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. make list request
|
||||
"""
|
||||
|
||||
|
||||
super().setUpTestData()
|
||||
|
||||
|
||||
client = Client()
|
||||
|
||||
url = reverse(
|
||||
self.route_name + '-list',
|
||||
kwargs = self.kwargs
|
||||
)
|
||||
|
||||
client.force_login(self.view_user)
|
||||
|
||||
self.http_options_response_list = client.options(url)
|
@ -3,8 +3,7 @@ import unittest
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
from access.models.tenancy import TenancyManager
|
||||
from access.models.tenancy import TenancyObject
|
||||
from access.models import TenancyObject, TenancyManager
|
||||
|
||||
from core.mixin.history_save import SaveHistory
|
||||
|
||||
@ -92,13 +91,3 @@ class TenancyObjectTests(TestCase):
|
||||
"""
|
||||
|
||||
assert self.item.objects is not None
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="write test")
|
||||
def test_field_not_none_organzation(self):
|
||||
""" Ensure field is set
|
||||
|
||||
Field organization must be defined for all tenancy objects
|
||||
"""
|
||||
|
||||
assert self.item.objects is not None
|
||||
|
@ -1,60 +0,0 @@
|
||||
from django.contrib.auth.models import User
|
||||
from django.shortcuts import reverse
|
||||
from django.test import Client, TestCase
|
||||
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
|
||||
from access.models.organization import Organization
|
||||
|
||||
from api.tests.abstract.viewsets import ViewSetCommon
|
||||
|
||||
from access.viewsets.index import Index
|
||||
|
||||
|
||||
class AccessViewset(
|
||||
TestCase,
|
||||
ViewSetCommon
|
||||
):
|
||||
|
||||
viewset = Index
|
||||
|
||||
route_name = 'API:_api_v2_access_home'
|
||||
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. Create an organization for user
|
||||
3. create super user
|
||||
"""
|
||||
|
||||
organization = Organization.objects.create(name='test_org')
|
||||
|
||||
self.organization = organization
|
||||
|
||||
self.view_user = User.objects.create_user(username="test_user_add", password="password", is_superuser=True)
|
||||
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.route_name + '-list')
|
||||
|
||||
|
||||
client.force_login(self.view_user)
|
||||
self.http_options_response_list = client.options(url)
|
||||
|
||||
self.kwargs = {}
|
||||
|
||||
|
||||
|
||||
def test_view_attr_permission_classes_value(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `permission_classes` must be metadata class `ReactUIMetadata`
|
||||
"""
|
||||
|
||||
view_set = self.viewset()
|
||||
|
||||
assert view_set.permission_classes[0] is IsAuthenticated
|
||||
|
||||
assert len(view_set.permission_classes) == 1
|
@ -4,7 +4,7 @@ from django.utils.decorators import method_decorator
|
||||
from django.views import generic
|
||||
|
||||
from access.mixin import *
|
||||
from access.models.organization import Organization
|
||||
from access.models import *
|
||||
|
||||
from access.forms.organization import OrganizationForm
|
||||
|
||||
|
@ -4,10 +4,8 @@ from django.utils.decorators import method_decorator
|
||||
from django.urls import reverse
|
||||
|
||||
from access.forms.team import TeamForm, TeamFormAdd
|
||||
from access.models import Team, TeamUsers, Organization
|
||||
from access.mixin import *
|
||||
from access.models.organization import Organization
|
||||
from access.models.team import Team
|
||||
from access.models.team_user import TeamUsers
|
||||
|
||||
from core.views.common import AddView, ChangeView, DeleteView
|
||||
|
||||
|
@ -2,13 +2,11 @@ from django.contrib.auth import decorators as auth_decorator
|
||||
from django.urls import reverse
|
||||
|
||||
from access.forms.team_users import TeamUsersForm
|
||||
from access.models.team import Team
|
||||
from access.models.team_user import TeamUsers
|
||||
from access.models import Team, TeamUsers
|
||||
|
||||
from core.views.common import AddView, DeleteView
|
||||
|
||||
|
||||
|
||||
class Add(AddView):
|
||||
|
||||
context_object_name = "teamuser"
|
||||
|
@ -1,30 +0,0 @@
|
||||
from drf_spectacular.utils import extend_schema
|
||||
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.reverse import reverse
|
||||
|
||||
from api.viewsets.common import IndexViewset
|
||||
|
||||
|
||||
|
||||
@extend_schema(exclude = True)
|
||||
class Index(IndexViewset):
|
||||
|
||||
allowed_methods: list = [
|
||||
'GET',
|
||||
'HEAD',
|
||||
'OPTIONS'
|
||||
]
|
||||
|
||||
view_description = "Access Module"
|
||||
|
||||
view_name = "Access"
|
||||
|
||||
|
||||
def list(self, request, pk=None):
|
||||
|
||||
return Response(
|
||||
{
|
||||
"organization": reverse('v2:_api_v2_organization-list', request=request)
|
||||
}
|
||||
)
|
@ -1,93 +0,0 @@
|
||||
from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiResponse
|
||||
|
||||
# THis import only exists so that the migrations can be created
|
||||
from access.models.organization_history import OrganizationHistory # pylint: disable=W0611:unused-import
|
||||
from access.serializers.organization import (
|
||||
Organization,
|
||||
OrganizationModelSerializer,
|
||||
OrganizationViewSerializer
|
||||
)
|
||||
|
||||
from api.viewsets.common import ModelViewSet
|
||||
|
||||
|
||||
|
||||
# @extend_schema(tags=['access'])
|
||||
@extend_schema_view(
|
||||
create=extend_schema(
|
||||
summary = 'Create an orgnaization',
|
||||
description='',
|
||||
responses = {
|
||||
# 200: OpenApiResponse(description='Allready exists', response=OrganizationViewSerializer),
|
||||
201: OpenApiResponse(description='Created', response=OrganizationViewSerializer),
|
||||
# 400: OpenApiResponse(description='Validation failed.'),
|
||||
403: OpenApiResponse(description='User is missing add permissions'),
|
||||
}
|
||||
),
|
||||
destroy = extend_schema(
|
||||
summary = 'Delete an orgnaization',
|
||||
description = '',
|
||||
responses = {
|
||||
204: OpenApiResponse(description=''),
|
||||
403: OpenApiResponse(description='User is missing delete permissions'),
|
||||
}
|
||||
),
|
||||
list = extend_schema(
|
||||
summary = 'Fetch all orgnaizations',
|
||||
description='',
|
||||
responses = {
|
||||
200: OpenApiResponse(description='', response=OrganizationViewSerializer),
|
||||
403: OpenApiResponse(description='User is missing view permissions'),
|
||||
}
|
||||
),
|
||||
retrieve = extend_schema(
|
||||
summary = 'Fetch a single orgnaization',
|
||||
description='',
|
||||
responses = {
|
||||
200: OpenApiResponse(description='', response=OrganizationViewSerializer),
|
||||
403: OpenApiResponse(description='User is missing view permissions'),
|
||||
}
|
||||
),
|
||||
update = extend_schema(exclude = True),
|
||||
partial_update = extend_schema(
|
||||
summary = 'Update an orgnaization',
|
||||
description = '',
|
||||
responses = {
|
||||
200: OpenApiResponse(description='', response=OrganizationViewSerializer),
|
||||
# 201: OpenApiResponse(description='Created', response=OrganizationViewSerializer),
|
||||
# # 400: OpenApiResponse(description='Validation failed.'),
|
||||
403: OpenApiResponse(description='User is missing change permissions'),
|
||||
}
|
||||
),
|
||||
)
|
||||
class ViewSet( ModelViewSet ):
|
||||
|
||||
filterset_fields = [
|
||||
'name',
|
||||
'manager',
|
||||
]
|
||||
|
||||
search_fields = [
|
||||
'name',
|
||||
]
|
||||
|
||||
model = Organization
|
||||
|
||||
view_description = 'Centurion Organizations'
|
||||
|
||||
def get_serializer_class(self):
|
||||
|
||||
if (
|
||||
self.action == 'list'
|
||||
or self.action == 'retrieve'
|
||||
):
|
||||
|
||||
self.serializer_class = globals()[str( self.model._meta.verbose_name) + 'ViewSerializer']
|
||||
|
||||
else:
|
||||
|
||||
self.serializer_class = globals()[str( self.model._meta.verbose_name) + 'ModelSerializer']
|
||||
|
||||
|
||||
return self.serializer_class
|
||||
|
@ -1,60 +0,0 @@
|
||||
from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiResponse
|
||||
|
||||
from access.serializers.organization_notes import (
|
||||
OrganizationNotes,
|
||||
OrganizationNoteModelSerializer,
|
||||
OrganizationNoteViewSerializer,
|
||||
)
|
||||
|
||||
from core.viewsets.model_notes import ModelNoteViewSet
|
||||
|
||||
|
||||
|
||||
@extend_schema_view(
|
||||
create=extend_schema(
|
||||
summary = 'Add a note to an organization',
|
||||
description = '',
|
||||
responses = {
|
||||
201: OpenApiResponse(description='created', response=OrganizationNoteViewSerializer),
|
||||
400: OpenApiResponse(description='Validation failed.'),
|
||||
403: OpenApiResponse(description='User is missing create permissions'),
|
||||
}
|
||||
),
|
||||
destroy = extend_schema(
|
||||
summary = 'Delete an organization note',
|
||||
description = ''
|
||||
),
|
||||
list = extend_schema(
|
||||
summary = 'Fetch all organization notes',
|
||||
description='',
|
||||
),
|
||||
retrieve = extend_schema(
|
||||
summary = 'Fetch a single organization note',
|
||||
description='',
|
||||
),
|
||||
update = extend_schema(exclude = True),
|
||||
partial_update = extend_schema(
|
||||
summary = 'Update an organization note',
|
||||
description = ''
|
||||
),
|
||||
)
|
||||
class ViewSet(ModelNoteViewSet):
|
||||
|
||||
model = OrganizationNotes
|
||||
|
||||
|
||||
def get_serializer_class(self):
|
||||
|
||||
if (
|
||||
self.action == 'list'
|
||||
or self.action == 'retrieve'
|
||||
):
|
||||
|
||||
self.serializer_class = OrganizationNoteViewSerializer
|
||||
|
||||
|
||||
else:
|
||||
|
||||
self.serializer_class = OrganizationNoteModelSerializer
|
||||
|
||||
return self.serializer_class
|
@ -1,192 +0,0 @@
|
||||
from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiParameter, OpenApiResponse
|
||||
|
||||
from access.models.organization import Organization
|
||||
# THis import only exists so that the migrations can be created
|
||||
from access.models.team_history import TeamHistory # pylint: disable=W0611:unused-import
|
||||
from access.serializers.teams import (
|
||||
Team,
|
||||
TeamModelSerializer,
|
||||
TeamViewSerializer
|
||||
)
|
||||
|
||||
from api.viewsets.common import ModelViewSet
|
||||
|
||||
|
||||
|
||||
# @extend_schema(tags=['access'])
|
||||
@extend_schema_view(
|
||||
create=extend_schema(
|
||||
summary = 'Create a team within this organization',
|
||||
description='',
|
||||
parameters = [
|
||||
OpenApiParameter(
|
||||
name = 'organization_id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
],
|
||||
responses = {
|
||||
200: OpenApiResponse(description='Allready exists', response=TeamViewSerializer),
|
||||
201: OpenApiResponse(description='Created', response=TeamViewSerializer),
|
||||
# 400: OpenApiResponse(description='Validation failed.'),
|
||||
403: OpenApiResponse(description='User is missing add permissions'),
|
||||
}
|
||||
),
|
||||
destroy = extend_schema(
|
||||
summary = 'Delete a team from this organization',
|
||||
description = '',
|
||||
parameters = [
|
||||
OpenApiParameter(
|
||||
name = 'id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
OpenApiParameter(
|
||||
name = 'organization_id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
],
|
||||
responses = {
|
||||
204: OpenApiResponse(description=''),
|
||||
403: OpenApiResponse(description='User is missing delete permissions'),
|
||||
}
|
||||
),
|
||||
list = extend_schema(
|
||||
summary = 'Fetch all teams from this organization',
|
||||
description='',
|
||||
parameters = [
|
||||
OpenApiParameter(
|
||||
name = 'organization_id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
],
|
||||
responses = {
|
||||
200: OpenApiResponse(description='', response=TeamViewSerializer),
|
||||
403: OpenApiResponse(description='User is missing view permissions'),
|
||||
}
|
||||
),
|
||||
retrieve = extend_schema(
|
||||
summary = 'Fetch a single team from this organization',
|
||||
description='',
|
||||
parameters = [
|
||||
OpenApiParameter(
|
||||
name = 'id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
OpenApiParameter(
|
||||
name = 'organization_id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
],
|
||||
responses = {
|
||||
200: OpenApiResponse(description='', response=TeamViewSerializer),
|
||||
403: OpenApiResponse(description='User is missing view permissions'),
|
||||
}
|
||||
),
|
||||
update = extend_schema(exclude = True),
|
||||
partial_update = extend_schema(
|
||||
summary = 'Update a team within this organization',
|
||||
description = '',
|
||||
parameters = [
|
||||
OpenApiParameter(
|
||||
name = 'id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
OpenApiParameter(
|
||||
name = 'organization_id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
],
|
||||
responses = {
|
||||
200: OpenApiResponse(description='', response=TeamViewSerializer),
|
||||
# 201: OpenApiResponse(description='Created', response=OrganizationViewSerializer),
|
||||
# # 400: OpenApiResponse(description='Validation failed.'),
|
||||
403: OpenApiResponse(description='User is missing change permissions'),
|
||||
}
|
||||
),
|
||||
)
|
||||
class ViewSet( ModelViewSet ):
|
||||
|
||||
filterset_fields = [
|
||||
'team_name',
|
||||
]
|
||||
|
||||
search_fields = [
|
||||
'team_name',
|
||||
]
|
||||
|
||||
model = Team
|
||||
|
||||
view_description = 'Teams belonging to a single organization'
|
||||
|
||||
|
||||
def get_back_url(self) -> str:
|
||||
|
||||
if(
|
||||
getattr(self, '_back_url', None) is None
|
||||
):
|
||||
|
||||
return_model = Organization.objects.get(
|
||||
pk = self.kwargs['organization_id']
|
||||
)
|
||||
|
||||
self._back_url = str(
|
||||
return_model.get_url( self.request )
|
||||
)
|
||||
|
||||
return self._back_url
|
||||
|
||||
|
||||
def get_queryset(self):
|
||||
|
||||
if self.queryset is not None:
|
||||
|
||||
return self.queryset
|
||||
|
||||
queryset = super().get_queryset()
|
||||
|
||||
queryset = queryset.filter(organization_id=self.kwargs['organization_id'])
|
||||
|
||||
self.queryset = queryset
|
||||
|
||||
return self.queryset
|
||||
|
||||
def get_serializer_class(self):
|
||||
|
||||
if (
|
||||
self.action == 'list'
|
||||
or self.action == 'retrieve'
|
||||
):
|
||||
|
||||
self.serializer_class = globals()[str( self.model._meta.verbose_name) + 'ViewSerializer']
|
||||
|
||||
else:
|
||||
|
||||
self.serializer_class = globals()[str( self.model._meta.verbose_name) + 'ModelSerializer']
|
||||
|
||||
return self.serializer_class
|
||||
|
||||
|
||||
def get_return_url(self) -> str:
|
||||
|
||||
if getattr(self, '_get_return_url', None):
|
||||
|
||||
return self._get_return_url
|
||||
|
||||
if self.kwargs.get('pk', None) is None:
|
||||
|
||||
return_model = Organization.objects.get(
|
||||
pk = self.kwargs['organization_id']
|
||||
)
|
||||
|
||||
self._get_return_url = return_model.get_url( self.request )
|
||||
|
||||
return self._get_return_url
|
||||
|
||||
return None
|
@ -1,60 +0,0 @@
|
||||
from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiResponse
|
||||
|
||||
from access.serializers.team_notes import (
|
||||
TeamNotes,
|
||||
TeamNoteModelSerializer,
|
||||
TeamNoteViewSerializer,
|
||||
)
|
||||
|
||||
from core.viewsets.model_notes import ModelNoteViewSet
|
||||
|
||||
|
||||
|
||||
@extend_schema_view(
|
||||
create=extend_schema(
|
||||
summary = 'Add a note to a Team',
|
||||
description = '',
|
||||
responses = {
|
||||
201: OpenApiResponse(description='created', response=TeamNoteViewSerializer),
|
||||
400: OpenApiResponse(description='Validation failed.'),
|
||||
403: OpenApiResponse(description='User is missing create permissions'),
|
||||
}
|
||||
),
|
||||
destroy = extend_schema(
|
||||
summary = 'Delete a team note',
|
||||
description = ''
|
||||
),
|
||||
list = extend_schema(
|
||||
summary = 'Fetch all team notes',
|
||||
description='',
|
||||
),
|
||||
retrieve = extend_schema(
|
||||
summary = 'Fetch a single team note',
|
||||
description='',
|
||||
),
|
||||
update = extend_schema(exclude = True),
|
||||
partial_update = extend_schema(
|
||||
summary = 'Update a team note',
|
||||
description = ''
|
||||
),
|
||||
)
|
||||
class ViewSet(ModelNoteViewSet):
|
||||
|
||||
model = TeamNotes
|
||||
|
||||
|
||||
def get_serializer_class(self):
|
||||
|
||||
if (
|
||||
self.action == 'list'
|
||||
or self.action == 'retrieve'
|
||||
):
|
||||
|
||||
self.serializer_class = TeamNoteViewSerializer
|
||||
|
||||
|
||||
else:
|
||||
|
||||
self.serializer_class = TeamNoteModelSerializer
|
||||
|
||||
return self.serializer_class
|
@ -1,220 +0,0 @@
|
||||
from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiParameter, OpenApiResponse
|
||||
|
||||
from access.models.team import Team
|
||||
|
||||
from access.serializers.team_user import (
|
||||
TeamUsers,
|
||||
TeamUserModelSerializer,
|
||||
TeamUserViewSerializer
|
||||
)
|
||||
|
||||
from api.viewsets.common import ModelViewSet
|
||||
|
||||
|
||||
|
||||
@extend_schema_view(
|
||||
create=extend_schema(
|
||||
summary = 'Create a user within this team',
|
||||
description='',
|
||||
parameters = [
|
||||
OpenApiParameter(
|
||||
name = 'organization_id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
OpenApiParameter(
|
||||
name = 'team_id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
],
|
||||
responses = {
|
||||
# 200: OpenApiResponse(description='Allready exists', response=TeamUserViewSerializer),
|
||||
201: OpenApiResponse(description='Created', response=TeamUserViewSerializer),
|
||||
# 400: OpenApiResponse(description='Validation failed.'),
|
||||
403: OpenApiResponse(description='User is missing add permissions'),
|
||||
}
|
||||
),
|
||||
destroy = extend_schema(
|
||||
summary = 'Delete a user from this team',
|
||||
description = '',
|
||||
parameters = [
|
||||
OpenApiParameter(
|
||||
name = 'id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
OpenApiParameter(
|
||||
name = 'organization_id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
OpenApiParameter(
|
||||
name = 'team_id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
],
|
||||
responses = {
|
||||
204: OpenApiResponse(description=''),
|
||||
403: OpenApiResponse(description='User is missing delete permissions'),
|
||||
}
|
||||
),
|
||||
list = extend_schema(
|
||||
summary = 'Fetch all users from this team',
|
||||
description='',
|
||||
parameters = [
|
||||
OpenApiParameter(
|
||||
name = 'organization_id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
OpenApiParameter(
|
||||
name = 'team_id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
],
|
||||
responses = {
|
||||
200: OpenApiResponse(description='', response=TeamUserViewSerializer),
|
||||
403: OpenApiResponse(description='User is missing view permissions'),
|
||||
}
|
||||
),
|
||||
retrieve = extend_schema(
|
||||
summary = 'Fetch a single user from this team',
|
||||
description='',
|
||||
parameters = [
|
||||
OpenApiParameter(
|
||||
name = 'id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
OpenApiParameter(
|
||||
name = 'organization_id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
OpenApiParameter(
|
||||
name = 'team_id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
],
|
||||
responses = {
|
||||
200: OpenApiResponse(description='', response=TeamUserViewSerializer),
|
||||
403: OpenApiResponse(description='User is missing view permissions'),
|
||||
}
|
||||
),
|
||||
update = extend_schema(exclude = True),
|
||||
partial_update = extend_schema(
|
||||
summary = 'Update a user within this team',
|
||||
description = '',
|
||||
parameters = [
|
||||
OpenApiParameter(
|
||||
name = 'id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
OpenApiParameter(
|
||||
name = 'organization_id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
OpenApiParameter(
|
||||
name = 'team_id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
],
|
||||
responses = {
|
||||
200: OpenApiResponse(description='', response=TeamUserViewSerializer),
|
||||
# 201: OpenApiResponse(description='Created', response=OrganizationViewSerializer),
|
||||
# # 400: OpenApiResponse(description='Validation failed.'),
|
||||
403: OpenApiResponse(description='User is missing change permissions'),
|
||||
}
|
||||
),
|
||||
)
|
||||
class ViewSet( ModelViewSet ):
|
||||
|
||||
filterset_fields = [
|
||||
'manager',
|
||||
'team__organization',
|
||||
]
|
||||
|
||||
search_fields = []
|
||||
|
||||
model = TeamUsers
|
||||
|
||||
parent_model = Team
|
||||
|
||||
parent_model_pk_kwarg = 'team_id'
|
||||
|
||||
view_description = 'Users belonging to a single team'
|
||||
|
||||
|
||||
def get_back_url(self) -> str:
|
||||
|
||||
if(
|
||||
getattr(self, '_back_url', None) is None
|
||||
):
|
||||
|
||||
return_model =Team.objects.get(
|
||||
pk = self.kwargs['team_id']
|
||||
)
|
||||
|
||||
self._back_url = str(
|
||||
return_model.get_url( self.request )
|
||||
)
|
||||
|
||||
return self._back_url
|
||||
|
||||
|
||||
def get_queryset(self):
|
||||
|
||||
if self.queryset is not None:
|
||||
|
||||
return self.queryset
|
||||
|
||||
self.queryset = super().get_queryset()
|
||||
|
||||
self.queryset = self.queryset.filter(
|
||||
team_id = self.kwargs['team_id']
|
||||
)
|
||||
|
||||
return self.queryset
|
||||
|
||||
|
||||
def get_serializer_class(self):
|
||||
|
||||
if (
|
||||
self.action == 'list'
|
||||
or self.action == 'retrieve'
|
||||
):
|
||||
|
||||
self.serializer_class = globals()[str( self.model._meta.verbose_name).replace(' ' , '') + 'ViewSerializer']
|
||||
|
||||
else:
|
||||
|
||||
self.serializer_class = globals()[str( self.model._meta.verbose_name).replace(' ' , '') + 'ModelSerializer']
|
||||
|
||||
|
||||
return self.serializer_class
|
||||
|
||||
|
||||
def get_return_url(self):
|
||||
|
||||
if getattr(self, '_get_return_url', None):
|
||||
|
||||
return self._get_return_url
|
||||
|
||||
if self.kwargs.get('pk', None) is None:
|
||||
|
||||
return_model = Team.objects.get(
|
||||
pk = self.kwargs['team_id']
|
||||
)
|
||||
|
||||
self._get_return_url = return_model.get_url( self.request )
|
||||
|
||||
return self._get_return_url
|
||||
|
||||
return None
|
@ -5,20 +5,6 @@ from rest_framework.authentication import BaseAuthentication, get_authorization_
|
||||
|
||||
from api.models.tokens import AuthToken
|
||||
|
||||
# scheme.py
|
||||
from drf_spectacular.extensions import OpenApiAuthenticationExtension
|
||||
|
||||
class TokenScheme(OpenApiAuthenticationExtension):
|
||||
target_class = "api.auth.TokenAuthentication"
|
||||
name = "TokenAuthentication"
|
||||
|
||||
def get_security_definition(self, auto_schema):
|
||||
return {
|
||||
"type": "apiKey",
|
||||
"in": "header",
|
||||
"name": "Token Authorization",
|
||||
"description": "Token-based authentication with required prefix 'Token'",
|
||||
}
|
||||
|
||||
|
||||
class TokenAuthentication(BaseAuthentication):
|
||||
|
@ -1,8 +0,0 @@
|
||||
from rest_framework.exceptions import APIException
|
||||
from rest_framework import status
|
||||
|
||||
|
||||
class UnknownTicketType(APIException):
|
||||
status_code = status.HTTP_400_BAD_REQUEST
|
||||
default_detail = 'Unable to determin the ticket type.'
|
||||
default_code = 'unknown_ticket_type'
|
@ -32,7 +32,7 @@ class AuthTokenForm(CommonModelForm):
|
||||
|
||||
if self.prefix + '-gen_token' not in self.data:
|
||||
|
||||
generated_token = self.instance.generate
|
||||
generated_token = self.instance.generate()
|
||||
|
||||
else:
|
||||
|
||||
|
@ -1,41 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-12-06 06:47
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('api', '0001_initial'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='authtoken',
|
||||
name='expires',
|
||||
field=models.DateTimeField(help_text='When this token expires', verbose_name='Expiry Date'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='authtoken',
|
||||
name='id',
|
||||
field=models.AutoField(help_text='ID of this token', primary_key=True, serialize=False, unique=True, verbose_name='ID'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='authtoken',
|
||||
name='note',
|
||||
field=models.CharField(blank=True, default=None, help_text='A note about this token', max_length=50, null=True, verbose_name='Note'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='authtoken',
|
||||
name='token',
|
||||
field=models.CharField(db_index=True, help_text='The authorization token', max_length=64, unique=True, verbose_name='Auth Token'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='authtoken',
|
||||
name='user',
|
||||
field=models.ForeignKey(help_text='User this token belongs to', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Owner'),
|
||||
),
|
||||
]
|
@ -1,17 +0,0 @@
|
||||
# Generated by Django 5.1.5 on 2025-02-28 15:12
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('api', '0002_alter_authtoken_expires_alter_authtoken_id_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='authtoken',
|
||||
options={'ordering': ['expires'], 'verbose_name': 'Auth Token', 'verbose_name_plural': 'Auth Tokens'},
|
||||
),
|
||||
]
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user