diff --git a/nixos/doc/manual/from_md/release-notes/rl-2111.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2111.section.xml
index f04d97beb53d..f1d7526dd6c8 100644
--- a/nixos/doc/manual/from_md/release-notes/rl-2111.section.xml
+++ b/nixos/doc/manual/from_md/release-notes/rl-2111.section.xml
@@ -556,6 +556,17 @@ Superuser created successfully.
services.geoipupdate.
+
+
+ ihatemoney has been updated to version
+ 5.1.1
+ (release
+ notes). If you serve ihatemoney by HTTP rather than
+ HTTPS, you must set
+ services.ihatemoney.secureCookie
+ to false.
+
+
PHP 7.3 is no longer supported due to upstream not supporting
diff --git a/nixos/doc/manual/release-notes/rl-2111.section.md b/nixos/doc/manual/release-notes/rl-2111.section.md
index 3b0c20ce5a29..b26e0773a174 100644
--- a/nixos/doc/manual/release-notes/rl-2111.section.md
+++ b/nixos/doc/manual/release-notes/rl-2111.section.md
@@ -204,6 +204,8 @@ In addition to numerous new and upgraded packages, this release has the followin
- `services.geoip-updater` was broken and has been replaced by [services.geoipupdate](options.html#opt-services.geoipupdate.enable).
+- `ihatemoney` has been updated to version 5.1.1 ([release notes](https://github.com/spiral-project/ihatemoney/blob/5.1.1/CHANGELOG.rst)). If you serve ihatemoney by HTTP rather than HTTPS, you must set [services.ihatemoney.secureCookie](options.html#opt-services.ihatemoney.secureCookie) to `false`.
+
- PHP 7.3 is no longer supported due to upstream not supporting this version for the entire lifecycle of the 21.11 release.
- Those making use of `buildBazelPackage` will need to regenerate the fetch hashes (preferred), or set `fetchConfigured = false;`.
diff --git a/nixos/modules/services/web-apps/ihatemoney/default.nix b/nixos/modules/services/web-apps/ihatemoney/default.nix
index b4987fa4702c..238241854c1c 100644
--- a/nixos/modules/services/web-apps/ihatemoney/default.nix
+++ b/nixos/modules/services/web-apps/ihatemoney/default.nix
@@ -33,11 +33,14 @@ let
then "sqlite:////var/lib/ihatemoney/ihatemoney.sqlite"
else "postgresql:///${db}"}'
SQLALCHEMY_TRACK_MODIFICATIONS = False
- MAIL_DEFAULT_SENDER = ("${cfg.defaultSender.name}", "${cfg.defaultSender.email}")
+ MAIL_DEFAULT_SENDER = (r"${cfg.defaultSender.name}", r"${cfg.defaultSender.email}")
ACTIVATE_DEMO_PROJECT = ${toBool cfg.enableDemoProject}
- ADMIN_PASSWORD = "${toString cfg.adminHashedPassword /*toString null == ""*/}"
+ ADMIN_PASSWORD = r"${toString cfg.adminHashedPassword /*toString null == ""*/}"
ALLOW_PUBLIC_PROJECT_CREATION = ${toBool cfg.enablePublicProjectCreation}
ACTIVATE_ADMIN_DASHBOARD = ${toBool cfg.enableAdminDashboard}
+ SESSION_COOKIE_SECURE = ${toBool cfg.secureCookie}
+ ENABLE_CAPTCHA = ${toBool cfg.enableCaptcha}
+ LEGAL_LINK = r"${toString cfg.legalLink}"
${cfg.extraConfig}
'';
@@ -79,9 +82,20 @@ in
description = "The email of the sender of ihatemoney emails";
};
};
+ secureCookie = mkOption {
+ type = types.bool;
+ default = true;
+ description = "Use secure cookies. Disable this when ihatemoney is served via http instead of https";
+ };
enableDemoProject = mkEnableOption "access to the demo project in ihatemoney";
enablePublicProjectCreation = mkEnableOption "permission to create projects in ihatemoney by anyone";
enableAdminDashboard = mkEnableOption "ihatemoney admin dashboard";
+ enableCaptcha = mkEnableOption "a simplistic captcha for some forms";
+ legalLink = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ description = "The URL to a page explaining legal statements about your service, eg. GDPR-related information.";
+ };
extraConfig = mkOption {
type = types.str;
default = "";
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index b1d1578975f3..8ecd9d0337c9 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -191,7 +191,7 @@ in
i3wm = handleTest ./i3wm.nix {};
icingaweb2 = handleTest ./icingaweb2.nix {};
iftop = handleTest ./iftop.nix {};
- ihatemoney = handleTest ./ihatemoney.nix {};
+ ihatemoney = handleTest ./ihatemoney {};
incron = handleTest ./incron.nix {};
influxdb = handleTest ./influxdb.nix {};
initrd-network-openvpn = handleTest ./initrd-network-openvpn {};
diff --git a/nixos/tests/ihatemoney.nix b/nixos/tests/ihatemoney/default.nix
similarity index 52%
rename from nixos/tests/ihatemoney.nix
rename to nixos/tests/ihatemoney/default.nix
index 0451a4505808..78278d2e8699 100644
--- a/nixos/tests/ihatemoney.nix
+++ b/nixos/tests/ihatemoney/default.nix
@@ -1,22 +1,36 @@
{ system ? builtins.currentSystem,
config ? {},
- pkgs ? import ../.. { inherit system config; }
+ pkgs ? import ../../.. { inherit system config; }
}:
let
- inherit (import ../lib/testing-python.nix { inherit system pkgs; }) makeTest;
+ inherit (import ../../lib/testing-python.nix { inherit system pkgs; }) makeTest;
f = backend: makeTest {
name = "ihatemoney-${backend}";
- machine = { lib, ... }: {
+ machine = { nodes, lib, ... }: {
services.ihatemoney = {
enable = true;
enablePublicProjectCreation = true;
+ secureCookie = false;
inherit backend;
uwsgiConfig = {
http = ":8000";
};
};
boot.cleanTmpDir = true;
+ # for exchange rates
+ security.pki.certificateFiles = [ ./server.crt ];
+ networking.extraHosts = "127.0.0.1 api.exchangerate.host";
+ services.nginx = {
+ enable = true;
+ virtualHosts."api.exchangerate.host" = {
+ addSSL = true;
+ # openssl req -x509 -newkey rsa:4096 -keyout server.key -out server.crt -days 1000000 -nodes -subj '/CN=api.exchangerate.host'
+ sslCertificate = ./server.crt;
+ sslCertificateKey = ./server.key;
+ locations."/".return = "200 '${builtins.readFile ./rates.json}'";
+ };
+ };
# ihatemoney needs a local smtp server otherwise project creation just crashes
services.opensmtpd = {
enable = true;
@@ -30,11 +44,13 @@ let
testScript = ''
machine.wait_for_open_port(8000)
machine.wait_for_unit("uwsgi.service")
- machine.wait_until_succeeds("curl http://localhost:8000")
+ machine.wait_until_succeeds("curl --fail https://api.exchangerate.host")
+ machine.wait_until_succeeds("curl --fail http://localhost:8000")
- assert '"yay"' in machine.succeed(
- "curl -X POST http://localhost:8000/api/projects -d 'name=yay&id=yay&password=yay&contact_email=yay@example.com'"
+ result = machine.succeed(
+ "curl --fail -X POST http://localhost:8000/api/projects -d 'name=yay&id=yay&password=yay&contact_email=yay@example.com&default_currency=XXX'"
)
+ assert '"yay"' in result, repr(result)
owner, timestamp = machine.succeed(
"stat --printf %U:%G___%Y /var/lib/ihatemoney/secret_key"
).split("___")
@@ -47,13 +63,13 @@ let
machine.wait_for_unit("uwsgi.service")
with subtest("check that the database is really persistent"):
- machine.succeed("curl --basic -u yay:yay http://localhost:8000/api/projects/yay")
+ machine.succeed("curl --fail --basic -u yay:yay http://localhost:8000/api/projects/yay")
with subtest("check that the secret key is really persistent"):
timestamp2 = machine.succeed("stat --printf %Y /var/lib/ihatemoney/secret_key")
assert timestamp == timestamp2
- assert "ihatemoney" in machine.succeed("curl http://localhost:8000")
+ assert "ihatemoney" in machine.succeed("curl --fail http://localhost:8000")
'';
};
in {
diff --git a/nixos/tests/ihatemoney/rates.json b/nixos/tests/ihatemoney/rates.json
new file mode 100644
index 000000000000..ebdd2651b040
--- /dev/null
+++ b/nixos/tests/ihatemoney/rates.json
@@ -0,0 +1,39 @@
+{
+ "rates": {
+ "CAD": 1.3420055134,
+ "HKD": 7.7513783598,
+ "ISK": 135.9407305307,
+ "PHP": 49.3762922123,
+ "DKK": 6.4126464507,
+ "HUF": 298.9145416954,
+ "CZK": 22.6292212267,
+ "GBP": 0.7838128877,
+ "RON": 4.1630771881,
+ "SEK": 8.8464851826,
+ "IDR": 14629.5658166782,
+ "INR": 74.8328738801,
+ "BRL": 5.2357856651,
+ "RUB": 71.8416609235,
+ "HRK": 6.4757064094,
+ "JPY": 106.2715368711,
+ "THB": 31.7203652653,
+ "CHF": 0.9243625086,
+ "EUR": 0.8614748449,
+ "MYR": 4.2644727774,
+ "BGN": 1.6848725017,
+ "TRY": 6.8483804273,
+ "CNY": 7.0169710544,
+ "NOK": 9.213731909,
+ "NZD": 1.5080978635,
+ "ZAR": 16.7427636113,
+ "USD": 1,
+ "MXN": 22.4676085458,
+ "SGD": 1.3855099931,
+ "AUD": 1.4107512061,
+ "ILS": 3.4150585803,
+ "KRW": 1203.3339076499,
+ "PLN": 3.794452102
+ },
+ "base": "USD",
+ "date": "2020-07-24"
+}
diff --git a/nixos/tests/ihatemoney/server.crt b/nixos/tests/ihatemoney/server.crt
new file mode 100644
index 000000000000..10e568b14b14
--- /dev/null
+++ b/nixos/tests/ihatemoney/server.crt
@@ -0,0 +1,28 @@
+-----BEGIN CERTIFICATE-----
+MIIEvjCCAqYCCQDkTQrENPCZjjANBgkqhkiG9w0BAQsFADAgMR4wHAYDVQQDDBVh
+cGkuZXhjaGFuZ2VyYXRlLmhvc3QwIBcNMjEwNzE0MTI1MzQ0WhgPNDc1OTA2MTEx
+MjUzNDRaMCAxHjAcBgNVBAMMFWFwaS5leGNoYW5nZXJhdGUuaG9zdDCCAiIwDQYJ
+KoZIhvcNAQEBBQADggIPADCCAgoCggIBAL5zpwUYa/ySqvJ/PUnXYsl1ww5SNGJh
+NujCRxC0Gw+5t5O7USSHRdz7Eb2PNFMa7JR+lliLAWdjHfqPXJWmP10X5ebvyxeQ
+TJkR1HpDSY6TQQlJvwr/JNGryyoQYjXvnyeyVu4TS3U0TTI631OonDAj+HbFIs9L
+gr/HfHzFmxRVLwaJ7hebanihc5RzoWTxgswiOwYQu5AivXQqcvUIxELeT7CxWwiw
+be/SlalDgoezB/poqaa215FUuN2av+nTn+swH3WOi9kwePLgVKn9BnDMwyh8et13
+yt27RWCSOcZagRSYsSbBaEJbClZvnuYvDqooJEy0GVbGBZpClKRKe92yd0PTf3ZJ
+GupyNoCFQlGugY//WLrsPv/Q4WwP+qZ6t97sV0CdM+epKVde/LfPKn+tFMv86qIg
+Q/uGHdDwUI8XH2EysAavhdlssSrovmpl4hyo9UkzTWfJgAbmOZY3Vba41wsq12FT
+usDsswGLBD10MdXWltR/Hdk8OnosLmeJxfZODAv31KSfd+4b6Ntr9BYQvAQSO+1/
+Mf7gEQtNhO003VKIyV5cpH4kVQieEcvoEKgq32NVBSKVf6UIPWIefu19kvrttaUu
+Q2QW2Qm4Ph/4cWpxl0jcrN5rjmgaBtIMmKYjRIS0ThDWzfVkJdmJuATzExJAplLN
+nYPBG3gOtQQpAgMBAAEwDQYJKoZIhvcNAQELBQADggIBAJzt/aN7wl88WrvBasVi
+fSJmJjRaW2rYyBUMptQNkm9ElHN2eQQxJgLi8+9ArQxuGKhHx+D1wMGF8w2yOp0j
+4atfbXDcT+cTQY55qdEeYgU8KhESHHGszGsUpv7hzU2cACZiXG0YbOmORFYcn49Z
+yPyN98kW8BViLzNF9v+I/NJPuaaCeWKjXCqY2GCzddiuotrlLtz0CODXZJ506I1F
+38vQgZb10yAe6+R4y0BK7sUlmfr9BBqVcDQ/z74Kph1aB32zwP8KrNitwG1Tyk6W
+rxD1dStEQyX8uDPAspe2JrToMWsOMje9F5lotmuzyvwRJYfAav300EtIggBqpiHR
+o0P/1xxBzmaCHxEUJegdoYg8Q27llqsjR2T78uv/BlxpX9Dv5kNex5EZThKqyz4a
+Fn1VqiA3D9IsvxH4ud+8eDaP24u1yYObSTDIBsw9xDvoV8fV+NWoNNhcAL5GwC0P
+Goh7/brZSHUprxGpwRB524E//8XmCsRd/+ShtXbi4gEODMH4xLdkD7fZIJC4eG1H
+GOVc1MwjiYvbQlPs6MOcQ0iKQneSlaEJmyyO5Ro5OKiKj89Az/mLYX3R17AIsu0T
+Q5pGcmhKVRyu0zXvkGfK352TLwoe+4vbmakDq21Pkkcy8V9M4wP+vpCfQkg1REQ1
++mr1Vg+SFya3mlCxpFTy3j8E
+-----END CERTIFICATE-----
diff --git a/nixos/tests/ihatemoney/server.key b/nixos/tests/ihatemoney/server.key
new file mode 100644
index 000000000000..72a43577d64d
--- /dev/null
+++ b/nixos/tests/ihatemoney/server.key
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+NaEkKVO0D0jFtk37s/YqJpCsXg/B7yc=
+-----END PRIVATE KEY-----
diff --git a/pkgs/development/python-modules/flask-talisman/default.nix b/pkgs/development/python-modules/flask-talisman/default.nix
new file mode 100644
index 000000000000..b57d58824705
--- /dev/null
+++ b/pkgs/development/python-modules/flask-talisman/default.nix
@@ -0,0 +1,36 @@
+{ lib
+, buildPythonPackage
+, fetchPypi
+, flask
+, six
+, pytestCheckHook
+}:
+
+buildPythonPackage rec {
+ pname = "flask-talisman";
+ version = "0.8.1";
+
+ src = fetchPypi {
+ inherit pname version;
+ sha256 = "11gjgqkpj2yqydb0pfhjyx56iy4l9szgz33vg5d7bw8vqp02wl2x";
+ };
+
+ buildInputs = [
+ flask
+ ];
+
+ propagatedBuildInputs = [
+ six
+ ];
+
+ nativeBuildInputs = [
+ pytestCheckHook
+ ];
+
+ meta = with lib; {
+ description = "HTTP security headers for Flask";
+ homepage = "https://github.com/wntrblm/flask-talisman";
+ license = licenses.asl20;
+ maintainers = [ lib.maintainers.symphorien ];
+ };
+}
diff --git a/pkgs/development/python-modules/ihatemoney/default.nix b/pkgs/development/python-modules/ihatemoney/default.nix
index fc1cb34d32c6..5d768170d806 100644
--- a/pkgs/development/python-modules/ihatemoney/default.nix
+++ b/pkgs/development/python-modules/ihatemoney/default.nix
@@ -1,14 +1,13 @@
{ buildPythonPackage
, lib
-, fetchFromGitHub
, isPy27
, nixosTests
-, fetchpatch
, fetchPypi
, alembic
, aniso8601
, Babel
, blinker
+, cachetools
, click
, dnspython
, email_validator
@@ -19,6 +18,7 @@
, flask_migrate
, flask-restful
, flask_sqlalchemy
+, flask-talisman
, flask_wtf
, debts
, idna
@@ -28,6 +28,7 @@
, markupsafe
, python-dateutil
, pytz
+, requests
, six
, sqlalchemy
, sqlalchemy-utils
@@ -43,17 +44,6 @@
# ihatemoney is not really a library. It will only ever be imported
# by the interpreter of uwsgi. So overrides for its depencies are fine.
let
- # fixed in next release, but patches don't apply
- # https://github.com/spiral-project/ihatemoney/issues/567
- pinned_wtforms = wtforms.overridePythonAttrs (old: rec {
- pname = "WTForms";
- version = "2.2.1";
- src = fetchPypi {
- inherit pname version;
- sha256 = "0q9vkcq6jnnn618h27lx9sas6s9qlg2mv8ja6dn0hy38gwzarnqc";
- };
- });
-
# sqlalchemy-continuum requires sqlalchemy < 1.4
pinned_sqlalchemy = sqlalchemy.overridePythonAttrs (
old: rec {
@@ -70,43 +60,20 @@ in
buildPythonPackage rec {
pname = "ihatemoney";
- version = "4.2";
+ version = "5.1.1";
- src = fetchFromGitHub {
- owner = "spiral-project";
- repo = pname;
- rev = version;
- sha256 = "0d4vc6m0jkwlz9ly0hcjghccydvqbldh2jb8yzf94jrgkd5fd7k1";
+ src = fetchPypi {
+ inherit pname version;
+ sha256 = "0gsqba9qbs1dpmfys8qpiahy4pbn4khcc6mgmdnhssmkjsb94sx6";
};
disabled = isPy27;
- patches = [
- # fix migration on postgresql
- # remove on next release
- (fetchpatch {
- url = "https://github.com/spiral-project/ihatemoney/commit/6129191b26784b895e203fa3eafb89cee7d88b71.patch";
- sha256 = "0yc24gsih9x3pnh2mhj4v5i71x02dq93a9jd2r8b1limhcl4p1sw";
- })
- (fetchpatch {
- name = "CVE-2020-15120.patch";
- url = "https://github.com/spiral-project/ihatemoney/commit/8d77cf5d5646e1d2d8ded13f0660638f57e98471.patch";
- sha256 = "0y855sk3qsbpq7slj876k2ifa1lccc2dccag98pkyaadpz5gbabv";
- })
- # backported from current master
- # remove dependency on flask-script, which removed support on some features ihm used to need
- ./remove_flask_script.patch
- ];
-
- postPatch = ''
- # remove draconian pinning
- sed -i 's/==.*$//' setup.cfg
- '';
-
propagatedBuildInputs = [
aniso8601
Babel
blinker
+ cachetools
click
dnspython
email_validator
@@ -125,7 +92,8 @@ buildPythonPackage rec {
}
)
flask-restful
- (flask_wtf.override { wtforms = pinned_wtforms; })
+ flask-talisman
+ flask_wtf
idna
itsdangerous
jinja2
@@ -133,6 +101,7 @@ buildPythonPackage rec {
markupsafe
python-dateutil
pytz
+ requests
six
(
(
@@ -158,7 +127,7 @@ buildPythonPackage rec {
)
)
werkzeug
- pinned_wtforms
+ wtforms
psycopg2
debts
];
@@ -168,10 +137,10 @@ buildPythonPackage rec {
pytestCheckHook
];
- pytestFlagsArray = [ "--pyargs ihatemoney.tests.tests" ];
disabledTests = [
"test_notifications" # requires running service.
"test_invite" # requires running service.
+ "test_invitation_email_failure" # requires dns resolution
];
passthru.tests = {
diff --git a/pkgs/development/python-modules/ihatemoney/remove_flask_script.patch b/pkgs/development/python-modules/ihatemoney/remove_flask_script.patch
deleted file mode 100644
index dac59680a09c..000000000000
--- a/pkgs/development/python-modules/ihatemoney/remove_flask_script.patch
+++ /dev/null
@@ -1,284 +0,0 @@
-commit 4d831ba2316d54f4916fb9d1160ec7a3856b47d4
-Author: Glandos
-Date: Sun Jun 6 14:30:52 2021 +0200
-
- remove usage of Flask-Script
-
- Use flask.cli instead with compatibility layer for existing commands,
- such as "runserver".
-
- cherry-pick from 74e222f1a1cbfc2fac102fefc1115e9d0a6586dc
-
-diff --git a/Makefile b/Makefile
-index a681709..90ab1bb 100644
---- a/Makefile
-+++ b/Makefile
-@@ -38,7 +38,7 @@ update: remove-install-stamp install ## Update the dependencies
- .PHONY: serve
- serve: install ## Run the ihatemoney server
- @echo 'Running ihatemoney on http://localhost:5000'
-- $(PYTHON) -m ihatemoney.manage runserver
-+ $(PYTHON) -m ihatemoney.manage run
-
- .PHONY: test
- test: install-dev ## Run the tests
-diff --git a/docs/installation.rst b/docs/installation.rst
-index 4994499..4df70a2 100644
---- a/docs/installation.rst
-+++ b/docs/installation.rst
-@@ -59,7 +59,7 @@ Test it
-
- Once installed, you can start a test server::
-
-- ihatemoney runserver
-+ ihatemoney run
-
- And point your browser at `http://localhost:5000 `_.
-
-diff --git a/ihatemoney/manage.py b/ihatemoney/manage.py
-index a192844..805a07f 100755
---- a/ihatemoney/manage.py
-+++ b/ihatemoney/manage.py
-@@ -5,8 +5,8 @@ import os
- import random
- import sys
-
--from flask_migrate import Migrate, MigrateCommand
--from flask_script import Command, Manager, Option
-+import click
-+from flask.cli import FlaskGroup
- from werkzeug.security import generate_password_hash
-
- from ihatemoney.models import Project, db
-@@ -14,31 +14,48 @@ from ihatemoney.run import create_app
- from ihatemoney.utils import create_jinja_env
-
-
--class GeneratePasswordHash(Command):
-+@click.group(cls=FlaskGroup, create_app=create_app)
-+def cli():
-+ """IHateMoney Management script"""
-
-- """Get password from user and hash it without printing it in clear text."""
-
-- def run(self):
-- password = getpass.getpass(prompt="Password: ")
-- print(generate_password_hash(password))
--
--
--class GenerateConfig(Command):
-- def get_options(self):
-- return [
-- Option(
-- "config_file",
-- choices=[
-- "ihatemoney.cfg",
-- "apache-vhost.conf",
-- "gunicorn.conf.py",
-- "supervisord.conf",
-- "nginx.conf",
-- ],
-- )
-+@cli.command(
-+ context_settings={"ignore_unknown_options": True, "allow_extra_args": True}
-+)
-+@click.pass_context
-+def runserver(ctx):
-+ """Deprecated, use the "run" command instead"""
-+ click.secho(
-+ '"runserver" is deprecated, please use the standard "run" flask command',
-+ fg="red",
-+ )
-+ run = cli.get_command(ctx, "run")
-+ ctx.forward(run)
-+
-+
-+@click.command(name="generate_password_hash")
-+def password_hash():
-+ """Get password from user and hash it without printing it in clear text."""
-+ password = getpass.getpass(prompt="Password: ")
-+ print(generate_password_hash(password))
-+
-+
-+@click.command()
-+@click.argument(
-+ "config_file",
-+ type=click.Choice(
-+ [
-+ "ihatemoney.cfg",
-+ "apache-vhost.conf",
-+ "gunicorn.conf.py",
-+ "supervisord.conf",
-+ "nginx.conf",
- ]
-+ ),
-+)
-+def generate_config(config_file):
-+ """Generate front-end server configuration"""
-
-- @staticmethod
- def gen_secret_key():
- return "".join(
- [
-@@ -49,59 +66,33 @@ class GenerateConfig(Command):
- ]
- )
-
-- def run(self, config_file):
-- env = create_jinja_env("conf-templates", strict_rendering=True)
-- template = env.get_template("%s.j2" % config_file)
-+ env = create_jinja_env("conf-templates", strict_rendering=True)
-+ template = env.get_template(f"{config_file}.j2")
-
-- bin_path = os.path.dirname(sys.executable)
-- pkg_path = os.path.abspath(os.path.dirname(__file__))
-+ bin_path = os.path.dirname(sys.executable)
-+ pkg_path = os.path.abspath(os.path.dirname(__file__))
-
-- print(
-- template.render(
-- pkg_path=pkg_path,
-- bin_path=bin_path,
-- sys_prefix=sys.prefix,
-- secret_key=self.gen_secret_key(),
-- )
-+ print(
-+ template.render(
-+ pkg_path=pkg_path,
-+ bin_path=bin_path,
-+ sys_prefix=sys.prefix,
-+ secret_key=gen_secret_key(),
- )
--
--
--class DeleteProject(Command):
-- def run(self, project_name):
-- demo_project = Project.query.get(project_name)
-- db.session.delete(demo_project)
-+ )
-+
-+
-+@cli.command()
-+@click.argument("project_name")
-+def delete_project(project_name):
-+ """Delete a project"""
-+ project = Project.query.get(project_name)
-+ if project is None:
-+ click.secho(f'Project "{project_name}" not found', fg="red")
-+ else:
-+ db.session.delete(project)
- db.session.commit()
-
-
--def main():
-- QUIET_COMMANDS = ("generate_password_hash", "generate-config")
--
-- exception = None
-- backup_stderr = sys.stderr
-- # Hack to divert stderr for commands generating content to stdout
-- # to avoid confusing the user
-- if len(sys.argv) > 1 and sys.argv[1] in QUIET_COMMANDS:
-- sys.stderr = open(os.devnull, "w")
--
-- try:
-- app = create_app()
-- Migrate(app, db)
-- except Exception as e:
-- exception = e
--
-- # Restore stderr
-- sys.stderr = backup_stderr
--
-- if exception:
-- raise exception
--
-- manager = Manager(app)
-- manager.add_command("db", MigrateCommand)
-- manager.add_command("generate_password_hash", GeneratePasswordHash)
-- manager.add_command("generate-config", GenerateConfig)
-- manager.add_command("delete-project", DeleteProject)
-- manager.run()
--
--
- if __name__ == "__main__":
-- main()
-+ cli()
-diff --git a/ihatemoney/tests/tests.py b/ihatemoney/tests/tests.py
-index b27fafc..23f19a6 100644
---- a/ihatemoney/tests/tests.py
-+++ b/ihatemoney/tests/tests.py
-@@ -15,7 +15,7 @@ from sqlalchemy import orm
- from werkzeug.security import check_password_hash, generate_password_hash
-
- from ihatemoney import history, models, utils
--from ihatemoney.manage import DeleteProject, GenerateConfig, GeneratePasswordHash
-+from ihatemoney.manage import delete_project, generate_config, password_hash
- from ihatemoney.run import create_app, db, load_configuration
- from ihatemoney.versioning import LoggingMode
-
-@@ -2157,28 +2157,24 @@ class CommandTestCase(BaseTestCase):
- - raise no exception
- - produce something non-empty
- """
-- cmd = GenerateConfig()
-- for config_file in cmd.get_options()[0].kwargs["choices"]:
-- with patch("sys.stdout", new=io.StringIO()) as stdout:
-- cmd.run(config_file)
-- print(stdout.getvalue())
-- self.assertNotEqual(len(stdout.getvalue().strip()), 0)
-+ runner = self.app.test_cli_runner()
-+ for config_file in generate_config.params[0].type.choices:
-+ result = runner.invoke(generate_config, config_file)
-+ self.assertNotEqual(len(result.output.strip()), 0)
-
- def test_generate_password_hash(self):
-- cmd = GeneratePasswordHash()
-- with patch("sys.stdout", new=io.StringIO()) as stdout, patch(
-- "getpass.getpass", new=lambda prompt: "secret"
-- ): # NOQA
-- cmd.run()
-- print(stdout.getvalue())
-- self.assertEqual(len(stdout.getvalue().strip()), 189)
-+ runner = self.app.test_cli_runner()
-+ with patch("getpass.getpass", new=lambda prompt: "secret"):
-+ result = runner.invoke(password_hash)
-+ print(result.output.strip())
-+ self.assertEqual(len(result.output.strip()), 102)
-
- def test_demo_project_deletion(self):
- self.create_project("demo")
- self.assertEquals(models.Project.query.get("demo").name, "demo")
-
-- cmd = DeleteProject()
-- cmd.run("demo")
-+ runner = self.app.test_cli_runner()
-+ runner.invoke(delete_project, "demo")
-
- self.assertEqual(len(models.Project.query.all()), 0)
-
-diff --git a/setup.cfg b/setup.cfg
-index d493717..48e447c 100644
---- a/setup.cfg
-+++ b/setup.cfg
-@@ -31,7 +31,6 @@ install_requires =
- Flask-Mail==0.9.1
- Flask-Migrate==2.5.3
- Flask-RESTful==0.3.8
-- Flask-Script==2.0.6
- Flask-SQLAlchemy==2.4.1
- Flask-WTF==0.14.3
- WTForms==2.2.1
-@@ -51,8 +50,12 @@ dev =
- zest.releaser==6.20.1
-
- [options.entry_points]
-+flask.commands =
-+ generate_password_hash = ihatemoney.manage:password_hash
-+ generate-config = ihatemoney.manage:generate_config
-+
- console_scripts =
-- ihatemoney = ihatemoney.manage:main
-+ ihatemoney = ihatemoney.manage:cli
-
- paste.app_factory =
- main = ihatemoney.run:main
diff --git a/pkgs/top-level/python-packages.nix b/pkgs/top-level/python-packages.nix
index 7894e6bb48e8..a02dec7e1c74 100644
--- a/pkgs/top-level/python-packages.nix
+++ b/pkgs/top-level/python-packages.nix
@@ -2791,6 +2791,8 @@ in {
flask-swagger-ui = callPackage ../development/python-modules/flask-swagger-ui { };
+ flask-talisman = callPackage ../development/python-modules/flask-talisman { };
+
flask_testing = callPackage ../development/python-modules/flask-testing { };
flask-versioned = callPackage ../development/python-modules/flask-versioned { };