Compare commits

...

70 commits
0.1.0 ... main

Author SHA1 Message Date
Jan Dittberner 998c3d220f Release version 0.7.0
- update changelog
- bump version
2023-05-07 21:45:23 +02:00
Jan Dittberner f524c08eff Add gvacommon dependency 2023-05-07 13:52:21 +02:00
Jan Dittberner afae6411c7 Update changelog 2023-05-02 17:43:43 +02:00
Jan Dittberner a684139749 Update dependencies 2023-05-02 17:42:51 +02:00
Jan Dittberner e197413127 Add TZ for consistent Celery timestamps 2023-02-20 15:42:02 +01:00
Jan Dittberner 46e3db3a89 Improve docker setup
- add entrypoint script
- add .dockerignore
- fix shellcheck warnings in gvafile.sh
- migrate to Celery 5 settings
- downgrade importlib-metadata for Celery compatibility
2023-02-20 13:54:23 +01:00
Jan Dittberner a04d5b754f Unify with gvaldap 2023-02-19 18:09:33 +01:00
Jan Dittberner f97fb6f700 Switch from pipenv to poetry 2023-02-19 17:56:11 +01:00
Jan Dittberner f999e65a10 Release 0.6.0
- use Pipenv for dependency management
 - ensure proper task names for Python 3
 - update Vagrant setup
 - add Docker setup
 -----BEGIN PGP SIGNATURE-----
 
 iQFIBAABCgAyFiEEKHuXKkUYdvdO9493DXkdyNc3wdkFAl6QRXIUHGphbkBkaXR0
 YmVybmVyLmluZm8ACgkQDXkdyNc3wdli3wgA2OwEeQfzlNk1woZcwPiMuAIgeJGr
 Rd47b56rdxZAxz4C6c8tQucA8+6R3s/mVcvAxZJcKIFm+Gs59H8TzwWfQw22S3PF
 X4jmV8NdFG0o3VPY2Vn9u02xWtgOHglajjFaWKe3Odpsotc/mMnkOVc2vpjrwXfE
 E4rnV78Tm9KGsHA/c2J9s9Ekg1QzzO1oiS4e5kC1xWuNLTxivMOsNXGbrtaM9OfZ
 PkqCEf2hw4C6pVZhTIftlaZ8WBWe+nJS6O4CIGi1HbcZ8QdS6G2zoUc/rTyqEI6m
 63gQl+Dk7Q2J2g8oH8gwfTldoMgElrGA/5jDR8vYWONEWytcelBD0XOmzQ==
 =FQ9d
 -----END PGP SIGNATURE-----

Merge tag '0.6.0'

Release 0.6.0

- use Pipenv for dependency management
- ensure proper task names for Python 3
- update Vagrant setup
- add Docker setup
2020-04-10 12:07:51 +02:00
Jan Dittberner 8898c1506e Merge branch 'release/0.6.0' into production 2020-04-10 12:06:52 +02:00
Jan Dittberner 94705f26bd Add release version to changelog 2020-04-10 12:05:55 +02:00
Jan Dittberner 7911df39fa Update project page URL in README 2020-04-10 12:04:12 +02:00
Jan Dittberner 4ad9b32f7e Update dependencies 2020-04-10 12:02:58 +02:00
Jan Dittberner c4e7e8c497 Unify celery config with gvaldap and gvaweb 2020-03-04 18:42:37 +01:00
Jan Dittberner aa2b87682f Add Docker setup for lightweight local testing 2020-03-04 17:19:14 +01:00
Jan Dittberner f2db400ed8 Update Vagrant setup to Debian Buster and libvirt 2020-03-04 17:06:14 +01:00
Jan Dittberner adae20cc7d Update documentation
Bump copyright years and document that we use systemd instead of
supervisord to run the celery worker.
2020-03-04 16:55:38 +01:00
Jan Dittberner 26aae0173d Refactor fileservertasks into top level module
This change moves the fileservertasks module to the top level to allow
keeping the same task names when running in a Python 3 environment.
2020-03-04 16:53:50 +01:00
Jan Dittberner cbd8e37e8e Switch dependency management to Pipenv
Fixes #2
2020-03-04 16:52:11 +01:00
Jan Dittberner 5361f9e82e Release 0.5.1 for Stretch
-----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEEKHuXKkUYdvdO9493DXkdyNc3wdkFAl10whQACgkQDXkdyNc3
 wdk6OQf/d/y8T8yG0fgTEUcianYcxuT1CYEMEdUTHt77SgYALc75yMfqvjj3FpRO
 rN/PeRc3LOXN9TaFxx2qXW9qPmATWBQwSRe0AmIreXSLctH/voR0mRdz114K1b1i
 hP5cTW6mztBDLZHLAmxcS2ej8Skwgz1kKxxlF9+f/gnP4n3V7SAdDuMcXlkvSH8p
 K15CxoAcktRfwM4odOrHk9g+BVRse3uxk2Z8ozy9vpL53qxRPUwOSCu2adbgpshB
 DrKA61TMsXV9B+CY+Lat0wQHVQAjJhTWPe/oz85/4sfCv7tD1430DZpeCwLeEhqE
 h0YvC1Xar19qijjhGD1E8C0AxcldwA==
 =yS5Q
 -----END PGP SIGNATURE-----

Merge tag '0.5.1'

Release 0.5.1 for Stretch

* tag '0.5.1':
  Update dependencies
2019-09-08 10:57:26 +02:00
Jan Dittberner e15c3a1fd9 Merge branch 'hotfix/0.5.1' into production
* hotfix/0.5.1:
  Update dependencies
2019-09-08 10:55:39 +02:00
Jan Dittberner b4ba90a7c9 Update dependencies 2019-09-08 10:55:20 +02:00
Jan Dittberner b71fbf78ff Change gvacommon dependency URL 2019-06-26 22:50:28 +02:00
Jan Dittberner 821e84f608 Protect /etc/salt/grains
Make sure that the permissions of /etc/salt/grains only allow access for the
root user.
2016-09-25 17:27:42 +02:00
Jan Dittberner d5a275dbbe Log directory path for exceptions
Improve the log message of setup_file_sftp_userdir to include the path that
has been attempted to create.
2016-09-25 16:33:02 +02:00
Jan Dittberner 4eb3a1c3f3 Ignore PyCharm files 2016-09-25 01:10:45 +02:00
Jan Dittberner 8d14826169 Improve Vagrant setup
Use debian/contrib-jessie64 base box, install python-cryptography before
saltstack bootstrap.
2016-09-25 01:10:17 +02:00
Jan Dittberner 88d8a29a14 Improve documentation 2016-09-24 21:59:01 +02:00
Jan Dittberner 20d5a86a71 Add code QA tools to local requirements 2016-02-07 22:45:07 +01:00
Jan Dittberner cbb695137c Add redis result backend support
This commit changes the Celery result backend to redis as it has already
been done for the webinterface and gvaldap parts of gnuviechadmin.
2016-02-07 22:43:42 +01:00
Jan Dittberner f64a87714d Update dependency versions 2016-02-06 21:34:27 +01:00
Jan Dittberner 62e1e8bad7 Derive local requirements from test requirements 2016-02-06 20:50:06 +01:00
Jan Dittberner dee8aa71a4 Replace internal gvacommon copy with external requirement 2016-02-06 20:48:17 +01:00
Jan Dittberner 2b74fe99e0 Initial vagrant setup 2016-02-06 14:25:22 +01:00
Jan Dittberner 2807ae5451 release 0.5.0
- add new task set_file_ssh_authorized_keys
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQEcBAABCgAGBQJUyq10AAoJEA15HcjXN8HZQ0gH/A02ECdKVc1RiaZHNa8Faxnw
 bKfXGLRbVZkpSmYV1oPuFUSlInGtHft0zJ3yBfM7pJvftuDVhROgFyphkl2y5P/d
 7exUnt2Dc36TcLJ5lDRRf81iXPql9lT8xpKmD5a4bQkrekxMrqqcMob3OsKNLbWu
 Ts08y9DSJFlEpr85qV96WtzJPjR7yriFtfBcsI+096mTUtn2Lc3aEmgYXjajHW9u
 Iia57ip6ZdvKTs6MAd0JDfl1YBrUaeu5uGzpQP44wkepfdNP4jrZUGWRooNoNP5G
 WR1ok54o9eKYJ06a1HWft8uuAJxV4X/i9j46a5+xbHX3ZsixQwppzhHqoFZ2RVg=
 =4XVe
 -----END PGP SIGNATURE-----

Merge tag '0.5.0'

release 0.5.0

- add new task set_file_ssh_authorized_keys

* tag '0.5.0':
  update docs version, add release to changelog
2015-01-29 23:00:27 +01:00
Jan Dittberner 420d34dfea Merge branch 'release/0.5.0' into production
* release/0.5.0:
  update docs version, add release to changelog
  add missing - to install parameter
  add return value as documented
  add logging for set_file_ssh_authorized_keys
  add new task set_file_ssh_authorized_keys
  improved logging in fileservertasks.tasks
2015-01-29 22:59:10 +01:00
Jan Dittberner dd866381be update docs version, add release to changelog 2015-01-29 22:58:10 +01:00
Jan Dittberner 92beb28816 add missing - to install parameter 2015-01-29 22:55:45 +01:00
Jan Dittberner 0db08db831 add return value as documented 2015-01-29 22:44:10 +01:00
Jan Dittberner 7dbb51da1a add logging for set_file_ssh_authorized_keys 2015-01-29 22:43:00 +01:00
Jan Dittberner 307ccf4307 add new task set_file_ssh_authorized_keys 2015-01-29 22:23:11 +01:00
Jan Dittberner 4e144fb49d improved logging in fileservertasks.tasks 2015-01-29 20:24:46 +01:00
Jan Dittberner 735a468806 release 0.4.0
This release adds website hierarchy tasks.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQEcBAABCgAGBQJUxnHYAAoJEA15HcjXN8HZio4IAMh/hpSs5eBmXTUwUAP8VEfF
 IJdbOEIxv+EShmMb82vmfKW+QyGcQl57RhDKVW3o9EvaqlAF06VQ0cML3aZrOClY
 hV5gvcVxoe+nKxvOJrf7p//eAgkoDPEU0PCWu1ttyoFsUBeUPqRMVUjy4bl8alNY
 7913GrkH7i1hhe73/wDmKIhD7qQdGsA3G80PL8X9N8ITW0yirXpZkfUAsp4s1Vkc
 agsfhqVshQCAV7G4iBxWYkFQ+u2RozoK56G3wrcelz6UGK+vKmcCqbS8WIKq4TnJ
 x/SD6Ngo2kABrX0dDJCUDoBnFPEPquVnOGuhyJzFCtL9wuNcN0+6vwZF8fJSGGg=
 =dXnA
 -----END PGP SIGNATURE-----

Merge tag '0.4.0'

release 0.4.0

This release adds website hierarchy tasks.

* tag '0.4.0':
  add release version in changelog, update conf.py
2015-01-26 17:57:00 +01:00
Jan Dittberner 4d2da5229c Merge branch 'release/0.4.0' into production
* release/0.4.0:
  add release version in changelog, update conf.py
  implement new tasks for website hierarchy
  remove Django dependencies as they are not used
  add new route 'web' for web server configuration
  implement viewmixins.StaffOrSelfLoginRequiredMixin
2015-01-26 17:56:27 +01:00
Jan Dittberner 65d1a7090c add release version in changelog, update conf.py 2015-01-26 17:56:15 +01:00
Jan Dittberner 0752570078 Merge commit 'd31c1d0fbf02bde5d4dc2be24762d872da64935f'
* commit 'd31c1d0fbf02bde5d4dc2be24762d872da64935f':
  add new route 'web' for web server configuration
  implement viewmixins.StaffOrSelfLoginRequiredMixin
2015-01-26 17:54:56 +01:00
Jan Dittberner 5955f3c38c implement new tasks for website hierarchy
- implement tasks fileservertasks.tasks.create_file_website_hierarchy and
  fileservertasks.tasks.delete_file_website_hierarchy
2015-01-26 17:32:24 +01:00
Jan Dittberner ea3164b762 remove Django dependencies as they are not used
- move settings to gvafile.settings
- use gvafile.settings directly without Django support
- initialize Celery without Django
- remove Django requirements
2015-01-26 17:07:31 +01:00
Jan Dittberner d31c1d0fbf add new route 'web' for web server configuration 2015-01-26 15:43:05 +01:00
Jan Dittberner 3c4d34cce5 implement viewmixins.StaffOrSelfLoginRequiredMixin 2015-01-24 15:38:08 +01:00
Jan Dittberner af5fb4c773 release 0.3.0
-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQEcBAABCgAGBQJUvXZvAAoJEA15HcjXN8HZlloH/0fnhCK/J0aLvvTZDhWxJVLn
 zS24II5EMGUj/9uf+tIVsu/cLUdr6O+QZbTEydLq1ceW2SDspokAv7jX3P67of8d
 RJiEJ903Clu/Zgab9kZlQec4J3azyw6eRAByIdaSGxTEnN1MUfWt5cECMQw03nTy
 Q609D8IBhU808l3oc7T57PtUXq1tD0lIUjTMctPFc1I5YnAP54Ne5hDPA8SS5/6H
 wtT7RTbSLjOOFD38DzhaCE4GS2pU8ybpjuGCFi/fMMIrm24hofAqiAkzmlBbn7xi
 U+EgaT2Q1JxytG7aiQDtBU4mJ7SsfK0Frehwv6V8O+hGojXQqtXmrvtzMj3rWT8=
 =noVz
 -----END PGP SIGNATURE-----

