# Copyright (c) 2015, JINGROW and Contributors
# License: MIT. See LICENSE
"""Use blog post test to test user permissions logic"""

import jingrow
import jingrow.defaults
import jingrow.model.meta
from jingrow.core.pagetype.pagetype.test_pagetype import new_pagetype
from jingrow.core.pagetype.user_permission.user_permission import clear_user_permissions
from jingrow.core.page.permission_manager.permission_manager import add, remove, reset, update
from jingrow.desk.form.load import getpage
from jingrow.permissions import (
	ALL_USER_ROLE,
	AUTOMATIC_ROLES,
	GUEST_ROLE,
	SYSTEM_USER_ROLE,
	add_permission,
	add_user_permission,
	clear_user_permissions_for_pagetype,
	get_pg_permissions,
	get_pagetypes_with_read,
	remove_user_permission,
	update_permission_property,
)
from jingrow.test_runner import make_test_records_for_pagetype
from jingrow.tests.utils import JingrowTestCase
from jingrow.utils.data import now_datetime

test_dependencies = ["Blogger", "Blog Post", "User", "Contact", "Salutation"]


class TestPermissions(JingrowTestCase):
	@classmethod
	def setUpClass(cls):
		super().setUpClass()
		jingrow.clear_cache(pagetype="Blog Post")
		user = jingrow.get_pg("User", "test1@example.com")
		user.add_roles("Website Manager")
		user.add_roles("System Manager")

		user = jingrow.get_pg("User", "test2@example.com")
		user.add_roles("Blogger")

		user = jingrow.get_pg("User", "test3@example.com")
		user.add_roles("Sales User")

		user = jingrow.get_pg("User", "testperm@example.com")
		user.add_roles("Website Manager")

	def setUp(self):
		jingrow.clear_cache(pagetype="Blog Post")

		reset("Blogger")
		reset("Blog Post")

		jingrow.db.delete("User Permission")

		jingrow.set_user("test1@example.com")

	def tearDown(self):
		jingrow.set_user("Administrator")
		jingrow.db.set_value("Blogger", "_Test Blogger 1", "user", None)

		clear_user_permissions_for_pagetype("Blog Category")
		clear_user_permissions_for_pagetype("Blog Post")
		clear_user_permissions_for_pagetype("Blogger")

	@staticmethod
	def set_strict_user_permissions(ignore):
		ss = jingrow.get_pg("System Settings")
		ss.apply_strict_user_permissions = ignore
		ss.flags.ignore_mandatory = 1
		ss.save()

	def test_basic_permission(self):
		post = jingrow.get_pg("Blog Post", "-test-blog-post")
		self.assertTrue(post.has_permission("read"))

	def test_select_permission(self):
		# grant only select perm to blog post
		add_permission("Blog Post", "Sales User", 0)
		update_permission_property("Blog Post", "Sales User", 0, "select", 1)
		update_permission_property("Blog Post", "Sales User", 0, "read", 0)
		update_permission_property("Blog Post", "Sales User", 0, "write", 0)

		jingrow.clear_cache(pagetype="Blog Post")
		jingrow.set_user("test3@example.com")

		# validate select perm
		post = jingrow.get_pg("Blog Post", "-test-blog-post")
		self.assertTrue(post.has_permission("select"))

		# validate does not have read and write perm
		self.assertFalse(post.has_permission("read"))
		self.assertRaises(jingrow.PermissionError, post.save)

		permitted_record = jingrow.get_list("Blog Post", fields="*", limit=1)[0]
		full_record = jingrow.get_all("Blog Post", fields="*", limit=1)[0]
		self.assertNotEqual(permitted_record, full_record)
		self.assertSequenceSubset(post.meta.get_search_fields(), permitted_record)

	def test_user_permissions_in_pg(self):
		add_user_permission("Blog Category", "-test-blog-category-1", "test2@example.com")

		jingrow.set_user("test2@example.com")

		post = jingrow.get_pg("Blog Post", "-test-blog-post")
		self.assertFalse(post.has_permission("read"))
		self.assertFalse(get_pg_permissions(post).get("read"))

		post1 = jingrow.get_pg("Blog Post", "-test-blog-post-1")
		self.assertTrue(post1.has_permission("read"))
		self.assertTrue(get_pg_permissions(post1).get("read"))

	def test_user_permissions_in_report(self):
		add_user_permission("Blog Category", "-test-blog-category-1", "test2@example.com")

		jingrow.set_user("test2@example.com")
		names = [d.name for d in jingrow.get_list("Blog Post", fields=["name", "blog_category"])]

		self.assertTrue("-test-blog-post-1" in names)
		self.assertFalse("-test-blog-post" in names)

	def test_default_values(self):
		pg = jingrow.new_pg("Blog Post")
		self.assertFalse(pg.get("blog_category"))

		# Fetch default based on single user permission
		add_user_permission("Blog Category", "-test-blog-category-1", "test2@example.com")

		jingrow.set_user("test2@example.com")
		pg = jingrow.new_pg("Blog Post")
		self.assertEqual(pg.get("blog_category"), "-test-blog-category-1")

		# Don't fetch default if user permissions is more than 1
		add_user_permission(
			"Blog Category", "-test-blog-category", "test2@example.com", ignore_permissions=True
		)
		jingrow.clear_cache()
		pg = jingrow.new_pg("Blog Post")
		self.assertFalse(pg.get("blog_category"))

		# Fetch user permission set as default from multiple user permission
		add_user_permission(
			"Blog Category",
			"-test-blog-category-2",
			"test2@example.com",
			ignore_permissions=True,
			is_default=1,
		)
		jingrow.clear_cache()
		pg = jingrow.new_pg("Blog Post")
		self.assertEqual(pg.get("blog_category"), "-test-blog-category-2")

	def test_user_link_match_pg(self):
		blogger = jingrow.get_pg("Blogger", "_Test Blogger 1")
		blogger.user = "test2@example.com"
		blogger.save()

		jingrow.set_user("test2@example.com")

		post = jingrow.get_pg("Blog Post", "-test-blog-post-2")
		self.assertTrue(post.has_permission("read"))

		post1 = jingrow.get_pg("Blog Post", "-test-blog-post-1")
		self.assertFalse(post1.has_permission("read"))

	def test_user_link_match_report(self):
		blogger = jingrow.get_pg("Blogger", "_Test Blogger 1")
		blogger.user = "test2@example.com"
		blogger.save()

		jingrow.set_user("test2@example.com")

		names = [d.name for d in jingrow.get_list("Blog Post", fields=["name", "owner"])]
		self.assertTrue("-test-blog-post-2" in names)
		self.assertFalse("-test-blog-post-1" in names)

	def test_set_user_permissions(self):
		jingrow.set_user("test1@example.com")
		add_user_permission("Blog Post", "-test-blog-post", "test2@example.com")

	def test_not_allowed_to_set_user_permissions(self):
		jingrow.set_user("test2@example.com")

		# this user can't add user permissions
		self.assertRaises(
			jingrow.PermissionError, add_user_permission, "Blog Post", "-test-blog-post", "test2@example.com"
		)

	def test_read_if_explicit_user_permissions_are_set(self):
		self.test_set_user_permissions()

		jingrow.set_user("test2@example.com")

		# user can only access permitted blog post
		pg = jingrow.get_pg("Blog Post", "-test-blog-post")
		self.assertTrue(pg.has_permission("read"))

		# and not this one
		pg = jingrow.get_pg("Blog Post", "-test-blog-post-1")
		self.assertFalse(pg.has_permission("read"))

	def test_not_allowed_to_remove_user_permissions(self):
		self.test_set_user_permissions()

		jingrow.set_user("test2@example.com")

		# user cannot remove their own user permissions
		self.assertRaises(
			jingrow.PermissionError,
			remove_user_permission,
			"Blog Post",
			"-test-blog-post",
			"test2@example.com",
		)

	def test_user_permissions_if_applied_on_pg_being_evaluated(self):
		jingrow.set_user("test2@example.com")
		pg = jingrow.get_pg("Blog Post", "-test-blog-post-1")
		self.assertTrue(pg.has_permission("read"))

		jingrow.set_user("test1@example.com")
		add_user_permission("Blog Post", "-test-blog-post", "test2@example.com")

		jingrow.set_user("test2@example.com")
		pg = jingrow.get_pg("Blog Post", "-test-blog-post-1")
		self.assertFalse(pg.has_permission("read"))

		pg = jingrow.get_pg("Blog Post", "-test-blog-post")
		self.assertTrue(pg.has_permission("read"))

	def test_set_standard_fields_manually(self):
		# check that creation and owner cannot be set manually
		from datetime import timedelta

		fake_creation = now_datetime() + timedelta(days=-7)
		fake_owner = jingrow.db.get_value("User", {"name": ("!=", jingrow.session.user)})

		d = jingrow.new_pg("ToDo")
		d.description = "ToDo created via test_set_standard_fields_manually"
		d.creation = fake_creation
		d.owner = fake_owner
		d.save()
		self.assertNotEqual(d.creation, fake_creation)
		self.assertNotEqual(d.owner, fake_owner)

	def test_dont_change_standard_constants(self):
		# check that Page.creation cannot be changed
		user = jingrow.get_pg("User", jingrow.session.user)
		user.creation = now_datetime()
		self.assertRaises(jingrow.CannotChangeConstantError, user.save)

		# check that Page.owner cannot be changed
		user.reload()
		user.owner = "Guest"
		self.assertRaises(jingrow.CannotChangeConstantError, user.save)

	def test_set_only_once(self):
		blog_post = jingrow.get_meta("Blog Post")
		pg = jingrow.get_pg("Blog Post", "-test-blog-post-1")
		pg.db_set("title", "Old")
		blog_post.get_field("title").set_only_once = 1
		pg.title = "New"
		self.assertRaises(jingrow.CannotChangeConstantError, pg.save)
		blog_post.get_field("title").set_only_once = 0

	def test_set_only_once_child_table_rows(self):
		pagetype_meta = jingrow.get_meta("PageType")
		pagetype_meta.get_field("fields").set_only_once = 1
		pg = jingrow.get_pg("PageType", "Blog Post")

		# remove last one
		pg.fields = pg.fields[:-1]
		self.assertRaises(jingrow.CannotChangeConstantError, pg.save)
		jingrow.clear_cache(pagetype="PageType")

	def test_set_only_once_child_table_row_value(self):
		pagetype_meta = jingrow.get_meta("PageType")
		pagetype_meta.get_field("fields").set_only_once = 1
		pg = jingrow.get_pg("PageType", "Blog Post")

		# change one property from the child table
		pg.fields[-1].fieldtype = "Check"
		self.assertRaises(jingrow.CannotChangeConstantError, pg.save)
		jingrow.clear_cache(pagetype="PageType")

	def test_set_only_once_child_table_okay(self):
		pagetype_meta = jingrow.get_meta("PageType")
		pagetype_meta.get_field("fields").set_only_once = 1
		pg = jingrow.get_pg("PageType", "Blog Post")

		pg.load_pg_before_save()
		self.assertFalse(pg.validate_set_only_once())
		jingrow.clear_cache(pagetype="PageType")

	def test_user_permission_pagetypes(self):
		add_user_permission("Blog Category", "-test-blog-category-1", "test2@example.com")
		add_user_permission("Blogger", "_Test Blogger 1", "test2@example.com")

		jingrow.set_user("test2@example.com")

		jingrow.clear_cache(pagetype="Blog Post")

		pg = jingrow.get_pg("Blog Post", "-test-blog-post")
		self.assertFalse(pg.has_permission("read"))

		pg = jingrow.get_pg("Blog Post", "-test-blog-post-2")
		self.assertTrue(pg.has_permission("read"))

		jingrow.clear_cache(pagetype="Blog Post")

	def if_owner_setup(self):
		update("Blog Post", "Blogger", 0, "if_owner", 1)

		add_user_permission("Blog Category", "-test-blog-category-1", "test2@example.com")
		add_user_permission("Blogger", "_Test Blogger 1", "test2@example.com")

		jingrow.clear_cache(pagetype="Blog Post")

	def test_insert_if_owner_with_user_permissions(self):
		"""If `If Owner` is checked for a Role, check if that document
		is allowed to be read, updated, submitted, etc. except be created,
		even if the document is restricted based on User Permissions."""
		jingrow.delete_pg("Blog Post", "-test-blog-post-title")

		self.if_owner_setup()

		jingrow.set_user("test2@example.com")

		pg = jingrow.get_pg(
			{
				"pagetype": "Blog Post",
				"blog_category": "-test-blog-category",
				"blogger": "_Test Blogger 1",
				"title": "_Test Blog Post Title",
				"content": "_Test Blog Post Content",
			}
		)

		self.assertRaises(jingrow.PermissionError, pg.insert)

		jingrow.set_user("test1@example.com")
		add_user_permission("Blog Category", "-test-blog-category", "test2@example.com")

		jingrow.set_user("test2@example.com")
		pg.insert()

		jingrow.set_user("Administrator")
		remove_user_permission("Blog Category", "-test-blog-category", "test2@example.com")

		jingrow.set_user("test2@example.com")
		pg = jingrow.get_pg(pg.pagetype, pg.name)
		self.assertTrue(pg.has_permission("read"))
		self.assertTrue(pg.has_permission("write"))
		self.assertFalse(pg.has_permission("create"))

		# delete created record
		jingrow.set_user("Administrator")
		jingrow.delete_pg("Blog Post", "-test-blog-post-title")

	def test_ignore_user_permissions_if_missing(self):
		"""If there are no user permissions, then allow as per role"""

		add_user_permission("Blog Category", "-test-blog-category", "test2@example.com")
		jingrow.set_user("test2@example.com")

		pg = jingrow.get_pg(
			{
				"pagetype": "Blog Post",
				"blog_category": "-test-blog-category-2",
				"blogger": "_Test Blogger 1",
				"title": "_Test Blog Post Title",
				"content": "_Test Blog Post Content",
			}
		)

		self.assertFalse(pg.has_permission("write"))

		jingrow.set_user("Administrator")
		remove_user_permission("Blog Category", "-test-blog-category", "test2@example.com")

		jingrow.set_user("test2@example.com")
		self.assertTrue(pg.has_permission("write"))

	def test_strict_user_permissions(self):
		"""If `Strict User Permissions` is checked in System Settings,
		show records even if User Permissions are missing for a linked
		pagetype"""

		jingrow.set_user("Administrator")
		jingrow.db.delete("Contact")
		jingrow.db.delete("Contact Email")
		jingrow.db.delete("Contact Phone")

		reset("Salutation")
		reset("Contact")

		make_test_records_for_pagetype("Contact", force=True)

		add_user_permission("Salutation", "Mr", "test3@example.com")
		self.set_strict_user_permissions(0)

		allowed_contact = jingrow.get_pg("Contact", "_Test Contact For _Test Customer")
		other_contact = jingrow.get_pg("Contact", "_Test Contact For _Test Supplier")

		jingrow.set_user("test3@example.com")
		self.assertTrue(allowed_contact.has_permission("read"))
		self.assertTrue(other_contact.has_permission("read"))
		self.assertEqual(len(jingrow.get_list("Contact")), 2)

		jingrow.set_user("Administrator")
		self.set_strict_user_permissions(1)

		jingrow.set_user("test3@example.com")
		self.assertTrue(allowed_contact.has_permission("read"))
		self.assertFalse(other_contact.has_permission("read"))
		self.assertTrue(len(jingrow.get_list("Contact")), 1)

		jingrow.set_user("Administrator")
		self.set_strict_user_permissions(0)

		clear_user_permissions_for_pagetype("Salutation")
		clear_user_permissions_for_pagetype("Contact")

	def test_user_permission_is_not_applied_if_user_roles_does_not_have_permission(self):
		add_user_permission("Blog Post", "-test-blog-post-1", "test3@example.com")
		jingrow.set_user("test3@example.com")
		pg = jingrow.get_pg("Blog Post", "-test-blog-post-1")
		self.assertFalse(pg.has_permission("read"))

		jingrow.set_user("Administrator")
		user = jingrow.get_pg("User", "test3@example.com")
		user.add_roles("Blogger")
		jingrow.set_user("test3@example.com")
		self.assertTrue(pg.has_permission("read"))

		jingrow.set_user("Administrator")
		user.remove_roles("Blogger")

	def test_contextual_user_permission(self):
		# should be applicable for across all pagetypes
		add_user_permission("Blogger", "_Test Blogger", "test2@example.com")
		# should be applicable only while accessing Blog Post
		add_user_permission("Blogger", "_Test Blogger 1", "test2@example.com", applicable_for="Blog Post")
		# should be applicable only while accessing User
		add_user_permission("Blogger", "_Test Blogger 2", "test2@example.com", applicable_for="User")

		posts = jingrow.get_all("Blog Post", fields=["name", "blogger"])

		# Get all posts for admin
		self.assertEqual(len(posts), 4)

		jingrow.set_user("test2@example.com")

		posts = jingrow.get_list("Blog Post", fields=["name", "blogger"])

		# Should get only posts with allowed blogger via user permission
		# only '_Test Blogger', '_Test Blogger 1' are allowed in Blog Post
		self.assertEqual(len(posts), 3)

		for post in posts:
			self.assertIn(
				post.blogger,
				["_Test Blogger", "_Test Blogger 1"],
				f"A post from {post.blogger} is not expected.",
			)

	def test_if_owner_permission_overrides_properly(self):
		# check if user is not granted access if the user is not the owner of the pg
		# Blogger has only read access on the blog post unless he is the owner of the blog
		update("Blog Post", "Blogger", 0, "if_owner", 1)
		update("Blog Post", "Blogger", 0, "read", 1, 1)
		update("Blog Post", "Blogger", 0, "write", 1, 1)
		update("Blog Post", "Blogger", 0, "delete", 1, 1)

		# currently test2 user has not created any document
		# still he should be able to do get_list query which should
		# not raise permission error but simply return empty list
		jingrow.set_user("test2@example.com")
		self.assertEqual(jingrow.get_list("Blog Post"), [])

		jingrow.set_user("Administrator")

		# creates a custom pageperm with just read access
		# now any user can read any blog post (but other rights are limited to the blog post owner)
		add_permission("Blog Post", "Blogger")
		jingrow.clear_cache(pagetype="Blog Post")

		jingrow.delete_pg("Blog Post", "-test-blog-post-title")

		jingrow.set_user("test1@example.com")

		pg = jingrow.get_pg(
			{
				"pagetype": "Blog Post",
				"blog_category": "-test-blog-category",
				"blogger": "_Test Blogger 1",
				"title": "_Test Blog Post Title",
				"content": "_Test Blog Post Content",
			}
		)

		pg.insert()

		jingrow.set_user("test2@example.com")
		pg = jingrow.get_pg(pg.pagetype, pg.name)

		self.assertTrue(pg.has_permission("read"))
		self.assertFalse(pg.has_permission("write"))
		self.assertFalse(pg.has_permission("delete"))

		# check if owner of the pg has the access that is available only for the owner of the pg
		jingrow.set_user("test1@example.com")
		pg = jingrow.get_pg(pg.pagetype, pg.name)

		self.assertTrue(pg.has_permission("read"))
		self.assertTrue(pg.has_permission("write"))
		self.assertTrue(pg.has_permission("delete"))

		# delete the created pg
		jingrow.delete_pg("Blog Post", "-test-blog-post-title")

	def test_if_owner_permission_on_getdoc(self):
		update("Blog Post", "Blogger", 0, "if_owner", 1)
		update("Blog Post", "Blogger", 0, "read", 1)
		update("Blog Post", "Blogger", 0, "write", 1)
		update("Blog Post", "Blogger", 0, "delete", 1)
		jingrow.clear_cache(pagetype="Blog Post")

		jingrow.set_user("test1@example.com")

		pg = jingrow.get_pg(
			{
				"pagetype": "Blog Post",
				"blog_category": "-test-blog-category",
				"blogger": "_Test Blogger 1",
				"title": "_Test Blog Post Title New",
				"content": "_Test Blog Post Content",
			}
		)

		pg.insert()

		getpage("Blog Post", pg.name)
		doclist = [d.name for d in jingrow.response.pgs]
		self.assertTrue(pg.name in doclist)

		jingrow.set_user("test2@example.com")
		self.assertRaises(jingrow.PermissionError, getpage, "Blog Post", pg.name)

	def test_if_owner_permission_on_get_list(self):
		pg = jingrow.get_pg(
			{
				"pagetype": "Blog Post",
				"blog_category": "-test-blog-category",
				"blogger": "_Test Blogger 1",
				"title": "_Test If Owner Permissions on Get List",
				"content": "_Test Blog Post Content",
			}
		)

		pg.insert(ignore_if_duplicate=True)

		update("Blog Post", "Blogger", 0, "if_owner", 1)
		update("Blog Post", "Blogger", 0, "read", 1)
		user = jingrow.get_pg("User", "test2@example.com")
		user.add_roles("Website Manager")
		jingrow.clear_cache(pagetype="Blog Post")

		jingrow.set_user("test2@example.com")
		self.assertIn(pg.name, jingrow.get_list("Blog Post", pluck="name"))

		# Become system manager to remove role
		jingrow.set_user("test1@example.com")
		user.remove_roles("Website Manager")
		jingrow.clear_cache(pagetype="Blog Post")

		jingrow.set_user("test2@example.com")
		self.assertNotIn(pg.name, jingrow.get_list("Blog Post", pluck="name"))

	def test_if_owner_permission_on_delete(self):
		update("Blog Post", "Blogger", 0, "if_owner", 1)
		update("Blog Post", "Blogger", 0, "read", 1, 1)
		update("Blog Post", "Blogger", 0, "write", 1, 1)
		update("Blog Post", "Blogger", 0, "delete", 1, 1)

		# Remove delete perm
		update("Blog Post", "Website Manager", 0, "delete", 0)

		jingrow.clear_cache(pagetype="Blog Post")

		jingrow.set_user("test2@example.com")

		pg = jingrow.get_pg(
			{
				"pagetype": "Blog Post",
				"blog_category": "-test-blog-category",
				"blogger": "_Test Blogger 1",
				"title": "_Test Blog Post Title New 1",
				"content": "_Test Blog Post Content",
			}
		)

		pg.insert()

		getpage("Blog Post", pg.name)
		doclist = [d.name for d in jingrow.response.pgs]
		self.assertTrue(pg.name in doclist)

		jingrow.set_user("testperm@example.com")

		# Website Manager able to read
		getpage("Blog Post", pg.name)
		doclist = [d.name for d in jingrow.response.pgs]
		self.assertTrue(pg.name in doclist)

		# Website Manager should not be able to delete
		self.assertRaises(jingrow.PermissionError, jingrow.delete_pg, "Blog Post", pg.name)

		jingrow.set_user("test2@example.com")
		jingrow.delete_pg("Blog Post", "-test-blog-post-title-new-1")
		update("Blog Post", "Website Manager", 0, "delete", 1, 1)

	def test_clear_user_permissions(self):
		current_user = jingrow.session.user
		jingrow.set_user("Administrator")
		clear_user_permissions_for_pagetype("Blog Category", "test2@example.com")
		clear_user_permissions_for_pagetype("Blog Post", "test2@example.com")

		add_user_permission("Blog Post", "-test-blog-post-1", "test2@example.com")
		add_user_permission("Blog Post", "-test-blog-post-2", "test2@example.com")
		add_user_permission("Blog Category", "-test-blog-category-1", "test2@example.com")

		deleted_user_permission_count = clear_user_permissions("test2@example.com", "Blog Post")

		self.assertEqual(deleted_user_permission_count, 2)

		blog_post_user_permission_count = jingrow.db.count(
			"User Permission", filters={"user": "test2@example.com", "allow": "Blog Post"}
		)

		self.assertEqual(blog_post_user_permission_count, 0)

		blog_category_user_permission_count = jingrow.db.count(
			"User Permission", filters={"user": "test2@example.com", "allow": "Blog Category"}
		)

		self.assertEqual(blog_category_user_permission_count, 1)

		# reset the user
		jingrow.set_user(current_user)

	def test_child_permissions(self):
		jingrow.set_user("test3@example.com")
		self.assertIsInstance(jingrow.get_list("DefaultValue", parent_pagetype="User", limit=1), list)

		# jingrow.get_list
		self.assertRaises(jingrow.PermissionError, jingrow.get_list, "DefaultValue")
		self.assertRaises(jingrow.PermissionError, jingrow.get_list, "DefaultValue", parent_pagetype="ToDo")
		self.assertRaises(
			jingrow.PermissionError, jingrow.get_list, "DefaultValue", parent_pagetype="DefaultValue"
		)

		# jingrow.get_pg
		user = jingrow.get_pg("User", jingrow.session.user)
		pg = user.append("defaults")
		pg.check_permission()

		# false due to missing parentfield
		pg = user.append("roles")
		pg.parentfield = None
		self.assertRaises(jingrow.PermissionError, pg.check_permission)

		# false due to invalid parentfield
		pg = user.append("roles")
		pg.parentfield = "first_name"
		self.assertRaises(jingrow.PermissionError, pg.check_permission)

		# false by permlevel
		pg = user.append("roles")
		self.assertRaises(jingrow.PermissionError, pg.check_permission)

		# false by user permission
		user = jingrow.get_pg("User", "Administrator")
		pg = user.append("defaults")
		self.assertRaises(jingrow.PermissionError, pg.check_permission)

	def test_select_user(self):
		"""If test3@example.com is restricted by a User Permission to see only
		users linked to a certain pagetype (in this case: Gender "Female"), he
		should not be able to query other users (Gender "Male").
		"""
		# ensure required genders exist
		for gender in ("Male", "Female"):
			if jingrow.db.exists("Gender", gender):
				continue

			jingrow.get_pg({"pagetype": "Gender", "gender": gender}).insert()

		# asssign gender to test users
		jingrow.db.set_value("User", "test1@example.com", "gender", "Male")
		jingrow.db.set_value("User", "test2@example.com", "gender", "Female")
		jingrow.db.set_value("User", "test3@example.com", "gender", "Female")

		# restrict test3@example.com to see only female users
		add_user_permission("Gender", "Female", "test3@example.com")

		# become user test3@example.com and see what users he can query
		jingrow.set_user("test3@example.com")
		users = jingrow.get_list("User", pluck="name")

		self.assertNotIn("test1@example.com", users)
		self.assertIn("test2@example.com", users)
		self.assertIn("test3@example.com", users)

	def test_automatic_permissions(self):
		def assertHasRole(*roles: str | tuple[str, ...]):
			for role in roles:
				self.assertIn(role, jingrow.get_roles())

		jingrow.set_user("Administrator")
		assertHasRole(*AUTOMATIC_ROLES)

		jingrow.set_user("Guest")
		assertHasRole(GUEST_ROLE)

		website_user = jingrow.db.get_value(
			"User",
			{"user_type": "Website User", "enabled": 1, "name": ("not in", AUTOMATIC_ROLES)},
		)
		jingrow.set_user(website_user)
		assertHasRole(GUEST_ROLE, ALL_USER_ROLE)

		system_user = jingrow.db.get_value(
			"User",
			{"user_type": "System User", "enabled": 1, "name": ("not in", AUTOMATIC_ROLES)},
		)
		jingrow.set_user(system_user)
		assertHasRole(GUEST_ROLE, ALL_USER_ROLE, SYSTEM_USER_ROLE)

	def test_get_pagetypes_with_read(self):
		with self.set_user("Administrator"):
			pagetype = new_pagetype(permissions=[{"select": 1, "role": "_Test Role", "read": 0}]).insert().name

		with self.set_user("test@example.com"):
			self.assertNotIn(pagetype, get_pagetypes_with_read())

	def test_overrides_work_as_expected(self):
		"""custom pageperms should completely override standard ones"""
		standard_role = "Desk User"
		custom_role = jingrow.new_pg("Role", role_name=jingrow.generate_hash()).insert().name
		with self.set_user("Administrator"):
			pagetype = new_pagetype(permissions=[{"role": standard_role, "read": 1}]).insert().name

		with self.set_user("test@example.com"):
			self.assertIn(pagetype, get_pagetypes_with_read())

		with self.set_user("Administrator"):
			# Allow perm to some other role and remove standard role
			add(pagetype, custom_role, 0)
			remove(pagetype, standard_role, 0)

		with self.set_user("test@example.com"):
			# No one has this role, so user shouldn't have permission.
			self.assertNotIn(pagetype, get_pagetypes_with_read())
