diff --git a/docker-compose.override.yml.skeleton b/docker-compose.override.yml.skeleton
index cb2f9787..9834f760 100644
--- a/docker-compose.override.yml.skeleton
+++ b/docker-compose.override.yml.skeleton
@@ -26,8 +26,8 @@ services:
- SITE_DESCRIPTION=Otter Wiki Development
# Permissions allow everything (for development)
- READ_ACCESS=ANONYMOUS
- - WRITE_ACCESS=ANONYMOUS
- - ATTACHMENT_ACCESS=ANONYMOUS
+ - WRITE_ACCESS=APPROVED
+ - ATTACHMENT_ACCESS=APPROVED
- AUTO_APPROVAL=True
- NOTIFY_ADMINS_ON_REGISTER=False
- EMAIL_NEEDS_CONFIRMATION=False
diff --git a/otterwiki/server.py b/otterwiki/server.py
index 744d2087..082f4343 100644
--- a/otterwiki/server.py
+++ b/otterwiki/server.py
@@ -37,8 +37,8 @@
AUTH_HEADERS_EMAIL="x-otterwiki-email",
AUTH_HEADERS_PERMISSIONS="x-otterwiki-permissions",
READ_ACCESS="ANONYMOUS",
- WRITE_ACCESS="ANONYMOUS",
- ATTACHMENT_ACCESS="ANONYMOUS",
+ WRITE_ACCESS="APPROVED",
+ ATTACHMENT_ACCESS="APPROVED",
AUTO_APPROVAL=True,
DISABLE_REGISTRATION=False,
EMAIL_NEEDS_CONFIRMATION=True,
diff --git a/settings.cfg.skeleton b/settings.cfg.skeleton
index 69fb24b5..7ae7f37c 100644
--- a/settings.cfg.skeleton
+++ b/settings.cfg.skeleton
@@ -14,8 +14,8 @@ REPOSITORY = '/path/to/the/repository/root'
# 'ANONYMOUS' or 'REGISTERED' or 'APPROVED'
READ_ACCESS = 'ANONYMOUS'
-WRITE_ACCESS = 'REGISTERED'
-ATTACHMENT_ACCESS = 'REGISTERED'
+WRITE_ACCESS = 'APPROVED'
+ATTACHMENT_ACCESS = 'APPROVED'
AUTO_APPROVAL = True
EMAIL_NEEDS_CONFIRMATION = True
# NOTIFY_ADMINS_ON_REGISTER = True
@@ -47,4 +47,3 @@ SQLALCHEMY_DATABASE_URI = ''
# MAIL_PORT = 1025
# MAIL_USERNAME = None
# MAIL_PASSWORD = None
-
diff --git a/tests/conftest.py b/tests/conftest.py
index 889b574b..fb259c10 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -74,8 +74,24 @@ def create_app(tmpdir):
@pytest.fixture
-def test_client(create_app):
- client = create_app.test_client()
+def test_client(app_with_user):
+ client = app_with_user.test_client()
+ result = client.post(
+ "/-/login",
+ data={
+ "email": "another@user.org",
+ "password": "password4567",
+ },
+ follow_redirects=True,
+ )
+ html = result.data.decode()
+ assert "You logged in successfully." in html
+ return client
+
+
+@pytest.fixture
+def anonymous_client(app_with_user):
+ client = app_with_user.test_client()
return client
@@ -85,6 +101,17 @@ def req_ctx(create_app):
yield ctx
+@pytest.fixture
+def anonymous_write_access(create_app):
+ """Temporarily set WRITE_ACCESS to ANONYMOUS for tests that don't
+ use authenticated users (e.g. rendering/preview unit tests).
+ Automatically restores the original value after the test."""
+ original = create_app.config["WRITE_ACCESS"]
+ create_app.config["WRITE_ACCESS"] = "ANONYMOUS"
+ yield
+ create_app.config["WRITE_ACCESS"] = original
+
+
@pytest.fixture
def app_with_user(create_app, req_ctx):
from otterwiki.auth import SimpleAuth, generate_password_hash, db
diff --git a/tests/test_attachments.py b/tests/test_attachments.py
index dd5c6865..7cd40d64 100644
--- a/tests/test_attachments.py
+++ b/tests/test_attachments.py
@@ -7,39 +7,39 @@
@pytest.fixture
-def app_with_attachments(create_app):
+def app_with_attachments(app_with_user):
# create test page
message = "Test.md commit"
filename = "test.md"
author = ("Example Author", "mail@example.com")
# create attachment
- create_app.storage.store(
+ app_with_user.storage.store(
filename,
content="# Test\nAttachment Test.",
author=author,
message=message,
)
- assert True == create_app.storage.exists(filename)
+ assert True == app_with_user.storage.exists(filename)
# create txt attachment
message = "Test/attachment0.txt attach0-commit"
- create_app.storage.store(
+ app_with_user.storage.store(
"test/attachment0.txt",
content="attachment0-content0",
author=author,
message=message,
)
- assert True == create_app.storage.exists("test/attachment0.txt")
+ assert True == app_with_user.storage.exists("test/attachment0.txt")
# create gif attachment
message = "Test/attachment1.gif attach1-commit"
content_b64 = "R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
- create_app.storage.store(
+ app_with_user.storage.store(
"test/attachment1.gif",
content=base64.b64decode(content_b64),
author=author,
message=message,
mode="wb",
)
- assert True == create_app.storage.exists("test/attachment1.gif")
+ assert True == app_with_user.storage.exists("test/attachment1.gif")
# create svg attachment
message = "Test/attachment2.svg attach2-commit"
svg_content = (
@@ -47,20 +47,30 @@ def app_with_attachments(create_app):
''
)
- create_app.storage.store(
+ app_with_user.storage.store(
"test/attachment2.svg",
content=svg_content,
author=author,
message=message,
)
- assert True == create_app.storage.exists("test/attachment2.svg")
- yield create_app
+ assert True == app_with_user.storage.exists("test/attachment2.svg")
+ yield app_with_user
@pytest.fixture
def test_client(app_with_attachments):
client = app_with_attachments.test_client()
client._app = app_with_attachments
+ # Log in as approved user for write/attachment access
+ result = client.post(
+ "/-/login",
+ data={
+ "email": "another@user.org",
+ "password": "password4567",
+ },
+ follow_redirects=True,
+ )
+ assert "You logged in successfully." in result.data.decode()
yield client
diff --git a/tests/test_auth.py b/tests/test_auth.py
index 73cfc5eb..4f9b98de 100644
--- a/tests/test_auth.py
+++ b/tests/test_auth.py
@@ -162,8 +162,8 @@ def test_logout(test_client):
assert "You logged out successfully." in html
-def test_login_required(test_client):
- html = test_client.get(
+def test_login_required(anonymous_client):
+ html = anonymous_client.get(
"/-/settings",
follow_redirects=True,
).data.decode()
@@ -184,11 +184,11 @@ def test_settins_minimal(app_with_user, test_client):
# test permissions
#
@pytest.fixture
-def app_with_permissions(app_with_user, test_client):
+def app_with_permissions(app_with_user, anonymous_client):
app_with_user.config["READ_ACCESS"] = "ANONYMOUS"
app_with_user.config["WRITE_ACCESS"] = "ANONYMOUS"
# create a Home
- html = test_client.post(
+ html = anonymous_client.post(
"/Home/save",
data={
"content": "There is no place like Home.",
@@ -196,25 +196,25 @@ def app_with_permissions(app_with_user, test_client):
},
follow_redirects=True,
).data.decode()
- html = test_client.get("/Home").data.decode()
+ html = anonymous_client.get("/Home").data.decode()
assert "There is no place like Home." in html
# update permissions
app_with_user.config["READ_ACCESS"] = "REGISTERED"
# and fetch again
- html = test_client.get("/Home").data.decode()
+ html = anonymous_client.get("/Home").data.decode()
assert "There is no place like Home." not in html
with app_with_user.test_request_context() as ctx:
yield app_with_user
-def test_page_view_permissions(app_with_permissions, test_client):
+def test_page_view_permissions(app_with_permissions, anonymous_client):
fun = "view"
app_with_permissions.config["READ_ACCESS"] = "ANONYMOUS"
- rv = test_client.get(url_for(fun, path="Home"))
+ rv = anonymous_client.get(url_for(fun, path="Home"))
assert "There is no place like Home." in rv.data.decode()
app_with_permissions.config["READ_ACCESS"] = "REGISTERED"
- rv = test_client.get(url_for(fun, path="Home"), follow_redirects=True)
+ rv = anonymous_client.get(url_for(fun, path="Home"), follow_redirects=True)
assert "There is no place like Home." not in rv.data.decode()
# check for the toast
assert "lack the permissions to access" in rv.data.decode()
@@ -222,24 +222,24 @@ def test_page_view_permissions(app_with_permissions, test_client):
assert url_for("login") in rv.data.decode()
assert 'name="password"' in rv.data.decode()
assert rv.status_code == 200
- login(test_client)
- rv = test_client.get(url_for(fun, path="Home"))
+ login(anonymous_client)
+ rv = anonymous_client.get(url_for(fun, path="Home"))
assert "There is no place like Home." in rv.data.decode()
assert rv.status_code == 200
-def test_page_view_login_next_redirect(app_with_permissions, test_client):
+def test_page_view_login_next_redirect(app_with_permissions, anonymous_client):
# READ_ACCESS=REGISTERED: anonymous request to a page must redirect to
# login with a ?next= pointing back to that page, and logging in must
# forward the user back there.
app_with_permissions.config["READ_ACCESS"] = "REGISTERED"
- rv = test_client.get(url_for("view", path="Home"))
+ rv = anonymous_client.get(url_for("view", path="Home"))
assert rv.status_code == 302
assert "/-/login" in rv.location
assert "next=" in rv.location
assert "Home" in rv.location
# login with the next parameter as the login form would submit it
- rv = test_client.post(
+ rv = anonymous_client.post(
"/-/login",
data={
"email": "mail@example.org",
@@ -250,8 +250,8 @@ def test_page_view_login_next_redirect(app_with_permissions, test_client):
assert rv.status_code == 302
assert rv.location.endswith("/Home")
# rejects external next as open-redirect protection
- rv = test_client.get("/-/logout", follow_redirects=True)
- rv = test_client.post(
+ rv = anonymous_client.get("/-/logout", follow_redirects=True)
+ rv = anonymous_client.post(
"/-/login",
data={
"email": "mail@example.org",
@@ -263,77 +263,77 @@ def test_page_view_login_next_redirect(app_with_permissions, test_client):
assert "evil.example.com" not in rv.location
-def test_page_blame_permissions(app_with_permissions, test_client):
+def test_page_blame_permissions(app_with_permissions, anonymous_client):
fun = "blame"
app_with_permissions.config["READ_ACCESS"] = "ANONYMOUS"
- rv = test_client.get(url_for(fun, path="Home"))
+ rv = anonymous_client.get(url_for(fun, path="Home"))
assert rv.status_code == 200
assert "There is no place like Home." in rv.data.decode()
app_with_permissions.config["READ_ACCESS"] = "REGISTERED"
- rv = test_client.get(url_for(fun, path="Home"))
+ rv = anonymous_client.get(url_for(fun, path="Home"))
assert rv.status_code == 302
assert "/-/login" in rv.location
- login(test_client)
- rv = test_client.get(url_for(fun, path="Home"))
+ login(anonymous_client)
+ rv = anonymous_client.get(url_for(fun, path="Home"))
assert "There is no place like Home." in rv.data.decode()
-def test_page_history_permissions(app_with_permissions, test_client):
+def test_page_history_permissions(app_with_permissions, anonymous_client):
fun = "history"
app_with_permissions.config["READ_ACCESS"] = "ANONYMOUS"
- rv = test_client.get(url_for(fun, path="Home"))
+ rv = anonymous_client.get(url_for(fun, path="Home"))
assert rv.status_code == 200
assert "initial test commit" in rv.data.decode()
app_with_permissions.config["READ_ACCESS"] = "REGISTERED"
- rv = test_client.get(url_for(fun, path="Home"))
+ rv = anonymous_client.get(url_for(fun, path="Home"))
assert rv.status_code == 302
assert "/-/login" in rv.location
- login(test_client)
- rv = test_client.get(url_for(fun, path="Home"))
+ login(anonymous_client)
+ rv = anonymous_client.get(url_for(fun, path="Home"))
assert rv.status_code == 200
assert "initial test commit" in rv.data.decode()
-def test_page_index_permissions(app_with_permissions, test_client):
+def test_page_index_permissions(app_with_permissions, anonymous_client):
fun = "pageindex"
app_with_permissions.config["READ_ACCESS"] = "ANONYMOUS"
- rv = test_client.get(url_for(fun))
+ rv = anonymous_client.get(url_for(fun))
assert rv.status_code == 200
assert "Page Index" in rv.data.decode()
app_with_permissions.config["READ_ACCESS"] = "REGISTERED"
- rv = test_client.get(url_for(fun))
+ rv = anonymous_client.get(url_for(fun))
assert rv.status_code == 302
assert "/-/login" in rv.location
- login(test_client)
- rv = test_client.get(url_for(fun))
+ login(anonymous_client)
+ rv = anonymous_client.get(url_for(fun))
assert rv.status_code == 200
assert "Page Index" in rv.data.decode()
-def test_page_changelog_permissions(app_with_permissions, test_client):
+def test_page_changelog_permissions(app_with_permissions, anonymous_client):
fun = "changelog"
app_with_permissions.config["READ_ACCESS"] = "ANONYMOUS"
- rv = test_client.get(url_for(fun, path="Home"))
+ rv = anonymous_client.get(url_for(fun, path="Home"))
assert rv.status_code == 200
assert "initial test commit" in rv.data.decode()
app_with_permissions.config["READ_ACCESS"] = "REGISTERED"
- rv = test_client.get(url_for(fun, path="Home"))
+ rv = anonymous_client.get(url_for(fun, path="Home"))
assert rv.status_code == 302
assert "/-/login" in rv.location
- login(test_client)
- rv = test_client.get(url_for(fun, path="Home"))
+ login(anonymous_client)
+ rv = anonymous_client.get(url_for(fun, path="Home"))
assert rv.status_code == 200
assert "initial test commit" in rv.data.decode()
-def test_page_edit_permissions(app_with_permissions, test_client):
+def test_page_edit_permissions(app_with_permissions, anonymous_client):
# update permissions
app_with_permissions.config["READ_ACCESS"] = "ANONYMOUS"
app_with_permissions.config["WRITE_ACCESS"] = "ANONYMOUS"
# helper
pagename = "RandomEdit"
# try to edit anonymous
- rv = test_client.get(url_for("edit", path=pagename))
+ rv = anonymous_client.get(url_for("edit", path=pagename))
assert rv.status_code == 200
html = rv.data.decode()
# check that there is an editor in the html
@@ -342,21 +342,21 @@ def test_page_edit_permissions(app_with_permissions, test_client):
app_with_permissions.config["READ_ACCESS"] = "REGISTERED"
app_with_permissions.config["WRITE_ACCESS"] = "REGISTERED"
# try edit
- rv = test_client.get(url_for("edit", path=pagename))
+ rv = anonymous_client.get(url_for("edit", path=pagename))
html = rv.data.decode()
# check that there is an editor in the html
assert rv.status_code == 403
assert "