Merge tag '0.3.0'

release 0.3.0

* tag '0.3.0':
  update changelog, add release version
2015-01-19 22:26:12 +01:00
Jan Dittberner 464a5ba47f Merge branch 'release/0.3.0' into production
* release/0.3.0:
  update changelog, add release version
  adapt documentation to changed package structure
  move tasks from osusers to fileservertasks.tasks
  switch to gvacommon.celeryrouters.GvaRouter
  fix typo
  unify routers, add support for mysql and pgsql tasks
  define celery timezone, restrict celery content to json
2015-01-19 22:25:55 +01:00
Jan Dittberner 7f4247bf76 update changelog, add release version 2015-01-19 22:25:42 +01:00
Jan Dittberner 70977c5a61 adapt documentation to changed package structure 2015-01-19 22:05:57 +01:00
Jan Dittberner cfc023cb14 move tasks from osusers to fileservertasks.tasks 2015-01-19 22:00:48 +01:00
Jan Dittberner 800fff8908 switch to gvacommon.celeryrouters.GvaRouter 2015-01-01 22:38:49 +01:00
Jan Dittberner 7c65fc3dc7 Merge commit '546441d49920caf43eb7d33976c2d71ae85f16d5'
* commit '546441d49920caf43eb7d33976c2d71ae85f16d5':
  unify routers, add support for mysql and pgsql tasks
2015-01-01 22:37:50 +01:00
Jan Dittberner 64688933fa fix typo 2015-01-01 22:37:39 +01:00
Jan Dittberner 546441d499 unify routers, add support for mysql and pgsql tasks
- add new celeryrouters.GvaRouter
- remove LdapRouter and FileRouter
2015-01-01 22:32:37 +01:00
Jan Dittberner 33b28c5598 define celery timezone, restrict celery content to json 2014-12-29 12:59:14 +01:00
Jan Dittberner 9d370cea60 Merge tag '0.2.0'
release 0.2.0

* tag '0.2.0':
  set version number, make sure bug fixes are included
2014-12-27 15:31:02 +01:00
Jan Dittberner 99adf6ffaf Merge branch 'release/0.2.0' into production
* release/0.2.0:
  set version number, make sure bug fixes are included
  restrict permissions of mailbox base directories to be read only
  add new mailbox handling tasks
  move celery routers to gvacommon
  initial version
  sftp directories are now owned by root instead of user
2014-12-27 15:30:49 +01:00
Jan Dittberner 71221257b0 set version number, make sure bug fixes are included 2014-12-27 15:30:15 +01:00
Jan Dittberner 7aeefd9a80 restrict permissions of mailbox base directories to be read only 2014-12-27 14:50:00 +01:00
Jan Dittberner a285133f75 add new mailbox handling tasks 2014-12-27 14:48:18 +01:00
Jan Dittberner 3bdf634703 move celery routers to gvacommon 2014-12-27 13:59:38 +01:00
Jan Dittberner 92e3212fe1 Add 'gvafile/gvacommon/' from commit 'e29646a94714d516d52272fc83d9a15bbc21e488'
git-subtree-dir: gvafile/gvacommon
git-subtree-mainline: b362693911
git-subtree-split: e29646a947
2014-12-27 13:56:49 +01:00
Jan Dittberner e29646a947 initial version 2014-12-27 13:52:45 +01:00
Jan Dittberner b362693911 sftp directories are now owned by root instead of user 2014-12-26 23:58:22 +01:00
Jan Dittberner 6a8a66f7d8 Merge tag '0.1.0'
release 0.1.0

* tag '0.1.0':
  define version number, document changes
2014-12-26 15:24:25 +01:00
42 changed files with 2055 additions and 892 deletions

17
.dockerignore Normal file
View file

@ -0,0 +1,17 @@
**/*.pyc
**/.*.swp
**/.coverage
**/__pycache__
.dockerignore
.env
.envrc
.git
.gitignore
.idea
.vagrant
Dockerfile
Vagrantfile
change-vmdebootstrap-default-dhcp.sh
coverage-report
docs
salt

3
.gitignore vendored
View file

@ -41,3 +41,6 @@ Desktop.ini
.ropeproject
_build/
*.sqlite3
.vagrant/
.idea/
.env

64
Dockerfile Normal file
View file

@ -0,0 +1,64 @@
ARG DEBIAN_RELEASE=buster
FROM debian:$DEBIAN_RELEASE AS builder
ARG GVAAPP=gvafile
ARG POETRY_VERSION=1.3.1
ENV LC_ALL=C.UTF-8
ENV LANG=C.UTF-8
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
build-essential \
curl \
git \
python3-dev \
python3-setuptools \
python3-virtualenv \
python3-wheel
RUN curl -sSL https://install.python-poetry.org | POETRY_HOME=/root/.local POETRY_VERSION=$POETRY_VERSION python3 - \
&& /root/.local/bin/poetry config virtualenvs.in-project true
WORKDIR /srv/$GVAAPP
COPY poetry.lock pyproject.toml /srv/$GVAAPP/
RUN /root/.local/bin/poetry install --only=main
FROM debian:$DEBIAN_RELEASE
LABEL maintainer="Jan Dittberner <jan@dittberner.info>"
ENV LC_ALL=C.UTF-8
ENV LANG=C.UTF-8
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
ca-certificates \
dumb-init \
gettext \
python3 \
python3-pip \
python3-wheel \
&& apt-get clean \
&& rm -rf /var/cache/apt/archives /var/lib/apt/lists/*
ARG GVAAPP=gvafile
ARG GVAGID=2000
ARG GVAUID=2000
WORKDIR /srv/$GVAAPP
RUN addgroup --gid $GVAGID $GVAAPP ; \
adduser --home /home/$GVAAPP --shell /bin/bash --uid $GVAUID --gid $GVAGID --disabled-password \
--gecos "User for gnuviechadmin component $GVAAPP" $GVAAPP
COPY --chown=$GVAAPP:$GVAAPP --from=builder /srv/$GVAAPP/.venv /srv/$GVAAPP/.venv
VOLUME /srv/$GVAAPP/$GVAAPP
COPY ${GVAAPP}.sh entrypoint.sh /srv/
ENTRYPOINT ["dumb-init", "/srv/entrypoint.sh"]

View file

@ -1,4 +1,4 @@
Copyright (c) 2014 Jan Dittberner
Copyright (c) 2014-2020 Jan Dittberner
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation

View file

@ -10,4 +10,4 @@ customer management at `Jan Dittberner IT-Consulting & -Solutions
Read the :doc:`Installation instructions <install>` to get started locally.
The project page for gvafile is at http://dev.gnuviech-server.de/gvafile.
The project page for gvafile is at http://git.dittberner.info/gnuviech/gvafile.

29
Vagrantfile vendored Normal file
View file

@ -0,0 +1,29 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure(2) do |config|
config.vm.box = "debian/buster64"
config.vm.hostname = "gvafile.local"
config.vm.network "private_network", ip: "172.16.3.4"
config.vm.synced_folder "../gvasalt/states/", "/srv/salt/"
config.vm.synced_folder "../gvasalt/pillar/", "/srv/pillar/"
config.vm.provider :libvirt do |libvirt|
libvirt.memory = 1024
end
config.vm.provision :shell, path: "change-vmdebootstrap-default-dhcp.sh"
config.vm.provision :salt do |salt|
salt.bootstrap_options = "-x python3"
salt.minion_id = "gvafile"
salt.masterless = true
salt.run_highstate = true
salt.verbose = true
salt.colorize = true
salt.log_level = "warning"
salt.grains_config = "salt/grains"
end
end

View file

@ -0,0 +1,15 @@
#!/bin/sh
set -e
debootstrap_network=/etc/systemd/network/99-dhcp.network
if grep -q '^Name=\\*' "${debootstrap_network}"; then
primary_nic=$(ls -1 /sys/class/net | grep -v lo |sort | head -1)
sed -i "s/^Name=e\\*/Name=${primary_nic}/" \
"${debootstrap_network}"
systemctl restart systemd-networkd.service
echo "Changed systemd network configuration"
else
echo "Systemd network configuration has already been changed"
fi

View file

@ -1,6 +1,42 @@
Changelog
=========
* :release:`0.7.0 <2023-05-07>`
* :support:`-` switch from Pipenv to Poetry
* :release:`0.6.0 <2020-04-10>`
* :support:`-` add Docker setup for lightweight local testing
* :support:`-` update Vagrant setup to libvirt and Debian Buster
* :support:`-` move fileservertasks to top level to keep the task names when
using Python 3
* :support:`2` use Pipenv for dependency management
* :release:`0.5.1 <2019-09-08>`
* :bug:`-` change dependency URLs
* :release:`0.5.0 <2015-01-29>`
* :feature:`-` add new task set_file_ssh_authorized_keys to add SSH keys for
users
* :support:`-` improved logging in fileservertasks.tasks, got rid of
GVAFileException
* :release:`0.4.0 <2015-01-26>`
* :feature:`-` implement new tasks create_file_website_hierarchy and
delete_file_website_hierarchy
* :support:`-` remove unneeded Django dependencies
* :release:`0.3.0 <2015-01-19>`
* :support:`-` refactor osusers.tasks, move to fileservertasks.tasks
* :release:`0.2.0 <2014-12-27>`
* :support:`-` restrict permissions of mailbox base directories to be read only
* :feature:`-` add new mailbox handling tasks
:py:func:`osusers.tasks.create_file_mailbox` and
:py:func:`osusers.tasks.delete_file_mailbox`
* :support:`-` move celery routers into gvacommon that is in it's own
repository to be used by others (gva, gvaldap)
* :bug:`- major` sftp directories are now owned by root instead of user
* :release:`0.1.0 <2014-12-26>`
* :support:`-` configure celery task serialization, add routing for ldap tasks

View file

@ -2,81 +2,34 @@
Code documentation
==================
.. index:: Django
gvafile is implemented as a set of `Celery`_ tasks.
gvafile is implemented as `Django`_ project and provides some `Celery`_ tasks.
.. _Django: https://www.djangoproject.com/
.. _Celery: http://www.celeryproject.org/
The project module :py:mod:`gvafile`
====================================
:py:mod:`fileservertasks` module
================================
.. automodule:: gvafile
.. automodule:: fileservertasks
:py:mod:`gvafile.celery`
------------------------
:py:mod:`celery <fileservertasks.celery>`
-----------------------------------------
.. automodule:: gvafile.celery
.. automodule:: fileservertasks.celery
:members:
:py:mod:`gvafile.urls`
----------------------
:py:mod:`settings <fileservertasks.settings>`
---------------------------------------------
.. automodule:: gvafile.urls
:py:mod:`gvafile.exceptions`
----------------------------
.. automodule:: gvafile.exceptions
:py:mod:`gvafile.wsgi`
----------------------
.. automodule:: gvafile.wsgi
.. automodule:: fileservertasks.settings
:members:
:py:mod:`gvafile.settings`
--------------------------
:py:mod:`tasks <fileservertasks.tasks>`
---------------------------------------
.. automodule:: gvafile.settings
:py:mod:`gvafile.settings.base`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: gvafile.settings.base
:members:
:py:mod:`gvafile.settings.local`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: gvafile.settings.local
:py:mod:`gvafile.settings.production`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: gvafile.settings.production
:py:mod:`gvafile.settings.test`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: gvafile.settings.test
:py:mod:`osusers` app
=====================
.. automodule:: osusers
:py:mod:`osusers.tasks`
-----------------------
.. automodule:: osusers.tasks
.. automodule:: fileservertasks.tasks
:members:
:undoc-members:

View file

@ -14,171 +14,172 @@
import sys
import os
import django
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath(os.path.join('..', 'gvafile')))
sys.path.insert(0, os.path.abspath(os.path.join("..", "gvafile")))
os.environ['GVAFILE_ALLOWED_HOSTS'] = 'localhost'
os.environ['GVAFILE_SERVER_EMAIL'] = 'root@localhost'
os.environ['GVAFILE_SFTP_DIRECTORY'] = '/home/www'
os.environ['GVAFILE_MAIL_DIRECTORY'] = '/home/mail'
django.setup()
os.environ["GVAFILE_BROKER_URL"] = "amqp://"
os.environ["GVAFILE_ALLOWED_HOSTS"] = "localhost"
os.environ["GVAFILE_MAIL_DIRECTORY"] = "/home/mail"
os.environ["GVAFILE_RESULTS_REDIS_URL"] = "redis://"
os.environ["GVAFILE_SERVER_EMAIL"] = "root@localhost"
os.environ["GVAFILE_SFTP_AUTHKEYS_DIRECTORY"] = "/srv/sftp/authorized_keys"
os.environ["GVAFILE_SFTP_DIRECTORY"] = "/home/www"
# -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['releases', 'sphinx.ext.autodoc', 'celery.contrib.sphinx']
extensions = ["releases", "sphinx.ext.autodoc", "celery.contrib.sphinx"]
# configuration for releases extension
releases_issue_uri = "https://git.dittberner.info/gnuviech/gvafile/issues/%s"
releases_release_uri = "https://git.dittberner.info/gnuviech/gvafile/src/tag/%s"
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
releases_issue_uri = 'https://dev.gnuviech-server.de/gvafile/ticket/%s'
releases_release_uri = 'https://dev.gnuviech-server.de/gvafile/milestone/%s'
templates_path = ["_templates"]
# The suffix of source filenames.
source_suffix = '.rst'
source_suffix = ".rst"
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
master_doc = "index"
# General information about the project.
project = u'gvafile'
copyright = u'2014, Jan Dittberner'
project = u"gvafile"
copyright = u"2014-2020, Jan Dittberner"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '0.1.0'
# The full version, including alpha/beta/rc tags.
release = '0.1.0'
from fileservertasks import __version__ as release
# The short X.Y version.
version = ".".join(release.split(".")[:2])
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
exclude_patterns = ["_build"]
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
pygments_style = "sphinx"
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'default'
html_theme = "alabaster"
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
html_static_path = ["_static"]
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'gvafiledoc'
htmlhelp_basename = "gvafiledoc"
# -- Options for LaTeX output --------------------------------------------------
@ -186,10 +187,8 @@ htmlhelp_basename = 'gvafiledoc'
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
@ -197,42 +196,38 @@ latex_elements = {
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'gvafile.tex', u'gvafile Documentation',
u'Jan Dittberner', 'manual'),
("index", "gvafile.tex", u"gvafile Documentation", u"Jan Dittberner", "manual")
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# latex_domain_indices = True
# -- Options for manual page output --------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'gvafile', u'gvafile Documentation',
[u'Jan Dittberner'], 1)
]
man_pages = [("index", "gvafile", u"gvafile Documentation", [u"Jan Dittberner"], 1)]
# If true, show URL addresses after external links.
#man_show_urls = False
# man_show_urls = False
# -- Options for Texinfo output ------------------------------------------------
@ -241,16 +236,22 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'gvafile', u'gvafile Documentation',
u'Jan Dittberner', 'gvafile', 'GNUViech admin filserver tools.',
'Miscellaneous'),
(
"index",
"gvafile",
u"gvafile Documentation",
u"Jan Dittberner",
"gvafile",
"GNUViech admin fileserver tools.",
"Miscellaneous",
)
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
# texinfo_show_urls = 'footnote'

