├── .coveragerc ├── .fmf └── version ├── .github ├── renovate.json └── workflows │ ├── label-when-deployed.yaml │ ├── main.yml │ └── stale.yml ├── .gitignore ├── .gitleaks.toml ├── .mergify.yml ├── .packit.yaml ├── .pre-commit-config.yaml ├── .readthedocs.yml ├── .rstcheck.cfg ├── .s2i ├── bin │ └── assemble └── environment ├── Dockerfile ├── LICENSE ├── README.md ├── Vagrantfile ├── babel.cfg ├── data-uri-to-svg.py ├── deployment ├── nginx.conf ├── noggin.cfg.example ├── noggin.service ├── noggin.sysconfig └── scripts │ └── sar.py ├── devel ├── ansible │ ├── noggin.yml │ ├── roles │ │ ├── cert │ │ │ ├── defaults │ │ │ │ └── main.yml │ │ │ └── tasks │ │ │ │ └── main.yml │ │ ├── common │ │ │ └── tasks │ │ │ │ └── main.yml │ │ ├── ipa-client │ │ │ └── tasks │ │ │ │ └── main.yml │ │ ├── noggin │ │ │ ├── files │ │ │ │ ├── bashrc │ │ │ │ ├── noggin.service │ │ │ │ └── synced-folder.service │ │ │ ├── handlers │ │ │ │ └── main.yml │ │ │ ├── tasks │ │ │ │ └── main.yml │ │ │ └── templates │ │ │ │ └── noggin.cfg │ │ └── postgres │ │ │ └── tasks │ │ │ └── main.yml │ └── vars.yml ├── create-test-data.py ├── rebuild-cassettes.sh └── run-liccheck.sh ├── docs ├── Makefile ├── _static │ └── screenshots │ │ ├── adduser1.png │ │ ├── adduser2.png │ │ ├── deleteuser.png │ │ ├── groupscreen-sponsorview.png │ │ ├── leave-group.png │ │ ├── newaccount1.png │ │ ├── newaccount2.png │ │ ├── newaccount3.png │ │ ├── otp1.png │ │ ├── otp2.png │ │ └── otp3.png ├── conf.py ├── contributing.rst ├── index.rst ├── installation │ ├── 01-freeipa-server.rst │ └── 02-freeipa-client.rst ├── release_notes.md └── userguide.rst ├── news ├── +freeipa_servers.feature ├── +max_password_length.bug ├── +popovers.bug ├── +py312.feature ├── +safety.dev.md ├── 1240.feature ├── 1415.feature ├── 1430.bug ├── 1447.feature ├── 1475.bug.md ├── 1484.feature.md ├── PR1432.other ├── _template.md └── get-authors.py ├── noggin ├── __init__.py ├── app.py ├── controller │ ├── __init__.py │ ├── authentication.py │ ├── group.py │ ├── password.py │ ├── registration.py │ ├── root.py │ └── user.py ├── defaults.py ├── form │ ├── __init__.py │ ├── base.py │ ├── edit_user.py │ ├── group.py │ ├── login_user.py │ ├── password_reset.py │ ├── register_user.py │ ├── sync_token.py │ └── validators.py ├── l10n.py ├── middleware.py ├── representation │ ├── __init__.py │ ├── agreement.py │ ├── base.py │ ├── group.py │ ├── otptoken.py │ └── user.py ├── security │ ├── __init__.py │ ├── ipa.py │ └── ipa_admin.py ├── signals.py ├── static │ ├── css │ │ └── main.css │ ├── fonts │ │ ├── font-awesome.css │ │ └── font-awesome │ │ │ ├── FontAwesome.otf │ │ │ ├── fontawesome-webfont.eot │ │ │ ├── fontawesome-webfont.svg │ │ │ ├── fontawesome-webfont.ttf │ │ │ ├── fontawesome-webfont.woff │ │ │ └── fontawesome-webfont.woff2 │ └── js │ │ ├── search.js │ │ └── vendor │ │ ├── corejs-typeahead-1.2.1 │ │ ├── bloodhound.min.js │ │ └── typeahead.jquery.min.js │ │ ├── jquery-qrcode-1.0 │ │ ├── MIT-LICENSE.txt │ │ ├── README.md │ │ └── jquery.qrcode.min.js │ │ ├── jquery.dirty-0.7.2 │ │ ├── LICENSE │ │ ├── README.md │ │ └── jquery.dirty.js │ │ ├── moment-2.24.0 │ │ └── moment.js │ │ ├── moment-timezone-0.5.28-2019c │ │ └── moment-timezone-with-data-10-year-range.js │ │ └── selectize-0.12.6 │ │ ├── selectize.bootstrap3.css │ │ ├── selectize.js │ │ └── selectize.min.js ├── templates │ ├── 404.html │ ├── _agreements_form.html │ ├── _form_macros.html │ ├── _login_form.html │ ├── _pagination.html │ ├── _register_form.html │ ├── base.html │ ├── forgot-password-ask.html │ ├── forgot-password-change.html │ ├── group.html │ ├── groups.html │ ├── index.html │ ├── ipa_error.html │ ├── password-reset.html │ ├── registering.html │ ├── registration-activation.html │ ├── registration-agreements.html │ ├── registration-confirmation.html │ ├── registration-spamcheck-wait.html │ ├── sync-token.html │ ├── user-settings-agreements.html │ ├── user-settings-email-validation.html │ ├── user-settings-email.html │ ├── user-settings-keys.html │ ├── user-settings-otp.html │ ├── user-settings-password.html │ ├── user-settings-profile.html │ ├── user-settings.html │ └── user.html ├── themes │ ├── __init__.py │ ├── almalinux │ │ ├── static │ │ │ ├── css │ │ │ │ └── default.css │ │ │ ├── favicon │ │ │ │ ├── apple-touch-icon.png │ │ │ │ ├── favicon-16x16.png │ │ │ │ ├── favicon-32x32.png │ │ │ │ ├── favicon.ico │ │ │ │ ├── safari-pinned-tab.svg │ │ │ │ └── site.webmanifest │ │ │ ├── fonts │ │ │ │ ├── Montserrat-Black.woff │ │ │ │ ├── Montserrat-Black.woff2 │ │ │ │ ├── Montserrat-BlackItalic.woff │ │ │ │ ├── Montserrat-BlackItalic.woff2 │ │ │ │ ├── Montserrat-Bold.woff │ │ │ │ ├── Montserrat-Bold.woff2 │ │ │ │ ├── Montserrat-BoldItalic.woff │ │ │ │ ├── Montserrat-BoldItalic.woff2 │ │ │ │ ├── Montserrat-ExtraBold.woff │ │ │ │ ├── Montserrat-ExtraBold.woff2 │ │ │ │ ├── Montserrat-ExtraBoldItalic.woff │ │ │ │ ├── Montserrat-ExtraBoldItalic.woff2 │ │ │ │ ├── Montserrat-ExtraLight.woff │ │ │ │ ├── Montserrat-ExtraLight.woff2 │ │ │ │ ├── Montserrat-ExtraLightItalic.woff │ │ │ │ ├── Montserrat-ExtraLightItalic.woff2 │ │ │ │ ├── Montserrat-Italic.woff │ │ │ │ ├── Montserrat-Italic.woff2 │ │ │ │ ├── Montserrat-Light.woff │ │ │ │ ├── Montserrat-Light.woff2 │ │ │ │ ├── Montserrat-LightItalic.woff │ │ │ │ ├── Montserrat-LightItalic.woff2 │ │ │ │ ├── Montserrat-Medium.woff │ │ │ │ ├── Montserrat-Medium.woff2 │ │ │ │ ├── Montserrat-MediumItalic.woff │ │ │ │ ├── Montserrat-MediumItalic.woff2 │ │ │ │ ├── Montserrat-Regular.woff │ │ │ │ ├── Montserrat-Regular.woff2 │ │ │ │ ├── Montserrat-SemiBold.woff │ │ │ │ ├── Montserrat-SemiBold.woff2 │ │ │ │ ├── Montserrat-SemiBoldItalic.woff │ │ │ │ ├── Montserrat-SemiBoldItalic.woff2 │ │ │ │ ├── Montserrat-Thin.woff │ │ │ │ ├── Montserrat-Thin.woff2 │ │ │ │ ├── Montserrat-ThinItalic.woff │ │ │ │ ├── Montserrat-ThinItalic.woff2 │ │ │ │ ├── Montserrat.css │ │ │ │ ├── MontserratAlternates-Black.woff │ │ │ │ ├── MontserratAlternates-Black.woff2 │ │ │ │ ├── MontserratAlternates-BlackItalic.woff │ │ │ │ ├── MontserratAlternates-BlackItalic.woff2 │ │ │ │ ├── MontserratAlternates-Bold.woff │ │ │ │ ├── MontserratAlternates-Bold.woff2 │ │ │ │ ├── MontserratAlternates-BoldItalic.woff │ │ │ │ ├── MontserratAlternates-BoldItalic.woff2 │ │ │ │ ├── MontserratAlternates-ExtraBold.woff │ │ │ │ ├── MontserratAlternates-ExtraBold.woff2 │ │ │ │ ├── MontserratAlternates-ExtraBoldItalic.woff │ │ │ │ ├── MontserratAlternates-ExtraBoldItalic.woff2 │ │ │ │ ├── MontserratAlternates-ExtraLight.woff │ │ │ │ ├── MontserratAlternates-ExtraLight.woff2 │ │ │ │ ├── MontserratAlternates-ExtraLightItalic.woff │ │ │ │ ├── MontserratAlternates-ExtraLightItalic.woff2 │ │ │ │ ├── MontserratAlternates-Italic.woff │ │ │ │ ├── MontserratAlternates-Italic.woff2 │ │ │ │ ├── MontserratAlternates-Light.woff │ │ │ │ ├── MontserratAlternates-Light.woff2 │ │ │ │ ├── MontserratAlternates-LightItalic.woff │ │ │ │ ├── MontserratAlternates-LightItalic.woff2 │ │ │ │ ├── MontserratAlternates-Medium.woff │ │ │ │ ├── MontserratAlternates-Medium.woff2 │ │ │ │ ├── MontserratAlternates-MediumItalic.woff │ │ │ │ ├── MontserratAlternates-MediumItalic.woff2 │ │ │ │ ├── MontserratAlternates-Regular.woff │ │ │ │ ├── MontserratAlternates-Regular.woff2 │ │ │ │ ├── MontserratAlternates-SemiBold.woff │ │ │ │ ├── MontserratAlternates-SemiBold.woff2 │ │ │ │ ├── MontserratAlternates-SemiBoldItalic.woff │ │ │ │ ├── MontserratAlternates-SemiBoldItalic.woff2 │ │ │ │ ├── MontserratAlternates-Thin.woff │ │ │ │ ├── MontserratAlternates-Thin.woff2 │ │ │ │ ├── MontserratAlternates-ThinItalic.woff │ │ │ │ ├── MontserratAlternates-ThinItalic.woff2 │ │ │ │ └── README.MD │ │ │ ├── images │ │ │ │ └── almalinux-account-services.svg │ │ │ └── vendor │ │ │ │ ├── bootstrap-4.3.1 │ │ │ │ ├── 0.svg │ │ │ │ ├── 1.svg │ │ │ │ ├── 2.svg │ │ │ │ ├── 3.svg │ │ │ │ ├── 4.svg │ │ │ │ ├── 5.svg │ │ │ │ ├── 6.svg │ │ │ │ ├── 7.svg │ │ │ │ ├── 8.svg │ │ │ │ ├── 9.svg │ │ │ │ ├── bootstrap.min.css │ │ │ │ ├── bootstrap.min.js │ │ │ │ └── bootstrap.min.nodata.css │ │ │ │ ├── font-awesome-4.7.0 │ │ │ │ ├── css │ │ │ │ │ └── font-awesome.min.css │ │ │ │ └── fonts │ │ │ │ │ ├── fontawesome-webfont.eot │ │ │ │ │ ├── fontawesome-webfont.svg │ │ │ │ │ ├── fontawesome-webfont.ttf │ │ │ │ │ ├── fontawesome-webfont.woff │ │ │ │ │ └── fontawesome-webfont.woff2 │ │ │ │ ├── jquery │ │ │ │ └── jquery-3.3.1.min.js │ │ │ │ └── popper-1.14.7 │ │ │ │ └── popper.min.js │ │ └── templates │ │ │ ├── email-validation.html │ │ │ ├── email-validation.txt │ │ │ ├── forgot-password-email.html │ │ │ ├── forgot-password-email.txt │ │ │ ├── main.html │ │ │ ├── settings-email-validation.html │ │ │ └── settings-email-validation.txt │ ├── centos │ │ ├── static │ │ │ ├── css │ │ │ │ └── default.css │ │ │ ├── fonts │ │ │ │ ├── Montserrat-Black.woff │ │ │ │ ├── Montserrat-Black.woff2 │ │ │ │ ├── Montserrat-BlackItalic.woff │ │ │ │ ├── Montserrat-BlackItalic.woff2 │ │ │ │ ├── Montserrat-Bold.woff │ │ │ │ ├── Montserrat-Bold.woff2 │ │ │ │ ├── Montserrat-BoldItalic.woff │ │ │ │ ├── Montserrat-BoldItalic.woff2 │ │ │ │ ├── Montserrat-ExtraBold.woff │ │ │ │ ├── Montserrat-ExtraBold.woff2 │ │ │ │ ├── Montserrat-ExtraBoldItalic.woff │ │ │ │ ├── Montserrat-ExtraBoldItalic.woff2 │ │ │ │ ├── Montserrat-ExtraLight.woff │ │ │ │ ├── Montserrat-ExtraLight.woff2 │ │ │ │ ├── Montserrat-ExtraLightItalic.woff │ │ │ │ ├── Montserrat-ExtraLightItalic.woff2 │ │ │ │ ├── Montserrat-Italic.woff │ │ │ │ ├── Montserrat-Italic.woff2 │ │ │ │ ├── Montserrat-Light.woff │ │ │ │ ├── Montserrat-Light.woff2 │ │ │ │ ├── Montserrat-LightItalic.woff │ │ │ │ ├── Montserrat-LightItalic.woff2 │ │ │ │ ├── Montserrat-Medium.woff │ │ │ │ ├── Montserrat-Medium.woff2 │ │ │ │ ├── Montserrat-MediumItalic.woff │ │ │ │ ├── Montserrat-MediumItalic.woff2 │ │ │ │ ├── Montserrat-Regular.woff │ │ │ │ ├── Montserrat-Regular.woff2 │ │ │ │ ├── Montserrat-SemiBold.woff │ │ │ │ ├── Montserrat-SemiBold.woff2 │ │ │ │ ├── Montserrat-SemiBoldItalic.woff │ │ │ │ ├── Montserrat-SemiBoldItalic.woff2 │ │ │ │ ├── Montserrat-Thin.woff │ │ │ │ ├── Montserrat-Thin.woff2 │ │ │ │ ├── Montserrat-ThinItalic.woff │ │ │ │ ├── Montserrat-ThinItalic.woff2 │ │ │ │ ├── Montserrat.css │ │ │ │ ├── MontserratAlternates-Black.woff │ │ │ │ ├── MontserratAlternates-Black.woff2 │ │ │ │ ├── MontserratAlternates-BlackItalic.woff │ │ │ │ ├── MontserratAlternates-BlackItalic.woff2 │ │ │ │ ├── MontserratAlternates-Bold.woff │ │ │ │ ├── MontserratAlternates-Bold.woff2 │ │ │ │ ├── MontserratAlternates-BoldItalic.woff │ │ │ │ ├── MontserratAlternates-BoldItalic.woff2 │ │ │ │ ├── MontserratAlternates-ExtraBold.woff │ │ │ │ ├── MontserratAlternates-ExtraBold.woff2 │ │ │ │ ├── MontserratAlternates-ExtraBoldItalic.woff │ │ │ │ ├── MontserratAlternates-ExtraBoldItalic.woff2 │ │ │ │ ├── MontserratAlternates-ExtraLight.woff │ │ │ │ ├── MontserratAlternates-ExtraLight.woff2 │ │ │ │ ├── MontserratAlternates-ExtraLightItalic.woff │ │ │ │ ├── MontserratAlternates-ExtraLightItalic.woff2 │ │ │ │ ├── MontserratAlternates-Italic.woff │ │ │ │ ├── MontserratAlternates-Italic.woff2 │ │ │ │ ├── MontserratAlternates-Light.woff │ │ │ │ ├── MontserratAlternates-Light.woff2 │ │ │ │ ├── MontserratAlternates-LightItalic.woff │ │ │ │ ├── MontserratAlternates-LightItalic.woff2 │ │ │ │ ├── MontserratAlternates-Medium.woff │ │ │ │ ├── MontserratAlternates-Medium.woff2 │ │ │ │ ├── MontserratAlternates-MediumItalic.woff │ │ │ │ ├── MontserratAlternates-MediumItalic.woff2 │ │ │ │ ├── MontserratAlternates-Regular.woff │ │ │ │ ├── MontserratAlternates-Regular.woff2 │ │ │ │ ├── MontserratAlternates-SemiBold.woff │ │ │ │ ├── MontserratAlternates-SemiBold.woff2 │ │ │ │ ├── MontserratAlternates-SemiBoldItalic.woff │ │ │ │ ├── MontserratAlternates-SemiBoldItalic.woff2 │ │ │ │ ├── MontserratAlternates-Thin.woff │ │ │ │ ├── MontserratAlternates-Thin.woff2 │ │ │ │ ├── MontserratAlternates-ThinItalic.woff │ │ │ │ ├── MontserratAlternates-ThinItalic.woff2 │ │ │ │ └── README.MD │ │ │ ├── images │ │ │ │ └── centos-accounts-logo.png │ │ │ └── vendor │ │ │ │ ├── bootstrap-5.2.0-beta1 │ │ │ │ ├── 0.svg │ │ │ │ ├── 1.svg │ │ │ │ ├── 10.svg │ │ │ │ ├── 11.svg │ │ │ │ ├── 12.svg │ │ │ │ ├── 13.svg │ │ │ │ ├── 14.svg │ │ │ │ ├── 15.svg │ │ │ │ ├── 2.svg │ │ │ │ ├── 3.svg │ │ │ │ ├── 4.svg │ │ │ │ ├── 5.svg │ │ │ │ ├── 6.svg │ │ │ │ ├── 7.svg │ │ │ │ ├── 8.svg │ │ │ │ ├── 9.svg │ │ │ │ ├── bootstrap.bundle.min.js │ │ │ │ ├── bootstrap.min.css │ │ │ │ └── bootstrap.min.nodata.css │ │ │ │ └── jquery │ │ │ │ └── jquery-3.3.1.min.js │ │ └── templates │ │ │ ├── email-validation.html │ │ │ ├── email-validation.txt │ │ │ ├── forgot-password-email.html │ │ │ ├── forgot-password-email.txt │ │ │ ├── main.html │ │ │ ├── settings-email-validation.html │ │ │ └── settings-email-validation.txt │ ├── default │ │ ├── static │ │ │ ├── css │ │ │ │ └── default.css │ │ │ └── vendor │ │ │ │ ├── bootstrap-5.2.0-beta1 │ │ │ │ ├── 0.svg │ │ │ │ ├── 1.svg │ │ │ │ ├── 10.svg │ │ │ │ ├── 11.svg │ │ │ │ ├── 12.svg │ │ │ │ ├── 13.svg │ │ │ │ ├── 14.svg │ │ │ │ ├── 15.svg │ │ │ │ ├── 2.svg │ │ │ │ ├── 3.svg │ │ │ │ ├── 4.svg │ │ │ │ ├── 5.svg │ │ │ │ ├── 6.svg │ │ │ │ ├── 7.svg │ │ │ │ ├── 8.svg │ │ │ │ ├── 9.svg │ │ │ │ ├── bootstrap.bundle.min.js │ │ │ │ ├── bootstrap.min.css │ │ │ │ └── bootstrap.min.nodata.css │ │ │ │ └── jquery │ │ │ │ └── jquery-3.3.1.min.js │ │ └── templates │ │ │ ├── email-validation.html │ │ │ ├── email-validation.txt │ │ │ ├── forgot-password-email.html │ │ │ ├── forgot-password-email.txt │ │ │ ├── main.html │ │ │ ├── settings-email-validation.html │ │ │ └── settings-email-validation.txt │ └── fas │ │ ├── static │ │ ├── css │ │ │ └── fedoraaccounts.css │ │ ├── favicon │ │ │ ├── android-chrome-192x192.png │ │ │ ├── android-chrome-512x512.png │ │ │ ├── apple-touch-icon.png │ │ │ ├── favicon-16x16.png │ │ │ ├── favicon-32x32.png │ │ │ ├── favicon.ico │ │ │ ├── safari-pinned-tab.svg │ │ │ └── site.webmanifest │ │ ├── fonts │ │ │ ├── open-sans.css │ │ │ └── open-sans │ │ │ │ ├── open-sans-v13-latin_latin-ext-300.eot │ │ │ │ ├── open-sans-v13-latin_latin-ext-300.svg │ │ │ │ ├── open-sans-v13-latin_latin-ext-300.ttf │ │ │ │ ├── open-sans-v13-latin_latin-ext-300.woff │ │ │ │ ├── open-sans-v13-latin_latin-ext-300.woff2 │ │ │ │ ├── open-sans-v13-latin_latin-ext-300italic.eot │ │ │ │ ├── open-sans-v13-latin_latin-ext-300italic.svg │ │ │ │ ├── open-sans-v13-latin_latin-ext-300italic.ttf │ │ │ │ ├── open-sans-v13-latin_latin-ext-300italic.woff │ │ │ │ ├── open-sans-v13-latin_latin-ext-300italic.woff2 │ │ │ │ ├── open-sans-v13-latin_latin-ext-700.eot │ │ │ │ ├── open-sans-v13-latin_latin-ext-700.svg │ │ │ │ ├── open-sans-v13-latin_latin-ext-700.ttf │ │ │ │ ├── open-sans-v13-latin_latin-ext-700.woff │ │ │ │ ├── open-sans-v13-latin_latin-ext-700.woff2 │ │ │ │ ├── open-sans-v13-latin_latin-ext-700italic.eot │ │ │ │ ├── open-sans-v13-latin_latin-ext-700italic.svg │ │ │ │ ├── open-sans-v13-latin_latin-ext-700italic.ttf │ │ │ │ ├── open-sans-v13-latin_latin-ext-700italic.woff │ │ │ │ ├── open-sans-v13-latin_latin-ext-700italic.woff2 │ │ │ │ ├── open-sans-v13-latin_latin-ext-italic.eot │ │ │ │ ├── open-sans-v13-latin_latin-ext-italic.svg │ │ │ │ ├── open-sans-v13-latin_latin-ext-italic.ttf │ │ │ │ ├── open-sans-v13-latin_latin-ext-italic.woff │ │ │ │ ├── open-sans-v13-latin_latin-ext-italic.woff2 │ │ │ │ ├── open-sans-v13-latin_latin-ext-regular.eot │ │ │ │ ├── open-sans-v13-latin_latin-ext-regular.svg │ │ │ │ ├── open-sans-v13-latin_latin-ext-regular.ttf │ │ │ │ ├── open-sans-v13-latin_latin-ext-regular.woff │ │ │ │ └── open-sans-v13-latin_latin-ext-regular.woff2 │ │ ├── images │ │ │ ├── FedoraAccounts.png │ │ │ ├── FedoraAccounts.svg │ │ │ └── redhat.png │ │ └── vendor │ │ │ ├── fedora-bootstrap-5.3.3-0 │ │ │ ├── 0.svg │ │ │ ├── 1.svg │ │ │ ├── 10.svg │ │ │ ├── 11.svg │ │ │ ├── 12.svg │ │ │ ├── 13.svg │ │ │ ├── 14.svg │ │ │ ├── 15.svg │ │ │ ├── 16.svg │ │ │ ├── 17.svg │ │ │ ├── 18.svg │ │ │ ├── 2.svg │ │ │ ├── 3.svg │ │ │ ├── 4.svg │ │ │ ├── 5.svg │ │ │ ├── 6.svg │ │ │ ├── 7.svg │ │ │ ├── 8.svg │ │ │ ├── 9.svg │ │ │ ├── fedora-bootstrap.min.css │ │ │ ├── fedora-bootstrap.min.css.map │ │ │ ├── fedora-bootstrap.min.js │ │ │ ├── fedora-bootstrap.min.js.map │ │ │ └── fedora-bootstrap.min.nodata.css │ │ │ ├── jquery │ │ │ └── jquery-3.3.1.min.js │ │ │ └── popperjs-2.9.2 │ │ │ └── popper.min.js │ │ └── templates │ │ ├── email-validation.html │ │ ├── email-validation.txt │ │ ├── forgot-password-email.html │ │ ├── forgot-password-email.txt │ │ ├── main.html │ │ ├── settings-email-validation.html │ │ └── settings-email-validation.txt ├── translations │ ├── be │ │ └── LC_MESSAGES │ │ │ └── messages.po │ ├── bs │ │ └── LC_MESSAGES │ │ │ └── messages.po │ ├── ca │ │ └── LC_MESSAGES │ │ │ └── messages.po │ ├── de │ │ └── LC_MESSAGES │ │ │ └── messages.po │ ├── es │ │ └── LC_MESSAGES │ │ │ └── messages.po │ ├── fi │ │ └── LC_MESSAGES │ │ │ └── messages.po │ ├── fr_FR │ │ └── LC_MESSAGES │ │ │ └── messages.po │ ├── he │ │ └── LC_MESSAGES │ │ │ └── messages.po │ ├── hi │ │ └── LC_MESSAGES │ │ │ └── messages.po │ ├── hu │ │ └── LC_MESSAGES │ │ │ └── messages.po │ ├── id │ │ └── LC_MESSAGES │ │ │ └── messages.po │ ├── it │ │ └── LC_MESSAGES │ │ │ └── messages.po │ ├── ko │ │ └── LC_MESSAGES │ │ │ └── messages.po │ ├── messages.pot │ ├── my │ │ └── LC_MESSAGES │ │ │ └── messages.po │ ├── pl │ │ └── LC_MESSAGES │ │ │ └── messages.po │ ├── pt_BR │ │ └── LC_MESSAGES │ │ │ └── messages.po │ ├── pt_PT │ │ └── LC_MESSAGES │ │ │ └── messages.po │ ├── si │ │ └── LC_MESSAGES │ │ │ └── messages.po │ ├── sv │ │ └── LC_MESSAGES │ │ │ └── messages.po │ ├── tr │ │ └── LC_MESSAGES │ │ │ └── messages.po │ ├── uk │ │ └── LC_MESSAGES │ │ │ └── messages.po │ ├── zh_Hans │ │ └── LC_MESSAGES │ │ │ └── messages.po │ └── zh_Hant │ │ └── LC_MESSAGES │ │ └── messages.po └── utility │ ├── __init__.py │ ├── controllers.py │ ├── forms.py │ ├── messaging.py │ ├── pagination.py │ ├── password_reset.py │ ├── templates.py │ ├── timezones.py │ └── token.py ├── packit └── installation │ └── main.fmf ├── poetry.lock ├── pyproject.toml ├── sonar-project.properties ├── tests ├── __init__.py ├── integration │ ├── __init__.py │ └── test_dummy.py └── unit │ ├── __init__.py │ ├── cassettes │ ├── ipa_testing_config.yaml │ └── test_signals │ │ ├── test_signal_basset.yaml │ │ ├── test_signal_basset_disabled.yaml │ │ └── test_signal_basset_failed.yaml │ ├── conftest.py │ ├── controller │ ├── __init__.py │ ├── cassettes │ │ ├── test_authentication │ │ │ ├── test_login.yaml │ │ │ ├── test_login_email_address.yaml │ │ │ ├── test_login_expired_password.yaml │ │ │ ├── test_login_incorrect_password.yaml │ │ │ ├── test_login_no_password.yaml │ │ │ ├── test_login_non_existent_email_address.yaml │ │ │ ├── test_login_username_case.yaml │ │ │ ├── test_login_username_created_with_case.yaml │ │ │ ├── test_login_with_bad_redirect.yaml │ │ │ ├── test_login_with_otp.yaml │ │ │ ├── test_login_with_redirect.yaml │ │ │ ├── test_logout.yaml │ │ │ ├── test_otp_sync_http_error.yaml │ │ │ ├── test_otp_sync_no_ipa_server.yaml │ │ │ ├── test_otp_sync_no_username.yaml │ │ │ ├── test_otp_sync_rejected.yaml │ │ │ └── test_otp_sync_success.yaml │ │ ├── test_forgot_password │ │ │ ├── test_ask_no_smtp.yaml │ │ │ ├── test_ask_post[data0].yaml │ │ │ ├── test_ask_post[data1].yaml │ │ │ ├── test_ask_post_mix_case_user.yaml │ │ │ ├── test_ask_post_no_last_password_change.yaml │ │ │ ├── test_ask_post_non_existant_user.yaml │ │ │ ├── test_ask_post_non_existant_user_email.yaml │ │ │ ├── test_ask_smtp_rcpt_refused.yaml │ │ │ ├── test_change_get.yaml │ │ │ ├── test_change_not_active.yaml │ │ │ ├── test_change_post.yaml │ │ │ ├── test_change_post_generic_error.yaml │ │ │ ├── test_change_post_no_earlier_password_change.yaml │ │ │ ├── test_change_post_no_ipa_server.yaml │ │ │ ├── test_change_post_password_policy_rejected.yaml │ │ │ ├── test_change_post_password_too_short.yaml │ │ │ ├── test_change_post_password_with_otp_not_given.yaml │ │ │ ├── test_change_post_password_with_otp_wrong_value.yaml │ │ │ ├── test_change_post_with_otp.yaml │ │ │ ├── test_change_recent_password_change.yaml │ │ │ └── test_change_too_old.yaml │ │ ├── test_group │ │ │ ├── test_group.yaml │ │ │ ├── test_group_add_member.yaml │ │ │ ├── test_group_add_member_forbidden.yaml │ │ │ ├── test_group_add_member_hidden_group.yaml │ │ │ ├── test_group_add_member_invalid.yaml │ │ │ ├── test_group_add_member_invalid_form.yaml │ │ │ ├── test_group_add_unknown_member.yaml │ │ │ ├── test_group_does_not_exist.yaml │ │ │ ├── test_group_hidden.yaml │ │ │ ├── test_group_many_members.yaml │ │ │ ├── test_group_remove_member.yaml │ │ │ ├── test_group_remove_member_hidden_group.yaml │ │ │ ├── test_group_remove_member_invalid.yaml │ │ │ ├── test_group_remove_member_invalid_form.yaml │ │ │ ├── test_group_remove_member_unknown.yaml │ │ │ ├── test_group_remove_self.yaml │ │ │ ├── test_group_remove_sponsor.yaml │ │ │ ├── test_group_remove_sponsor_last.yaml │ │ │ ├── test_group_remove_sponsor_unknown.yaml │ │ │ ├── test_groups_list.yaml │ │ │ ├── test_groups_list_no_hidden.yaml │ │ │ └── test_groups_list_search.yaml │ │ ├── test_password_reset │ │ │ ├── test_non_matching_passwords_user.yaml │ │ │ ├── test_password.yaml │ │ │ ├── test_password_changes.yaml │ │ │ ├── test_password_changes_user.yaml │ │ │ ├── test_password_changes_wrong_user.yaml │ │ │ ├── test_password_form_with_otp.yaml │ │ │ ├── test_password_form_without_otp.yaml │ │ │ ├── test_password_no_user.yaml │ │ │ ├── test_password_reset_user.yaml │ │ │ ├── test_password_user.yaml │ │ │ ├── test_short_password_form.yaml │ │ │ ├── test_short_password_policy.yaml │ │ │ └── test_time_sensitive_password_policy.yaml │ │ ├── test_registration │ │ │ ├── test_agreements.yaml │ │ │ ├── test_agreements_post.yaml │ │ │ ├── test_duplicate.yaml │ │ │ ├── test_field_error_step_3.yaml │ │ │ ├── test_gecos.yaml │ │ │ ├── test_generic_activate_error.yaml │ │ │ ├── test_generic_pwchange_error.yaml │ │ │ ├── test_lowercase_email.yaml │ │ │ ├── test_no_agreement.yaml │ │ │ ├── test_no_direct_login.yaml │ │ │ ├── test_no_ipa_server.yaml │ │ │ ├── test_password_form_too_long.yaml │ │ │ ├── test_registering.yaml │ │ │ ├── test_registering_change_status[accept-active-Accepted registering user dummy].yaml │ │ │ ├── test_registering_change_status[spam-spamcheck_denied-Flagged registering user dummy as spam].yaml │ │ │ ├── test_registering_change_status_error[accept-Could not accept registering user dummy].yaml │ │ │ ├── test_registering_change_status_error[spam-Could not flag registering user dummy as spam].yaml │ │ │ ├── test_registering_delete.yaml │ │ │ ├── test_registering_delete_error.yaml │ │ │ ├── test_registering_invalid_action.yaml │ │ │ ├── test_registering_unknown_user.yaml │ │ │ ├── test_short_password_form.yaml │ │ │ ├── test_short_password_policy.yaml │ │ │ ├── test_spamcheck[active].yaml │ │ │ ├── test_spamcheck[spamcheck_denied].yaml │ │ │ ├── test_spamcheck[spamcheck_manual].yaml │ │ │ ├── test_spamcheck_bad_missing_key[payload0].yaml │ │ │ ├── test_spamcheck_bad_missing_key[payload1].yaml │ │ │ ├── test_spamcheck_bad_payload.yaml │ │ │ ├── test_spamcheck_disabled.yaml │ │ │ ├── test_spamcheck_expired_token.yaml │ │ │ ├── test_spamcheck_invalid_token.yaml │ │ │ ├── test_spamcheck_wait[active].yaml │ │ │ ├── test_spamcheck_wait[spamcheck_denied].yaml │ │ │ ├── test_spamcheck_wait[spamcheck_manual].yaml │ │ │ ├── test_spamcheck_wait_active.yaml │ │ │ ├── test_spamcheck_wait_bad_username.yaml │ │ │ ├── test_spamcheck_wait_no_username.yaml │ │ │ ├── test_spamcheck_wrong_status.yaml │ │ │ ├── test_step_1.yaml │ │ │ ├── test_step_1_no_smtp.yaml │ │ │ ├── test_step_1_registration_closed.yaml │ │ │ ├── test_step_1_spamcheck.yaml │ │ │ ├── test_step_2.yaml │ │ │ ├── test_step_2_resend.yaml │ │ │ ├── test_step_2_spamchecking_user.yaml │ │ │ ├── test_step_2_unknown_user.yaml │ │ │ ├── test_step_3.yaml │ │ │ ├── test_step_3_garbled_token.yaml │ │ │ ├── test_step_3_invalid_token.yaml │ │ │ ├── test_step_3_no_token.yaml │ │ │ ├── test_step_3_unknown_user.yaml │ │ │ ├── test_step_3_wrong_address.yaml │ │ │ ├── test_strip[firstname].yaml │ │ │ └── test_strip[lastname].yaml │ │ ├── test_root │ │ │ ├── test_healthz_no_https.yaml │ │ │ ├── test_healthz_readiness_ok.yaml │ │ │ ├── test_root_authenticated.yaml │ │ │ ├── test_search_json.yaml │ │ │ ├── test_search_json_empty.yaml │ │ │ ├── test_search_json_group_nonfas.yaml │ │ │ ├── test_search_json_locked.yaml │ │ │ └── test_search_json_user_nonfas.yaml │ │ ├── test_user │ │ │ ├── test_user.yaml │ │ │ ├── test_user_can_see_dummy_group.yaml │ │ │ ├── test_user_cant_see_hidden_groups.yaml │ │ │ ├── test_user_edit.yaml │ │ │ ├── test_user_edit_no_permission[GET].yaml │ │ │ ├── test_user_edit_no_permission[POST].yaml │ │ │ ├── test_user_edit_post.yaml │ │ │ ├── test_user_edit_post_bad_request.yaml │ │ │ ├── test_user_edit_post_gecos.yaml │ │ │ ├── test_user_edit_post_minimal_values.yaml │ │ │ ├── test_user_edit_post_no_change.yaml │ │ │ ├── test_user_locked.yaml │ │ │ ├── test_user_private.yaml │ │ │ ├── test_user_settings_agreements.yaml │ │ │ ├── test_user_settings_agreements_disabled.yaml │ │ │ ├── test_user_settings_agreements_post.yaml │ │ │ ├── test_user_settings_agreements_post_bad_request.yaml │ │ │ ├── test_user_settings_agreements_post_unknown.yaml │ │ │ ├── test_user_settings_email.yaml │ │ │ ├── test_user_settings_email_no_change.yaml │ │ │ ├── test_user_settings_email_post.yaml │ │ │ ├── test_user_settings_email_post_multiple.yaml │ │ │ ├── test_user_settings_email_post_no_smtp.yaml │ │ │ ├── test_user_settings_email_removal.yaml │ │ │ ├── test_user_settings_email_validation.yaml │ │ │ ├── test_user_settings_email_validation_bad_token.yaml │ │ │ ├── test_user_settings_email_validation_expired_token.yaml │ │ │ ├── test_user_settings_email_validation_no_token.yaml │ │ │ ├── test_user_settings_email_validation_other_user.yaml │ │ │ ├── test_user_settings_email_validation_post.yaml │ │ │ ├── test_user_settings_email_validation_post_failure.yaml │ │ │ ├── test_user_settings_keys.yaml │ │ │ ├── test_user_settings_keys_no_permission[GET].yaml │ │ │ ├── test_user_settings_keys_no_permission[POST].yaml │ │ │ ├── test_user_settings_keys_post.yaml │ │ │ ├── test_user_settings_keys_post_bad_request.yaml │ │ │ ├── test_user_settings_keys_post_no_change.yaml │ │ │ ├── test_user_settings_keys_post_whitespace.yaml │ │ │ ├── test_user_with_indirect_groups.yaml │ │ │ └── test_user_with_no_groups.yaml │ │ └── test_user_otp │ │ │ ├── test_user_settings_otp.yaml │ │ │ ├── test_user_settings_otp_add.yaml │ │ │ ├── test_user_settings_otp_add_invalid.yaml │ │ │ ├── test_user_settings_otp_add_invalid_form.yaml │ │ │ ├── test_user_settings_otp_add_no_permission.yaml │ │ │ ├── test_user_settings_otp_add_second.yaml │ │ │ ├── test_user_settings_otp_add_second_confirm.yaml │ │ │ ├── test_user_settings_otp_add_wrong_code.yaml │ │ │ ├── test_user_settings_otp_add_wrong_password.yaml │ │ │ ├── test_user_settings_otp_check_description_escaping.yaml │ │ │ ├── test_user_settings_otp_check_no_description.yaml │ │ │ ├── test_user_settings_otp_confirm.yaml │ │ │ ├── test_user_settings_otp_delete.yaml │ │ │ ├── test_user_settings_otp_delete_invalid_form.yaml │ │ │ ├── test_user_settings_otp_delete_ipabadrequest.yaml │ │ │ ├── test_user_settings_otp_delete_ipafailure.yaml │ │ │ ├── test_user_settings_otp_delete_lasttoken.yaml │ │ │ ├── test_user_settings_otp_delete_no_permission.yaml │ │ │ ├── test_user_settings_otp_disable.yaml │ │ │ ├── test_user_settings_otp_disable_invalid_form.yaml │ │ │ ├── test_user_settings_otp_disable_ipabadrequest.yaml │ │ │ ├── test_user_settings_otp_disable_ipaerror.yaml │ │ │ ├── test_user_settings_otp_disable_lasttoken.yaml │ │ │ ├── test_user_settings_otp_disable_no_permission.yaml │ │ │ ├── test_user_settings_otp_enable.yaml │ │ │ ├── test_user_settings_otp_enable_invalid_form.yaml │ │ │ ├── test_user_settings_otp_enable_ipaerror.yaml │ │ │ ├── test_user_settings_otp_enable_no_permission.yaml │ │ │ ├── test_user_settings_otp_no_permission.yaml │ │ │ ├── test_user_settings_otp_rename.yaml │ │ │ ├── test_user_settings_otp_rename_invalid_form.yaml │ │ │ ├── test_user_settings_otp_rename_ipaerror.yaml │ │ │ └── test_user_settings_otp_rename_no_change.yaml │ ├── test_authentication.py │ ├── test_forgot_password.py │ ├── test_group.py │ ├── test_password_reset.py │ ├── test_registration.py │ ├── test_root.py │ ├── test_user.py │ └── test_user_otp.py │ ├── form │ ├── __init__.py │ ├── test_base.py │ └── test_edit_user.py │ ├── representation │ ├── __init__.py │ ├── conftest.py │ ├── test_agreement.py │ ├── test_base.py │ ├── test_group.py │ ├── test_otptoken.py │ └── test_user.py │ ├── security │ ├── __init__.py │ ├── cassettes │ │ └── test_ipa │ │ │ ├── test_ipa_client_batch.yaml │ │ │ ├── test_ipa_client_batch_no_raise_errors.yaml │ │ │ ├── test_ipa_client_batch_unknown_method.yaml │ │ │ ├── test_ipa_client_batch_unknown_option.yaml │ │ │ ├── test_ipa_client_fasagreement_add.yaml │ │ │ ├── test_ipa_client_fasagreement_add_group.yaml │ │ │ ├── test_ipa_client_fasagreement_add_user.yaml │ │ │ ├── test_ipa_client_fasagreement_find.yaml │ │ │ ├── test_ipa_login.yaml │ │ │ ├── test_ipa_session_authed.yaml │ │ │ ├── test_ipa_session_invalid.yaml │ │ │ └── test_ipa_session_unauthorized.yaml │ ├── test_ipa.py │ └── test_ipa_admin.py │ ├── test_app.py │ ├── test_l10n.py │ ├── test_middleware.py │ ├── test_signals.py │ ├── test_themes.py │ ├── translations │ ├── __init__.py │ ├── cassettes │ │ └── test_translations │ │ │ └── test_translation_in_code_french.yaml │ └── test_translations.py │ ├── utilities.py │ └── utility │ ├── __init__.py │ ├── cassettes │ ├── test_controllers │ │ ├── test_group_or_404.yaml │ │ ├── test_group_or_404_unknown.yaml │ │ ├── test_user_or_404.yaml │ │ ├── test_user_or_404_unknown.yaml │ │ └── test_with_ipa.yaml │ └── test_pagination │ │ ├── test_groups_page.yaml │ │ ├── test_groups_page_nopaging.yaml │ │ ├── test_mounted_subdir.yaml │ │ ├── test_pagination_bar[1-40-page_result3].yaml │ │ ├── test_pagination_bar[2-3-page_result0].yaml │ │ ├── test_pagination_bar[30-3-page_result2].yaml │ │ └── test_pagination_bar[67-3-page_result1].yaml │ ├── test_controllers.py │ ├── test_forms.py │ ├── test_messaging.py │ ├── test_pagination.py │ ├── test_password_reset.py │ └── test_templates.py └── tox.ini /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | branch = True 3 | source = 4 | noggin 5 | 6 | [paths] 7 | source = 8 | noggin 9 | .tox/*/site-packages/noggin 10 | 11 | [report] 12 | fail_under = 100 13 | exclude_lines = 14 | pragma: no cover 15 | if __name__ == .__main__.: 16 | omit = 17 | tests/* 18 | noggin/__init__.py 19 | -------------------------------------------------------------------------------- /.fmf/version: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["local>fedora-infra/shared:renovate-config"], 4 | "ignoreDeps": ["typer"] 5 | } 6 | -------------------------------------------------------------------------------- /.github/workflows/label-when-deployed.yaml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Contributors to the Fedora Project 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | name: Apply labels when deployed 6 | 7 | on: 8 | push: 9 | branches: 10 | - staging 11 | - stable 12 | 13 | jobs: 14 | label: 15 | name: Apply labels 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - name: Staging deployment 20 | uses: fedora-infra/label-when-in-branch@v1 21 | with: 22 | token: ${{ secrets.GITHUB_TOKEN }} 23 | branch: staging 24 | label: deployed:staging 25 | - name: Production deployment 26 | uses: fedora-infra/label-when-in-branch@v1 27 | with: 28 | token: ${{ secrets.GITHUB_TOKEN }} 29 | branch: stable 30 | label: deployed:prod 31 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: Clean Stale Issues 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: "0 1 * * *" 7 | 8 | permissions: 9 | issues: write 10 | pull-requests: write 11 | 12 | jobs: 13 | stale: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Close stale issues 17 | uses: actions/stale@v9.1.0 18 | with: 19 | exempt-all-assignees: true 20 | # exclude issues/PRs with the specified labels 21 | exempt-issue-labels: todo 22 | stale-issue-message: "This issue is stale because it has been open for 60 days with no activity." 23 | close-issue-message: "This issue was closed because it has been inactive for 7 days since being marked as stale." 24 | operations-per-run: 1000 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dev config file 2 | /noggin.cfg 3 | 4 | # dev repl environment 5 | /env 6 | 7 | # dev cacrt 8 | /.containerdev-public/ipa01 9 | /.containerdev-public/ipatest01 10 | 11 | # dev hacks 12 | /python-freeipa 13 | 14 | # Byte-compiled / optimized 15 | __pycache__/ 16 | *.py[cod] 17 | 18 | # Distribution / packaging 19 | build/ 20 | develop-eggs/ 21 | dist/ 22 | eggs/ 23 | .eggs/ 24 | lib/ 25 | lib64/ 26 | sdist/ 27 | var/ 28 | wheels/ 29 | *.egg-info/ 30 | .installed.cfg 31 | *.egg 32 | 33 | # Unit test / coverage reports 34 | htmlcov/ 35 | .tox/ 36 | .coverage 37 | .coverage.* 38 | .cache 39 | nosetests.xml 40 | coverage.xml 41 | 42 | # Sphinx documentation 43 | docs/_build/ 44 | docs/_source/ 45 | 46 | # pytest 47 | .pytest_cache/ 48 | 49 | # VS Code 50 | .vscode 51 | 52 | # Vagrant 53 | .vagrant 54 | 55 | # Translations 56 | *.mo 57 | -------------------------------------------------------------------------------- /.gitleaks.toml: -------------------------------------------------------------------------------- 1 | [allowlist] 2 | paths = [ 3 | 4 | """noggin/tests/unit/controller/cassettes/test_forgot_password/test_change_post.yaml""", 5 | """noggin/tests/unit/controller/cassettes/test_forgot_password/test_change_post_password_policy_rejected.yaml""", 6 | """noggin/tests/unit/controller/cassettes/test_forgot_password/test_change_post_password_with_otp_not_given.yaml""", 7 | """noggin/tests/unit/controller/cassettes/test_forgot_password/test_change_post_password_with_otp_wrong_value.yaml""", 8 | """noggin/tests/unit/controller/cassettes/test_forgot_password/test_change_post_with_otp.yaml""", 9 | """noggin/tests/unit/controller/cassettes/test_forgot_password/test_change_post_no_earlier_password_change.yaml""", 10 | """noggin/tests/unit/controller/cassettes/test_forgot_password/test_change_post_password_too_short.yaml""", 11 | ] 12 | 13 | # these commits all contain python files that use a password that is used for the tests. 14 | # the same password has been there for a while, but keeps getting flagged because the file 15 | # has been moved around and renamed. 16 | # 54f8749f986ebb755b1219160fdf4e6b5132e77f/tests/unit/controller/test_authentication.py#L148 17 | # we also now have a gitleaks:allow on this line too. 18 | 19 | commits = [ 20 | "187716c9480916cf15a4f5f44e9465ae5455219e", 21 | "b7b306e6518c750583c98b3709ab6fc5df0c9f3d", 22 | "ee0a0289a56bf7a97c52acc7c90892fcabce97a6", 23 | "3fc59f0905b6c97984d3d1f1f2baab90502e9cbd", 24 | "3dd46cf59d439c21ea2bd846b75f58fbe70c9658", 25 | "226f904f1886516d834044db054568a25042a2a9", 26 | "c23a473b04588374aeef2c7f16b43239e70ee844", 27 | "108a9b58b218dea01a9c5d003f6e681ba5c857c1", 28 | ] 29 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | # - repo: https://github.com/asottile/pyupgrade 3 | # rev: v2.15.0 4 | # hooks: 5 | # - id: pyupgrade 6 | # args: 7 | # - --py39-plus 8 | 9 | - repo: https://github.com/psf/black 10 | rev: 25.1.0 11 | hooks: 12 | - id: black 13 | language_version: python3 14 | args: ["--check", "--diff"] 15 | 16 | - repo: https://github.com/pycqa/flake8 17 | rev: 7.2.0 18 | hooks: 19 | - id: flake8 20 | 21 | - repo: https://github.com/pycqa/isort 22 | rev: 6.0.1 23 | hooks: 24 | - id: isort 25 | args: ["-c", "--diff"] 26 | 27 | - repo: https://github.com/Lucas-C/pre-commit-hooks-bandit 28 | rev: v1.0.6 29 | hooks: 30 | - id: python-bandit-vulnerability-check 31 | alias: bandit 32 | args: ["-r", "noggin/", "-ll"] 33 | # - repo: local 34 | # hooks: 35 | # - id: bandit-local 36 | # name: bandit 37 | # entry: bandit 38 | # args: ["-r", "noggin/", "-ll"] 39 | # pass_filenames: false 40 | # language: system 41 | 42 | - repo: https://github.com/myint/rstcheck 43 | rev: v6.2.4 44 | hooks: 45 | - id: rstcheck 46 | # Use ignore-messages here until we can put it in the release_notes directly 47 | # https://github.com/rstcheck/rstcheck/issues/167 48 | args: ["-r", "docs", "--ignore-messages=Duplicate implicit target name"] 49 | additional_dependencies: [sphinx] 50 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | # Set the version of Python and other tools you might need 9 | build: 10 | os: ubuntu-22.04 11 | apt_packages: 12 | - libkrb5-dev 13 | - libpq-dev 14 | tools: 15 | python: "3" 16 | 17 | # Build documentation in the docs/ directory with Sphinx 18 | sphinx: 19 | configuration: docs/conf.py 20 | 21 | # Optionally build your docs in additional formats such as PDF and ePub 22 | formats: 23 | - htmlzip 24 | 25 | # Optionally set the version of Python and requirements required to build your docs 26 | python: 27 | install: 28 | - method: pip 29 | path: . 30 | extra_requirements: 31 | - docs 32 | -------------------------------------------------------------------------------- /.rstcheck.cfg: -------------------------------------------------------------------------------- 1 | [rstcheck] 2 | ignore_directives = automodule 3 | ignore_roles = issue,pr,commit 4 | report = warning 5 | -------------------------------------------------------------------------------- /.s2i/bin/assemble: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # The assemble script builds the application artifacts from a source and 4 | # places them into appropriate directories inside the image. 5 | 6 | # Execute the default S2I script 7 | /usr/libexec/s2i/assemble 8 | 9 | echo "---> Upgrade pip to the latest version ..." 10 | pip install --upgrade pip 11 | 12 | echo "---> Installing poetry ..." 13 | pip install poetry 14 | 15 | # Don't create virtual environments 16 | poetry config virtualenvs.create false 17 | 18 | echo "---> Installing application and dependencies ..." 19 | poetry install --without dev --extras deploy 20 | 21 | echo "---> Compiling translations ..." 22 | pybabel compile -d noggin/translations 23 | 24 | # set permissions for any installed artifacts 25 | fix-permissions /opt/app-root -P 26 | -------------------------------------------------------------------------------- /.s2i/environment: -------------------------------------------------------------------------------- 1 | APP_MODULE=noggin.app:app 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM registry.fedoraproject.org/fedora-minimal:latest 2 | # install the base packages 3 | RUN microdnf install -y python3-flask poetry fedora-messaging gcc git libffi-devel python3-cryptography python3-devel python3-pip vim 4 | # copy this entire directory to /opt/noggin 5 | WORKDIR /opt/noggin 6 | ADD . /opt/noggin 7 | RUN poetry install --no-dev --extras deploy 8 | RUN poetry run pybabel compile -d /opt/noggin/noggin/translations 9 | 10 | 11 | ENV FLASK_APP=/opt/noggin/noggin/app.py 12 | ENV NOGGIN_CONFIG_PATH=/etc/noggin.cfg 13 | CMD [ "poetry", "run", "flask", "run", "-h", "0.0.0.0", "-p", "5000" ] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Rick Elrod 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Noggin 2 | 3 | [](https://pypi.org/project/noggin-aaa/) 4 | [](https://pypi.org/project/noggin-aaa/) 5 |  6 | [](https://noggin-aaa.readthedocs.io/en/latest/) 7 | 8 | *noggin* is a self-service portal for FreeIPA. 9 | The primary purpose of the portal is to allow users to sign up and manage their 10 | account information and group membership. 11 | 12 | The documentation is available online at https://noggin-aaa.readthedocs.io/ 13 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | ENV['VAGRANT_NO_PARALLEL'] = 'yes' 4 | 5 | Vagrant.configure(2) do |config| 6 | config.hostmanager.enabled = true 7 | config.hostmanager.manage_host = true 8 | config.hostmanager.manage_guest = true 9 | 10 | config.vm.define "noggin" do |noggin| 11 | noggin.vm.box_url = "https://download.fedoraproject.org/pub/fedora/linux/releases/40/Cloud/x86_64/images/Fedora-Cloud-Base-Vagrant-libvirt.x86_64-40-1.14.vagrant.libvirt.box" 12 | noggin.vm.box = "f40-cloud-libvirt" 13 | noggin.vm.hostname = "noggin-dev.tinystage.test" 14 | 15 | noggin.vm.synced_folder '.', '/vagrant', disabled: true 16 | noggin.vm.synced_folder ".", "/home/vagrant/noggin", type: "sshfs" 17 | 18 | noggin.vm.provider :libvirt do |libvirt| 19 | libvirt.cpus = 2 20 | libvirt.memory = 2048 21 | end 22 | 23 | noggin.vm.provision "ansible" do |ansible| 24 | ansible.playbook = "devel/ansible/noggin.yml" 25 | ansible.verbose = true 26 | end 27 | end 28 | 29 | end 30 | -------------------------------------------------------------------------------- /babel.cfg: -------------------------------------------------------------------------------- 1 | [python: **.py] 2 | [jinja2: **/templates/**.html] 3 | extensions=jinja2.ext.i18n,jinja2.ext.autoescape,jinja2.ext.with_ 4 | -------------------------------------------------------------------------------- /deployment/nginx.conf: -------------------------------------------------------------------------------- 1 | #server { 2 | #listen 80; 3 | #listen [::]:80; 4 | #server_name localhost.localdomain; 5 | #return 301 https://localhost.localdomain$request_uri; 6 | #} 7 | 8 | #server { 9 | #listen 443 ssl; 10 | #listen [::]:443 ssl; 11 | 12 | #server_name localhost.localdomain; 13 | 14 | #access_log /var/log/nginx/noggin.access.log; 15 | #error_log /var/log/nginx/noggin.error.log; 16 | 17 | #ssl_certificate /etc/pki/tls/....crt 18 | #ssl_certificate_key /etc/pki/tls/....key 19 | 20 | #location @noggin { 21 | #proxy_set_header Host $http_host; 22 | #proxy_set_header X-Real-IP $remote_addr; 23 | #proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 24 | #proxy_set_header X-Forwarded-Proto $scheme; 25 | #proxy_pass http://127.0.0.1:8000; 26 | #} 27 | 28 | #location / { 29 | #try_files $uri @noggin; 30 | #} 31 | 32 | #} 33 | -------------------------------------------------------------------------------- /deployment/noggin.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Noggin Accounts Management Portal 3 | After=network-online.target 4 | Wants=network-online.target 5 | 6 | [Service] 7 | EnvironmentFile=/etc/sysconfig/noggin 8 | ExecStart=sh -c "gunicorn-3 ${GUNICORN_OPTS} -w ${GUNICORN_WORKERS} --env NOGGIN_CONFIG_PATH=/etc/noggin/noggin.cfg --access-logfile /var/log/noggin/access.log --error-logfile /var/log/noggin/error.log --bind tcp://127.0.0.1:8000 'noggin.app:create_app()'" 9 | 10 | [Install] 11 | WantedBy=multi-user.target 12 | -------------------------------------------------------------------------------- /deployment/noggin.sysconfig: -------------------------------------------------------------------------------- 1 | GUNICORN_OPTS="" 2 | GUNICORN_WORKERS="3" 3 | 4 | -------------------------------------------------------------------------------- /devel/ansible/noggin.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: noggin 3 | become: true 4 | become_method: sudo 5 | vars_files: 6 | - vars.yml 7 | roles: 8 | - common 9 | - ipa-client 10 | - cert 11 | - noggin 12 | -------------------------------------------------------------------------------- /devel/ansible/roles/cert/defaults/main.yml: -------------------------------------------------------------------------------- 1 | krb_service: HTTP 2 | cert_hostname: "{{ ansible_fqdn }}" 3 | cert_basename: server 4 | -------------------------------------------------------------------------------- /devel/ansible/roles/cert/tasks/main.yml: -------------------------------------------------------------------------------- 1 | 2 | 3 | - name: Generate and get SSL cert 4 | shell: ipa-getcert request -f /etc/pki/tls/certs/{{ cert_basename }}.pem -k /etc/pki/tls/private/{{ cert_basename }}.key -K {{ krb_service }}/{{ cert_hostname }} -N {{ cert_hostname }} 5 | args: 6 | creates: /etc/pki/tls/certs/{{ cert_basename }}.pem 7 | 8 | - name: Check the cert is there 9 | wait_for: 10 | path: /etc/pki/tls/certs/{{ cert_basename }}.pem 11 | state: present 12 | 13 | - name: Check the key is there 14 | wait_for: 15 | path: /etc/pki/tls/private/{{ cert_basename }}.key 16 | state: present 17 | 18 | - name: Change file ownership, group and permissions 19 | ansible.builtin.file: 20 | path: /etc/pki/tls/certs/{{ cert_basename }}.pem 21 | mode: '0644' 22 | 23 | - name: Change file ownership, group and permissions 24 | ansible.builtin.file: 25 | path: /etc/pki/tls/private/{{ cert_basename }}.key 26 | mode: '0644' 27 | -------------------------------------------------------------------------------- /devel/ansible/roles/common/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install common packages 3 | dnf: 4 | name: 5 | - bash-completion 6 | - tmux 7 | - vim-enhanced 8 | - git 9 | - python3 10 | - fedora-messaging 11 | state: present 12 | 13 | - name: Determine Python version 14 | command: 15 | argv: 16 | - python3 17 | - -c 18 | - "from sys import version_info as vi; print(f'{vi[0]}.{vi[1]}')" 19 | register: _python3_version_result 20 | changed_when: False 21 | 22 | - name: Prepare the facts dir 23 | file: 24 | path: /etc/ansible/facts.d 25 | state: directory 26 | 27 | - name: Set Python version fact 28 | ini_file: 29 | path: /etc/ansible/facts.d/python.fact 30 | section: py3 31 | option: version 32 | value: "{{ _python3_version_result.stdout | trim }}" 33 | register: fact_ini 34 | 35 | - name: Re-read facts after adding custom fact 36 | ansible.builtin.setup: 37 | filter: ansible_local 38 | when: fact_ini.changed 39 | 40 | - name: configure fedora-messaging to point at tinystage rabbitmq 41 | lineinfile: 42 | path: /etc/fedora-messaging/config.toml 43 | regexp: "amqp_url = \"amqp://\"" 44 | line: "amqp_url = \"amqp://fedoramessages:fedoramessages@tinystage.tinystage.test\"" # gitleaks:allow 45 | -------------------------------------------------------------------------------- /devel/ansible/roles/ipa-client/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install RPM packages 3 | dnf: 4 | name: 5 | - ipa-client 6 | state: present 7 | 8 | - name: Enroll system as IPA client 9 | shell: | 10 | ipa-client-install \ 11 | --hostname {{ ansible_fqdn }} \ 12 | --domain {{ ansible_domain }} \ 13 | --realm {{ ansible_domain | upper }} \ 14 | --server ipa.{{ ansible_domain }} \ 15 | -p {{ ipa_admin_user }} \ 16 | -w {{ ipa_admin_password }} \ 17 | -U -N --force-join 18 | args: 19 | creates: /etc/ipa/default.conf 20 | -------------------------------------------------------------------------------- /devel/ansible/roles/noggin/files/bashrc: -------------------------------------------------------------------------------- 1 | # .bashrc 2 | 3 | export PATH=$PATH:/home/vagrant/.local/bin 4 | 5 | alias noggin-start="sudo systemctl start noggin.service && echo 'Noggin is running on https://noggin-dev.tinystage.test'" 6 | alias noggin-logs="sudo journalctl -u noggin.service" 7 | alias noggin-restart="sudo systemctl restart noggin.service && echo 'Noggin is running on https://noggin-dev.tinystage.test'" 8 | alias noggin-stop="sudo systemctl stop noggin.service && echo 'Noggin service stopped'" 9 | alias noggin-unit-tests="(cd /home/vagrant/noggin && poetry run pytest -v --cov tests/unit)" 10 | -------------------------------------------------------------------------------- /devel/ansible/roles/noggin/files/noggin.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=noggin 3 | After=network-online.target 4 | Wants=network-online.target 5 | After=synced-folder.service 6 | Requires=synced-folder.service 7 | 8 | [Service] 9 | # Allow the service to run on a priviledged port as an unpriviledged user 10 | AmbientCapabilities = CAP_NET_BIND_SERVICE 11 | 12 | # Environment variables 13 | Environment=FLASK_APP=/home/vagrant/noggin/noggin/app.py 14 | Environment=NOGGIN_CONFIG_PATH=/home/vagrant/noggin.cfg 15 | Environment=REQUESTS_CA_BUNDLE=/etc/pki/tls/certs/ca-bundle.crt 16 | Environment=FLASK_DEBUG=1 17 | Environment=PYTHONUNBUFFERED=1 18 | 19 | User=vagrant 20 | WorkingDirectory=/home/vagrant/noggin/noggin 21 | ExecStart=poetry run flask run -h 0.0.0.0 --port 443 --cert "/etc/pki/tls/certs/server.pem" --key "/etc/pki/tls/private/server.key" 22 | 23 | [Install] 24 | WantedBy=multi-user.target 25 | -------------------------------------------------------------------------------- /devel/ansible/roles/noggin/files/synced-folder.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Synced Folder with sshfs 3 | After=network-online.target 4 | Wants=network-online.target 5 | 6 | [Service] 7 | ExecStartPre=sh -c "while true; do [ -d /home/vagrant/noggin/.git ] && break; sleep 1; done" 8 | ExecStart=sh -c "while true; do [ -d /home/vagrant/noggin/.git ] || exit 1; sleep 1; done" 9 | Restart=always 10 | TimeoutStartSec=0 11 | 12 | [Install] 13 | WantedBy=multi-user.target 14 | 15 | -------------------------------------------------------------------------------- /devel/ansible/roles/noggin/handlers/main.yml: -------------------------------------------------------------------------------- 1 | - name: reload systemd 2 | systemd: 3 | daemon_reload: yes 4 | 5 | - name: reload user systemd 6 | systemd: 7 | scope: user 8 | daemon_reload: yes 9 | 10 | - name: restart synced-folder 11 | systemd: 12 | name: synced-folder 13 | state: restarted 14 | 15 | - name: restart noggin 16 | systemd: 17 | name: noggin 18 | state: restarted 19 | -------------------------------------------------------------------------------- /devel/ansible/roles/noggin/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install RPM packages 3 | dnf: 4 | name: 5 | - gcc 6 | - libffi-devel 7 | - python3-cryptography 8 | - python3-devel 9 | - python3-flask 10 | - python3-pip 11 | state: present 12 | 13 | - name: Install poetry Python package 14 | command: pip install poetry 15 | 16 | - name: install python deps with poetry 17 | become_user: vagrant 18 | command: 19 | cmd: poetry install 20 | chdir: noggin 21 | 22 | - name: Install the .bashrc 23 | copy: 24 | src: bashrc 25 | dest: /home/vagrant/.bashrc 26 | mode: 0644 27 | owner: vagrant 28 | group: vagrant 29 | 30 | - name: Install noggin.cfg 31 | become_user: vagrant 32 | template: 33 | src: noggin.cfg 34 | dest: noggin.cfg 35 | 36 | - name: compile the translations 37 | become_user: vagrant 38 | command: 39 | cmd: poetry run pybabel compile -d /home/vagrant/noggin/noggin/translations 40 | chdir: noggin 41 | 42 | - name: Install the systemd unit files 43 | copy: 44 | src: "{{ item }}" 45 | dest: /etc/systemd/system/{{ item }} 46 | owner: vagrant 47 | group: vagrant 48 | mode: 0644 49 | loop: 50 | - synced-folder.service 51 | - noggin.service 52 | notify: 53 | - reload systemd 54 | - restart synced-folder 55 | - restart noggin 56 | 57 | - name: Start the services using systemd 58 | systemd: 59 | state: started 60 | name: "{{ item }}" 61 | daemon_reload: yes 62 | enabled: yes 63 | loop: 64 | - synced-folder.service 65 | - noggin.service 66 | -------------------------------------------------------------------------------- /devel/ansible/roles/postgres/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install RPM packages 3 | dnf: 4 | name: 5 | - python3-psycopg2 6 | - postgresql-server 7 | - acl 8 | state: present 9 | 10 | - name: Setup the postgresql DB 11 | command: postgresql-setup --initdb 12 | args: 13 | creates: /var/lib/pgsql/data/postgresql.conf 14 | 15 | 16 | - name: Configure access to postgresql 17 | postgresql_pg_hba: 18 | dest: /var/lib/pgsql/data/pg_hba.conf 19 | contype: host 20 | databases: all 21 | users: all 22 | address: "{{item}}" 23 | method: md5 24 | loop: 25 | - 127.0.0.1/32 26 | - ::1/128 27 | 28 | - name: Start postgresql 29 | service: 30 | name: postgresql 31 | enabled: yes 32 | state: started 33 | 34 | - block: 35 | - name: Create the user 36 | postgresql_user: 37 | name: kerneltest 38 | password: kerneltest 39 | 40 | - name: Create the database 41 | postgresql_db: 42 | name: kerneltestdb 43 | owner: kerneltest 44 | 45 | become: yes 46 | become_user: postgres 47 | become_method: sudo 48 | 49 | - name: Make connection easier 50 | copy: 51 | dest: /home/vagrant/.pgpass 52 | content: "*:*:kerneltestdb:kerneltest:kerneltest\n" 53 | owner: vagrant 54 | group: vagrant 55 | mode: 0600 -------------------------------------------------------------------------------- /devel/ansible/vars.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ipa_admin_user: admin 3 | ipa_admin_password: password 4 | -------------------------------------------------------------------------------- /devel/rebuild-cassettes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | topdir=$(realpath $(dirname $0)/..) 6 | cd $topdir 7 | 8 | set -x 9 | 10 | find tests/unit/ -path "*/cassettes/*" -a -name "*.yaml" -delete 11 | poetry run pytest --no-cov -vv -x tests/unit 12 | -------------------------------------------------------------------------------- /devel/run-liccheck.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # SPDX-FileCopyrightText: Contributors to the Fedora Project 4 | # 5 | # SPDX-License-Identifier: GPL-3.0-or-later 6 | 7 | 8 | STRATEGY_URL=https://raw.githubusercontent.com/fedora-infra/shared/main/liccheck-strategy.ini 9 | 10 | trap 'rm -rf "$TMPFILE $STRATEGY_TMPFILE $LOCK_TMPDIR"' EXIT 11 | 12 | set -e 13 | set -x 14 | 15 | TMPFILE=$(mktemp -t requirements-XXXXXX.txt) 16 | STRATEGY_TMPFILE=$(mktemp -t liccheck-strategy-XXXXXX.ini) 17 | LOCK_TMPDIR=$(mktemp -d -t poetry-lock-XXXXXX) 18 | 19 | mkdir -p $LOCK_TMPDIR 20 | # Workaround https://github.com/python-poetry/poetry-plugin-export/issues/183 21 | sed -e '/^\s\+{version = "<2"/d' poetry.lock > $LOCK_TMPDIR/poetry.lock 22 | cp -p pyproject.toml $LOCK_TMPDIR/ 23 | 24 | curl -o $STRATEGY_TMPFILE $STRATEGY_URL 25 | 26 | poetry export --with dev --without-hashes -f requirements.txt -o $TMPFILE -C $LOCK_TMPDIR 27 | 28 | # Use pip freeze instead of poetry when it fails 29 | # poetry run pip freeze --exclude-editable --isolated > $TMPFILE 30 | 31 | poetry run liccheck -r $TMPFILE -s $STRATEGY_TMPFILE 32 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/_static/screenshots/adduser1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/docs/_static/screenshots/adduser1.png -------------------------------------------------------------------------------- /docs/_static/screenshots/adduser2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/docs/_static/screenshots/adduser2.png -------------------------------------------------------------------------------- /docs/_static/screenshots/deleteuser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/docs/_static/screenshots/deleteuser.png -------------------------------------------------------------------------------- /docs/_static/screenshots/groupscreen-sponsorview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/docs/_static/screenshots/groupscreen-sponsorview.png -------------------------------------------------------------------------------- /docs/_static/screenshots/leave-group.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/docs/_static/screenshots/leave-group.png -------------------------------------------------------------------------------- /docs/_static/screenshots/newaccount1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/docs/_static/screenshots/newaccount1.png -------------------------------------------------------------------------------- /docs/_static/screenshots/newaccount2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/docs/_static/screenshots/newaccount2.png -------------------------------------------------------------------------------- /docs/_static/screenshots/newaccount3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/docs/_static/screenshots/newaccount3.png -------------------------------------------------------------------------------- /docs/_static/screenshots/otp1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/docs/_static/screenshots/otp1.png -------------------------------------------------------------------------------- /docs/_static/screenshots/otp2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/docs/_static/screenshots/otp2.png -------------------------------------------------------------------------------- /docs/_static/screenshots/otp3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/docs/_static/screenshots/otp3.png -------------------------------------------------------------------------------- /news/+freeipa_servers.feature: -------------------------------------------------------------------------------- 1 | Restore the `FREEIPA_SERVERS` config option in case DNS discovery is not available 2 | -------------------------------------------------------------------------------- /news/+max_password_length.bug: -------------------------------------------------------------------------------- 1 | Don't allow passwords longer than 122 chars (see [IPA#9600](https://pagure.io/freeipa/issue/9600)) 2 | 3 | -------------------------------------------------------------------------------- /news/+popovers.bug: -------------------------------------------------------------------------------- 1 | Fix the popovers in the FAS theme -------------------------------------------------------------------------------- /news/+py312.feature: -------------------------------------------------------------------------------- 1 | Support Python 3.11 and 3.12 2 | -------------------------------------------------------------------------------- /news/+safety.dev.md: -------------------------------------------------------------------------------- 1 | Drop the Safety scanner as it now requires an online account 2 | -------------------------------------------------------------------------------- /news/1240.feature: -------------------------------------------------------------------------------- 1 | Password reset now possible with email address as well as username 2 | -------------------------------------------------------------------------------- /news/1415.feature: -------------------------------------------------------------------------------- 1 | Send message to messagebus when user is removed from a group 2 | -------------------------------------------------------------------------------- /news/1430.bug: -------------------------------------------------------------------------------- 1 | Remove the toasts when they are autohidden -------------------------------------------------------------------------------- /news/1447.feature: -------------------------------------------------------------------------------- 1 | Show the account creation date on user profiles 2 | -------------------------------------------------------------------------------- /news/1475.bug.md: -------------------------------------------------------------------------------- 1 | Warn users that logging in with email addresses is not supported 2 | -------------------------------------------------------------------------------- /news/1484.feature.md: -------------------------------------------------------------------------------- 1 | Allow logging in with an email address instead of a username 2 | -------------------------------------------------------------------------------- /news/PR1432.other: -------------------------------------------------------------------------------- 1 | Use fedora-bootstrap 5.3.3-0 in fas theme -------------------------------------------------------------------------------- /news/_template.md: -------------------------------------------------------------------------------- 1 | {% macro reference(value) -%} 2 | {%- if value.startswith("PR") -%} 3 | PR #{{ value[2:] }} 4 | {%- elif value.startswith("C") -%} 5 | [{{ value[1:] }}](https://github.com/fedora-infra/noggin/commit/{{ value[1:] }}) 6 | {%- else -%} 7 | #{{ value }} 8 | {%- endif -%} 9 | {%- endmacro -%} 10 | 11 | {{- top_line -}} 12 | 13 | Released on {{ versiondata.date }}. 14 | 15 | {% for section, _ in sections.items() -%} 16 | {%- if section -%} 17 | ## {{section}} 18 | {%- endif -%} 19 | 20 | {%- if sections[section] -%} 21 | {%- for category, val in definitions.items() if category in sections[section] and category != "author" -%} 22 | ### {{ definitions[category]['name'] }} 23 | 24 | {% if definitions[category]['showcontent'] -%} 25 | {%- for text, values in sections[section][category].items() %} 26 | - {{ text }} 27 | {%- if values %} 28 | {% if "\n - " in text or '\n * ' in text %} 29 | 30 | 31 | ( 32 | {%- else %} 33 | ( 34 | {%- endif -%} 35 | {%- for issue in values %} 36 | {{ reference(issue) }}{% if not loop.last %}, {% endif %} 37 | {%- endfor %} 38 | ) 39 | {% else %} 40 | 41 | {% endif %} 42 | {% endfor -%} 43 | {%- else -%} 44 | - {{ sections[section][category]['']|sort|join(', ') }} 45 | 46 | {% endif -%} 47 | {%- if sections[section][category]|length == 0 %} 48 | No significant changes. 49 | 50 | {% else -%} 51 | {%- endif %} 52 | 53 | {% endfor -%} 54 | {% if sections[section]["author"] -%} 55 | ### {{definitions['author']["name"]}} 56 | 57 | Many thanks to the contributors of bug reports, pull requests, and pull request reviews for this release: 58 | 59 | {% for text, values in sections[section]["author"].items() -%} 60 | - {{ text }} 61 | {% endfor -%} 62 | {%- endif %} 63 | 64 | {% else -%} 65 | No significant changes. 66 | 67 | {% endif %} 68 | {%- endfor +%} 69 | -------------------------------------------------------------------------------- /news/get-authors.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | This script browses through git commit history (starting at latest tag), collects all authors of 5 | commits and creates fragment for `towncrier`_ tool. 6 | 7 | It's meant to be run during the release process, before generating the release notes. 8 | 9 | Example:: 10 | 11 | $ python get_authors.py 12 | 13 | .. _towncrier: https://github.com/hawkowl/towncrier/ 14 | 15 | Authors: 16 | Aurelien Bompard 17 | Michal Konecny 18 | """ 19 | 20 | import os 21 | from argparse import ArgumentParser 22 | from subprocess import check_output 23 | 24 | 25 | EXCLUDE = ["Weblate (bot)"] 26 | 27 | last_tag = check_output( 28 | "git tag | sort -n | tail -n 1", shell=True, universal_newlines=True 29 | ).strip() 30 | 31 | args_parser = ArgumentParser() 32 | args_parser.add_argument( 33 | "until", 34 | nargs="?", 35 | default="HEAD", 36 | help="Consider all commits until this one (default: %(default)s).", 37 | ) 38 | args_parser.add_argument( 39 | "since", 40 | nargs="?", 41 | default=last_tag, 42 | help="Consider all commits since this one (default: %(default)s).", 43 | ) 44 | args = args_parser.parse_args() 45 | 46 | authors = {} 47 | log_range = args.since + ".." + args.until 48 | output = check_output( 49 | ["git", "log", log_range, "--format=%ae\t%an"], universal_newlines=True 50 | ) 51 | for line in output.splitlines(): 52 | email, fullname = line.split("\t") 53 | email = email.split("@")[0].replace(".", "") 54 | if email in authors: 55 | continue 56 | authors[email] = fullname 57 | 58 | for nick, fullname in authors.items(): 59 | if fullname in EXCLUDE or fullname.endswith("[bot]"): 60 | continue 61 | filename = f"{nick}.author" 62 | if os.path.exists(filename): 63 | continue 64 | print(f"Adding author {fullname} ({nick})") 65 | with open(filename, "w") as f: 66 | f.write(fullname) 67 | f.write("\n") 68 | -------------------------------------------------------------------------------- /noggin/__init__.py: -------------------------------------------------------------------------------- 1 | import importlib.metadata 2 | 3 | 4 | __version__ = importlib.metadata.version("noggin-aaa") 5 | -------------------------------------------------------------------------------- /noggin/controller/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from flask import Blueprint, g, render_template, session 4 | 5 | from noggin import __version__ 6 | from noggin.utility.templates import gravatar 7 | 8 | 9 | blueprint = Blueprint("root", __name__) 10 | 11 | 12 | @blueprint.app_errorhandler(404) 13 | def page_not_found(e): 14 | return render_template('404.html'), 404 15 | 16 | 17 | @blueprint.app_context_processor 18 | def inject_global_template_vars(): 19 | version = __version__ 20 | if ( 21 | "OPENSHIFT_BUILD_COMMIT" in os.environ 22 | and "OPENSHIFT_BUILD_REFERENCE" in os.environ 23 | ): 24 | version_ext = [ 25 | os.environ['OPENSHIFT_BUILD_REFERENCE'], 26 | os.environ['OPENSHIFT_BUILD_COMMIT'][:7], 27 | ] 28 | version = f"{version} ({':'.join(version_ext)})" 29 | 30 | return dict( 31 | gravatar=gravatar, 32 | ipa=g.ipa if 'ipa' in g else None, 33 | current_user=g.current_user if 'current_user' in g else None, 34 | current_username=session.get('noggin_username'), 35 | noggin_version=version, 36 | ) 37 | -------------------------------------------------------------------------------- /noggin/form/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/form/__init__.py -------------------------------------------------------------------------------- /noggin/form/group.py: -------------------------------------------------------------------------------- 1 | from flask_babel import lazy_gettext as _ 2 | from wtforms import HiddenField, StringField 3 | from wtforms.validators import DataRequired 4 | 5 | from .base import BaseForm 6 | 7 | 8 | class AddGroupMemberForm(BaseForm): 9 | new_member_username = StringField( 10 | 'New Member Username', 11 | validators=[DataRequired(message=_('New member username must not be empty'))], 12 | ) 13 | 14 | 15 | class RemoveGroupMemberForm(BaseForm): 16 | username = HiddenField( 17 | _('Username'), 18 | validators=[DataRequired(message=_('Username must not be empty'))], 19 | ) 20 | -------------------------------------------------------------------------------- /noggin/form/login_user.py: -------------------------------------------------------------------------------- 1 | from flask_babel import lazy_gettext as _ 2 | from wtforms import PasswordField, StringField 3 | from wtforms.validators import DataRequired, Optional 4 | 5 | from .base import ModestForm, SubmitButtonField 6 | from .validators import no_mixed_case 7 | 8 | 9 | class LoginUserForm(ModestForm): 10 | username = StringField( 11 | _('Username (or email address)'), 12 | validators=[ 13 | DataRequired(message=_('You must provide a user name')), 14 | no_mixed_case, 15 | ], 16 | ) 17 | 18 | password = PasswordField( 19 | _('Password'), 20 | validators=[DataRequired(message=_('You must provide a password'))], 21 | ) 22 | 23 | otp = StringField(_('One-Time Password'), validators=[Optional()]) 24 | 25 | submit = SubmitButtonField(_('Log In')) 26 | -------------------------------------------------------------------------------- /noggin/form/password_reset.py: -------------------------------------------------------------------------------- 1 | from flask_babel import lazy_gettext as _ 2 | from wtforms import PasswordField, StringField 3 | from wtforms.validators import DataRequired, EqualTo, Optional 4 | 5 | from .base import BaseForm, lower 6 | from .validators import PasswordLength 7 | 8 | 9 | class NewPasswordForm(BaseForm): 10 | password = PasswordField( 11 | _('New Password'), 12 | validators=[ 13 | DataRequired(message=_('Password must not be empty')), 14 | PasswordLength(), 15 | EqualTo('password_confirm', message=_('Passwords must match')), 16 | ], 17 | ) 18 | 19 | password_confirm = PasswordField(_('Confirm New Password')) 20 | 21 | otp = StringField( 22 | _('One-Time Password (if your account has Two-Factor Authentication enabled)'), 23 | validators=[Optional()], 24 | ) 25 | 26 | 27 | class PasswordResetForm(NewPasswordForm): 28 | current_password = PasswordField( 29 | _('Current Password'), 30 | validators=[DataRequired(message=_('Current password must not be empty'))], 31 | ) 32 | 33 | 34 | class ForgottenPasswordForm(BaseForm): 35 | username = StringField( 36 | _('Username or Email'), 37 | validators=[DataRequired(message=_('User name must not be empty'))], 38 | description=_("Enter your username or email to reset your password"), 39 | filters=[lower], 40 | ) 41 | -------------------------------------------------------------------------------- /noggin/form/sync_token.py: -------------------------------------------------------------------------------- 1 | from flask_babel import lazy_gettext as _ 2 | from wtforms import PasswordField, StringField 3 | from wtforms.validators import DataRequired, Optional 4 | 5 | from .base import BaseForm 6 | 7 | 8 | class SyncTokenForm(BaseForm): 9 | username = StringField( 10 | _('Username'), 11 | validators=[DataRequired(message=_('You must provide a user name'))], 12 | ) 13 | 14 | password = PasswordField( 15 | _('Password'), 16 | validators=[DataRequired(message=_('You must provide a password'))], 17 | ) 18 | 19 | first_code = StringField( 20 | _('First OTP'), 21 | validators=[DataRequired(message=_('You must provide a first code'))], 22 | ) 23 | 24 | second_code = StringField( 25 | _('Second OTP'), 26 | validators=[DataRequired(message=_('You must provide a second code'))], 27 | ) 28 | 29 | token = StringField(_('Token ID'), validators=[Optional()]) 30 | -------------------------------------------------------------------------------- /noggin/middleware.py: -------------------------------------------------------------------------------- 1 | import python_freeipa 2 | from flask import current_app, make_response, render_template 3 | 4 | 5 | class IPAErrorHandler: 6 | def __init__(self, app=None, error_template="ipa_error.html"): 7 | self.template = error_template 8 | if app is not None: 9 | self.init_app(app) 10 | 11 | def init_app(self, app): 12 | app.register_error_handler( 13 | python_freeipa.exceptions.FreeIPAError, self.get_error_response 14 | ) 15 | 16 | def get_error_response(self, error): 17 | current_app.logger.error(f"Uncaught IPA exception: {error}") 18 | return make_response(render_template(self.template, error=error), 500) 19 | -------------------------------------------------------------------------------- /noggin/representation/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/representation/__init__.py -------------------------------------------------------------------------------- /noggin/representation/agreement.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | from .base import Representation 4 | 5 | 6 | NOT_ASCII_RE = re.compile(r"\W") 7 | 8 | 9 | class Agreement(Representation): 10 | attr_names = { 11 | "name": "cn", 12 | "enabled": "ipaenabledflag", 13 | "description": "description", 14 | "users": "memberuser_user", 15 | "groups": "member_group", 16 | "uniqueid": "ipauniqueid", 17 | } 18 | attr_types = { 19 | "users": "list", 20 | "groups": "list", 21 | "enabled": "bool", 22 | } 23 | pkey = "name" 24 | ipa_object = "fasagreement" 25 | 26 | @property 27 | def slug(self): 28 | return NOT_ASCII_RE.sub("", self.name) 29 | -------------------------------------------------------------------------------- /noggin/representation/group.py: -------------------------------------------------------------------------------- 1 | from .base import Representation 2 | 3 | 4 | class Group(Representation): 5 | attr_names = { 6 | "name": "cn", 7 | "description": "description", 8 | "members": "member_user", 9 | "sponsors": "membermanager_user", 10 | "urls": "fasurl", 11 | "irc_channel": "fasircchannel", 12 | "mailing_list": "fasmailinglist", 13 | "discussion_url": "fasdiscussionurl", 14 | } 15 | attr_types = { 16 | "members": "list", 17 | "sponsors": "list", 18 | "urls": "list", 19 | } 20 | pkey = "name" 21 | ipa_object = "group" 22 | -------------------------------------------------------------------------------- /noggin/representation/otptoken.py: -------------------------------------------------------------------------------- 1 | from .base import Representation 2 | 3 | 4 | class OTPToken(Representation): 5 | attr_names = { 6 | "uniqueid": "ipatokenuniqueid", 7 | "description": "description", 8 | "disabled": "ipatokendisabled", 9 | } 10 | attr_types = { 11 | "disabled": "bool", 12 | } 13 | pkey = "uniqueid" 14 | ipa_object = "otptoken" 15 | 16 | @property 17 | def uri(self): 18 | return self.raw.get('uri') 19 | -------------------------------------------------------------------------------- /noggin/security/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/security/__init__.py -------------------------------------------------------------------------------- /noggin/static/css/main.css: -------------------------------------------------------------------------------- 1 | /* Typeahead stuff... based on css found here: 2 | https://github.com/twhetzel/flask-jquery-autocomplete-demo/blob/b8b07a59a120e3254483848707da993463c56e91/templates/materialTagsExamples.html 3 | */ 4 | .tt-hint { 5 | color: #999; 6 | left: inherit !important; 7 | } 8 | 9 | .tt-menu { 10 | width: 100%; 11 | padding: 8px 0; 12 | background-color: #fff; 13 | border: 1px solid #ccc; 14 | border: 1px solid rgba(0, 0, 0, 0.2); 15 | box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); 16 | color: #000; 17 | } 18 | 19 | .tt-menu h5 { 20 | padding: 5px; 21 | } 22 | 23 | .tt-suggestion { 24 | padding: 3px 20px; 25 | font-size: 18px; 26 | line-height: 24px; 27 | } 28 | 29 | .tt-suggestion p { 30 | margin: 0; 31 | } 32 | .tt-suggestion > p:hover, 33 | .tt-suggestion > p:focus { 34 | color: #fff; 35 | text-decoration: none; 36 | outline: 0; 37 | background-color: #428bca; 38 | } 39 | .tt-suggestion.tt-cursor { 40 | color: #fff; 41 | background-color: #428bca; 42 | } 43 | .tt-suggestion.tt-cursor, 44 | .tt-suggestion:hover { 45 | color: #fff; 46 | background-color: #428bca; 47 | cursor: pointer; 48 | } 49 | 50 | .modal.with-typeahead { 51 | overflow-y: visible; 52 | } 53 | .modal.with-typeahead .tt-menu { 54 | top: 50% !important; 55 | } 56 | 57 | .twitter-typeahead { 58 | width: 100%; 59 | } 60 | 61 | /* Moves the footer to the bottom of the page */ 62 | 63 | .bodycontent { 64 | min-height: 70vh; 65 | max-width: 100vw; 66 | margin: 0; 67 | display: flex; 68 | flex-flow: column; 69 | } 70 | 71 | /* Support select tags as prepended fields */ 72 | .input-group > .input-group-prepend > select.form-control, 73 | .input-group > .input-group-prepend > select.form-select { 74 | border-top-right-radius: 0; 75 | border-bottom-right-radius: 0; 76 | } 77 | 78 | /* Registering users */ 79 | 80 | .fasstatus .badge { 81 | font-size: 90%; 82 | } 83 | -------------------------------------------------------------------------------- /noggin/static/fonts/font-awesome/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/static/fonts/font-awesome/FontAwesome.otf -------------------------------------------------------------------------------- /noggin/static/fonts/font-awesome/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/static/fonts/font-awesome/fontawesome-webfont.eot -------------------------------------------------------------------------------- /noggin/static/fonts/font-awesome/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/static/fonts/font-awesome/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /noggin/static/fonts/font-awesome/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/static/fonts/font-awesome/fontawesome-webfont.woff -------------------------------------------------------------------------------- /noggin/static/fonts/font-awesome/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/static/fonts/font-awesome/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /noggin/static/js/search.js: -------------------------------------------------------------------------------- 1 | var bloodhound_users = new Bloodhound({ 2 | datumTokenizer: Bloodhound.tokenizers.obj.whitespace("value"), 3 | queryTokenizer: Bloodhound.tokenizers.whitespace, 4 | remote: { 5 | url: URL_SEARCH + "?username=%QUERY", 6 | wildcard: "%QUERY", 7 | }, 8 | }); 9 | 10 | var bloodhound_groups = new Bloodhound({ 11 | datumTokenizer: Bloodhound.tokenizers.obj.whitespace("value"), 12 | queryTokenizer: Bloodhound.tokenizers.whitespace, 13 | remote: { 14 | url: URL_SEARCH + "?group=%QUERY", 15 | wildcard: "%QUERY", 16 | }, 17 | }); 18 | 19 | $("#search") 20 | .typeahead( 21 | { highlight: true }, 22 | { 23 | name: "users", 24 | display: "uid", 25 | source: bloodhound_users, 26 | templates: { 27 | header: "
13 | {{ _("There was a problem with the IPA backend, please try again later.")}} 14 | {% if 'noggin_session' in session %} 15 | {{ _("You can also log out and log back in if the problem persists.", url=url_for(".logout")) }} 16 | {% endif %} 17 |
18 |{{_("Hello %(name)s. Please sign the agreement(s) below if you agree with them.", name=user.name)}}
17 | {% include '_agreements_form.html' %} 18 |{{_("Congratulations %(name)s, your account has been created!", name=user.name)}}
17 |
{{_("Before you can log in, your email address: %(mail)s needs to be validated. Please check your inbox and click on the link to proceed.", mail=user.mail)}}
18 |19 | {{_("If you can't find the email in a couple minutes, check your spam folder. If it's not there, you can ask for another validation email by clicking on the button below.")}} 20 |
21 | 27 |AlmaLinux Accounts
2 |{{_("Hello %(name)s,", name=user.name)}}
3 |{{_("To activate your account with username %(username)s, click on the link below:", username=user.username)}}
4 |5 | 6 | {{ url_for('.activate_account', _external=True) }}?token={{ token }} 7 | 8 |
9 |{{_("If you did not create an account for username %(username)s, you can ignore this email.", username=user.username)}}
10 | 11 |{{_("The AlmaLinux Team")}}
12 | -------------------------------------------------------------------------------- /noggin/themes/almalinux/templates/email-validation.txt: -------------------------------------------------------------------------------- 1 | == AlmaLinux Accounts == 2 | 3 | {{_("Hello %(name)s,", name=user.name)}} 4 | 5 | {{_("To activate your account with username %(username)s, click on the link below:", username=user.username)}} 6 | 7 | {{ url_for('.activate_account', _external=True) }}?token={{ token }} 8 | 9 | {{_("If you did not create an account for username %(username)s, you can ignore this email.", username=user.username)}} 10 | 11 | -- {{_("The AlmaLinux Team")}} 12 | -------------------------------------------------------------------------------- /noggin/themes/almalinux/templates/forgot-password-email.html: -------------------------------------------------------------------------------- 1 |{{_("Hi,")}}
2 |{{_("Click the link below to reset your password. If you did not request a password reset, please ignore this email.")}}
3 |4 | 5 | {{ url_for('.forgot_password_change', _external=True) }}?token={{ token }} 6 | 7 |
8 | 9 |- {{_("The AlmaLinux Team")}}
10 | -------------------------------------------------------------------------------- /noggin/themes/almalinux/templates/forgot-password-email.txt: -------------------------------------------------------------------------------- 1 | {{_("Hi,")}} 2 | 3 | {{_("Click the link below to reset your password. If you did not request a password reset, please ignore this email.")}} 4 | {{ url_for('.forgot_password_change', _external=True) }}?token={{ token }} 5 | 6 | - {{_("The AlmaLinux Team")}} 7 | -------------------------------------------------------------------------------- /noggin/themes/almalinux/templates/settings-email-validation.html: -------------------------------------------------------------------------------- 1 |AlmaLinux Accounts
2 |{{_("Hello %(name)s,", name=user.name)}}
3 |{{_("To validate the email address %(address)s, click on the link below:", address=address)}}
4 | 9 |{{_("If you did not set the email address %(address)s in your account %(username)s, you can ignore this email.", username=user.username, address=address)}}
10 | 11 |{{_("The AlmaLinux Team")}}
12 | -------------------------------------------------------------------------------- /noggin/themes/almalinux/templates/settings-email-validation.txt: -------------------------------------------------------------------------------- 1 | == AlmaLinux Accounts == 2 | 3 | {{_("Hello %(name)s,", name=user.name)}} 4 | 5 | {{_("To validate the email address %(address)s, click on the link below:", address=address)}} 6 | 7 | {{ url_for('.user_settings_email_validate', username=user.username, _external=True) }}?token={{ token }} 8 | 9 | {{_("If you did not set the email address %(address)s in your account %(username)s, you can ignore this email.", username=user.username, address=address)}} 10 | 11 | -- {{_("The AlmaLinux Team")}} 12 | -------------------------------------------------------------------------------- /noggin/themes/centos/static/css/default.css: -------------------------------------------------------------------------------- 1 | /*CSS rules for the 'centos' noggin theme */ 2 | 3 | body { 4 | font-family: 'Montserrat', sans-serif; 5 | font-weight: 400; 6 | color: #2e3344; 7 | } 8 | 9 | .h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 { 10 | font-weight: 650; 11 | line-height: 2; 12 | } 13 | 14 | .navbar-dark { background-color: #2e3440; color: #fff; } 15 | 16 | .row { margin-left: 0 } 17 | 18 | .btn-primary, 19 | .btn-outline-primary { color: #5e81ac; background-color: #fff; border-color: #81a1c1; } 20 | .btn-outline-primary:hover, 21 | .btn-primary:hover { color: #fff; background-color: #5e81ac; border-color: #81a1c1; } 22 | a, .btn-link { color: #5e81ac } 23 | a:hover, .btn-link { color: #81a1c1 } 24 | 25 | .badge-secondary { background-color: #2e3344; } 26 | 27 | .text-info { color: #88c0d0 } 28 | .text-success { color: #8fbcbb; } 29 | 30 | div.media.align-items-center img { border-radius: 50%; } 31 | 32 | .card.card-body { 33 | margin-bottom: 10px !important; 34 | } 35 | 36 | ul.list-unstyled.row li { 37 | padding-left: 0px; 38 | } 39 | -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-Black.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-Black.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-Black.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-Black.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-BlackItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-BlackItalic.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-BlackItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-BlackItalic.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-Bold.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-Bold.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-BoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-BoldItalic.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-BoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-BoldItalic.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-ExtraBold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-ExtraBold.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-ExtraBold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-ExtraBold.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-ExtraBoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-ExtraBoldItalic.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-ExtraBoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-ExtraBoldItalic.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-ExtraLight.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-ExtraLight.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-ExtraLight.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-ExtraLight.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-ExtraLightItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-ExtraLightItalic.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-ExtraLightItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-ExtraLightItalic.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-Italic.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-Italic.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-Light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-Light.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-Light.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-Light.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-LightItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-LightItalic.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-LightItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-LightItalic.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-Medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-Medium.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-Medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-Medium.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-MediumItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-MediumItalic.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-MediumItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-MediumItalic.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-Regular.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-Regular.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-SemiBold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-SemiBold.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-SemiBold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-SemiBold.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-SemiBoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-SemiBoldItalic.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-SemiBoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-SemiBoldItalic.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-Thin.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-Thin.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-Thin.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-Thin.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-ThinItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-ThinItalic.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/Montserrat-ThinItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/Montserrat-ThinItalic.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-Black.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-Black.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-Black.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-Black.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-BlackItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-BlackItalic.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-BlackItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-BlackItalic.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-Bold.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-Bold.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-BoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-BoldItalic.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-BoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-BoldItalic.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-ExtraBold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-ExtraBold.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-ExtraBold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-ExtraBold.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-ExtraBoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-ExtraBoldItalic.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-ExtraBoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-ExtraBoldItalic.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-ExtraLight.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-ExtraLight.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-ExtraLight.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-ExtraLight.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-ExtraLightItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-ExtraLightItalic.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-ExtraLightItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-ExtraLightItalic.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-Italic.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-Italic.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-Light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-Light.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-Light.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-Light.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-LightItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-LightItalic.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-LightItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-LightItalic.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-Medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-Medium.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-Medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-Medium.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-MediumItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-MediumItalic.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-MediumItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-MediumItalic.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-Regular.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-Regular.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-SemiBold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-SemiBold.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-SemiBold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-SemiBold.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-SemiBoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-SemiBoldItalic.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-SemiBoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-SemiBoldItalic.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-Thin.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-Thin.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-Thin.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-Thin.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-ThinItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-ThinItalic.woff -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/MontserratAlternates-ThinItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/fonts/MontserratAlternates-ThinItalic.woff2 -------------------------------------------------------------------------------- /noggin/themes/centos/static/fonts/README.MD: -------------------------------------------------------------------------------- 1 | # The Montserrat Font Project 2 | To use this font as a webfont, ```Montserrat.css``` is included. 3 | 4 | ## How to use 5 | ### 1. @import 6 | You can import the file into your stylesheet as follows: 7 | ```css 8 | @import url("static/fonts/Montserrat/fonts/webfonts/Montserrat.css"); 9 | ``` 10 | 11 | **NOTE:** The directory where the stylesheet is placed. 12 | 13 | Then we can use it to style elements: 14 | ```css 15 | body { 16 | font-family: 'Montserrat', sans-serif; 17 | font-weight: 400; 18 | } 19 | ``` 20 | 21 | ### 2. \ing a stylesheet 22 | Similarly, you could link to the same asset as you would any other CSS filter, in the \ of the HTML document rather than in the CSS: 23 | ```html 24 | 25 | ``` 26 | 27 | **NOTE:** The directory where the stylesheet is placed. 28 | 29 | Then we can use it to style elements: 30 | ```css 31 | body { 32 | font-family: 'Montserrat', sans-serif; 33 | font-weight: 400; 34 | } 35 | ``` -------------------------------------------------------------------------------- /noggin/themes/centos/static/images/centos-accounts-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/centos/static/images/centos-accounts-logo.png -------------------------------------------------------------------------------- /noggin/themes/centos/static/vendor/bootstrap-5.2.0-beta1/0.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/centos/static/vendor/bootstrap-5.2.0-beta1/1.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/centos/static/vendor/bootstrap-5.2.0-beta1/10.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/centos/static/vendor/bootstrap-5.2.0-beta1/11.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/centos/static/vendor/bootstrap-5.2.0-beta1/12.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/centos/static/vendor/bootstrap-5.2.0-beta1/13.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/centos/static/vendor/bootstrap-5.2.0-beta1/14.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/centos/static/vendor/bootstrap-5.2.0-beta1/15.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/centos/static/vendor/bootstrap-5.2.0-beta1/2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/centos/static/vendor/bootstrap-5.2.0-beta1/3.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/centos/static/vendor/bootstrap-5.2.0-beta1/4.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/centos/static/vendor/bootstrap-5.2.0-beta1/5.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/centos/static/vendor/bootstrap-5.2.0-beta1/6.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/centos/static/vendor/bootstrap-5.2.0-beta1/7.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/centos/static/vendor/bootstrap-5.2.0-beta1/8.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/centos/static/vendor/bootstrap-5.2.0-beta1/9.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/centos/templates/email-validation.html: -------------------------------------------------------------------------------- 1 |Noggin
2 |{{_("Hello %(name)s,", name=user.name)}}
3 |{{_("To activate your account with username %(username)s, click on the link below:", username=user.username)}}
4 |5 | 6 | {{ url_for('.activate_account', _external=True) }}?token={{ token }} 7 | 8 |
9 |{{_("If you did not create an account for username %(username)s, you can ignore this email.", username=user.username)}}
10 | 11 |{{_("The Noggin team")}}
12 | -------------------------------------------------------------------------------- /noggin/themes/centos/templates/email-validation.txt: -------------------------------------------------------------------------------- 1 | == Noggin == 2 | 3 | {{_("Hello %(name)s,", name=user.name)}} 4 | 5 | {{_("To activate your account with username %(username)s, click on the link below:", username=user.username)}} 6 | 7 | {{ url_for('.activate_account', _external=True) }}?token={{ token }} 8 | 9 | {{_("If you did not create an account for username %(username)s, you can ignore this email.", username=user.username)}} 10 | 11 | -- {{_("The Noggin team")}} 12 | -------------------------------------------------------------------------------- /noggin/themes/centos/templates/forgot-password-email.html: -------------------------------------------------------------------------------- 1 |{{_("Hi,")}}
2 |{{_("Click the link below to reset your password. If you did not request a password reset, please ignore this email.")}}
3 |4 | 5 | {{ url_for('.forgot_password_change', _external=True) }}?token={{ token }} 6 | 7 |
8 | 9 |- {{_("The Noggin team")}}
10 | -------------------------------------------------------------------------------- /noggin/themes/centos/templates/forgot-password-email.txt: -------------------------------------------------------------------------------- 1 | {{_("Hi,")}} 2 | 3 | {{_("Click the link below to reset your password. If you did not request a password reset, please ignore this email.")}} 4 | {{ url_for('.forgot_password_change', _external=True) }}?token={{ token }} 5 | 6 | - {{_("The Noggin team")}} 7 | -------------------------------------------------------------------------------- /noggin/themes/centos/templates/settings-email-validation.html: -------------------------------------------------------------------------------- 1 |Noggin
2 |{{_("Hello %(name)s,", name=user.name)}}
3 |{{_("To validate the email address %(address)s, click on the link below:", address=address)}}
4 | 9 |{{_("If you did not set the email address %(address)s in your account %(username)s, you can ignore this email.", username=user.username, address=address)}}
10 | 11 |{{_("The Noggin team")}}
12 | -------------------------------------------------------------------------------- /noggin/themes/centos/templates/settings-email-validation.txt: -------------------------------------------------------------------------------- 1 | == Noggin == 2 | 3 | {{_("Hello %(name)s,", name=user.name)}} 4 | 5 | {{_("To validate the email address %(address)s, click on the link below:", address=address)}} 6 | 7 | {{ url_for('.user_settings_email_validate', username=user.username, _external=True) }}?token={{ token }} 8 | 9 | {{_("If you did not set the email address %(address)s in your account %(username)s, you can ignore this email.", username=user.username, address=address)}} 10 | 11 | -- {{_("The Noggin team")}} 12 | -------------------------------------------------------------------------------- /noggin/themes/default/static/css/default.css: -------------------------------------------------------------------------------- 1 | /*CSS rules for the 'default' noggin theme */ 2 | -------------------------------------------------------------------------------- /noggin/themes/default/static/vendor/bootstrap-5.2.0-beta1/0.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/default/static/vendor/bootstrap-5.2.0-beta1/1.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/default/static/vendor/bootstrap-5.2.0-beta1/10.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/default/static/vendor/bootstrap-5.2.0-beta1/11.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/default/static/vendor/bootstrap-5.2.0-beta1/12.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/default/static/vendor/bootstrap-5.2.0-beta1/13.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/default/static/vendor/bootstrap-5.2.0-beta1/14.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/default/static/vendor/bootstrap-5.2.0-beta1/15.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/default/static/vendor/bootstrap-5.2.0-beta1/2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/default/static/vendor/bootstrap-5.2.0-beta1/3.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/default/static/vendor/bootstrap-5.2.0-beta1/4.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/default/static/vendor/bootstrap-5.2.0-beta1/5.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/default/static/vendor/bootstrap-5.2.0-beta1/6.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/default/static/vendor/bootstrap-5.2.0-beta1/7.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/default/static/vendor/bootstrap-5.2.0-beta1/8.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/default/static/vendor/bootstrap-5.2.0-beta1/9.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/default/templates/email-validation.html: -------------------------------------------------------------------------------- 1 |Noggin
2 |{{_("Hello %(name)s,", name=user.name)}}
3 |{{_("To activate your account with username %(username)s, click on the link below:", username=user.username)}}
4 |5 | 6 | {{ url_for('.activate_account', _external=True) }}?token={{ token }} 7 | 8 |
9 |10 | {{_("This link will be valid for %(ttl)s minutes (until %(valid_until)s UTC).", ttl=ttl, valid_until=valid_until.strftime("%H:%M"))}} 11 | {{_("If the link has expired, you can request a new one here: ")}} 12 | 13 | {{ url_for('.confirm_registration', _external=True) }}?username={{ user.username }} 14 | 15 |
16 |{{_("If you did not create an account for username %(username)s, you can ignore this email.", username=user.username)}}
17 | 18 |{{_("The Noggin team")}}
19 | -------------------------------------------------------------------------------- /noggin/themes/default/templates/email-validation.txt: -------------------------------------------------------------------------------- 1 | == Noggin == 2 | 3 | {{_("Hello %(name)s,", name=user.name)}} 4 | 5 | {{_("To activate your account with username %(username)s, click on the link below:", username=user.username)}} 6 | 7 | {{ url_for('.activate_account', _external=True) }}?token={{ token }} 8 | 9 | {{_("This link will be valid for %(ttl)s minutes (until %(valid_until)s UTC).", ttl=ttl, valid_until=valid_until.strftime("%H:%M"))}} 10 | {{_("If the link has expired, you can request a new one here:")}} 11 | 12 | {{ url_for('.confirm_registration', _external=True) }}?username={{ user.username }} 13 | 14 | {{_("If you did not create an account for username %(username)s, you can ignore this email.", username=user.username)}} 15 | 16 | -- {{_("The Noggin team")}} 17 | -------------------------------------------------------------------------------- /noggin/themes/default/templates/forgot-password-email.html: -------------------------------------------------------------------------------- 1 |{{_("Hi,")}}
2 |{{_("Click the link below to reset your password. If you did not request a password reset, please ignore this email.")}}
3 |4 | 5 | {{ url_for('.forgot_password_change', _external=True) }}?token={{ token }} 6 | 7 |
8 | 9 |- {{_("The Noggin team")}}
10 | -------------------------------------------------------------------------------- /noggin/themes/default/templates/forgot-password-email.txt: -------------------------------------------------------------------------------- 1 | {{_("Hi,")}} 2 | 3 | {{_("Click the link below to reset your password. If you did not request a password reset, please ignore this email.")}} 4 | {{ url_for('.forgot_password_change', _external=True) }}?token={{ token }} 5 | 6 | - {{_("The Noggin team")}} 7 | -------------------------------------------------------------------------------- /noggin/themes/default/templates/settings-email-validation.html: -------------------------------------------------------------------------------- 1 |Noggin
2 |{{_("Hello %(name)s,", name=user.name)}}
3 |{{_("To validate the email address %(address)s, click on the link below:", address=address)}}
4 | 9 |{{_("If you did not set the email address %(address)s in your account %(username)s, you can ignore this email.", username=user.username, address=address)}}
10 | 11 |{{_("The Noggin team")}}
12 | -------------------------------------------------------------------------------- /noggin/themes/default/templates/settings-email-validation.txt: -------------------------------------------------------------------------------- 1 | == Noggin == 2 | 3 | {{_("Hello %(name)s,", name=user.name)}} 4 | 5 | {{_("To validate the email address %(address)s, click on the link below:", address=address)}} 6 | 7 | {{ url_for('.user_settings_email_validate', username=user.username, _external=True) }}?token={{ token }} 8 | 9 | {{_("If you did not set the email address %(address)s in your account %(username)s, you can ignore this email.", username=user.username, address=address)}} 10 | 11 | -- {{_("The Noggin team")}} 12 | -------------------------------------------------------------------------------- /noggin/themes/fas/static/css/fedoraaccounts.css: -------------------------------------------------------------------------------- 1 | /*CSS rules for the 'FAS' noggin theme */ 2 | -------------------------------------------------------------------------------- /noggin/themes/fas/static/favicon/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/fas/static/favicon/android-chrome-192x192.png -------------------------------------------------------------------------------- /noggin/themes/fas/static/favicon/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/fas/static/favicon/android-chrome-512x512.png -------------------------------------------------------------------------------- /noggin/themes/fas/static/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/fas/static/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /noggin/themes/fas/static/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/fas/static/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /noggin/themes/fas/static/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/fas/static/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /noggin/themes/fas/static/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/fas/static/favicon/favicon.ico -------------------------------------------------------------------------------- /noggin/themes/fas/static/favicon/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "short_name": "", 4 | "icons": [ 5 | { 6 | "src": "/theme/static/favicon/android-chrome-192x192.png?v=alJGQ5vmvC", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/theme/static/favicon/android-chrome-512x512.png?v=alJGQ5vmvC", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-300.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-300.eot -------------------------------------------------------------------------------- /noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-300.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-300.ttf -------------------------------------------------------------------------------- /noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-300.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-300.woff -------------------------------------------------------------------------------- /noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-300.woff2 -------------------------------------------------------------------------------- /noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-300italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-300italic.eot -------------------------------------------------------------------------------- /noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-300italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-300italic.ttf -------------------------------------------------------------------------------- /noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-300italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-300italic.woff -------------------------------------------------------------------------------- /noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-300italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-300italic.woff2 -------------------------------------------------------------------------------- /noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-700.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-700.eot -------------------------------------------------------------------------------- /noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-700.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-700.ttf -------------------------------------------------------------------------------- /noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-700.woff -------------------------------------------------------------------------------- /noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-700.woff2 -------------------------------------------------------------------------------- /noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-700italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-700italic.eot -------------------------------------------------------------------------------- /noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-700italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-700italic.ttf -------------------------------------------------------------------------------- /noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-700italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-700italic.woff -------------------------------------------------------------------------------- /noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-700italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-700italic.woff2 -------------------------------------------------------------------------------- /noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-italic.eot -------------------------------------------------------------------------------- /noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-italic.ttf -------------------------------------------------------------------------------- /noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-italic.woff -------------------------------------------------------------------------------- /noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-italic.woff2 -------------------------------------------------------------------------------- /noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-regular.eot -------------------------------------------------------------------------------- /noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-regular.ttf -------------------------------------------------------------------------------- /noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-regular.woff -------------------------------------------------------------------------------- /noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/fas/static/fonts/open-sans/open-sans-v13-latin_latin-ext-regular.woff2 -------------------------------------------------------------------------------- /noggin/themes/fas/static/images/FedoraAccounts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/fas/static/images/FedoraAccounts.png -------------------------------------------------------------------------------- /noggin/themes/fas/static/images/redhat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/noggin/themes/fas/static/images/redhat.png -------------------------------------------------------------------------------- /noggin/themes/fas/static/vendor/fedora-bootstrap-5.3.3-0/0.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/fas/static/vendor/fedora-bootstrap-5.3.3-0/1.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/fas/static/vendor/fedora-bootstrap-5.3.3-0/10.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/fas/static/vendor/fedora-bootstrap-5.3.3-0/11.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/fas/static/vendor/fedora-bootstrap-5.3.3-0/12.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/fas/static/vendor/fedora-bootstrap-5.3.3-0/13.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/fas/static/vendor/fedora-bootstrap-5.3.3-0/14.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/fas/static/vendor/fedora-bootstrap-5.3.3-0/15.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/fas/static/vendor/fedora-bootstrap-5.3.3-0/16.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/fas/static/vendor/fedora-bootstrap-5.3.3-0/17.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/fas/static/vendor/fedora-bootstrap-5.3.3-0/18.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/fas/static/vendor/fedora-bootstrap-5.3.3-0/2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/fas/static/vendor/fedora-bootstrap-5.3.3-0/3.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/fas/static/vendor/fedora-bootstrap-5.3.3-0/4.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/fas/static/vendor/fedora-bootstrap-5.3.3-0/5.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/fas/static/vendor/fedora-bootstrap-5.3.3-0/6.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/fas/static/vendor/fedora-bootstrap-5.3.3-0/7.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/fas/static/vendor/fedora-bootstrap-5.3.3-0/8.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/fas/static/vendor/fedora-bootstrap-5.3.3-0/9.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /noggin/themes/fas/templates/email-validation.html: -------------------------------------------------------------------------------- 1 |{{_("Fedora Accounts System")}}
2 |{{_("Hello %(name)s,", name=user.name)}}
3 |{{_("To activate your account with username %(username)s, click on the link below:", username=user.username)}}
4 |5 | 6 | {{ url_for('.activate_account', _external=True) }}?token={{ token }} 7 | 8 |
9 |10 | {{_("This link will be valid for %(ttl)s minutes (until %(valid_until)s UTC).", ttl=ttl, valid_until=valid_until.strftime("%H:%M"))}} 11 | {{_("If the link has expired, you can request a new one here: ")}} 12 | 13 | {{ url_for('.confirm_registration', _external=True) }}?username={{ user.username }} 14 | 15 |
16 |{{_("If you did not create an account for username %(username)s, you can ignore this email.", username=user.username)}}
17 | 18 |{{_("Fedora Accounts System")}}
19 | -------------------------------------------------------------------------------- /noggin/themes/fas/templates/email-validation.txt: -------------------------------------------------------------------------------- 1 | == {{_("Fedora Accounts System")}} == 2 | 3 | {{_("Hello %(name)s,", name=user.name)}} 4 | 5 | {{_("To activate your account with username %(username)s, click on the link below:", username=user.username)}} 6 | 7 | {{ url_for('.activate_account', _external=True) }}?token={{ token }} 8 | 9 | {{_("This link will be valid for %(ttl)s minutes (until %(valid_until)s UTC).", ttl=ttl, valid_until=valid_until.strftime("%H:%M"))}} 10 | {{_("If the link has expired, you can request a new one here:")}} 11 | 12 | {{ url_for('.confirm_registration', _external=True) }}?username={{ user.username }} 13 | 14 | {{_("If you did not create an account for username %(username)s, you can ignore this email.", username=user.username)}} 15 | 16 | -- {{_("Fedora Accounts System")}} 17 | -------------------------------------------------------------------------------- /noggin/themes/fas/templates/forgot-password-email.html: -------------------------------------------------------------------------------- 1 |{{_("Hi,")}}
2 |{{_("Click the link below to reset your password. If you did not request a password reset, please ignore this email.")}}
3 |4 | 5 | {{ url_for('.forgot_password_change', _external=True) }}?token={{ token }} 6 | 7 |
8 | 9 |- {{_("Fedora Accounts System")}}
10 | -------------------------------------------------------------------------------- /noggin/themes/fas/templates/forgot-password-email.txt: -------------------------------------------------------------------------------- 1 | {{_("Hi,")}} 2 | 3 | {{_("Click the link below to reset your password. If you did not request a password reset, please ignore this email.")}} 4 | {{ url_for('.forgot_password_change', _external=True) }}?token={{ token }} 5 | 6 | - {{_("Fedora Accounts System")}} 7 | -------------------------------------------------------------------------------- /noggin/themes/fas/templates/settings-email-validation.html: -------------------------------------------------------------------------------- 1 |{{_("Fedora Accounts System")}}
2 |{{_("Hello %(name)s,", name=user.name)}}
3 |{{_("To validate the email address %(address)s, click on the link below:", address=address)}}
4 | 9 |{{_("If you did not set the email address %(address)s in your account %(username)s, you can ignore this email.", username=user.username, address=address)}}
10 | 11 |{{_("Fedora Accounts System")}}
12 | -------------------------------------------------------------------------------- /noggin/themes/fas/templates/settings-email-validation.txt: -------------------------------------------------------------------------------- 1 | == {{_("Fedora Accounts System")}} == 2 | 3 | {{_("Hello %(name)s,", name=user.name)}} 4 | 5 | {{_("To validate the email address %(address)s, click on the link below:", address=address)}} 6 | 7 | {{ url_for('.user_settings_email_validate', username=user.username, _external=True) }}?token={{ token }} 8 | 9 | {{_("If you did not set the email address %(address)s in your account %(username)s, you can ignore this email.", username=user.username, address=address)}} 10 | 11 | -- {{_("Fedora Accounts System")}} 12 | -------------------------------------------------------------------------------- /noggin/utility/__init__.py: -------------------------------------------------------------------------------- 1 | from werkzeug.utils import find_modules, import_string 2 | 3 | 4 | def import_all(import_name): 5 | for module in find_modules(import_name, include_packages=True, recursive=True): 6 | import_string(module) 7 | -------------------------------------------------------------------------------- /noggin/utility/messaging.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import traceback 3 | 4 | import backoff 5 | from fedora_messaging import api 6 | from fedora_messaging import exceptions as fml_exceptions 7 | from flask import current_app 8 | 9 | 10 | def backoff_hdlr(details): 11 | current_app.logger.warning( 12 | f"Publishing message failed. Retrying. {traceback.format_tb(sys.exc_info()[2])}" 13 | ) 14 | 15 | 16 | @backoff.on_exception( 17 | backoff.expo, 18 | (fml_exceptions.ConnectionException, fml_exceptions.PublishException), 19 | max_tries=3, 20 | on_backoff=backoff_hdlr, 21 | ) 22 | def _publish(message): 23 | api.publish(message) 24 | 25 | 26 | def publish(message): 27 | if not current_app.config["FEDORA_MESSAGING_ENABLED"]: 28 | current_app.logger.info( 29 | f"Fedora Messaging is disabled, not publishing the message on {message.topic}" 30 | ) 31 | return 32 | try: 33 | _publish(message) 34 | except fml_exceptions.BaseException: 35 | current_app.logger.error( 36 | f"Publishing message failed. Giving up. {traceback.format_tb(sys.exc_info()[2])}" 37 | ) 38 | return 39 | -------------------------------------------------------------------------------- /noggin/utility/password_reset.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import os 3 | 4 | from flask import current_app 5 | 6 | 7 | class PasswordResetLock: 8 | def __init__(self, username): 9 | self.username = username.replace("/", "") 10 | 11 | def valid_until(self): 12 | try: 13 | mtime = os.path.getmtime(self._get_file_path()) 14 | except FileNotFoundError: 15 | return None 16 | mtime = datetime.datetime.fromtimestamp(mtime) 17 | return mtime + datetime.timedelta( 18 | minutes=current_app.config["PASSWORD_RESET_EXPIRATION"] 19 | ) 20 | 21 | def store(self): 22 | file_path = self._get_file_path() 23 | try: 24 | os.makedirs(os.path.dirname(file_path)) 25 | except FileExistsError: 26 | pass 27 | open(file_path, "w").close() 28 | 29 | def delete(self): 30 | try: 31 | os.remove(self._get_file_path()) 32 | except FileNotFoundError: 33 | pass # It's already been removed 34 | 35 | def _get_file_path(self): 36 | base_dir = current_app.config["PASSWORD_RESET_LOCK_DIR"] 37 | return os.path.join(base_dir, self.username) 38 | -------------------------------------------------------------------------------- /noggin/utility/token.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime, timedelta 2 | from enum import Enum 3 | 4 | import jwt 5 | from flask import current_app 6 | 7 | 8 | class Audience(Enum): 9 | """ 10 | In JWT the audience is a constant that must remain the same between the token creator and the 11 | token reader, as a way to prevent token re-use. 12 | 13 | The longer the string, the longer the token, so we try to keep it short because some tokens end 14 | up in URLs and that's limited. 15 | """ 16 | 17 | password_reset = "pr" 18 | email_validation = "ev" 19 | spam_check = "sc" 20 | 21 | 22 | def make_token(data, audience, ttl=None): 23 | data["aud"] = audience.value 24 | if ttl is not None: 25 | data["exp"] = datetime.utcnow() + timedelta(minutes=ttl) 26 | token = jwt.encode(data, current_app.config["SECRET_KEY"], algorithm="HS256") 27 | return token 28 | 29 | 30 | def read_token(token, audience=None): 31 | return jwt.decode( 32 | token, 33 | current_app.config["SECRET_KEY"], 34 | algorithms=["HS256"], 35 | audience=audience.value, 36 | ) 37 | 38 | 39 | def make_password_change_token(user): 40 | lpc = user.last_password_change 41 | if lpc is not None: 42 | lpc = lpc.isoformat() 43 | return make_token( 44 | {"sub": user.username, "lpc": lpc}, 45 | audience=Audience.password_reset, 46 | ) 47 | -------------------------------------------------------------------------------- /packit/installation/main.fmf: -------------------------------------------------------------------------------- 1 | summary: Check information about the installed package 2 | require: 3 | - rpm 4 | - grep 5 | execute: 6 | script: 7 | - rpm -qi noggin 8 | - rpm -qa | grep noggin 9 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.organization=fedora-infra 2 | sonar.projectKey=fedora-infra/noggin 3 | sonar.sources=noggin 4 | sonar.host.url=https://sonarcloud.io 5 | sonar.tests=tests 6 | sonar.exclusions=/devel/create-test-data.py, /noggin/themes/*/static/, /noggin/themes/*/templates/, /noggin/templates/*, 7 | sonar.python.version=3 8 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/tests/__init__.py -------------------------------------------------------------------------------- /tests/integration/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/tests/integration/__init__.py -------------------------------------------------------------------------------- /tests/integration/test_dummy.py: -------------------------------------------------------------------------------- 1 | def test_dummy(): 2 | """This is a dummy test""" 3 | assert True 4 | -------------------------------------------------------------------------------- /tests/unit/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/tests/unit/__init__.py -------------------------------------------------------------------------------- /tests/unit/controller/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/tests/unit/controller/__init__.py -------------------------------------------------------------------------------- /tests/unit/controller/cassettes/test_password_reset/test_password_no_user.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: user=dudemcpants&new_password=LongSuperSafePassword&old_password=1 4 | headers: 5 | Accept: 6 | - text/plain 7 | Accept-Encoding: 8 | - gzip, deflate 9 | Connection: 10 | - keep-alive 11 | Content-Length: 12 | - '66' 13 | Content-Type: 14 | - application/x-www-form-urlencoded 15 | Referer: 16 | - https://ipa.tinystage.test/ipa/session/change_password 17 | User-Agent: 18 | - python-requests/2.31.0 19 | method: POST 20 | uri: https://ipa.tinystage.test/ipa/session/change_password 21 | response: 22 | body: 23 | string: ' 24 | 25 | 26 | 27 |36 | 37 | The old password or username is not correct. 38 | 39 |
40 | 41 | 42 | 43 | ' 44 | headers: 45 | Cache-Control: 46 | - no-cache, private 47 | Connection: 48 | - Keep-Alive 49 | Content-Encoding: 50 | - gzip 51 | Content-Security-Policy: 52 | - frame-ancestors 'none' 53 | Content-Type: 54 | - text/html; charset=utf-8 55 | Date: 56 | - Mon, 15 Apr 2024 14:23:24 GMT 57 | Keep-Alive: 58 | - timeout=30, max=100 59 | Server: 60 | - Apache/2.4.58 (Fedora Linux) OpenSSL/3.0.8 mod_wsgi/4.9.4 Python/3.11 mod_auth_gssapi/1.6.5 61 | Transfer-Encoding: 62 | - chunked 63 | Vary: 64 | - Accept-Encoding 65 | X-Frame-Options: 66 | - DENY 67 | X-IPA-Pwchange-Result: 68 | - invalid-password 69 | status: 70 | code: 200 71 | message: Success 72 | version: 1 73 | -------------------------------------------------------------------------------- /tests/unit/form/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/tests/unit/form/__init__.py -------------------------------------------------------------------------------- /tests/unit/representation/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/tests/unit/representation/__init__.py -------------------------------------------------------------------------------- /tests/unit/representation/test_agreement.py: -------------------------------------------------------------------------------- 1 | from noggin.representation.agreement import Agreement 2 | 3 | 4 | def test_agreement(dummy_agreement_dict): 5 | """Test the Agreement representation""" 6 | agreement = Agreement(dummy_agreement_dict) 7 | assert agreement.name == "CentOS Agreement" 8 | assert agreement.description == "Particularly \nsing purpose \nhere" 9 | assert agreement.users == [ 10 | 'andrew0', 11 | 'austin5', 12 | 'terri10', 13 | 'austin15', 14 | 'brittney20', 15 | 'logan25', 16 | 'tracy30', 17 | 'alexis35', 18 | 'james40', 19 | 'julie45', 20 | ] 21 | assert agreement.groups == ["designers"] 22 | assert agreement.enabled is True 23 | 24 | 25 | def test_agreement_disabled(dummy_agreement_dict): 26 | """Test the Agreement representation when disabled""" 27 | dummy_agreement_dict["ipaenabledflag"] = ["FALSE"] 28 | agreement = Agreement(dummy_agreement_dict) 29 | assert agreement.enabled is False 30 | -------------------------------------------------------------------------------- /tests/unit/representation/test_base.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from noggin.representation.base import Representation 4 | from noggin.representation.group import Group 5 | from noggin.representation.user import User 6 | 7 | 8 | def test_diff_fields(dummy_user_dict): 9 | """Check the method to compare the diff between two User objects works""" 10 | user = User(dummy_user_dict) 11 | 12 | new_data = dummy_user_dict.copy() 13 | new_data["fasgithubusername"] = ["newusername"] 14 | changed_user = User(new_data) 15 | 16 | diff = user.diff_fields(changed_user) 17 | assert diff == ['github'] 18 | 19 | 20 | def test_diff_fields_check_mismatch(dummy_user_dict, dummy_group_dict): 21 | """Check we cannot diff two different objects""" 22 | user = User(dummy_user_dict) 23 | group = Group(dummy_group_dict) 24 | 25 | with pytest.raises(ValueError): 26 | user.diff_fields(group) 27 | 28 | 29 | def test_wrong_attribute(dummy_user_dict): 30 | user = User(dummy_user_dict) 31 | with pytest.raises(AttributeError) as e: 32 | user.does_not_exist 33 | assert str(e.value) == "does_not_exist" 34 | 35 | 36 | def test_pkey_unset(): 37 | with pytest.raises(NotImplementedError): 38 | Representation.get_ipa_pkey() 39 | 40 | 41 | def test_pkey_missing_map(): 42 | class Dummy(Representation): 43 | pkey = "dummy" 44 | 45 | with pytest.raises(NotImplementedError): 46 | Dummy.get_ipa_pkey() 47 | 48 | 49 | @pytest.mark.parametrize("value", [True, "TRUE"]) 50 | def test_bool(value): 51 | class Dummy(Representation): 52 | attr_names = {"boolean_attr": "boolean_attr"} 53 | attr_types = {"boolean_attr": "bool"} 54 | 55 | obj = Dummy({"boolean_attr": [value]}) 56 | assert obj.boolean_attr is True 57 | -------------------------------------------------------------------------------- /tests/unit/representation/test_group.py: -------------------------------------------------------------------------------- 1 | from noggin.representation.group import Group 2 | 3 | 4 | def test_group(dummy_group_dict): 5 | """Test the Group representation""" 6 | group = Group(dummy_group_dict) 7 | assert group.name == "dummy-group" 8 | assert group.description == "A dummy group" 9 | assert group.members == ["dummy", "testuser"] 10 | assert group.sponsors == ["dummy"] 11 | assert group.dn == "cn=dummy-group,cn=groups,cn=accounts,dc=example,dc=com" 12 | assert group.mailing_list == 'dummygroup@lists.fedoraproject.org' 13 | assert group.urls == ['http://unit.tests', "https://www.dummygroup.com.au"] 14 | assert group.irc_channel == 'irc://irc.unit.tests/#dummy-group' 15 | 16 | 17 | def test_group_no_dn(dummy_group_dict): 18 | """Test that we fallback to gecos if there is no displayname""" 19 | del dummy_group_dict["dn"] 20 | group = Group(dummy_group_dict) 21 | assert group.dn is None 22 | 23 | 24 | def test_group_eq(dummy_group_dict): 25 | """Test that Groups can be compared based on their content""" 26 | group_1 = Group(dummy_group_dict) 27 | group_2 = Group(dummy_group_dict) 28 | assert group_1 == group_2 29 | -------------------------------------------------------------------------------- /tests/unit/representation/test_otptoken.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from noggin.representation.otptoken import OTPToken 4 | 5 | 6 | @pytest.fixture 7 | def dummy_otptoken_dict(): 8 | return { 9 | 'ipatokenuniqueid': ['ee0338e4-3cff-11ea-a002-52540019b1a3'], 10 | 'description': ['pants token'], 11 | } 12 | 13 | 14 | def test_otptoken(dummy_otptoken_dict): 15 | """Test the Group representation""" 16 | token = OTPToken(dummy_otptoken_dict) 17 | assert token.uniqueid == "ee0338e4-3cff-11ea-a002-52540019b1a3" 18 | assert token.description == "pants token" 19 | -------------------------------------------------------------------------------- /tests/unit/security/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/tests/unit/security/__init__.py -------------------------------------------------------------------------------- /tests/unit/security/test_ipa_admin.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from noggin.app import ipa_admin 4 | from noggin.security.ipa_admin import IPAAdmin 5 | 6 | 7 | def test_flask_ext(mocker): 8 | init_app = mocker.patch.object(IPAAdmin, "init_app") 9 | dummy_app = object() 10 | IPAAdmin(dummy_app) 11 | init_app.assert_called_once_with(dummy_app) 12 | 13 | 14 | def test_wrong_attribute(app): 15 | with app.test_request_context('/'), pytest.raises(AttributeError): 16 | ipa_admin.does_not_exist 17 | -------------------------------------------------------------------------------- /tests/unit/test_l10n.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from noggin.defaults import USER_DEFAULTS 4 | from noggin.l10n import guess_locale 5 | 6 | 7 | @pytest.mark.parametrize( 8 | "accepted,expected", 9 | [ 10 | ("fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3", "fr-FR"), 11 | ("fr,en;q=0.8", "fr-FR"), 12 | ("fr,it-IT;q=0.8", "it-IT"), 13 | ("en", "en-US"), 14 | ("en-GB,en;q=0.8", "en-GB"), 15 | ("it", "it-IT"), 16 | ("xx", USER_DEFAULTS["locale"]), 17 | ], 18 | ) 19 | def test_guess_locale(app, client, accepted, expected): 20 | headers = {"Accept-Language": accepted} 21 | with app.test_request_context('/', headers=headers): 22 | assert guess_locale() == expected 23 | -------------------------------------------------------------------------------- /tests/unit/test_middleware.py: -------------------------------------------------------------------------------- 1 | import python_freeipa 2 | from bs4 import BeautifulSoup 3 | 4 | from noggin.middleware import IPAErrorHandler 5 | 6 | 7 | def test_ipa_error(client, mocker): 8 | """Test the error page for IPA exceptions""" 9 | maybe_ipa_session = mocker.patch("noggin.controller.root.maybe_ipa_session") 10 | maybe_ipa_session.side_effect = python_freeipa.exceptions.FreeIPAError 11 | 12 | result = client.get('/') 13 | assert result.status_code == 500 14 | page = BeautifulSoup(result.data, 'html.parser') 15 | assert page.title 16 | assert page.title.string == 'IPA Error - noggin' 17 | 18 | 19 | def test_flask_ext(mocker): 20 | init_app = mocker.patch.object(IPAErrorHandler, "init_app") 21 | dummy_app = object() 22 | IPAErrorHandler(dummy_app) 23 | init_app.assert_called_once_with(dummy_app) 24 | -------------------------------------------------------------------------------- /tests/unit/test_themes.py: -------------------------------------------------------------------------------- 1 | from noggin.themes import Theme 2 | 3 | 4 | def test_flask_ext_without_whitenoise(mocker): 5 | app = mocker.Mock() 6 | Theme(app) 7 | app.register_blueprint.assert_called_once() 8 | 9 | 10 | def test_flask_ext_with_whitenoise(mocker): 11 | app = mocker.Mock() 12 | app.root_path = "/somewhere" 13 | app.config = {"THEME": "dummy-theme"} 14 | whitenoise = mocker.Mock() 15 | Theme(app, whitenoise) 16 | app.register_blueprint.assert_called_once() 17 | whitenoise.add_files.assert_called_once_with( 18 | "/somewhere/themes/dummy-theme/static/", prefix="/theme/static" 19 | ) 20 | -------------------------------------------------------------------------------- /tests/unit/translations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/tests/unit/translations/__init__.py -------------------------------------------------------------------------------- /tests/unit/translations/test_translations.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import pytest 4 | from babel.messages.frontend import compile_catalog 5 | from bs4 import BeautifulSoup 6 | 7 | import noggin 8 | 9 | from ..utilities import assert_redirects_with_flash 10 | 11 | 12 | @pytest.fixture 13 | def compile_catalogs(): 14 | cmd = compile_catalog() 15 | cmd.directory = os.path.abspath(os.path.join(noggin.__path__[0], "translations")) 16 | cmd.domain = ["messages"] 17 | cmd.run() 18 | 19 | 20 | @pytest.mark.vcr() 21 | def test_translation_in_code_french( 22 | client, logged_in_dummy_user_with_otp, compile_catalogs 23 | ): 24 | """Test translations are working if the string is in the code""" 25 | headers = {"Accept-Language": "fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3"} 26 | result = client.post( 27 | "/user/dummy/settings/otp/disable/", 28 | data={"token": logged_in_dummy_user_with_otp.uniqueid}, 29 | headers=headers, 30 | ) 31 | assert_redirects_with_flash( 32 | result, 33 | expected_url="/user/dummy/settings/otp/", 34 | expected_message="Désolé, vous ne pouvez pas désactiver votre dernier jeton actif.", 35 | expected_category="warning", 36 | ) 37 | 38 | 39 | def test_translation_in_template_french(client, compile_catalogs): 40 | """Test translations are working if the string is in the template""" 41 | headers = {"Accept-Language": "fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3"} 42 | result = client.get("/this-should-give-a-404", headers=headers) 43 | assert result.status_code == 404 44 | page = BeautifulSoup(result.data, 'html.parser') 45 | message = page.select_one(".alert.alert-danger") 46 | assert message.get_text(strip=True) == "404Cette page n'a pas été trouvée." 47 | -------------------------------------------------------------------------------- /tests/unit/utility/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/noggin/0fe765b672439a4a9d06c8d95adfdd0d123c7e22/tests/unit/utility/__init__.py -------------------------------------------------------------------------------- /tests/unit/utility/test_messaging.py: -------------------------------------------------------------------------------- 1 | from fedora_messaging import exceptions as fml_exceptions 2 | from flask import current_app 3 | 4 | from noggin.utility import messaging 5 | from noggin_messages import MemberSponsorV1 6 | 7 | 8 | def test_publish(request_context, mocker): 9 | api_publish = mocker.patch("fedora_messaging.api.publish") 10 | messaging.publish( 11 | MemberSponsorV1( 12 | {"msg": {"agent": "dummy", "user": "testuser", "group": "dummy-group"}} 13 | ) 14 | ) 15 | api_publish.assert_called_once() 16 | 17 | 18 | def test_publish_with_errors(request_context, mocker): 19 | api_publish = mocker.patch("fedora_messaging.api.publish") 20 | api_publish.side_effect = fml_exceptions.ConnectionException() 21 | messaging.publish( 22 | MemberSponsorV1( 23 | {"msg": {"agent": "dummy", "user": "testuser", "group": "dummy-group"}} 24 | ) 25 | ) 26 | assert api_publish.call_count == 3 27 | 28 | 29 | def test_publish_disabled(request_context, mocker): 30 | mocker.patch.dict(current_app.config, {"FEDORA_MESSAGING_ENABLED": False}) 31 | api_publish = mocker.patch("fedora_messaging.api.publish") 32 | messaging.publish( 33 | MemberSponsorV1( 34 | {"msg": {"agent": "dummy", "user": "testuser", "group": "dummy-group"}} 35 | ) 36 | ) 37 | api_publish.assert_not_called() 38 | -------------------------------------------------------------------------------- /tests/unit/utility/test_password_reset.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | from unittest import mock 3 | 4 | import pytest 5 | 6 | from noggin.utility.password_reset import PasswordResetLock 7 | 8 | 9 | @pytest.fixture 10 | def tmp_lock_dir(app, tmp_path): 11 | with app.test_request_context('/'): 12 | with mock.patch.dict(app.config, {"PASSWORD_RESET_LOCK_DIR": tmp_path}): 13 | yield tmp_path 14 | 15 | 16 | def test_lock_store(tmp_lock_dir): 17 | lock = PasswordResetLock("dummy") 18 | lock.store() 19 | assert tmp_lock_dir.joinpath("dummy").exists() 20 | 21 | 22 | def test_lock_valid_until(tmp_lock_dir): 23 | lock = PasswordResetLock("dummy") 24 | lock.store() 25 | assert lock.valid_until() is not None 26 | now = datetime.datetime.now() 27 | assert lock.valid_until() > now 28 | 29 | 30 | def test_lock_delete(tmp_lock_dir): 31 | lock = PasswordResetLock("dummy") 32 | lock.store() 33 | lock.delete() 34 | assert lock.valid_until() is None 35 | 36 | 37 | def test_lock_delete_alread_deleted(tmp_lock_dir): 38 | lock = PasswordResetLock("dummy") 39 | try: 40 | lock.delete() 41 | except FileNotFoundError: 42 | assert False, "delete() crashes on absent files" 43 | assert lock.valid_until() is None 44 | 45 | 46 | def test_lock_bad_username(tmp_lock_dir): 47 | username = "/etc/passwd" 48 | lock = PasswordResetLock(username) 49 | valid_until = lock.valid_until() 50 | assert valid_until is None 51 | assert lock._get_file_path() != username 52 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = lint,format,licenses,security,covclean,docs,{py39,py310,py311,py312}-{unittest,integration},covreport 3 | isolated_build = true 4 | 5 | [testenv] 6 | passenv = HOME 7 | sitepackages = false 8 | skip_install = true 9 | allowlist_externals = 10 | poetry 11 | commands_pre = 12 | poetry install --all-extras 13 | py39-unittest: poetry run pip install --upgrade "urllib3>=2.0" 14 | commands = 15 | unittest: poetry run pytest -vv --cov --cov-append --cov-report term-missing --cov-report= tests/unit {posargs} 16 | integration: poetry run pytest -vv --no-cov tests/integration {posargs} 17 | depends = 18 | {py39,py310,py311,py312}: covclean 19 | covreport: py39-unittest,py310-unittest,py311-unittest,py312-unittest 20 | 21 | [testenv:covreport] 22 | basepython = python3.12 23 | commands = 24 | -poetry run coverage html 25 | poetry run coverage report -m 26 | 27 | [testenv:covclean] 28 | commands = poetry run coverage erase 29 | 30 | 31 | [testenv:docs] 32 | changedir = docs 33 | allowlist_externals = 34 | {[testenv]allowlist_externals} 35 | mkdir 36 | rm 37 | commands= 38 | rm -rf {toxinidir}/noggin_aaa.egg-info 39 | mkdir -p _static 40 | rm -rf _build 41 | rm -rf _source 42 | poetry run sphinx-build -W -b html -d {envtmpdir}/doctrees . _build/html 43 | 44 | [testenv:lint] 45 | commands = 46 | pre-commit run --all-files flake8 47 | pre-commit run --all-files rstcheck 48 | 49 | [testenv:format] 50 | commands = 51 | pre-commit run --all-files black 52 | pre-commit run --all-files isort 53 | 54 | [testenv:licenses] 55 | allowlist_externals = 56 | {[testenv]allowlist_externals} 57 | {toxinidir}/devel/run-liccheck.sh 58 | commands = 59 | {toxinidir}/devel/run-liccheck.sh 60 | 61 | [testenv:security] 62 | commands = 63 | poetry run pip install --upgrade pip 64 | pre-commit run --all-files bandit 65 | 66 | 67 | [flake8] 68 | show-source = True 69 | max-line-length = 100 70 | exclude = .git,.tox,dist,*egg 71 | --------------------------------------------------------------------------------