{"id":19342908,"url":"https://github.com/violantecodes/anonymous-gitlab-ticketing-for-tor","last_synced_at":"2026-05-05T20:33:16.482Z","repository":{"id":131719682,"uuid":"342424893","full_name":"ViolanteCodes/Anonymous-GitLab-Ticketing-For-Tor","owner":"ViolanteCodes","description":"Clone of my Django web application, built for Tor, which allows users to create anonymous tickets on the Tor Browser's GitLab instance by leveraging the GitLab API (Python-Gitlab package). Created as an Outreachy winter of 2021 internship project.","archived":false,"fork":false,"pushed_at":"2021-02-26T01:14:51.000Z","size":5128,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-01T15:48:23.702Z","etag":null,"topics":["api","django","gitlab","privacy","python","tor"],"latest_commit_sha":null,"homepage":"https://anonticket.onionize.space","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ViolanteCodes.png","metadata":{"files":{"readme":"readme.MD","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-02-26T01:13:00.000Z","updated_at":"2021-06-22T19:47:05.000Z","dependencies_parsed_at":null,"dependency_job_id":"432d647c-a593-449d-b0e5-aa17659b498f","html_url":"https://github.com/ViolanteCodes/Anonymous-GitLab-Ticketing-For-Tor","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ViolanteCodes%2FAnonymous-GitLab-Ticketing-For-Tor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ViolanteCodes%2FAnonymous-GitLab-Ticketing-For-Tor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ViolanteCodes%2FAnonymous-GitLab-Ticketing-For-Tor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ViolanteCodes%2FAnonymous-GitLab-Ticketing-For-Tor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ViolanteCodes","download_url":"https://codeload.github.com/ViolanteCodes/Anonymous-GitLab-Ticketing-For-Tor/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ViolanteCodes%2FAnonymous-GitLab-Ticketing-For-Tor/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":258681688,"owners_count":22740556,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","django","gitlab","privacy","python","tor"],"created_at":"2024-11-10T03:36:40.882Z","updated_at":"2026-05-05T20:33:16.431Z","avatar_url":"https://github.com/ViolanteCodes.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Anon-Ticket: Anonymous Gitlab Reporting for Tor\n\nA web application that allows users to create\nanonymous tickets on the Tor Browser's GitLab instance by leveraging\nthe GitLab API (Python-Gitlab package). Created as an\n[Outreachy](https://www.outreachy.org/) winter of 2021 internship project\nby [Maria Violante](https://mviolante.com), who is also the \ncurrent maintainer.\n\nFor bug reports and feature requests, please consider submitting an \nanonymous ticket ... to Anon-Ticket ... via [Anon-Ticket](https://anonticket.onionize.space/)! ;)\n\u003cbr\u003e\n\n***\n\n## Table of Contents:\n\n1. Intro and Setup\n    - 1.1 Quickstart\n    - 1.2 Project Aim and Use\n2. Notes for Admins\n    - 2.1 Fast Project Add From Gitlab\n    - 2.2 Programmatic Groups and Permissions\n    - 2.3 Adding Moderators and Account Approvers\n3. Project Structure and Function:\n    - 3.1 Folder Layout\n    - 3.2 Anon-Ticket Request-Response in a Nutshell\n    - 3.3 Models\n    - 3.4 Views\n    - 3.5 URL Pathing\n    - 3.6 Templates\n4. Packages\n    - 4.1 General notes\n    - 4.2 Python-Decouple\n    - 4.3 Python-Gitlab\n    - 4.4 Django-Markdownify\n    - 4.5 Django-Ratelimit\n    - 4.6 Django-Test-Plus\n    - 4.7 Python Coverage\n5. Tests\n    - 5.0 Running Tests\n    - 5.1 Tagged Tests\n    - 5.2 Run with Coverage\n\n***\n\n## 1.0 Intro\n\n***\n\n### 1.1 QuickStart\n\n***\nSETTINGS:\n\n1.  Clone the repo from a fork.\n2.  Rename the env_sample.txt file to just “.env”. \n3.  Open it and delete first line.\n4.  Set the secret key to one of your choosing.\n5.  Set GITLAB_SECRET_TOKEN as your Tor GitLab account token.\n6.  If you have the ability to make user accounts, you can set the\n    GITLAB_ACCOUNTS_SECRET_TOKEN variable.\n7.  Set the GITLAB_TIMEOUT to a number of seconds.\n8.  Enter a value in MAIN_RATE_GROUP - can be any string of your choosing.\n9.  Enter a value in LIMIT_RATE using an integer, a slash, and a unit of\n    time for the denominator, e.g., 1/s, 10/m, 100/hr, etc.\n10. BLOCK_ALL should be left as false, setting as True will block all\n    POST requests from forms in Anon-Ticket.\n11. If running on a server, add an extra host in ALLOWED_HOSTS.\n\nINSTALLATION:\n\nRun the following commands:\n\n```\n1. Make a virtual environment:\n    $ python3 -m venv myvenv\n2. Activate the environment:\n    $ source myvenv/bin/activate\n3. Install required packages:\n    $ pip install -r requirements.txt\n4. If you have not set up your .env file, do it now, as you will need some of these\n    settings in place to make migrations or runserver.\n5. Make all migrations to set up database.\n    $ python manage.py makemigrations\n    (It may say nonothing to migrate, this is fine.)\n6. Migrate the database:\n    $ python manage.py migrate\n7. You can check or launch by using the \"runserver command.\"\n    $ python manage.py runserver\n8. Stop runserver and create a superuser\n    $ python manage.py createsuperuser\n    (follow the prompts)\n9. Create groups \"Moderators\" and \"Account Approvers\"(see explanation above):\n    $ python manage.py create_groups\n\n```\nWhen `runserver` is running, you should be able to point your browser to\n\u003chttp://127.0.0.1:8000/\u003e and reach the local version of the application.\n\n*** \n\n### 1.2 Project Aim and Use\n\n***\n\nAnon-Ticket is Django/Bootstrap web portal designed to allow anonymous\nusers to submit tickets to supported GitLab repos without signing up\nfor an account with GitLab.\n\nUsers do not interact with the system via a username/password or\nemail combination. Instead, when they first arrive at the home screen,\nthey have the option to create a new user identifier, which is a\ncode phrase made via random dice rolls from the EFF's New Wordslist \nfor Random Passphrases.\n\nOnce a user identifier has been created, and the user has chosen to log\nin with the identifier, they are taken to a landing page specific to \ntheir user identifier, which is passed forward through the system at \neach step as a kwarg in the URL path. Each time a new page is loaded \nor an object is created, the user identifier prhase is checked by a \nvalidator.\n\nFrom the landing page, the user has the optiont to view supported\nrepos and their issues/notes, search for a specific issue, or create\na new issue or note of their own. \n\nOnce an object is created by the user, it's held in a pending status\nfor a moderator. The moderator view displays multiple simultaneous \nformsets demonstrating all pending issues, notes, and (coming soon!)\nGitLab account reqquests that the moderator has permissions to view.\n(To that end, there are two different groups of moderator permissions:\n\"moderator\", which can see/approve pending issues and notes, and \n\"account approvers\", which can see/approve pending GitlLab account\nrequests. A moderator can belong to either or both groups.) Moderators\nalso have the option to edit a submitted ticket or write a comment\non a ticket for other moderators.\n\nThe final major view is a customized admin panel for the superuser, \nwhich includes display filters for items by status, e.g., \"all pending\nissues\", as well as the ability to add projects to the repo with only \nthe project's GitLab ID. Once the ID is filled in and the project is \nsaved, the rest of the project details automatically populate from GitLab.\nThese features are described in greater detail below in section 2.0:\nNotes for admins.\n\n\u003cbr\u003e\n\n***\n\n## 2.0 Notes for Admins\n\n***\n\n### 2.1 Fast Project Add from GitLab\t\nIn order to use Anon-Ticket to receive issues, notes, and gitlab account \nrequests, a project has to be first be added to the database by a \nsuperuser via the admin panel. The only piece of information needed to \ndo this is the ***project’s Gitlab ID number***, available on that \nproject’s gitlab page. Once the number is filled in and the project is \nsaved, Anon-Ticket will ***automatically fetch*** all necessary project \ninformation from the GitLab API, including the group, title, description, \nweb_urls, etc.\n\nAnon-Ticket will also check the GitLabGroup objects to see if a\nmatching group already exists in the database; if not, Anon-Ticket will\n***automatically create*** the GitlabGroup object, including fetching \nthe information from Gitlab, creating the group, and assigning the\nproject to the relevant group.\n\u003cbr\u003e\n\n### 2.2 Programmatic Groups and Permissions:\n\nOnce the project has been installed and migrations applied, Moderator\nand Account Approvers can be created from the command line:\n\n$ python manage.py create_groups\n\nAnon-Ticket will automatically create two groups, \"Moderators\" and \n\"Account Approvers\", and assign the permissions defined in the dictionaries\nin /anonticket/management/commands/create_groups.py. These permissions \ncan be changed by changing this file. These Group names are important; \nthey are used during the authentication process for Moderator and \nAccount-Approver specific views.\n\nThe \"Moderators\" will automatically have the following permissions assigned:\n- View User Identifier\n- View Git Lab Group\n- View Project\n- Add/Delete/Change/View Issue\n- Add/Delete/Change/View Note\n\nThe \"Account Approvers\" will automatically have the following permissions assigned:\n- View User Identifier\n- Add/Delete/Change/View Gitlab Account Request\n\nIf, in the future, you wish to update the permissions for the group, you\ncan do so via the admin panel, but it's recommended to update the file\nin anonticket/management/commands/create_groups.py and then rerun \n“python manage.py create_groups”  instead, as this will ensure \nconsistency at a later date. All users assigned to a group will have\ntheir permissions updated.\n\n### 2.3 Adding Moderators and Account Approvers (Users):\n\n1. Create the user in the admin panel - only username and email are \nnecessary.\n2. Assign \"staff\" status; without \"staff\", the user will not be able to\nlog into the system to perform moderation tasks.\n3. Assign the group \"Moderators\" to users that will be editing notes\nand issues.\n4. Assign the group \"Account Approvers\" to users that will be approving\nGitlab Account Requests (this functionality has not yet been added to\nAnon-Ticket, but should be coming at a future date.)\n5. Note: Users can be in more than one group.\n\n\u003cbr\u003e\n\n***\n\n## 3.0 Project Structure and Function\n\n***\n\n### 3.1 Folder Structure:\nDjango applications or projects generally contain a main project folder \n(in this case 'ticketlobby'), as well as several app folders. Apps can \nbe turned “on” or “off” by changing the allowed_apps setting in  \n/ticketlobby/settings.py.\n\nThis project has been structured in such a way to maximize the potential\nfor later development and expansion. It is currently divided into four \nmain folders:\n\n1. ***/ticketlobby***: The main project folder, which includes the\nsettings.py file.\n\n2. ***/anonticket***: The folder for the AnonTicket app.\n\n3. ***/shared***: This is a pseudo-app, primarily for static files and \ntemplates likely to be expanded or utilized in other parts of the project.\nIt also includes custom template tags, filters, and middleware.\n\n4. ***/gl_bot***: This folder contains the python file and tests for \nGitLabDownObject, a mock python-gitlab gitlab.GitLab object, which is \ninstantiated in case of a timeout error from GitLab. Instead of having\naffected views reroute to a \"GitLab is down right now\" page, the \nGitLabDownObject returns a mock project and issue detailing the problem\nwhile allowing the view to render normally.\n\n### 3.2 Anon-Ticket Request-Response in a Nutshell \n\nWhen a user navigates to an URL associated with this project, \nDjango matches the URL to the appropriate pattern in urls.py. It strips \nany needed arguments from the URL based on the logic in urls.py, and, \nbased on urls.py, determines which VIEW to use. The view itself may \ncontain logic, including what to do with any arguments from the URL, how \nto handle forms, GET vs POST requests, when to communicate with the database, etc. \n\nOnce those steps are carried out, the view generally instructs Django to \nreturn a redirect to another view, or to render an html page using one \nor more html TEMPLATES. Arguments for rendering the html template are \nusually passed to the template context as a dictionary (associative array),\nand can be called by the template (e.g., if the dictionary is passed as \n{results}, a {{results.user_identifier}} call in the template is equivalent to \nresults['user_identifier'].) Context dictionaries can contain strings, lists, or \nother dictionaries.\n\nIn order to increase privacy for end users, who may wish to be anonymous,\nusers do not authenticate via a standard username/password cookie method. \nInstead, once a User Identifier code-phrase is created, it is passed \nto views via an arg/kwarg from the URL path, e.g., \u003c/user/\u003cstr:identifier\u003e,\ninto a dictionary called \"results\". In order to create consistency between\nclass based views (CBV's) and function-based views (FBV's), both of which\nare utilized in this project, a Mixin has been created called\nPassUserIdentifierMixin, which passed the user_identifier kwarg (if it\nexists) from the URL to the CBV's context dictionary inside of another \ndictionary called \"results\". This minimzes code duplication across \nviews and allows developers to repurpose the same template for CBVs and\nFBVs.\n\n### 3.3 Models\n\nThe models for database objects are in anonticket/models.py, with the exception of\nthe GitLabDownObject, which is in gl_bot/gitlabdown.py. Additionally, the models for\nmost of the forms are in anonticket/forms.py\n\n### 3.4 Views\n\nThe 'engine' that drives this project is primarily contained in anonticket/views.py. \nAt current, this project uses a mixture of class-based views and function-based views.\nEach view is explained with a doc-string and has comments throughout to explain specific\nfunctions from within the view.\n\nIf a view relies heavily on a form, processing for that view may have been moved to forms.py.\n\nAdditionally, views can leverage decorators like @validate_user, which\nwraps the view in a function that determines if the user_identifier codephrase in the \nURL path meets validation requirements; if not, it redirects the user to an invalid\nuser_identifier view.\n\n### 3.5 URL-Pathing\n\nThe main URL structures at this time are located in anonticket/urls.py (and all of \nthese URLs are included in the main URLS.py located at ticketlobby/urls.py) Values that\ncontain arguments like \u003cstr:identifier\u003e pass that argument as an arg/kwarg to be used by the \nassociated view. \n\nUser Identifier code-phrases are not saved to the database until they have been used\nto perform an action, such as create a ticket. As such, there are validation functions\nin the views which take arguments from the URL as noted above.\n\n### 3.6 Templates\n\nTemplates specific to the anonticket portion of this project are in anonticket/templates/anonticket.\nThe repetition of anonticket above is Django's recommend method for namespacing; as Django will\nsearch for templates within *any* app folder called 'templates', this name-spacing prevents confusion.\n\nAdditionally, there is a folder called 'templates' in 'shared'; here, 'shared' is \na pseudo-app that contains files that will be used across various apps in this project. The\noverall layout template, including side-bar menu, is here, as well as the css files, fonts,\nand other static files (like images.) The CSS is based on Tor Project's style-guide \n(styleguide.torproject.com), which uses bootstrap templates for layout.\n\n***\n\n## 4.0 Packages\n\n***\n\n### 4.1 Notes on Packages:\n\nThe packages in the requirements.txt file are necessary for the ticketlobby\nto function.\n\nEnvironment variables are tracked using the python-decouple package, which\nincreases security by moving important keys, tokens, etc. to the .env\nfile located in the base folder. If you're unable to perform \nmanage.py runserver, make sure you have set something in the SECRET_KEY\nfield in the .env file, and that DEBUG is set to \"True\" in the .env. \nIf DEBUG is False, you will need to make sure something is filled in for\nthe ALLOWED_HOSTS field.\n\n### 4.2 Python-Decouple\n\nThis package is used to easily extract settings from the .env file.\nSettings are called with a config() function, e.g., \nmy_key = config(MY_KEY) would pull the line MY_KEY = (value) from the\n.env file and assign it to the variable my_key. \n\nBy default, all values pulled from python-decouple are strings; in those\ninstances where another data type (such as boolean, int, etc) are needed,\ncall the variable with the \"cast\" parameter, e.g:\nmy_key = config(MY_KEY, cast=bool).\n\nMore information available at: [https://github.com/henriquebastos/python-decouple/]\n\n### 4.3 Python-GitLab\n\nThe anonticket app uses the Python-Gitlab package to communicate with \nthe GitLab API. If GitLab is down, anonticket will instantiate a \nmock python-gitlab object from gl_bot/gitlabdown.py that takes \nthe same calls as the python-gitlab object, but will instead \nreturn a message stating that GitLab appears down.\n\nThe dictionary that describes an object (issue, note, etc) is\nusually returned by getting object.attributes from python-gitlab,\ne.g., my_issue = my_gitlab_object.issues.get(issue_iid), dict = \nmy_issue.attributes.\n\nSome sample pretty-printed reference files to demonstrate dictionaries \nreturned by get queries, including project, isssue and note dictionaries, \nare available in shared/reference_files.\n\nDocumentation at: [https://python-gitlab.readthedocs.io/en/stable/]\n\n### 4.4 Django-Markdownify\n\nPython-Django package that allows you to use a 'markdownify' filter \nto render markdown as html, including safe functions. Automatically\ninstalls Markdown and Bleach dependencies. \n\nTo run, markdownify is added as an app in ticketlobby/settings.py \n(and can be disabled by removing that line.)\n\nTo load into a template, use the {% loadmarkdownify %} template tag.\nAdd '|markdownify|' as a filter where you want markdown rendered as \nhtml.\n\nDocumentation here: [https://django-markdownify.readthedocs.io/en/latest/index.html]\n\n### 4.5 Django-Ratelimit\n\nAnon-Ticket uses the Django-Ratelimit package. Two custom decorators \nhave been written for Anon-Ticket, expanding on the standard ip and \nkey decorators included with the package: @custom_ratelimit_ip, \nand @custom_ratelimit_post. These custom decorators default to settings \nspecified in the settings.py file, as well as taking a callable function, \n\"get_rate_limit()\", which pulls the desired limit-rate out of the .env file. \nIf BLOCK_ALL is set to \"True\" in the .env file, all POST functions on \ndecorated views will immediately be disabled, protecting the site \nin the event of an attack.\n\nAdditionally, a custom MiddleWare has been included in shared.middleware \nto faciliate rate-limiting with a reverse-proxy enabled.\n\n### 4.6 Django-Test-Plus\n\nSeveral of the tests use DjangoTestPlus, which adds extra functions to\nTestCase, allowing for easier url/view/integration tests. Tests that \ndo not utilize Django-Test-Plus are slowly being rewritten to \nincorporate it due to the ease of use.\n\nMore information is available here: \n[https://django-test-plus.readthedocs.io/en/latest/]\n\n### 4.7 Python-Coverage\n\nTest coverage tends to hover around 94 percent as measured by \npython-coverage. More information about python-coverage is available\nbelow in 5.0: Tests. Docs for python-coverage are also available \nat [https://coverage.readthedocs.io/en/coverage-5.4/].\n\n***\n\n## 5.0 Tests\n\n***\n\n### 5.1 Running Tests\n\nThere are multiple tests.py files containing tests, including \nanonticket/tests.py and gl_bot/tests.py. All tests can be run\nat once from the command line via $ python manage.py test.\n\n1. To run tests from the command line without coverage:\n\n$ python manage.py test\n\nNote that during the running of the tests, the console will likely\nraise several exceptions; these can be disregarded as long as the \ntest itself passes. For example, rate-limit tests are designed to\nraise a 403 error to ensure the rate-limiting is applied correctly.\n\n2. If you would like a more verbose output from testing (e.g, with test_names),\nyou can add -v1, -v2, or -v3 as an argument:\n\n$ python manage.py test -v2\n\n3. Note that several of the tests have a tearDown method that \nincludes clearing the cache; this is necessary as the test battery includes\ntests designed to exceed the limit-rate, which provokes a 403 forbidden response. \n\n### 5.2 Tagged Tests\n\nMany of the tests are also tagged using the @tag decorator. So, for \nexample, anonticket/tests.py has multiple test classes tagged with \n@tag('shared-non-gitlab'), like class TestUserIdentifierInDatabase(TestCase).\n\nTo just run tests associated with a particular tag, use --tag:\n\n$ python manage.py test --tag shared-non-gitlab\n\nNote that if the terminal reports 0 tests run in 0.00 seconds, there \nis likely an error in one of the tests. To diagnose the error, just\nrun the normal test suite ($ python manage.py test)\n\n### 5.3 Run With Python-Coverage\n\nCoverage is verified through python coverage.\n[https://coverage.readthedocs.io/en/latest/]. Coverage includes a C \nextension for speed (that is also required to execute some functions.) \nOnce requirements have all installed from requirements.txt, you can \nverify the C extension is installed correctly with \n\n$ coverage --version\n\nwhich should return \"with C extension\" or \"without C extension\".\n[https://coverage.readthedocs.io/en/latest/install.html]\n\nTesting sets up a test_database and should not interact with the project's\nactual database.\n\n1. To run tests with coverage (using Python-Coverage package):\n\n$ coverage erase\n$ coverage run manage.py test\n\n2. To access the coverage data, you can either type:\n\n$ coverage report \n\nto see the coverage report generated on-screen in the terminal, or \n\n$ coverage html\n\nto generate a folder called htmlcov in the base directory. If you\nopen htmlcov/index.html, you'll see an easy to parse, color-coded \nversion of the report with pretty tables.\n\nNote that files with 100 percent coverage will not show up \nin the report. ","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fviolantecodes%2Fanonymous-gitlab-ticketing-for-tor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fviolantecodes%2Fanonymous-gitlab-ticketing-for-tor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fviolantecodes%2Fanonymous-gitlab-ticketing-for-tor/lists"}