View file

@ -7,4 +7,4 @@ of the following steps:
* installation of native dependencies
* setup of a virtualenv
* installation of gvafile production dependencies inside the virtualenv
* setup of celery worker under control of supervisord
* setup of celery worker under control of systemd

View file

@ -7,78 +7,30 @@ Install
Working Environment
===================
You have several options in setting up your working environment. We recommend
using virtualenv to separate the dependencies of your project from your
system's python environment. If on Linux or Mac OS X, you can also use
virtualenvwrapper to help manage multiple virtualenvs across different
projects.
To get a running work environment use `pipenv`_.
.. index:: virtualenv
.. _pipenv: https://pipenv.kennethreitz.org/en/latest/
Virtualenv Only
---------------
First, make sure you are using `virtualenv`_. Once that's installed, create
your virtualenv:
To get started install `pip` and `pipenv` and use `pipenv install --dev`:
.. code-block:: sh
$ virtualenv --distribute gvafile
.. _virtualenv: https://virtualenv.pypa.io/en/latest/
You will also need to ensure that the virtualenv has the project directory
added to the path. Adding the project directory will allow `django-admin.py` to
be able to change settings using the `--settings` flag.
.. index:: virtualenvwrapper
Virtualenv with virtualenvwrapper
------------------------------------
In Linux and Mac OSX, you can install `virtualenvwrapper
<http://virtualenvwrapper.readthedocs.org/en/latest/>`_, which will take care
of managing your virtual environments and adding the project path to the
`site-directory` for you:
.. code-block:: sh
$ mkdir gvafile
$ mkvirtualenv -a gvafile gvafile-dev
$ cd gvafile && add2virtualenv `pwd`
.. index:: pip, requirements, dependencies
Installation of Dependencies
=============================
Depending on where you are installing dependencies:
In development:
.. code-block:: sh
$ pip install -r requirements/local.txt
For production:
.. code-block:: sh
$ pip install -r requirements.txt
$ apt install python3-pip
$ python3 -m pip install --user -U pipenv
$ pipenv install --dev
.. index:: celery, worker, file queue
Running the Celery worker
=========================
gvafile uses the `Celery`_ distributed task queue system. The gvafile logix is
gvafile uses the `Celery`_ distributed task queue system. The gvafile logic is
executed by a celery worker. After all dependencies are installed you can go
into the gvafile directory and run the celery worker with:
.. code-block:: sh
$ cd gvafile
$ celery -A gvafile worker -Q file -l info
$ pipenv run celery -A filerservertasks worker -Q web -l info
.. _Celery: http://www.celeryproject.org/

5
entrypoint.sh Executable file
View file

@ -0,0 +1,5 @@
#!/bin/sh
set -e
su -c /srv/gvafile.sh gvafile

12
gvafile.sh Executable file
View file

@ -0,0 +1,12 @@
#!/bin/sh
set -e
QUEUE="file"
TASKS="fileservertasks"
APP="gvafile"
export TZ="Europe/Berlin"
. "/srv/${APP}/.venv/bin/activate"
cd /srv/${APP}/${APP}
celery -A "${TASKS}" worker -Q "${QUEUE}" -E -l info

View file

@ -0,0 +1,9 @@
"""
This module contains :py:mod:`fileservertasks.tasks`.
"""
__version__ = "0.7.0"
from fileservertasks.celery import app as celery_app
__all__ = ("celery_app",)

View file

@ -0,0 +1,15 @@
"""
This module defines the Celery_ app for gvafile.
.. _Celery: http://www.celeryproject.org/
"""
from __future__ import absolute_import
from celery import Celery
#: The Celery application
app = Celery('fileservertasks')
app.config_from_object('fileservertasks.settings', namespace="CELERY")
app.autodiscover_tasks(['fileservertasks.tasks'], force=True)

View file

@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
# pymode:lint_ignore=E501
"""
Common settings and globals.
"""
from os import environ
def get_env_variable(setting):
"""
Get the environment setting or return exception.
:param str setting: name of an environment setting
:raises ImproperlyConfigured: if the environment setting is not defined
:return: environment setting value
:rtype: str
"""
try:
return environ[setting]
except KeyError:
error_msg = "Set the %s env variable" % setting
raise AssertionError(error_msg)
########## CELERY CONFIGURATION
accept_content = ["json"]
broker_url = get_env_variable("GVAFILE_BROKER_URL")
enable_utc = True
result_backend = get_env_variable("GVAFILE_RESULTS_REDIS_URL")
result_expires = None
result_persistent = True
result_serializer = "json"
task_routes = ("gvacommon.celeryrouters.GvaRouter",)
task_serializer = "json"
timezone = "Europe/Berlin"
########## END CELERY CONFIGURATION
########## GVAFILE CONFIGURATION
GVAFILE_SFTP_DIRECTORY = get_env_variable("GVAFILE_SFTP_DIRECTORY")
GVAFILE_MAIL_DIRECTORY = get_env_variable("GVAFILE_MAIL_DIRECTORY")
GVAFILE_SFTP_AUTHKEYS_DIRECTORY = get_env_variable("GVAFILE_SFTP_AUTHKEYS_DIRECTORY")
########## END GVAFILE CONFIGURATION

View file

@ -0,0 +1,670 @@
"""
This module defines `Celery`_ tasks to manage file system entities.
.. _Celery: http://www.celeryproject.org/
"""
from __future__ import absolute_import, unicode_literals
from copy import deepcopy
import os
import subprocess
from tempfile import mkstemp
from fileservertasks import settings
from celery import shared_task
from celery.utils.log import get_task_logger
_LOGGER = get_task_logger(__name__)
SUDO_CMD = '/usr/bin/sudo'
INSTALL_CMD = '/usr/bin/install'
SETFACL_CMD = '/usr/bin/setfacl'
RM_CMD = '/bin/rm'
def log_and_raise(exception, message, *args):
"""
Log and raise a :py:class:`subprocess.CalledProcessError`.
:param exception: exception
:param str message: log message
:param args: arguments to fill placeholders in message
:raises Exception: raises an exception with the formatted message
"""
logargs = list(args) + [exception.returncode, exception.output]
_LOGGER.error(message + "\nreturncode: %d\noutput:\n%s", *logargs)
raise Exception(message % args)
def _build_authorized_keys_path(username):
"""
Constructs the file path for the authorized_keys file for a given username.
:param str username: the user name
:return: the file name
:rtype: str
"""
return os.path.join(
settings.GVAFILE_SFTP_AUTHKEYS_DIRECTORY, username, 'keys')
def _build_sftp_directory_name(username):
"""
Constructs the SFTP directory name for a given username.
"""
return os.path.join(settings.GVAFILE_SFTP_DIRECTORY, username)
def _build_mail_directory_name(username):
"""
Constructs the mailbox directory name for a given username.
"""
return os.path.join(settings.GVAFILE_MAIL_DIRECTORY, username)
@shared_task
def setup_file_sftp_userdir(username, *args, **kwargs):
"""
This task creates the home directory for an SFTP user if it does not exist
yet.
:param str username: the username
:raises Exception: if the SFTP directory of the user cannot be created
:return: a dictionary with the key :py:const:`username` set to the
username value and a new key :py:const:`sftp_directory` set to the
path of the created SFTP directory
:rtype: dict
.. note::
This variant can only be used at the beginning of a Celery task chain
or as a standalone task.
Use :py:func:`fileservertasks.tasks.setup_file_sftp_userdir_chained`
at other positions in the task chain.
"""
sftp_directory = _build_sftp_directory_name(username)
try:
subprocess.check_output([
SUDO_CMD, INSTALL_CMD, '-o', 'root', '-g', username,
'-m', '0750', '-d', sftp_directory], stderr=subprocess.STDOUT)
subprocess.check_output([
SUDO_CMD, SETFACL_CMD, '-m', 'www-data:--x',
sftp_directory], stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as cpe:
log_and_raise(
cpe, 'could not create SFTP directory %s for user %s',
sftp_directory, username)
_LOGGER.info(
'created sftp directory %s for user %s', sftp_directory, username)
return {'username': username, 'sftp_directory': sftp_directory}
@shared_task
def setup_file_sftp_userdir_chained(previous_result, *args, **kwargs):
"""
This task creates the home directory for an SFTP user if it does not exist
yet.
:param dict previous_result: a dictionary describing the result of the
previous step in the Celery task chain. This dictionary must contain a
:py:const:`username` key
:raises Exception: if the SFTP directory of the user cannot be created
:return: a copy of the :py:obj:`previous_result` dictionary with a new
:py:const:`sftp_directory` key set to the path of the created SFTP
directory
:rtype: dict
"""
username = previous_result['username']
retval = deepcopy(previous_result)
retval.update(setup_file_sftp_userdir(username))
return retval
@shared_task
def delete_file_sftp_userdir(username, *args, **kwargs):
"""
This task recursively deletes the home directory of an SFTP user if it
exists.
:param str username: the username
:raises Exception: if the SFTP directory of the user cannot be removed
:return: a dictionary with the key :py:const:`username` set to the username
value and the new key :py:const:`sftp_directory` set to the path of the
deleted SFTP directory
:rtype: dict
.. note::
This variant can only be used at the beginning of a Celery task chain
or as a standalone task.
Use :py:func:`fileservertasks.tasks.delete_file_sftp_userdir_chained`
at other positions in the task chain.
"""
sftp_directory = _build_sftp_directory_name(username)
try:
subprocess.check_output([
SUDO_CMD, RM_CMD, '-r', '-f', sftp_directory],
stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as cpe:
log_and_raise(
cpe, 'could not remove SFTP directory for user %s', username)
_LOGGER.info(
"deleted sftp directory %s of user %s", sftp_directory, username)
return {'username': username, 'sftp_directory': sftp_directory}
@shared_task
def delete_file_sftp_userdir_chained(previous_result, *args, **kwargs):
"""
This task recursively deletes the home directory of an SFTP user if it
exists.
:param dict previous_result: a dictionary describing the result of the
previous step in the Celery task chain. This dictionary must contain a
:py:const:`username` key
:raises Exception: if the SFTP directory of the user cannot be removed
:return: a copy of the :py:obj:`previous_result` dictionary with a new
:py:const:`sftp_directory` key set to the path of the removed SFTP
directory
:rtype: dict
"""
username = previous_result['username']
retval = deepcopy(previous_result)
retval.update(delete_file_sftp_userdir(username))
return retval
@shared_task
def setup_file_mail_userdir(username, *args, **kwargs):
"""
This task creates the mail base directory for a user if it does not exist
yet.
:param str username: the username
:raises Exception: if the mail base directory for the user cannot be
created
:return: a dictionary with the key :py:const:`username` set to the
username value and a new key :py:const:`mail_directory` set to the path
of the created mail directory
:rtype: dict
.. note::
This variant can only be used at the beginning of a Celery task chain
or as a standalone task.
Use :py:func:`fileservertasks.tasks.setup_file_mail_userdir_chained`
at other positions in the task chain.
"""
mail_directory = _build_mail_directory_name(username)
try:
subprocess.check_output([
SUDO_CMD, INSTALL_CMD, '-o', username, '-g', username,
'-m', '0500', '-d', mail_directory], stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as cpe:
log_and_raise(
cpe, 'could not create mail base directory for user %s', username)
_LOGGER.info(
'created mail directory %s for user %s', mail_directory, username)
return {'username': username, 'mail_directory': mail_directory}
@shared_task
def setup_file_mail_userdir_chained(previous_result, *args, **kwargs):
"""
This task creates the mail base directory for a user if it does not exist
yet.
:param dict previous_result: a dictionary containing the result of the
previous step in the Celery task chain. This dictionary must contain a
:py:const:`username` key
:raises Exception: if the mail base directory for the user cannot be
created
:return: a copy of the :py:obj:`previous_result` dictionary with a new
:py:const:`mail_directory` key set to the path of the created mail
directory
:rtype: dict
"""
username = previous_result['username']
retval = deepcopy(previous_result)
retval.update(setup_file_mail_userdir(username))
return retval
@shared_task
def delete_file_mail_userdir(username, *args, **kwargs):
"""
This task recursively deletes the mail base directory for a user if it
does not exist yet.
:param str username: the username
:raises Exception: if the mail base directory of the user cannot be deleted
:return: a dictionary with the key :py:const:`username` set to the
username value and a new key :py:const:`mail_directory` set to the path
of the deleted mail directory
:rtype: dict
.. note::
This variant can only be used at the beginning of a Celery task chain
or as a standalone task.
Use :py:func:`fileservertasks.tasks.delete_file_mail_userdir_chained`
at other positions in the task chain.
"""
mail_directory = _build_mail_directory_name(username)
try:
subprocess.check_output([
SUDO_CMD, RM_CMD, '-r', '-f', mail_directory],
stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as cpe:
log_and_raise(
cpe, 'could not remove mail base directory of user %s', username)
_LOGGER.info(
'deleted mail directory %s of user %s', mail_directory, username)
return {'username': username, 'mail_directory': mail_directory}
@shared_task
def delete_file_mail_userdir_chained(previous_result, *args, **kwargs):
"""
This task recursively deletes the mail base directory for a user if it
does not exist yet.
:param dict previous_result: a dictionary describing the result of the
previous step in the Celery task chain. This dictionary must contain a
:py:const:`username` key
:raises Exception: if the mail base directory of the user cannot be deleted
:return: a copy of the :py:obj:`previous_result` dictionary with a new
:py:const:`mail_directory` key set to the path of the deleted mail
directory
:rtype: str
"""
username = previous_result['username']
retval = deepcopy(previous_result)
retval.update(delete_file_mail_userdir(username))
return retval
@shared_task
def create_file_mailbox(username, mailboxname, *args, **kwargs):
"""
This task creates a new mailbox directory for the given user and mailbox
name.
:param str username: the username
:param str mailboxname: the mailbox name
:raises Exception: if the mailbox directory cannot be created
:return: a dictionary with the keys :py:const:`username` and
:py:const:`mailboxname` set to the values of username and mailboxname
and a new key :py:const:`mailbox_directory` set to the path of the
created mailbox directory
:rtype: dict
.. note::
This variant can only be used at the beginning of a Celery task chain
or as a standalone task.
Use :py:func:`fileservertasks.tasks.create_file_mailbox_chained` at
other positions in the task chain.
"""
mailbox_directory = os.path.join(
_build_mail_directory_name(username), mailboxname)
try:
subprocess.check_output([
SUDO_CMD, INSTALL_CMD, '-o', username, '-g', username,
'-m', '0700', '-d', mailbox_directory], stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as cpe:
log_and_raise(
cpe, 'could not create mailbox %s for user %s', mailboxname,
username)
_LOGGER.info(
'created mailbox directory %s for user %s', mailbox_directory,
username)
return {
'username': username, 'mailboxname': mailboxname,
'mailbox_directory': mailbox_directory
}
@shared_task
def create_file_mailbox_chained(previous_result, *args, **kwargs):
"""
This task creates a new mailbox directory for the given user and mailbox
name.
:param dict previous_result: a dictionary describing the result of the
previous step in the Celery task chain. This dictionary must contain a
:py:const:`username` and a :py:const:`mailboxname` key
:raises Exception: if the mailbox directory cannot be created
:return: a copy of the :py:obj:`previous_result` dictionary with a new
:py:const:`mailbox_directory` key set to the path of the created
mailbox directory
:rtype: dict
"""
username = previous_result['username']
mailboxname = previous_result['mailboxname']
retval = deepcopy(previous_result)
retval.update(create_file_mailbox(username, mailboxname))
return retval
@shared_task
def delete_file_mailbox(username, mailboxname, *args, **kwargs):
"""
This task deletes the given mailbox of the given user.
:param str username: the username
:param str mailboxname: the mailbox name
:raises Exception: if the mailbox directory cannot be deleted
:return: a dictionary with the keys :py:const:`username` and
:py:const:`mailboxname` set to the values of username and mailboxname
and a new key :py:const:`mailbox_directory` set to the path of the
deleted mailbox directory
:rtype: dict
.. note::
This variant can only be used at the beginning of a Celery task chain
or as a standalone task.
Use :py:func:`fileservertasks.tasks.delete_file_mailbox_chained` for
other positions in the task chain.
"""
mailbox_directory = os.path.join(
_build_mail_directory_name(username), mailboxname)
try:
subprocess.check_output([
SUDO_CMD, RM_CMD, '-r', '-f', mailbox_directory],
stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as cpe:
log_and_raise(
cpe, 'could not remove mailbox %s of user %s', mailboxname,
username)
_LOGGER.info(
'deleted mailbox directory %s of user %s', mailbox_directory, username)
return {
'username': username, 'mailboxname': mailboxname,
'mailbox_directory': mailbox_directory
}
@shared_task
def delete_file_mailbox_chained(previous_result, *args, **kwargs):
"""
This task deletes the given mailbox of the given user.
:param dict previous_result: a dictionary describing the result of the
previous step in the Celery task chain. This dictionary must contain a
:py:const:`username` and a :py:const:`mailboxname` key
:raises Exception: if the mailbox directory cannot be deleted
:return: a copy of the :py:obj:`previous_result` dictionary with a new
:py:const:`mailbox_directory` key set to the path of the deleted
mailbox directory
:rtype: dict
"""
username = previous_result['username']
mailboxname = previous_result['mailboxname']
retval = deepcopy(previous_result)
retval.update(delete_file_mailbox(username, mailboxname))
return retval
@shared_task
def create_file_website_hierarchy(username, sitename, *args, **kwargs):
"""
This task creates the directory hierarchy for a website.
:param str username: the username
:param str sitename: the sitename
:raises Exception: if the website directory hierarchy directory cannot be
created
:return: a dictionary with the keys :py:const:`username` and
:py:const:`sitename` set to the values of username and sitename and a
new key :py:const:`website_directory` set to the path of the created
website directory
:rtype: dict
.. note::
This variant can only be used at the beginning of a Celery task chain
or as a standalone task.
Use
:py:func:`fileservertasks.tasks.create_file_website_hierarchy_chained`
at other positions in the task chain
"""
website_directory = os.path.join(
_build_sftp_directory_name(username), sitename)
try:
subprocess.check_output([
SUDO_CMD, INSTALL_CMD, '-o', 'root', '-g', username,
'-m', '0750', '-d', website_directory], stderr=subprocess.STDOUT)
subprocess.check_output([
SUDO_CMD, SETFACL_CMD, '-m', 'www-data:--x',
website_directory], stderr=subprocess.STDOUT)
htmldir = os.path.join(website_directory, 'html')
subprocess.check_output([
SUDO_CMD, INSTALL_CMD, '-o', username, '-g', username,
'-m', '0750', '-d', htmldir], stderr=subprocess.STDOUT)
subprocess.check_output([
SUDO_CMD, SETFACL_CMD, '-m', 'www-data:r-x',
htmldir], stderr=subprocess.STDOUT)
subprocess.check_output([
SUDO_CMD, SETFACL_CMD, '-d', '-m', 'www-data:r-X',
htmldir], stderr=subprocess.STDOUT)
tmpdir = os.path.join(website_directory, 'tmp')
subprocess.check_output([
SUDO_CMD, INSTALL_CMD, '-o', username, '-g', username,
'-m', '0750', '-d', tmpdir], stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as cpe:
log_and_raise(
cpe,
("could not setup website file hierarchy for user %s's "
"website %s"), username, sitename)
_LOGGER.info(
'created website directory %s for user %s', website_directory,
username)
return {
'username': username, 'sitename': sitename,
'website_directory': website_directory,
}
@shared_task
def create_file_website_hierarchy_chained(previous_result, *args, **kwargs):
"""
This task creates the directory hierarchy for a website.
:param dict previous_result: a dictionary describing the result of the
previous step in the Celery task chain. This dictionary must contain a
:py:const:`username` and a :py:const:`sitename` key
:raises Exception: if the website directory hierarchy directory cannot be
created
:return: a copy of the :py:obj:`previous_result` dictionary with a new
:py:const:`website_directory` key set to the path of the created
website directory
:rtype: dict
"""
username = previous_result['username']
sitename = previous_result['sitename']
retval = deepcopy(previous_result)
retval.update(create_file_website_hierarchy(username, sitename))
return retval
@shared_task
def delete_file_website_hierarchy(username, sitename, *args, **kwargs):
"""
This task deletes a website hierarchy recursively.
:param str username: a username
:param str sitename: a site name
:return: a dictionary with the keys :py:const:`username` and
:py:const:`sitename` set to their original values and a new key
:py:const:`website_directory` set to the path of the deleted website
:rtype: dict
.. note::
This variant can only be used at the beginning of a Celery task chain
or as a standalone task.
Use
:py:func:`fileservertasks.tasks.delete_file_website_hierarchy_chained`
at other positions in the task chain
"""
website_directory = os.path.join(
_build_sftp_directory_name(username), sitename)
try:
subprocess.check_output([
SUDO_CMD, RM_CMD, '-r', '-f', website_directory],
stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as cpe:
log_and_raise(
cpe,
("could not delete the website file hierarchy of user %s's "
"website %s"), username, sitename)
_LOGGER.info(
'deleted website directory %s of user %s', website_directory, username)
return {
'username': username, 'sitename': sitename,
'website_directory': website_directory,
}
@shared_task
def delete_file_website_hierarchy_chained(previous_result, *args, **kwargs):
"""
This task deletes the website hierarchy recursively.
:param dict previous_result: a dictionary describing the result of the
previous step in the Celery task chain. This dictionary must contain a
:py:const:`username` and a :py:const:`sitename` key
:raises Exception: if the website directory hierarchy directory cannot be
deleted
:return: a copy of the :py:obj:`previous_result` dictionary with a new
:py:const:`website_directory` set to the path of the deleted website
directory
:rtype: dict
"""
username = previous_result['username']
sitename = previous_result['sitename']
retval = deepcopy(previous_result)
retval.update(delete_file_website_hierarchy(username, sitename))
return retval
@shared_task
def set_file_ssh_authorized_keys(username, ssh_keys, *args, **kwargs):
"""
This task set the authorized keys for ssh logins.
:param str username: a username
:param list ssh_keys: a list of ssh keys
:raises Exception: if the update of the creation or update of ssh
authorized_keys failed
:return: a dictionary with the keys :py:const:`username` and
:py:const:`ssh_keys` set to their original values and a new key
:py:const:`ssh_authorized_keys` set to the path of the SSH
authorized_keys file
:rtype: dict
.. note::
This variant can only be used at the beginning of a Celery task chain
or as a standalone task.
Use
:py:func:`fileservertasks.tasks.set_file_ssh_authorized_keys_chained`
at other positions in the task chain
"""
ssh_authorized_keys_file = _build_authorized_keys_path(username)
if ssh_keys:
try:
authkeystemp, filename = mkstemp()
conffile = os.fdopen(authkeystemp, 'w')
conffile.write("\n".join(ssh_keys))
finally:
if conffile:
conffile.close()
try:
subprocess.check_output([
SUDO_CMD, INSTALL_CMD, '-o', username, '-g', username,
'-m', '0500', '-d', os.path.dirname(ssh_authorized_keys_file)],
stderr=subprocess.STDOUT)
subprocess.check_output([
SUDO_CMD, INSTALL_CMD, '-o', username, '-g', username,
'-m', '0400', filename, ssh_authorized_keys_file],
stderr=subprocess.STDOUT)
subprocess.check_output([
SUDO_CMD, RM_CMD, filename], stderr=subprocess.STDOUT)
_LOGGER.info(
'set %d authorized_keys for user %s', len(ssh_keys), username)
except subprocess.CalledProcessError as cpe:
log_and_raise(
cpe, 'could not write authorized_keys file for user %s',
username)
else:
try:
subprocess.check_output([
SUDO_CMD, RM_CMD, '-rf',
os.path.dirname(ssh_authorized_keys_file)],
stderr=subprocess.STDOUT)
_LOGGER.info(
'deleted authorized_keys of user %s', username)
except subprocess.CalledProcessError as cpe:
log_and_raise(
cpe, 'could not remove the authorized_keys file of user %s',
username)
return {
'username': username, 'ssh_keys': ssh_keys,
'ssh_authorized_keys': ssh_authorized_keys_file,
}
@shared_task
def set_file_ssh_authorized_keys_chained(previous_result, *args, **kwargs):
"""
This task sets the authorized keys for ssh logins.
:param dict previous_result: a dictionary describing the result of the
previous step in the Celery task chain. This dictionary must contain a
:py:const:`username` and a :py:const:`ssh_keys` key
:raises Exception: if the update of the creation or update of ssh
authorized_keys failed
:return: a copy of the :py:obj:`previous_result` dictionary with a new
:py:const:`ssh_authorized_keys` set to the path of the SSH
authorized_keys file
:rtype: dict
"""
username = previous_result['username']
ssh_keys = previous_result['ssh_keys']
retval = deepcopy(previous_result)
retval.update(set_file_ssh_authorized_keys(username, ssh_keys))
return retval

View file

@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
class GvaRouter(object):
def route_for_task(self, task, args=None, kwargs=None):
for route in ["ldap", "file", "mysql", "pgsql", "web"]:
if route in task:
return {"exchange": route, "exchange_type": "direct", "queue": route}
return None

View file

@ -1,23 +0,0 @@
"""
This module defines the Celery_ app for gvafile.
.. _Celery: http://www.celeryproject.org/
"""
from __future__ import absolute_import
import os
from celery import Celery
from django.conf import settings
os.environ.setdefault('DJANGO_SETTINGS_MODULE',
'gvafile.settings.production')
#: The Celery application
app = Celery('gvafile')
app.config_from_object('django.conf:settings')
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)

View file

@ -1,12 +0,0 @@
"""
This module defines exceptions for gvafile.
"""
from __future__ import unicode_literals
class GVAFileException(Exception):
"""
Generic Exception class for gvafile.
"""

View file

@ -1,3 +0,0 @@
"""
This module contains settings for various environments.
"""

View file

@ -1,292 +0,0 @@
# -*- coding: utf-8 -*-
# pymode:lint_ignore=E501
"""
Common settings and globals.
"""
from os.path import abspath, basename, dirname, join, normpath
from sys import path
from os import environ
# Normally you should not import ANYTHING from Django directly
# into your settings, but ImproperlyConfigured is an exception.
from django.core.exceptions import ImproperlyConfigured
def get_env_setting(setting):
"""
Get the environment setting or return exception.
:param str setting: name of an environment setting
:raises ImproperlyConfigured: if the environment setting is not defined
:return: environment setting value
:rtype: str
"""
try:
return environ[setting]
except KeyError:
error_msg = "Set the %s env variable" % setting
raise ImproperlyConfigured(error_msg)
########## PATH CONFIGURATION
# Absolute filesystem path to the Django project directory:
DJANGO_ROOT = dirname(dirname(abspath(__file__)))
# Absolute filesystem path to the top-level project folder:
SITE_ROOT = dirname(DJANGO_ROOT)
# Site name:
SITE_NAME = basename(DJANGO_ROOT)
# Add our project to our pythonpath, this way we don't need to type our project
# name in our dotted import paths:
path.append(DJANGO_ROOT)
########## END PATH CONFIGURATION
########## DEBUG CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#debug
DEBUG = False
# See: https://docs.djangoproject.com/en/dev/ref/settings/#template-debug
TEMPLATE_DEBUG = DEBUG
########## END DEBUG CONFIGURATION
########## MANAGER CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#admins
ADMINS = (
(get_env_setting('GVAFILE_ADMIN_NAME'), get_env_setting('GVAFILE_ADMIN_EMAIL')),
)
# See: https://docs.djangoproject.com/en/dev/ref/settings/#managers
MANAGERS = ADMINS
########## END MANAGER CONFIGURATION
########## DATABASE CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': normpath(join(DJANGO_ROOT, 'default.db')),
'USER': '',
'PASSWORD': '',
'HOST': '',
'PORT': '',
},
}
########## END DATABASE CONFIGURATION
########## GENERAL CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#time-zone
TIME_ZONE = 'Europe/Berlin'
# See: https://docs.djangoproject.com/en/dev/ref/settings/#language-code
LANGUAGE_CODE = 'en-us'
# See: https://docs.djangoproject.com/en/dev/ref/settings/#site-id
SITE_ID = 1
# See: https://docs.djangoproject.com/en/dev/ref/settings/#use-i18n
USE_I18N = True
# See: https://docs.djangoproject.com/en/dev/ref/settings/#use-l10n
USE_L10N = True
# See: https://docs.djangoproject.com/en/dev/ref/settings/#use-tz
USE_TZ = True
########## END GENERAL CONFIGURATION
########## MEDIA CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#media-root
MEDIA_ROOT = normpath(join(SITE_ROOT, 'media'))
# See: https://docs.djangoproject.com/en/dev/ref/settings/#media-url
MEDIA_URL = '/media/'
########## END MEDIA CONFIGURATION
########## STATIC FILE CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#static-root
STATIC_ROOT = normpath(join(SITE_ROOT, 'assets'))
# See: https://docs.djangoproject.com/en/dev/ref/settings/#static-url
STATIC_URL = '/static/'
# See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#std:setting-STATICFILES_DIRS
STATICFILES_DIRS = (
normpath(join(SITE_ROOT, 'static')),
)
# See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#staticfiles-finders
STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
)
########## END STATIC FILE CONFIGURATION
########## SECRET CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#secret-key
# Note: This key should only be used for development and testing.
SECRET_KEY = get_env_setting('GVAFILE_SECRETKEY')
########## END SECRET CONFIGURATION
########## SITE CONFIGURATION
# Hosts/domain names that are valid for this site
# See https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts
ALLOWED_HOSTS = []
########## END SITE CONFIGURATION
########## FIXTURE CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-FIXTURE_DIRS
FIXTURE_DIRS = (
normpath(join(SITE_ROOT, 'fixtures')),
)
########## END FIXTURE CONFIGURATION
########## TEMPLATE CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#template-context-processors
TEMPLATE_CONTEXT_PROCESSORS = (
'django.contrib.auth.context_processors.auth',
'django.core.context_processors.debug',
'django.core.context_processors.i18n',
'django.core.context_processors.media',
'django.core.context_processors.static',
'django.core.context_processors.tz',
'django.contrib.messages.context_processors.messages',
'django.core.context_processors.request',
)
# See: https://docs.djangoproject.com/en/dev/ref/settings/#template-loaders
TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
)
# See: https://docs.djangoproject.com/en/dev/ref/settings/#template-dirs
TEMPLATE_DIRS = (
normpath(join(SITE_ROOT, 'templates')),
)
########## END TEMPLATE CONFIGURATION
########## MIDDLEWARE CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#middleware-classes
MIDDLEWARE_CLASSES = (
# Default Django middleware.
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
########## END MIDDLEWARE CONFIGURATION
########## URL CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#root-urlconf
ROOT_URLCONF = '%s.urls' % SITE_NAME
########## END URL CONFIGURATION
########## TEST RUNNER CONFIGURATION
TEST_RUNNER = 'django.test.runner.DiscoverRunner'
########## END TEST RUNNER CONFIGURATION
########## APP CONFIGURATION
DJANGO_APPS = (
# Default Django apps:
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
# Useful template tags:
# 'django.contrib.humanize',
# Admin panel and documentation:
'django.contrib.admin',
# 'django.contrib.admindocs',
)
# Apps specific for this project go here.
LOCAL_APPS = (
'osusers',
)
# See: https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps
INSTALLED_APPS = DJANGO_APPS + LOCAL_APPS
########## END APP CONFIGURATION
########## LOGGING CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#logging
# A sample logging configuration. The only tangible logging
# performed by this configuration is to send an email to
# the site admins on every HTTP 500 error when DEBUG=False.
# See http://docs.djangoproject.com/en/dev/topics/logging for
# more details on how to customize your logging configuration.
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'filters': {
'require_debug_false': {
'()': 'django.utils.log.RequireDebugFalse'
}
},
'handlers': {
'mail_admins': {
'level': 'ERROR',
'filters': ['require_debug_false'],
'class': 'django.utils.log.AdminEmailHandler'
}
},
'loggers': {
'django.request': {
'handlers': ['mail_admins'],
'level': 'ERROR',
'propagate': True,
},
}
}
########## END LOGGING CONFIGURATION
########## WSGI CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#wsgi-application
WSGI_APPLICATION = '%s.wsgi.application' % SITE_NAME
########## END WSGI CONFIGURATION
########## GVAFILE CONFIGURATION
GVAFILE_SFTP_DIRECTORY = get_env_setting('GVAFILE_SFTP_DIRECTORY')
GVAFILE_MAIL_DIRECTORY = get_env_setting('GVAFILE_MAIL_DIRECTORY')
########## END GVAFILE CONFIGURATION
########## CELERY CONFIGURATION
BROKER_URL = get_env_setting('GVAFILE_BROKER_URL')
CELERY_RESULT_BACKEND = 'amqp'
CELERY_RESULT_PERSISTENT = True
CELERY_TASK_RESULT_EXPIRES = None
CELERY_ROUTES = (
'osusers.tasks.LdapRouter',
'osusers.tasks.FileRouter',
)
CELERY_ACCEPT_CONTENT = ['pickle', 'yaml', 'json']
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'
########## END CELERY CONFIGURATION

View file

@ -1,51 +0,0 @@
# pymode:lint_ignore=W0401,E501
"""
Development settings and globals based on :py:mod:`gvafile.settings.base`.
"""
from __future__ import absolute_import
from .base import *
########## DEBUG CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#debug
DEBUG = True
# See: https://docs.djangoproject.com/en/dev/ref/settings/#template-debug
TEMPLATE_DEBUG = DEBUG
########## END DEBUG CONFIGURATION
########## EMAIL CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#email-backend
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
########## END EMAIL CONFIGURATION
########## CACHE CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#caches
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
}
}
########## END CACHE CONFIGURATION
########## TOOLBAR CONFIGURATION
# See: http://django-debug-toolbar.readthedocs.org/en/latest/installation.html#explicit-setup
INSTALLED_APPS += (
'debug_toolbar',
)
MIDDLEWARE_CLASSES += (
'debug_toolbar.middleware.DebugToolbarMiddleware',
)
DEBUG_TOOLBAR_PATCH_SETTINGS = False
# http://django-debug-toolbar.readthedocs.org/en/latest/installation.html
INTERNAL_IPS = ('127.0.0.1',)
########## END TOOLBAR CONFIGURATION

View file

@ -1,50 +0,0 @@
# pymode:lint_ignore=W0401,E501
"""
Production settings and globals based on :py:mod:`gvafile.settings.base`.
"""
from __future__ import absolute_import
from .base import *
########## HOST CONFIGURATION
# See: https://docs.djangoproject.com/en/1.5/releases/1.5/#allowed-hosts-required-in-production
ALLOWED_HOSTS = get_env_setting('GVAFILE_ALLOWED_HOSTS').split(',')
########## END HOST CONFIGURATION
########## EMAIL CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#email-backend
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
# See: https://docs.djangoproject.com/en/dev/ref/settings/#email-host
#EMAIL_HOST = environ.get('EMAIL_HOST', 'smtp.gmail.com')
# See: https://docs.djangoproject.com/en/dev/ref/settings/#email-host-password
#EMAIL_HOST_PASSWORD = environ.get('EMAIL_HOST_PASSWORD', '')
# See: https://docs.djangoproject.com/en/dev/ref/settings/#email-host-user
#EMAIL_HOST_USER = environ.get('EMAIL_HOST_USER', 'your_email@example.com')
# See: https://docs.djangoproject.com/en/dev/ref/settings/#email-port
#EMAIL_PORT = environ.get('EMAIL_PORT', 587)
# See: https://docs.djangoproject.com/en/dev/ref/settings/#email-subject-prefix
EMAIL_SUBJECT_PREFIX = '[%s] ' % SITE_NAME
# See: https://docs.djangoproject.com/en/dev/ref/settings/#email-use-tls
#EMAIL_USE_TLS = True
# See: https://docs.djangoproject.com/en/dev/ref/settings/#server-email
SERVER_EMAIL = get_env_setting('GVAFILE_SERVER_EMAIL')
########## END EMAIL CONFIGURATION
########## DATABASE CONFIGURATION
#DATABASES = {}
########## END DATABASE CONFIGURATION
########## CACHE CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#caches
#CACHES = {}
########## END CACHE CONFIGURATION

View file

@ -1,20 +0,0 @@
# pymode:lint_ignore=W0401
"""
Test settings based on :py:mod:`gvafile.settings.base`.
"""
from __future__ import absolute_import
from .base import *
########## IN-MEMORY TEST DATABASE
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": ":memory:",
"USER": "",
"PASSWORD": "",
"HOST": "",
"PORT": "",
},
}

View file

@ -1,25 +0,0 @@
"""
This module defines the main URLConf for gvaldap.
"""
from django.conf.urls import patterns, include, url
from django.conf import settings
# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns(
'',
url(r'^admin/', include(admin.site.urls)),
)
# Uncomment the next line to serve media files in dev.
# urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
if settings.DEBUG:
import debug_toolbar
urlpatterns += patterns('',
url(r'^__debug__/', include(debug_toolbar.urls)),
)

View file

@ -1,14 +0,0 @@
"""
WSGI config for gvafile project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/1.6/howto/deployment/wsgi/
"""
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gvafile.settings")
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()

View file

@ -1,10 +0,0 @@
#!/usr/bin/env python
import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gvafile.settings")
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)

View file

@ -1,3 +0,0 @@
"""
This module contains :py:mod:`osusers.tasks`.
"""

View file

@ -1,3 +0,0 @@
"""
Empty models module required for Django to accept this as an app.
"""

View file

@ -1,165 +0,0 @@
"""
This module defines `Celery`_ tasks to manage file system entities.
.. _Celery: http://www.celeryproject.org/
"""
from __future__ import absolute_import, unicode_literals
import os
import subprocess
from django.conf import settings
from celery import shared_task
from celery.utils.log import get_task_logger
from gvafile.exceptions import GVAFileException
_logger = get_task_logger(__name__)
SUDO_CMD = '/usr/bin/sudo'
INSTALL_CMD = '/usr/bin/install'
SETFACL_CMD = '/usr/bin/setfacl'
RM_CMD = '/bin/rm'
class LdapRouter(object):
def route_for_task(self, task, args=None, kwargs=None):
if 'ldap' in task:
return {'exchange': 'ldap',
'exchange_type': 'direct',
'queue': 'ldap'}
return None
class FileRouter(object):
def route_for_task(self, task, args=None, kwargs=None):
if 'file' in task:
return {'exchange': 'file',
'exchange_type': 'direct',
'queue': 'file'}
return None
def _build_sftp_directory_name(username):
"""
Constructs the SFTP directory name for a given username.
"""
return os.path.join(settings.GVAFILE_SFTP_DIRECTORY, username)
def _build_mail_directory_name(username):
"""
Constructs the mailbox directory name for a given username.
"""
return os.path.join(settings.GVAFILE_MAIL_DIRECTORY, username)
@shared_task
def setup_file_sftp_userdir(username):
"""
This task creates the home directory for an SFTP user if it does not exist
yet.
:param str username: the user name
:raises Exception: if the SFTP directory of the user cannot be created
:return: the created directory name
:rtype: str
"""
sftp_directory = _build_sftp_directory_name(username)
try:
subprocess.check_output([
SUDO_CMD, INSTALL_CMD, '-o', username, '-g', username,
'-m', '0750', '-d', sftp_directory], stderr=subprocess.STDOUT)
subprocess.check_output([
SUDO_CMD, SETFACL_CMD, '-m', 'www-data:--x',
sftp_directory], stderr=subprocess.STDOUT)
except subprocess.CalledProcessError:
_logger.exception(
'could not create SFTP directory for user %s', username)
raise GVAFileException(
"could not create SFTP directory for user %s" % username)
return sftp_directory
@shared_task
def delete_file_sftp_userdir(username):
"""
This task recursively deletes the home directory of an SFTP user if it
does not exist yet.
:param str username: the user name
:raises Exception: if the SFTP directory of the user cannot be removed
:return: the removed directory name
:rtype: str
"""
sftp_directory = _build_sftp_directory_name(username)
try:
subprocess.check_output([
SUDO_CMD, RM_CMD, '-r', '-f', sftp_directory],
stderr=subprocess.STDOUT)
except subprocess.CalledProcessError:
_logger.exception(
'could not remove SFTP directory for user %s', username)
raise GVAFileException(
"could not remove SFTP directory for user %s" % username)
return sftp_directory
@shared_task
def setup_file_mail_userdir(username):
"""
This task creates the mail base directory for a user if it does not exist
yet.
:param str username: the user name
:raises Exception: if the mail base directory for the user cannot be
created
:return: the created directory name
:rtype: str
"""
mail_directory = _build_mail_directory_name(username)
try:
subprocess.check_output([
SUDO_CMD, INSTALL_CMD, '-o', username, '-g', username,
'-m', '0750', '-d', mail_directory], stderr=subprocess.STDOUT)
except subprocess.CalledProcessError:
_logger.exception(
'could not create mail base directory for user %s', username)
raise GVAFileException(
"could not create mail base directory for user %s" % username)
return mail_directory
@shared_task
def delete_file_mail_userdir(username):
"""
This task recursively deletes the mail base directory for a user if it
does not exist yet.
:param str username: the user name
:raises Exception: if the mail base directory of the user cannot be removed
:return: the removed directory name
:rtype: str
"""
mail_directory = _build_mail_directory_name(username)
try:
subprocess.check_output([
SUDO_CMD, RM_CMD, '-r', '-f', mail_directory],
stderr=subprocess.STDOUT)
except subprocess.CalledProcessError:
_logger.exception(
'could not remove mail base directory of user %s', username)
raise GVAFileException(
"could not remove mail base directory of user %s" % username)
return mail_directory

975
poetry.lock generated Normal file
View file

@ -0,0 +1,975 @@
# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand.
[[package]]
name = "alabaster"
version = "0.7.13"
description = "A configurable sidebar-enabled Sphinx theme"
category = "dev"
optional = false
python-versions = ">=3.6"
files = [
{file = "alabaster-0.7.13-py3-none-any.whl", hash = "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3"},
{file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"},
]
[[package]]
name = "amqp"
version = "5.1.1"
description = "Low-level AMQP client for Python (fork of amqplib)."
category = "main"
optional = false
python-versions = ">=3.6"
files = [
{file = "amqp-5.1.1-py3-none-any.whl", hash = "sha256:6f0956d2c23d8fa6e7691934d8c3930eadb44972cbbd1a7ae3a520f735d43359"},
{file = "amqp-5.1.1.tar.gz", hash = "sha256:2c1b13fecc0893e946c65cbd5f36427861cffa4ea2201d8f6fca22e2a373b5e2"},
]
[package.dependencies]
vine = ">=5.0.0"
[[package]]
name = "asgiref"
version = "3.6.0"
description = "ASGI specs, helper code, and adapters"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "asgiref-3.6.0-py3-none-any.whl", hash = "sha256:71e68008da809b957b7ee4b43dbccff33d1b23519fb8344e33f049897077afac"},
{file = "asgiref-3.6.0.tar.gz", hash = "sha256:9567dfe7bd8d3c8c892227827c41cce860b368104c3431da67a0c5a65a949506"},
]
[package.dependencies]
typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
[package.extras]
tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"]
[[package]]
name = "async-timeout"
version = "4.0.2"
description = "Timeout context manager for asyncio programs"
category = "main"
optional = false
python-versions = ">=3.6"
files = [
{file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"},
{file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"},
]
[package.dependencies]
typing-extensions = {version = ">=3.6.5", markers = "python_version < \"3.8\""}
[[package]]
name = "babel"
version = "2.12.1"
description = "Internationalization utilities"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "Babel-2.12.1-py3-none-any.whl", hash = "sha256:b4246fb7677d3b98f501a39d43396d3cafdc8eadb045f4a31be01863f655c610"},
{file = "Babel-2.12.1.tar.gz", hash = "sha256:cc2d99999cd01d44420ae725a21c9e3711b3aadc7976d6147f622d8581963455"},
]
[package.dependencies]
pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""}
[[package]]
name = "billiard"
version = "3.6.4.0"
description = "Python multiprocessing fork with improvements and bugfixes"
category = "main"
optional = false
python-versions = "*"
files = [
{file = "billiard-3.6.4.0-py3-none-any.whl", hash = "sha256:87103ea78fa6ab4d5c751c4909bcff74617d985de7fa8b672cf8618afd5a875b"},
{file = "billiard-3.6.4.0.tar.gz", hash = "sha256:299de5a8da28a783d51b197d496bef4f1595dd023a93a4f59dde1886ae905547"},
]
[[package]]
name = "cached-property"
version = "1.5.2"
description = "A decorator for caching properties in classes."
category = "main"
optional = false
python-versions = "*"
files = [
{file = "cached-property-1.5.2.tar.gz", hash = "sha256:9fa5755838eecbb2d234c3aa390bd80fbd3ac6b6869109bfc1b499f7bd89a130"},
{file = "cached_property-1.5.2-py2.py3-none-any.whl", hash = "sha256:df4f613cf7ad9a588cc381aaf4a512d26265ecebd5eb9e1ba12f1319eb85a6a0"},
]
[[package]]
name = "celery"
version = "5.2.7"
description = "Distributed Task Queue."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "celery-5.2.7-py3-none-any.whl", hash = "sha256:138420c020cd58d6707e6257b6beda91fd39af7afde5d36c6334d175302c0e14"},
{file = "celery-5.2.7.tar.gz", hash = "sha256:fafbd82934d30f8a004f81e8f7a062e31413a23d444be8ee3326553915958c6d"},
]
[package.dependencies]
billiard = ">=3.6.4.0,<4.0"
click = ">=8.0.3,<9.0"
click-didyoumean = ">=0.0.3"
click-plugins = ">=1.1.1"
click-repl = ">=0.2.0"
importlib-metadata = {version = ">=1.4.0", markers = "python_version < \"3.8\""}
kombu = ">=5.2.3,<6.0"
pytz = ">=2021.3"
vine = ">=5.0.0,<6.0"
[package.extras]
arangodb = ["pyArango (>=1.3.2)"]
auth = ["cryptography"]
azureblockblob = ["azure-storage-blob (==12.9.0)"]
brotli = ["brotli (>=1.0.0)", "brotlipy (>=0.7.0)"]
cassandra = ["cassandra-driver (<3.21.0)"]
consul = ["python-consul2"]
cosmosdbsql = ["pydocumentdb (==2.3.2)"]
couchbase = ["couchbase (>=3.0.0)"]
couchdb = ["pycouchdb"]
django = ["Django (>=1.11)"]
dynamodb = ["boto3 (>=1.9.178)"]
elasticsearch = ["elasticsearch"]
eventlet = ["eventlet (>=0.32.0)"]
gevent = ["gevent (>=1.5.0)"]
librabbitmq = ["librabbitmq (>=1.5.0)"]
memcache = ["pylibmc"]
mongodb = ["pymongo[srv] (>=3.11.1)"]
msgpack = ["msgpack"]
pymemcache = ["python-memcached"]
pyro = ["pyro4"]
pytest = ["pytest-celery"]
redis = ["redis (>=3.4.1,!=4.0.0,!=4.0.1)"]
s3 = ["boto3 (>=1.9.125)"]
slmq = ["softlayer-messaging (>=1.0.3)"]
solar = ["ephem"]
sqlalchemy = ["sqlalchemy"]
sqs = ["kombu[sqs]"]
tblib = ["tblib (>=1.3.0)", "tblib (>=1.5.0)"]
yaml = ["PyYAML (>=3.10)"]
zookeeper = ["kazoo (>=1.3.1)"]
zstd = ["zstandard"]
[[package]]
name = "certifi"
version = "2022.12.7"
description = "Python package for providing Mozilla's CA Bundle."
category = "dev"
optional = false
python-versions = ">=3.6"
files = [
{file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"},
{file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"},
]
[[package]]
name = "charset-normalizer"
version = "3.1.0"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
category = "dev"
optional = false
python-versions = ">=3.7.0"
files = [
{file = "charset-normalizer-3.1.0.tar.gz", hash = "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5"},
{file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b"},
{file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60"},
{file = "charset_normalizer-3.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1"},
{file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0"},
{file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f"},
{file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0"},
{file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795"},
{file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c"},
{file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203"},
{file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1"},
{file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137"},
{file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce"},
{file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a"},
{file = "charset_normalizer-3.1.0-cp310-cp310-win32.whl", hash = "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448"},
{file = "charset_normalizer-3.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8"},
{file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19"},
{file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017"},
{file = "charset_normalizer-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df"},
{file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a"},
{file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41"},
{file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1"},
{file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62"},
{file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6"},
{file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5"},
{file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be"},
{file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb"},
{file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac"},
{file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324"},
{file = "charset_normalizer-3.1.0-cp311-cp311-win32.whl", hash = "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909"},
{file = "charset_normalizer-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755"},
{file = "charset_normalizer-3.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373"},
{file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"},
{file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9"},
{file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f"},
{file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28"},
{file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d"},
{file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d"},
{file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d"},
{file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6"},
{file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84"},
{file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c"},
{file = "charset_normalizer-3.1.0-cp37-cp37m-win32.whl", hash = "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974"},
{file = "charset_normalizer-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23"},
{file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531"},
{file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c"},
{file = "charset_normalizer-3.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14"},
{file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb"},
{file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1"},
{file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b"},
{file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0"},
{file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649"},
{file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326"},
{file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11"},
{file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b"},
{file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd"},
{file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8"},
{file = "charset_normalizer-3.1.0-cp38-cp38-win32.whl", hash = "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0"},
{file = "charset_normalizer-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59"},
{file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e"},
{file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31"},
{file = "charset_normalizer-3.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f"},
{file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e"},
{file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f"},
{file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854"},
{file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706"},
{file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e"},
{file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0"},
{file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230"},
{file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7"},
{file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e"},
{file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f"},
{file = "charset_normalizer-3.1.0-cp39-cp39-win32.whl", hash = "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1"},
{file = "charset_normalizer-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b"},
{file = "charset_normalizer-3.1.0-py3-none-any.whl", hash = "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d"},
]
[[package]]
name = "click"
version = "8.1.3"
description = "Composable command line interface toolkit"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
{file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
]
[package.dependencies]
colorama = {version = "*", markers = "platform_system == \"Windows\""}
importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
[[package]]
name = "click-didyoumean"
version = "0.3.0"
description = "Enables git-like *did-you-mean* feature in click"
category = "main"
optional = false
python-versions = ">=3.6.2,<4.0.0"
files = [
{file = "click-didyoumean-0.3.0.tar.gz", hash = "sha256:f184f0d851d96b6d29297354ed981b7dd71df7ff500d82fa6d11f0856bee8035"},
{file = "click_didyoumean-0.3.0-py3-none-any.whl", hash = "sha256:a0713dc7a1de3f06bc0df5a9567ad19ead2d3d5689b434768a6145bff77c0667"},
]
[package.dependencies]
click = ">=7"
[[package]]
name = "click-plugins"
version = "1.1.1"
description = "An extension module for click to enable registering CLI commands via setuptools entry-points."
category = "main"
optional = false
python-versions = "*"
files = [
{file = "click-plugins-1.1.1.tar.gz", hash = "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b"},
{file = "click_plugins-1.1.1-py2.py3-none-any.whl", hash = "sha256:5d262006d3222f5057fd81e1623d4443e41dcda5dc815c06b442aa3c02889fc8"},
]
[package.dependencies]
click = ">=4.0"
[package.extras]
dev = ["coveralls", "pytest (>=3.6)", "pytest-cov", "wheel"]
[[package]]
name = "click-repl"
version = "0.2.0"
description = "REPL plugin for Click"
category = "main"
optional = false
python-versions = "*"
files = [
{file = "click-repl-0.2.0.tar.gz", hash = "sha256:cd12f68d745bf6151210790540b4cb064c7b13e571bc64b6957d98d120dacfd8"},
{file = "click_repl-0.2.0-py3-none-any.whl", hash = "sha256:94b3fbbc9406a236f176e0506524b2937e4b23b6f4c0c0b2a0a83f8a64e9194b"},
]
[package.dependencies]
click = "*"
prompt-toolkit = "*"
six = "*"
[[package]]
name = "colorama"
version = "0.4.6"
description = "Cross-platform colored terminal text."
category = "main"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
[[package]]
name = "coverage"
version = "7.2.5"
description = "Code coverage measurement for Python"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "coverage-7.2.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:883123d0bbe1c136f76b56276074b0c79b5817dd4238097ffa64ac67257f4b6c"},
{file = "coverage-7.2.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d2fbc2a127e857d2f8898aaabcc34c37771bf78a4d5e17d3e1f5c30cd0cbc62a"},
{file = "coverage-7.2.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f3671662dc4b422b15776cdca89c041a6349b4864a43aa2350b6b0b03bbcc7f"},
{file = "coverage-7.2.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780551e47d62095e088f251f5db428473c26db7829884323e56d9c0c3118791a"},
{file = "coverage-7.2.5-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:066b44897c493e0dcbc9e6a6d9f8bbb6607ef82367cf6810d387c09f0cd4fe9a"},
{file = "coverage-7.2.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b9a4ee55174b04f6af539218f9f8083140f61a46eabcaa4234f3c2a452c4ed11"},
{file = "coverage-7.2.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:706ec567267c96717ab9363904d846ec009a48d5f832140b6ad08aad3791b1f5"},
{file = "coverage-7.2.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ae453f655640157d76209f42c62c64c4d4f2c7f97256d3567e3b439bd5c9b06c"},
{file = "coverage-7.2.5-cp310-cp310-win32.whl", hash = "sha256:f81c9b4bd8aa747d417407a7f6f0b1469a43b36a85748145e144ac4e8d303cb5"},
{file = "coverage-7.2.5-cp310-cp310-win_amd64.whl", hash = "sha256:dc945064a8783b86fcce9a0a705abd7db2117d95e340df8a4333f00be5efb64c"},
{file = "coverage-7.2.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:40cc0f91c6cde033da493227797be2826cbf8f388eaa36a0271a97a332bfd7ce"},
{file = "coverage-7.2.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a66e055254a26c82aead7ff420d9fa8dc2da10c82679ea850d8feebf11074d88"},
{file = "coverage-7.2.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c10fbc8a64aa0f3ed136b0b086b6b577bc64d67d5581acd7cc129af52654384e"},
{file = "coverage-7.2.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a22cbb5ede6fade0482111fa7f01115ff04039795d7092ed0db43522431b4f2"},
{file = "coverage-7.2.5-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:292300f76440651529b8ceec283a9370532f4ecba9ad67d120617021bb5ef139"},
{file = "coverage-7.2.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7ff8f3fb38233035028dbc93715551d81eadc110199e14bbbfa01c5c4a43f8d8"},
{file = "coverage-7.2.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:a08c7401d0b24e8c2982f4e307124b671c6736d40d1c39e09d7a8687bddf83ed"},
{file = "coverage-7.2.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ef9659d1cda9ce9ac9585c045aaa1e59223b143f2407db0eaee0b61a4f266fb6"},
{file = "coverage-7.2.5-cp311-cp311-win32.whl", hash = "sha256:30dcaf05adfa69c2a7b9f7dfd9f60bc8e36b282d7ed25c308ef9e114de7fc23b"},
{file = "coverage-7.2.5-cp311-cp311-win_amd64.whl", hash = "sha256:97072cc90f1009386c8a5b7de9d4fc1a9f91ba5ef2146c55c1f005e7b5c5e068"},
{file = "coverage-7.2.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:bebea5f5ed41f618797ce3ffb4606c64a5de92e9c3f26d26c2e0aae292f015c1"},
{file = "coverage-7.2.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:828189fcdda99aae0d6bf718ea766b2e715eabc1868670a0a07bf8404bf58c33"},
{file = "coverage-7.2.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e8a95f243d01ba572341c52f89f3acb98a3b6d1d5d830efba86033dd3687ade"},
{file = "coverage-7.2.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8834e5f17d89e05697c3c043d3e58a8b19682bf365048837383abfe39adaed5"},
{file = "coverage-7.2.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d1f25ee9de21a39b3a8516f2c5feb8de248f17da7eead089c2e04aa097936b47"},
{file = "coverage-7.2.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1637253b11a18f453e34013c665d8bf15904c9e3c44fbda34c643fbdc9d452cd"},
{file = "coverage-7.2.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8e575a59315a91ccd00c7757127f6b2488c2f914096077c745c2f1ba5b8c0969"},
{file = "coverage-7.2.5-cp37-cp37m-win32.whl", hash = "sha256:509ecd8334c380000d259dc66feb191dd0a93b21f2453faa75f7f9cdcefc0718"},
{file = "coverage-7.2.5-cp37-cp37m-win_amd64.whl", hash = "sha256:12580845917b1e59f8a1c2ffa6af6d0908cb39220f3019e36c110c943dc875b0"},
{file = "coverage-7.2.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b5016e331b75310610c2cf955d9f58a9749943ed5f7b8cfc0bb89c6134ab0a84"},
{file = "coverage-7.2.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:373ea34dca98f2fdb3e5cb33d83b6d801007a8074f992b80311fc589d3e6b790"},
{file = "coverage-7.2.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a063aad9f7b4c9f9da7b2550eae0a582ffc7623dca1c925e50c3fbde7a579771"},
{file = "coverage-7.2.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38c0a497a000d50491055805313ed83ddba069353d102ece8aef5d11b5faf045"},
{file = "coverage-7.2.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2b3b05e22a77bb0ae1a3125126a4e08535961c946b62f30985535ed40e26614"},
{file = "coverage-7.2.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0342a28617e63ad15d96dca0f7ae9479a37b7d8a295f749c14f3436ea59fdcb3"},
{file = "coverage-7.2.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cf97ed82ca986e5c637ea286ba2793c85325b30f869bf64d3009ccc1a31ae3fd"},
{file = "coverage-7.2.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c2c41c1b1866b670573657d584de413df701f482574bad7e28214a2362cb1fd1"},
{file = "coverage-7.2.5-cp38-cp38-win32.whl", hash = "sha256:10b15394c13544fce02382360cab54e51a9e0fd1bd61ae9ce012c0d1e103c813"},
{file = "coverage-7.2.5-cp38-cp38-win_amd64.whl", hash = "sha256:a0b273fe6dc655b110e8dc89b8ec7f1a778d78c9fd9b4bda7c384c8906072212"},
{file = "coverage-7.2.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5c587f52c81211d4530fa6857884d37f514bcf9453bdeee0ff93eaaf906a5c1b"},
{file = "coverage-7.2.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4436cc9ba5414c2c998eaedee5343f49c02ca93b21769c5fdfa4f9d799e84200"},
{file = "coverage-7.2.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6599bf92f33ab041e36e06d25890afbdf12078aacfe1f1d08c713906e49a3fe5"},
{file = "coverage-7.2.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:857abe2fa6a4973f8663e039ead8d22215d31db613ace76e4a98f52ec919068e"},
{file = "coverage-7.2.5-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6f5cab2d7f0c12f8187a376cc6582c477d2df91d63f75341307fcdcb5d60303"},
{file = "coverage-7.2.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:aa387bd7489f3e1787ff82068b295bcaafbf6f79c3dad3cbc82ef88ce3f48ad3"},
{file = "coverage-7.2.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:156192e5fd3dbbcb11cd777cc469cf010a294f4c736a2b2c891c77618cb1379a"},
{file = "coverage-7.2.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bd3b4b8175c1db502adf209d06136c000df4d245105c8839e9d0be71c94aefe1"},
{file = "coverage-7.2.5-cp39-cp39-win32.whl", hash = "sha256:ddc5a54edb653e9e215f75de377354e2455376f416c4378e1d43b08ec50acc31"},
{file = "coverage-7.2.5-cp39-cp39-win_amd64.whl", hash = "sha256:338aa9d9883aaaad53695cb14ccdeb36d4060485bb9388446330bef9c361c252"},
{file = "coverage-7.2.5-pp37.pp38.pp39-none-any.whl", hash = "sha256:8877d9b437b35a85c18e3c6499b23674684bf690f5d96c1006a1ef61f9fdf0f3"},
{file = "coverage-7.2.5.tar.gz", hash = "sha256:f99ef080288f09ffc687423b8d60978cf3a465d3f404a18d1a05474bd8575a47"},
]
[package.extras]
toml = ["tomli"]
[[package]]
name = "django"
version = "3.2.19"
description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design."
category = "main"
optional = false
python-versions = ">=3.6"
files = [
{file = "Django-3.2.19-py3-none-any.whl", hash = "sha256:21cc991466245d659ab79cb01204f9515690f8dae00e5eabde307f14d24d4d7d"},
{file = "Django-3.2.19.tar.gz", hash = "sha256:031365bae96814da19c10706218c44dff3b654cc4de20a98bd2d29b9bde469f0"},
]
[package.dependencies]
asgiref = ">=3.3.2,<4"
pytz = "*"
sqlparse = ">=0.2.2"
[package.extras]
argon2 = ["argon2-cffi (>=19.1.0)"]
bcrypt = ["bcrypt"]
[[package]]
name = "docutils"
version = "0.19"
description = "Docutils -- Python Documentation Utilities"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "docutils-0.19-py3-none-any.whl", hash = "sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc"},
{file = "docutils-0.19.tar.gz", hash = "sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6"},
]
[[package]]
name = "gvacommon"
version = "0.6.0"
description = ""
category = "main"
optional = false
python-versions = ">=3.7,<4.0"
files = [
{file = "gvacommon-0.6.0-py3-none-any.whl", hash = "sha256:89977984ae8fd76860c326b0221a3327c48b7f3349ef058541770f0d1a3a55d8"},
]
[package.dependencies]
django = "<4"
[package.source]
type = "legacy"
url = "https://pypi.gnuviech-server.de/simple"
reference = "gnuviech"
[[package]]
name = "idna"
version = "3.4"
description = "Internationalized Domain Names in Applications (IDNA)"
category = "dev"
optional = false
python-versions = ">=3.5"
files = [
{file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"},
{file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},
]
[[package]]
name = "imagesize"
version = "1.4.1"
description = "Getting image size from png/jpeg/jpeg2000/gif file"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
files = [
{file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"},
{file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"},
]
[[package]]
name = "importlib-metadata"
version = "4.13.0"
description = "Read metadata from Python packages"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "importlib_metadata-4.13.0-py3-none-any.whl", hash = "sha256:8a8a81bcf996e74fee46f0d16bd3eaa382a7eb20fd82445c3ad11f4090334116"},
{file = "importlib_metadata-4.13.0.tar.gz", hash = "sha256:dd0173e8f150d6815e098fd354f6414b0f079af4644ddfe90c71e2fc6174346d"},
]
[package.dependencies]
typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""}
zipp = ">=0.5"
[package.extras]
docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"]
perf = ["ipython"]
testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"]
[[package]]
name = "jinja2"
version = "3.1.2"
description = "A very fast and expressive template engine."
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"},
{file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"},
]
[package.dependencies]
MarkupSafe = ">=2.0"
[package.extras]
i18n = ["Babel (>=2.7)"]
[[package]]
name = "kombu"
version = "5.2.4"
description = "Messaging library for Python."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "kombu-5.2.4-py3-none-any.whl", hash = "sha256:8b213b24293d3417bcf0d2f5537b7f756079e3ea232a8386dcc89a59fd2361a4"},
{file = "kombu-5.2.4.tar.gz", hash = "sha256:37cee3ee725f94ea8bb173eaab7c1760203ea53bbebae226328600f9d2799610"},
]
[package.dependencies]
amqp = ">=5.0.9,<6.0.0"
cached-property = {version = "*", markers = "python_version < \"3.8\""}
importlib-metadata = {version = ">=0.18", markers = "python_version < \"3.8\""}
vine = "*"
[package.extras]
azureservicebus = ["azure-servicebus (>=7.0.0)"]
azurestoragequeues = ["azure-storage-queue"]
consul = ["python-consul (>=0.6.0)"]
librabbitmq = ["librabbitmq (>=2.0.0)"]
mongodb = ["pymongo (>=3.3.0,<3.12.1)"]
msgpack = ["msgpack"]
pyro = ["pyro4"]
qpid = ["qpid-python (>=0.26)", "qpid-tools (>=0.26)"]
redis = ["redis (>=3.4.1,!=4.0.0,!=4.0.1)"]
slmq = ["softlayer-messaging (>=1.0.3)"]
sqlalchemy = ["sqlalchemy"]
sqs = ["boto3 (>=1.9.12)", "pycurl (>=7.44.1,<7.45.0)", "urllib3 (>=1.26.7)"]
yaml = ["PyYAML (>=3.10)"]
zookeeper = ["kazoo (>=1.3.1)"]
[[package]]
name = "markupsafe"
version = "2.1.2"
description = "Safely add untrusted strings to HTML/XML markup."
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7"},
{file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036"},
{file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1"},
{file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323"},
{file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601"},
{file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1"},
{file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff"},
{file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65"},
{file = "MarkupSafe-2.1.2-cp310-cp310-win32.whl", hash = "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603"},
{file = "MarkupSafe-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156"},
{file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013"},
{file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a"},
{file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd"},
{file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6"},
{file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d"},
{file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1"},
{file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc"},
{file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0"},
{file = "MarkupSafe-2.1.2-cp311-cp311-win32.whl", hash = "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625"},
{file = "MarkupSafe-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3"},
{file = "MarkupSafe-2.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a"},
{file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a"},
{file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a"},
{file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2"},
{file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619"},
{file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513"},
{file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460"},
{file = "MarkupSafe-2.1.2-cp37-cp37m-win32.whl", hash = "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859"},
{file = "MarkupSafe-2.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666"},
{file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed"},
{file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094"},
{file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54"},
{file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419"},
{file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa"},
{file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58"},
{file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba"},
{file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03"},
{file = "MarkupSafe-2.1.2-cp38-cp38-win32.whl", hash = "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2"},
{file = "MarkupSafe-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147"},
{file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f"},
{file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd"},
{file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f"},
{file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4"},
{file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2"},
{file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65"},
{file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c"},
{file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3"},
{file = "MarkupSafe-2.1.2-cp39-cp39-win32.whl", hash = "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7"},
{file = "MarkupSafe-2.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed"},
{file = "MarkupSafe-2.1.2.tar.gz", hash = "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d"},
]
[[package]]
name = "packaging"
version = "23.1"
description = "Core utilities for Python packages"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"},
{file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"},
]
[[package]]
name = "prompt-toolkit"
version = "3.0.38"
description = "Library for building powerful interactive command lines in Python"
category = "main"
optional = false
python-versions = ">=3.7.0"
files = [
{file = "prompt_toolkit-3.0.38-py3-none-any.whl", hash = "sha256:45ea77a2f7c60418850331366c81cf6b5b9cf4c7fd34616f733c5427e6abbb1f"},
{file = "prompt_toolkit-3.0.38.tar.gz", hash = "sha256:23ac5d50538a9a38c8bde05fecb47d0b403ecd0662857a86f886f798563d5b9b"},
]
[package.dependencies]
wcwidth = "*"
[[package]]
name = "pygments"
version = "2.15.1"
description = "Pygments is a syntax highlighting package written in Python."
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "Pygments-2.15.1-py3-none-any.whl", hash = "sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1"},
{file = "Pygments-2.15.1.tar.gz", hash = "sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c"},
]
[package.extras]
plugins = ["importlib-metadata"]
[[package]]
name = "pytz"
version = "2023.3"
description = "World timezone definitions, modern and historical"
category = "main"
optional = false
python-versions = "*"
files = [
{file = "pytz-2023.3-py2.py3-none-any.whl", hash = "sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb"},
{file = "pytz-2023.3.tar.gz", hash = "sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588"},
]
[[package]]
name = "redis"
version = "4.5.4"
description = "Python client for Redis database and key-value store"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "redis-4.5.4-py3-none-any.whl", hash = "sha256:2c19e6767c474f2e85167909061d525ed65bea9301c0770bb151e041b7ac89a2"},
{file = "redis-4.5.4.tar.gz", hash = "sha256:73ec35da4da267d6847e47f68730fdd5f62e2ca69e3ef5885c6a78a9374c3893"},
]
[package.dependencies]
async-timeout = {version = ">=4.0.2", markers = "python_version <= \"3.11.2\""}
importlib-metadata = {version = ">=1.0", markers = "python_version < \"3.8\""}
typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
[package.extras]
hiredis = ["hiredis (>=1.0.0)"]
ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"]
[[package]]
name = "releases"
version = "2.1.1"
description = "A Sphinx extension for changelog manipulation"
category = "dev"
optional = false
python-versions = ">=3.6"
files = [
{file = "releases-2.1.1-py3-none-any.whl", hash = "sha256:cd06449c915ce9729aaebe19a836744998f83f9ebb0cf92e54949c88ab3402f1"},
{file = "releases-2.1.1.tar.gz", hash = "sha256:ae0683c3f309931a3717c6976e0f079929d05d34214b432a95764e61367f58d1"},
]
[package.dependencies]
semantic-version = "<2.7"
sphinx = ">=4"
[[package]]
name = "requests"
version = "2.29.0"
description = "Python HTTP for Humans."
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "requests-2.29.0-py3-none-any.whl", hash = "sha256:e8f3c9be120d3333921d213eef078af392fba3933ab7ed2d1cba3b56f2568c3b"},
{file = "requests-2.29.0.tar.gz", hash = "sha256:f2e34a75f4749019bb0e3effb66683630e4ffeaf75819fb51bebef1bf5aef059"},
]
[package.dependencies]
certifi = ">=2017.4.17"
charset-normalizer = ">=2,<4"
idna = ">=2.5,<4"
urllib3 = ">=1.21.1,<1.27"
[package.extras]
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
[[package]]
name = "semantic-version"
version = "2.6.0"
description = "A library implementing the 'SemVer' scheme."
category = "dev"
optional = false
python-versions = "*"
files = [
{file = "semantic_version-2.6.0-py3-none-any.whl", hash = "sha256:2d06ab7372034bcb8b54f2205370f4aa0643c133b7e6dbd129c5200b83ab394b"},
{file = "semantic_version-2.6.0.tar.gz", hash = "sha256:2a4328680073e9b243667b201119772aefc5fc63ae32398d6afafff07c4f54c0"},
]
[[package]]
name = "six"
version = "1.16.0"
description = "Python 2 and 3 compatibility utilities"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
files = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
]
[[package]]
name = "snowballstemmer"
version = "2.2.0"
description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms."
category = "dev"
optional = false
python-versions = "*"
files = [
{file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"},
{file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"},
]
[[package]]
name = "sphinx"
version = "5.3.0"
description = "Python documentation generator"
category = "dev"
optional = false
python-versions = ">=3.6"
files = [
{file = "Sphinx-5.3.0.tar.gz", hash = "sha256:51026de0a9ff9fc13c05d74913ad66047e104f56a129ff73e174eb5c3ee794b5"},
{file = "sphinx-5.3.0-py3-none-any.whl", hash = "sha256:060ca5c9f7ba57a08a1219e547b269fadf125ae25b06b9fa7f66768efb652d6d"},
]
[package.dependencies]
alabaster = ">=0.7,<0.8"
babel = ">=2.9"
colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""}
docutils = ">=0.14,<0.20"
imagesize = ">=1.3"
importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""}
Jinja2 = ">=3.0"
packaging = ">=21.0"
Pygments = ">=2.12"
requests = ">=2.5.0"
snowballstemmer = ">=2.0"
sphinxcontrib-applehelp = "*"
sphinxcontrib-devhelp = "*"
sphinxcontrib-htmlhelp = ">=2.0.0"
sphinxcontrib-jsmath = "*"
sphinxcontrib-qthelp = "*"
sphinxcontrib-serializinghtml = ">=1.1.5"
[package.extras]
docs = ["sphinxcontrib-websupport"]
lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-bugbear", "flake8-comprehensions", "flake8-simplify", "isort", "mypy (>=0.981)", "sphinx-lint", "types-requests", "types-typed-ast"]
test = ["cython", "html5lib", "pytest (>=4.6)", "typed_ast"]
[[package]]
name = "sphinxcontrib-applehelp"
version = "1.0.2"
description = "sphinxcontrib-applehelp is a sphinx extension which outputs Apple help books"
category = "dev"
optional = false
python-versions = ">=3.5"
files = [
{file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"},
{file = "sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a"},
]
[package.extras]
lint = ["docutils-stubs", "flake8", "mypy"]
test = ["pytest"]
[[package]]
name = "sphinxcontrib-devhelp"
version = "1.0.2"
description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document."
category = "dev"
optional = false
python-versions = ">=3.5"
files = [
{file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"},
{file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"},
]
[package.extras]
lint = ["docutils-stubs", "flake8", "mypy"]
test = ["pytest"]
[[package]]
name = "sphinxcontrib-htmlhelp"
version = "2.0.0"
description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files"
category = "dev"
optional = false
python-versions = ">=3.6"
files = [
{file = "sphinxcontrib-htmlhelp-2.0.0.tar.gz", hash = "sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"},
{file = "sphinxcontrib_htmlhelp-2.0.0-py2.py3-none-any.whl", hash = "sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07"},
]
[package.extras]
lint = ["docutils-stubs", "flake8", "mypy"]
test = ["html5lib", "pytest"]
[[package]]
name = "sphinxcontrib-jsmath"
version = "1.0.1"
description = "A sphinx extension which renders display math in HTML via JavaScript"
category = "dev"
optional = false
python-versions = ">=3.5"
files = [
{file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"},
{file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"},
]
[package.extras]
test = ["flake8", "mypy", "pytest"]
[[package]]
name = "sphinxcontrib-qthelp"
version = "1.0.3"
description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document."
category = "dev"
optional = false
python-versions = ">=3.5"
files = [
{file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"},
{file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"},
]
[package.extras]
lint = ["docutils-stubs", "flake8", "mypy"]
test = ["pytest"]
[[package]]
name = "sphinxcontrib-serializinghtml"
version = "1.1.5"
description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)."
category = "dev"
optional = false
python-versions = ">=3.5"
files = [
{file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"},
{file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"},
]
[package.extras]
lint = ["docutils-stubs", "flake8", "mypy"]
test = ["pytest"]
[[package]]
name = "sqlparse"
version = "0.4.4"
description = "A non-validating SQL parser."
category = "main"
optional = false
python-versions = ">=3.5"
files = [
{file = "sqlparse-0.4.4-py3-none-any.whl", hash = "sha256:5430a4fe2ac7d0f93e66f1efc6e1338a41884b7ddf2a350cedd20ccc4d9d28f3"},
{file = "sqlparse-0.4.4.tar.gz", hash = "sha256:d446183e84b8349fa3061f0fe7f06ca94ba65b426946ffebe6e3e8295332420c"},
]
[package.extras]
dev = ["build", "flake8"]
doc = ["sphinx"]
test = ["pytest", "pytest-cov"]
[[package]]
name = "typing-extensions"
version = "4.5.0"
description = "Backported and Experimental Type Hints for Python 3.7+"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "typing_extensions-4.5.0-py3-none-any.whl", hash = "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"},
{file = "typing_extensions-4.5.0.tar.gz", hash = "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb"},
]
[[package]]
name = "urllib3"
version = "1.26.15"
description = "HTTP library with thread-safe connection pooling, file post, and more."
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
files = [
{file = "urllib3-1.26.15-py2.py3-none-any.whl", hash = "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42"},
{file = "urllib3-1.26.15.tar.gz", hash = "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305"},
]
[package.extras]
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"]
secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"]
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
[[package]]
name = "vine"
version = "5.0.0"
description = "Promises, promises, promises."
category = "main"
optional = false
python-versions = ">=3.6"
files = [
{file = "vine-5.0.0-py2.py3-none-any.whl", hash = "sha256:4c9dceab6f76ed92105027c49c823800dd33cacce13bdedc5b914e3514b7fb30"},
{file = "vine-5.0.0.tar.gz", hash = "sha256:7d3b1624a953da82ef63462013bbd271d3eb75751489f9807598e8f340bd637e"},
]
[[package]]
name = "wcwidth"
version = "0.2.6"
description = "Measures the displayed width of unicode strings in a terminal"
category = "main"
optional = false
python-versions = "*"
files = [
{file = "wcwidth-0.2.6-py2.py3-none-any.whl", hash = "sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e"},
{file = "wcwidth-0.2.6.tar.gz", hash = "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0"},
]
[[package]]
name = "zipp"
version = "3.15.0"
description = "Backport of pathlib-compatible object wrapper for zip files"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"},
{file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"},
]
[package.extras]
docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"]
[metadata]
lock-version = "2.0"
python-versions = "^3.7"
content-hash = "1bdf2e39fdc05be7a0aaa245fd80ca653dd10dd480774577eb19d3c7d697f5e4"

31
pyproject.toml Normal file
View file

@ -0,0 +1,31 @@
[tool.poetry]
name = "gvafile"
version = "0.7.0"
description = "GNUViech Admin fileserver administration tool"
authors = ["Jan Dittberner <jan@dittberner.info>"]
license = "AGPL-3.0+"
readme = "README.rst"
[tool.poetry.dependencies]
python = "^3.7"
celery = "^5.2.7"
redis = "^4.5.1"
importlib-metadata = "<5"
gvacommon = {version = "^0.6.0", source = "gnuviech"}
[tool.poetry.group.dev.dependencies]
sphinx = "<6"
releases = "^2.0.0"
coverage = "^7.1.0"
[[tool.poetry.source]]
name = "gnuviech"
url = "https://pypi.gnuviech-server.de/simple"
default = false
secondary = false
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

View file

@ -1,3 +0,0 @@
# This file is here because many Platforms as a Service look for
# requirements.txt in the root directory of a project.
-r requirements/production.txt

View file

@ -1,11 +0,0 @@
Django==1.7.1
amqp==1.4.6
bpython==0.13.1
anyjson==0.3.3
argparse==1.2.1
billiard==3.3.0.19
celery==3.1.17
kombu==3.0.24
pytz==2014.10
wsgiref==0.1.2
PyYAML==3.11

View file

@ -1,7 +0,0 @@
# Local development dependencies go here
-r base.txt
coverage==3.7.1
django-debug-toolbar==1.2.2
Sphinx==1.2.3
sqlparse==0.1.14
releases==0.7.0

View file

@ -1,3 +0,0 @@
# Pro-tip: Try not to put anything here. There should be no dependency in
# production that isn't in development.
-r base.txt

View file

@ -1,3 +0,0 @@
# Test dependencies go here.
-r base.txt
coverage==3.7.1

12
salt/grains Normal file
View file

@ -0,0 +1,12 @@
gnuviechadmin:
user: vagrant
group: vagrant
checkout: /vagrant
home: /home/vagrant
update_git: False
roles:
- vagrant
- ldapclient
- nfsserver
- gnuviechadmin.gvafile

11
salt/minion Normal file
View file

@ -0,0 +1,11 @@
file_client: local
file_roots:
base:
- /srv/salt/
pillar_roots:
base:
- /srv/pillar
log_file: file:///dev/log