Przeglądaj źródła

Merge pull request #37 from ohartl/feature-limit-admin-domains

Adding feature to limit domains an admin can manage
Oliver Hartl 9 lat temu
rodzic
commit
dda397074c

+ 34 - 34
README.md

@@ -101,11 +101,11 @@ With subdirectory `webmum/` in URL (e.g. `http://mydomain.tld/webmum/`):
     ServerName domain.tld
     DocumentRoot /var/www/domain.tld
 
-	RewriteEngine on
-	RewriteCond %{REQUEST_FILENAME} !-d
-	RewriteCond %{REQUEST_FILENAME} !-f
-	RewriteRule ^\/webmum/(.*)\.css$ /webmum/$1.css [L]
-	RewriteRule ^\/webmum/(.*)$ /webmum/index.php [L,QSA]
+    RewriteEngine on
+    RewriteCond %{REQUEST_FILENAME} !-d
+    RewriteCond %{REQUEST_FILENAME} !-f
+    RewriteRule ^\/webmum/(.*)\.css$ /webmum/$1.css [L]
+    RewriteRule ^\/webmum/(.*)$ /webmum/index.php [L,QSA]
 </VirtualHost>
 ```
 
@@ -116,11 +116,11 @@ Without subdirectory in URL (e.g. `http://webmum.mydomain.tld/`):
     ServerName webmum.domain.tld
     DocumentRoot /var/www/domain.tld/webmum
 
-	RewriteEngine on
-	RewriteCond %{REQUEST_FILENAME} !-d
-	RewriteCond %{REQUEST_FILENAME} !-f
-	RewriteRule (.*)\.css$ $1.css [L]
-	RewriteRule ^(.*)$ /index.php [L,QSA]
+    RewriteEngine on
+    RewriteCond %{REQUEST_FILENAME} !-d
+    RewriteCond %{REQUEST_FILENAME} !-f
+    RewriteRule (.*)\.css$ $1.css [L]
+    RewriteRule ^(.*)$ /index.php [L,QSA]
 </VirtualHost>
 ```
 
@@ -137,10 +137,6 @@ Configure WebMUM via the configuration file at `config/config.inc.php`.
 At first the database access has to be configured.
 
 ```php
-/*
- * MySQL server and database settings
- */
-
 define("MYSQL_HOST", "localhost");
 define("MYSQL_USER", "vmail");
 define("MYSQL_PASSWORD", "vmail");
@@ -150,10 +146,6 @@ define("MYSQL_DATABASE", "vmail");
 ... then define the table names according to your own setup:
 
 ```php
-/*
- * Database table names
- */
-
 // Table names
 define("DBT_USERS", "users");
 define("DBT_DOMAINS", "domains");
@@ -170,7 +162,6 @@ define("DBC_USERS_DOMAIN", "domain");
 define("DBC_USERS_PASSWORD", "password");
 //define("DBC_USERS_MAILBOXLIMIT", "mailbox_limit");
 
-
 // Domains table columns
 define("DBC_DOMAINS_ID", "id");
 define("DBC_DOMAINS_DOMAIN", "domain");
@@ -182,7 +173,8 @@ define("DBC_ALIASES_DESTINATION", "destination");
 //define("DBC_ALIASES_MULTI_SOURCE", "multi_source");
 ```
 
-### Mailbox limit
+### Mailbox limit (Optional)
+
 If you have a "mailbox_limit" column to limit the size of your users' mailboxes, just comment in the line
 
 ```php
@@ -192,7 +184,7 @@ define("DBC_USERS_MAILBOXLIMIT", "mailbox_limit");
 in your configuration. WebMUM will then show a new field "Mailbox limit" in the frontend.
 
 
-### Multiple source redirect support
+### Multiple source redirect support (Optional)
 
 As mailservers can only process a single source address for redirects the database table for aliases / redirects can only hold a single source address in a row.
 WebMum will, if you enabled the multiple source redirect support, do some magic so there is only a single address in a row even though multiple adresses where entered.
@@ -213,6 +205,26 @@ ALTER TABLE `aliases` ADD COLUMN `multi_source` VARCHAR(32) NULL DEFAULT NULL;
 
 WebMUM will then show a larger field for source addresses in the frontend and you can not list emails in source field.
 
+
+### Admin domain limits (Optional)
+
+If you share your mailserver with others, host their domains and they should be able to manage their domains, but not all domains on that mailserver then this is the right option for you. 
+You have to add that user to the `$admins` array in your configuration and enable admin domain limits be uncommenting the following line:
+
+```php
+define("ADMIN_DOMAIN_LIMITS_ENABLED", true); // (Optional)
+```
+
+also you have to make an entry in the `$adminDomainLimits` array, for example `peter@his.tld` should be able to manage his domains `his.tld` and `his-company.tld` then configure the following:
+
+```php
+$adminDomainLimits = array(
+    "peter@his.tld" => array("his.tld", "his-company.tld"),
+);
+```
+
+Admins that have been listed in `$adminDomainLimits` don't have access to the "Manage domains" pages, otherwise they could delete domains they are managing, but maybe someone else owns.
+
 ### Paths
 
 The `FRONTEND_BASE_PATH` is the URL your WebMUM installation is accessible from outside, this also includes subfolders if you installed it in a subfolder for that specific domain.
@@ -233,10 +245,6 @@ define("FRONTEND_BASE_PATH", "http://webmum.mydomain.tld/");
 Only users with one of the specified email addresses will have access to the administrator's dashboard and will be able to create, edit and delete users, domains and redirects.
 
 ```php
-/*
- * Admin e-mail address
- */
-
 $admins = array("admin@domain.tld");
 ```
 
@@ -245,10 +253,6 @@ Admin email accounts must exist in the virtual user database on your own server.
 ### Minimal required password length
 
 ```php
-/*
- * Minimal password length
- */
-
 define("MIN_PASS_LENGTH", 8);
 ```
 
@@ -322,15 +326,11 @@ Please check if your config.inc.php fits the current requirements by comparing y
 
 ## FAQ
 
-### Which password scheme does WebMUM use?
+### Which password scheme / encryption does WebMUM use?
 
 By default WebMUM uses `SHA-512` encryption for passwords. You can also between the alternatives `SHA-256` or `BLOWFISH` in the config.
 
 ```php
- /*
- * Select one of the following algorithms
- * SHA-512, SHA-256, BLOWFISH
- */
 define("PASS_HASH_SCHEMA", "SHA-512");
 ```
 

+ 15 - 0
config/config.inc.php.example

@@ -68,6 +68,21 @@ define("FRONTEND_BASE_PATH", "http://localhost/webmum/");
 $admins = array("admin@domain.tld");
 
 
+/**
+ * Limited admin domain access
+ *
+ * Unlisted admins have access to every domain, the admin is limited to listed domains only!
+ * Unlisted domains aren't accessible by that admin.
+ *
+ * Note that listed admins also cannot create new domains!
+ */
+
+//define("ADMIN_DOMAIN_LIMITS_ENABLED", true); // (Optional)
+
+$adminDomainLimits = array(
+// Example:
+// "low_rank_admin@domain.tld" => array("his-domain.tld", "shared-domain.tld"),
+);
 
 /*
  * Password

+ 28 - 34
include/css/style.css

@@ -5,16 +5,12 @@ body {
 	background-color: white;
 }
 
-/*
- * Main Layout
- */
 
 #header {
 	position: relative;
 	height: 50px;
 	width: 100%;
-	background-color: rgba(15, 15, 15, 1);
-	background: linear-gradient(rgba(63, 63, 63, 1), rgba(15, 15, 15, 1));
+	background: rgba(15, 15, 15, 1) linear-gradient(rgba(63, 63, 63, 1), rgba(15, 15, 15, 1));
 	color: white;
 	line-height: 50px;
 	box-sizing: border-box;
@@ -289,9 +285,33 @@ body {
 			color: blue;
 		}
 
-/*
- * Footer
- */
+
+	#content .notification {
+		height: auto;
+		width: 100%;
+		margin: 15px 0;
+		text-align: center;
+		border: 1px solid;
+		border-radius: 3px;
+		padding: 15px 10px;
+		box-sizing: border-box;
+	}
+
+	#content .notification.notification-fail {
+		background-color: #fcacac;
+		border-color: red;
+	}
+
+		#content .notification.notification-warning {
+			background-color: #fcf897;
+			border-color: #ffe600;
+		}
+
+		#content .notification.notification-success {
+			background-color: rgba(182, 255, 183, 1);
+			border-color: green;
+		}
+
 
 #footer {
 	position: relative;
@@ -306,30 +326,4 @@ body {
 #footer a {
 	text-decoration: none;
 	color: grey;
-}
-
-/*
- * Notifications
- */
-
-#content .notification {
-	height: auto;
-	width: 100%;
-	margin: 15px 0;
-	text-align: center;
-	border-style: solid;
-	border-width: 1px;
-	border-radius: 3px;
-	padding: 15px 10px;
-	box-sizing: border-box;
-}
-
-#content .notification.notification-fail {
-	background-color: #fcacac;
-	border-color: red;
-}
-
-#content .notification.notification-success {
-	background-color: rgba(182, 255, 183, 1);
-	border-color: green;
 }

+ 12 - 1
include/php/classes/Auth.php

@@ -116,6 +116,17 @@ class Auth
 	}
 
 
+	/**
+	 * @return void
+	 */
+	public static function logout()
+	{
+		unset($_SESSION[static::SESSION_IDENTIFIER]);
+
+		session_destroy();
+	}
+
+
 	/**
 	 * Check if current user has a certain role, but User::ROLE_ADMIN will have access to all
 	 *
@@ -144,7 +155,7 @@ class Auth
 	 *
 	 * @throws Exception Codes explained below
 	 * 		2: One password field is empty
-	 * 		3: Passwords are not equal
+	 * 		3: Passwords aren't equal
 	 * 		4: Passwort is too snort
 	 */
 	public static function validateNewPassword($password, $passwordRepeated)

+ 36 - 9
include/php/classes/Router.php

@@ -2,6 +2,10 @@
 
 class Router
 {
+	const METHOD_GET = 'GET';
+	const METHOD_POST = 'POST';
+
+
 	/**
 	 * @var array
 	 */
@@ -58,34 +62,34 @@ class Router
 
 	/**
 	 * @param string $pattern
-	 * @param callable|array|string $routeConfig
+	 * @param callable|string $routeConfig
 	 * @param array $permission
 	 */
 	public static function addGet($pattern, $routeConfig, $permission = null)
 	{
-		static::addRoute('GET', $pattern, $routeConfig, $permission);
+		static::addRoute(static::METHOD_GET, $pattern, $routeConfig, $permission);
 	}
 
 
 	/**
 	 * @param string $pattern
-	 * @param callable|array|string $routeConfig
+	 * @param callable|string $routeConfig
 	 * @param array $permission
 	 */
 	public static function addPost($pattern, $routeConfig, $permission = null)
 	{
-		static::addRoute('POST', $pattern, $routeConfig, $permission);
+		static::addRoute(static::METHOD_POST, $pattern, $routeConfig, $permission);
 	}
 
 
 	/**
 	 * @param string $pattern
-	 * @param callable|array|string $routeConfig
+	 * @param callable|string $routeConfig
 	 * @param array $permission
 	 */
 	public static function addMixed($pattern, $routeConfig, $permission = null)
 	{
-		static::addRoute(array('GET', 'POST'), $pattern, $routeConfig, $permission);
+		static::addRoute(array(static::METHOD_GET, static::METHOD_POST), $pattern, $routeConfig, $permission);
 	}
 
 
@@ -95,11 +99,11 @@ class Router
 	 *
 	 * @return string
 	 */
-	public static function execute($url, $method = 'GET')
+	public static function execute($url, $method = self::METHOD_GET)
 	{
 		$method = strtoupper($method);
 
-		if(!in_array($method, array('GET', 'POST')) && !isset(self::$routes[$method])){
+		if(!in_array($method, array(static::METHOD_GET, static::METHOD_POST)) && !isset(self::$routes[$method])){
 			return 'Unsupported HTTP method.';
 		}
 
@@ -125,11 +129,31 @@ class Router
 	{
 		return static::execute(
 			static::getCurrentUrlPath(),
-			isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET'
+			isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : static::METHOD_GET
 		);
 	}
 
 
+	/**
+	 * @param int $errorNumber
+	 * @return string|null
+	 */
+	public static function displayError($errorNumber)
+	{
+		$errorPage = isset(static::$errorPages[$errorNumber])
+			? static::loadAndBufferOutput(static::$errorPages[$errorNumber])
+			: '';
+
+		echo Router::loadAndBufferOutput(
+			'include/php/template/layout.php',
+			array(
+				'content' => $errorPage,
+			)
+		);
+		exit;
+	}
+
+
 	/**
 	 * @param bool $removeGetParameters
 	 *
@@ -169,6 +193,9 @@ class Router
 				return static::loadAndBufferOutput($config);
 			}
 		}
+		elseif(is_callable($config) && $config instanceof Closure){
+			return $config();
+		}
 
 		return static::loadAndBufferOutput(static::$errorPages[404]);
 	}

+ 1 - 1
include/php/global.inc.php

@@ -63,7 +63,7 @@ function writeLog($text)
 			fclose($logfile);
 		}
 		else{
-			die("Directory \"".WRITE_LOG_PATH."\" is not writable");
+			die("Directory \"".WRITE_LOG_PATH."\" isn't writable");
 		}
 	}
 }

+ 58 - 28
include/php/models/AbstractRedirect.php

@@ -2,6 +2,7 @@
 
 abstract class AbstractRedirect extends AbstractModel
 {
+	use DomainLimitTrait;
 
 	/**
 	 * @inheritdoc
@@ -180,6 +181,28 @@ abstract class AbstractRedirect extends AbstractModel
 	}
 
 
+	/**
+	 * @return array
+	 */
+	protected function getDomain()
+	{
+		$sources = $this->getSource();
+		if(is_string($sources)){
+			$sources = array($sources);
+		}
+
+		$domains = array();
+		foreach($sources as $source){
+			$emailParts = explode('@', $source);
+			if(count($emailParts) === 2) {
+				$domains[] = $emailParts[1];
+			}
+		}
+
+		return array_unique($domains);
+	}
+
+
 	/**
 	 * @return ModelCollection
 	 */
@@ -239,37 +262,34 @@ abstract class AbstractRedirect extends AbstractModel
 	}
 
 
-	private static $baseSqlQueryCache = null;
-
+	/**
+	 * @return string
+	 */
 	private static function generateRedirectBaseQuery()
 	{
-		if(is_null(static::$baseSqlQueryCache)){
-			if(defined('DBC_ALIASES_MULTI_SOURCE')){
-				static::$baseSqlQueryCache = "SELECT r.* FROM (
-		SELECT
-			GROUP_CONCAT(g.`".static::$idAttribute."` ORDER BY g.`".static::$idAttribute."` SEPARATOR ',') AS `".static::$idAttribute."`,
-			GROUP_CONCAT(g.`".DBC_ALIASES_SOURCE."` SEPARATOR ',') AS `".DBC_ALIASES_SOURCE."`,
-			g.`".DBC_ALIASES_DESTINATION."`,
-			g.`".DBC_ALIASES_MULTI_SOURCE."`
-		FROM `".static::$table."` AS g
-		WHERE g.`".DBC_ALIASES_MULTI_SOURCE."` IS NOT NULL
-		GROUP BY g.`".DBC_ALIASES_MULTI_SOURCE."`
-	UNION
-		SELECT
-			s.`".DBC_ALIASES_ID."`,
-			s.`".DBC_ALIASES_SOURCE."`,
-			s.`".DBC_ALIASES_DESTINATION."`,
-			s.`".DBC_ALIASES_MULTI_SOURCE."`
-		FROM `".static::$table."` AS s
-		WHERE s.`".DBC_ALIASES_MULTI_SOURCE."` IS NULL
-	) AS r";
-			}
-			else{
-				static::$baseSqlQueryCache = "SELECT * FROM `".static::$table."`";
-			}
+		if(defined('DBC_ALIASES_MULTI_SOURCE')){
+			return "SELECT r.* FROM (
+	SELECT
+		GROUP_CONCAT(g.`".static::$idAttribute."` ORDER BY g.`".static::$idAttribute."` SEPARATOR ',') AS `".static::$idAttribute."`,
+		GROUP_CONCAT(g.`".DBC_ALIASES_SOURCE."` SEPARATOR ',') AS `".DBC_ALIASES_SOURCE."`,
+		g.`".DBC_ALIASES_DESTINATION."`,
+		g.`".DBC_ALIASES_MULTI_SOURCE."`
+	FROM `".static::$table."` AS g
+	WHERE g.`".DBC_ALIASES_MULTI_SOURCE."` IS NOT NULL
+	GROUP BY g.`".DBC_ALIASES_MULTI_SOURCE."`
+UNION
+	SELECT
+		s.`".DBC_ALIASES_ID."`,
+		s.`".DBC_ALIASES_SOURCE."`,
+		s.`".DBC_ALIASES_DESTINATION."`,
+		s.`".DBC_ALIASES_MULTI_SOURCE."`
+	FROM `".static::$table."` AS s
+	WHERE s.`".DBC_ALIASES_MULTI_SOURCE."` IS NULL
+) AS r";
+		}
+		else{
+			return "SELECT * FROM `".static::$table."`";
 		}
-
-		return static::$baseSqlQueryCache;
 	}
 
 
@@ -308,4 +328,14 @@ abstract class AbstractRedirect extends AbstractModel
 		return static::findMultiWhereFirst(array(static::$idAttribute, $id));
 	}
 
+
+	/**
+	 * @param array|User|null $limitedBy
+	 *
+	 * @return ModelCollection|static[]
+	 */
+	public static function getMultiByLimitedDomains($limitedBy = null)
+	{
+		return static::filterModelCollectionByLimitedDomains(static::findMultiAll(), $limitedBy);
+	}
 }

+ 2 - 0
include/php/models/Domain.php

@@ -2,6 +2,7 @@
 
 class Domain extends AbstractModel
 {
+	use DomainLimitTrait;
 
 	/**
 	 * @inheritdoc
@@ -87,4 +88,5 @@ class Domain extends AbstractModel
 
 		return $result->fetch_array(MYSQLI_NUM)[0];
 	}
+
 }

+ 68 - 0
include/php/models/DomainLimitTrait.php

@@ -0,0 +1,68 @@
+<?php
+
+trait DomainLimitTrait
+{
+	/**
+	 * @param array|User|null $limitedBy
+	 *
+	 * @return bool
+	 */
+	public function isInLimitedDomains($limitedBy = null)
+	{
+		if(!defined('ADMIN_DOMAIN_LIMITS_ENABLED')) {
+			return true;
+		}
+		if(is_null($limitedBy)){
+			return static::isInLimitedDomains(Auth::getUser());
+		}
+		elseif($limitedBy instanceof User) {
+			/** @var User $limitedBy */
+			return $limitedBy->isDomainLimited() && static::isInLimitedDomains($limitedBy->getDomainLimits());
+		}
+
+		if(!is_array($limitedBy)){
+			throw new InvalidArgumentException;
+		}
+
+		/** @var string|array|string[] $domain */
+		$domain = $this->getDomain();
+
+		if(is_string($domain)) {
+			return in_array($domain, $limitedBy);
+		}
+
+		foreach($domain as $d){
+			if(!in_array($d, $limitedBy)) {
+				return false;
+			}
+		}
+
+		return true;
+	}
+
+
+	/**
+	 * @param ModelCollection|static[] $collection
+	 * @param array|User|null $limitedBy
+	 *
+	 * @return ModelCollection|static[]
+	 */
+	protected static function filterModelCollectionByLimitedDomains($collection, $limitedBy = null)
+	{
+		return $collection->searchAll(function($model) use ($limitedBy){
+			/** @var static $model */
+			return $model->isInLimitedDomains($limitedBy);
+		});
+	}
+
+
+	/**
+	 * @param array|User|null $limitedBy
+	 *
+	 * @return ModelCollection|static[]
+	 */
+	public static function getByLimitedDomains($limitedBy = null)
+	{
+		return static::filterModelCollectionByLimitedDomains(static::findAll(), $limitedBy);
+	}
+}

+ 22 - 1
include/php/models/ModelCollection.php

@@ -43,7 +43,7 @@ class ModelCollection implements Iterator, ArrayAccess, Countable
 
 	/**
 	 * Adds a model to the collection,
-	 * but will not replace if it exists with that key
+	 * but won't replace if it exists with that key
 	 *
 	 * @param AbstractModel $model
 	 * @param mixed|null $key
@@ -166,6 +166,27 @@ class ModelCollection implements Iterator, ArrayAccess, Countable
 	}
 
 
+	/**
+	 * Convert models to an array of strings
+	 *
+	 * @param callable $callable Gives back a string for a model
+	 *
+	 * @return array|string[]
+	 */
+	public function toStringArray($callable)
+	{
+		$strings = array();
+
+		if(is_callable($callable)){
+			foreach($this->models as $model){
+				$strings[] = $callable($model);
+			}
+		}
+
+		return $strings;
+	}
+
+
 	/**
 	 * @inheritdoc
 	 */

+ 36 - 0
include/php/models/User.php

@@ -2,6 +2,7 @@
 
 class User extends AbstractModel
 {
+	use DomainLimitTrait;
 
 	/**
 	 * @inheritdoc
@@ -196,6 +197,41 @@ class User extends AbstractModel
 	}
 
 
+	/**
+	 * Is user limited by domain limits?
+	 *
+	 * @return bool
+	 */
+	public function isDomainLimited()
+	{
+		global $adminDomainLimits;
+
+		return defined('ADMIN_DOMAIN_LIMITS_ENABLED')
+		&& isset($adminDomainLimits) && isset($adminDomainLimits[$this->getEmail()]);
+	}
+
+
+	/**
+	 * Get domain limits, returns an empty array if user has no limits or ADMIN_DOMAIN_LIMITS_ENABLED is disabled
+	 *
+	 * @return array
+	 */
+	public function getDomainLimits()
+	{
+		global $adminDomainLimits;
+
+		if($this->isDomainLimited()){
+			if (!is_array($adminDomainLimits[$this->getEmail()])) {
+				throw new InvalidArgumentException('Config value of admin domain limits for email "'.$this->getEmail().'" needs to be of type array.');
+			}
+
+			return $adminDomainLimits[$this->getEmail()];
+		}
+
+		return array();
+	}
+
+
 	/**
 	 * @return AbstractRedirect
 	 */

+ 1 - 1
include/php/pages/404.php

@@ -1,5 +1,5 @@
 <h1>This page does not exist.</h1>
 
 <p>
-	Sorry, the page you requested could not be found.
+	Sorry, the page you requested couldn't be found.
 </p>

+ 6 - 2
include/php/pages/admin/createdomain.php

@@ -1,4 +1,8 @@
-<?php 
+<?php
+
+if(Auth::getUser()->isDomainLimited()){
+	Router::displayError(403);
+}
 
 if(isset($_POST['domain'])){
 	$inputDomain = $_POST['domain'];
@@ -23,7 +27,7 @@ if(isset($_POST['domain'])){
 		}
 	}
 	else{
-		add_message("fail", "Empty domain could not be created.");
+		add_message("fail", "Empty domain couldn't be created.");
 	}
 }
 

+ 11 - 3
include/php/pages/admin/deletedomain.php

@@ -1,5 +1,9 @@
 <?php
 
+if(Auth::getUser()->isDomainLimited()){
+	Router::displayError(403);
+}
+
 if(!isset($_GET['id'])){
 	// Domain id not set, redirect to overview
 	redirect("admin/listdomains");
@@ -11,10 +15,14 @@ $id = $_GET['id'];
 $domain = Domain::find($id);
 
 if(is_null($domain)){
-	// Domain does not exist, redirect to overview
+	// Domain doesn't exist, redirect to overview
 	redirect("admin/listdomains");
 }
 
+if(!$domain->isInLimitedDomains()){
+	redirect("admin/listdomains/?missing-permission=1");
+}
+
 // Delete domain
 if(isset($_POST['confirm'])){
 	$confirm = $_POST['confirm'];
@@ -64,14 +72,14 @@ if(isset($_POST['confirm'])){
 	<a class="button" href="<?php echo url('admin/listdomains'); ?>">&#10092; Back to domain list</a>
 </div>
 
-<form class="form" action="" method="post">
+<form class="form" action="" method="post" autocomplete="off">
 	<div class="input-group">
 		<label>All mailboxes matching the domain will be deleted from the user database!</label>
 		<div class="input-info">Mailbox directories in the filesystem won't be affected.</div>
 	</div>
 
 	<div class="input-group">
-		<label>Do you realy want to delete this domain?</label>
+		<label for="confirm">Do you realy want to delete this domain?</label>
 		<div class="input">
 			<select name="confirm" autofocus required>
 				<option value="no">No!</option>

+ 7 - 3
include/php/pages/admin/deleteredirect.php

@@ -11,10 +11,14 @@ $id = $_GET['id'];
 $redirect = AbstractRedirect::findMulti($id);
 
 if(is_null($redirect)){
-	// Redirect does not exist, redirect to overview
+	// Redirect doesn't exist, redirect to overview
 	redirect("admin/listredirects");
 }
 
+if(!$redirect->isInLimitedDomains()){
+	redirect("admin/listredirects/?missing-permission=1");
+}
+
 if(isset($_POST['confirm'])){
 	$confirm = $_POST['confirm'];
 
@@ -53,7 +57,7 @@ else{
 		<a class="button" href="<?php echo url('admin/listredirects'); ?>">&#10092; Back to redirect list</a>
 	</div>
 
-	<form class="form" action="" method="post">
+	<form class="form" action="" method="post" autocomplete="off">
 		<div class="input-group">
 			<label>Source</label>
 			<div class="input-info"><?php echo formatEmails($redirect->getSource(), str_replace(PHP_EOL, '<br>', FRONTEND_EMAIL_SEPARATOR_TEXT)); ?></div>
@@ -65,7 +69,7 @@ else{
 		</div>
 
 		<div class="input-group">
-			<label>Do you realy want to delete this redirect?</label>
+			<label for="confirm">Do you realy want to delete this redirect?</label>
 			<div class="input">
 				<select name="confirm" autofocus required>
 					<option value="no">No!</option>

+ 14 - 8
include/php/pages/admin/deleteuser.php

@@ -11,10 +11,14 @@ $id = $_GET['id'];
 $user = User::find($id);
 
 if(is_null($user)){
-	// User does not exist, redirect to overview
+	// User doesn't exist, redirect to overview
 	redirect("admin/listusers");
 }
 
+if(!$user->isInLimitedDomains()){
+	redirect("admin/listusers/?missing-permission=1");
+}
+
 // Delete user
 if(isset($_POST['confirm'])){
 	$confirm = $_POST['confirm'];
@@ -25,7 +29,7 @@ if(isset($_POST['confirm'])){
 
 			$user->delete();
 
-			// Delete user successfull, redirect to overview
+			// Delete user successful, redirect to overview
 			redirect("admin/listusers/?deleted=1");
 		}
 		else{
@@ -47,19 +51,21 @@ if(isset($_POST['confirm'])){
 	<a class="button" href="<?php echo url('admin/listusers'); ?>">&#10092; Back to user list</a>
 </div>
 
-<form class="form" action="" method="post">
+<form class="form" action="" method="post" autocomplete="off">
 	<div class="input-group">
 		<label>The user's mailbox will be deleted from the database only!</label>
 		<div class="input-info">The mailbox in the filesystem won't be affected.</div>
 	</div>
 
 	<div class="input-group">
-		<label>Do you realy want to delete this user?</label>
+		<label for="confirm">Do you realy want to delete this user?</label>
 		<div class="input">
-			<select name="confirm" autofocus required>
-				<option value="no">No!</option>
-				<option value="yes">Yes!</option>
-			</select>
+			<label>
+				<select name="confirm" autofocus required>
+					<option value="no">No!</option>
+					<option value="yes">Yes!</option>
+				</select>
+			</label>
 		</div>
 	</div>
 

+ 67 - 38
include/php/pages/admin/editredirect.php

@@ -10,9 +10,13 @@ if(isset($_GET['id'])){
 	$redirect = AbstractRedirect::findMulti($id);
 
 	if(is_null($redirect)){
-		// Redirect does not exist, redirect to overview
+		// Redirect doesn't exist, redirect to overview
 		redirect("admin/listredirects");
 	}
+
+	if(!$redirect->isInLimitedDomains()){
+		redirect("admin/listredirects/?missing-permission=1");
+	}
 }
 
 if(isset($_POST['savemode'])){
@@ -24,16 +28,16 @@ if(isset($_POST['savemode'])){
 	// validate emails
 	$emailErrors = array();
 
-	// basic email validation is not working 100% correct though
+	// basic email validation isn't working 100% correct though
 	foreach(array_merge($inputSources, $inputDestinations) as $email){
 		if(!filter_var($email, FILTER_VALIDATE_EMAIL)){
-			$emailErrors[$email] = "Address \"{$email}\" is not a valid email address.";
+			$emailErrors[$email] = "Address \"{$email}\" isn't a valid email address.";
 		}
 	}
 
 	// validate source emails are on domains
 	if(defined('VALIDATE_ALIASES_SOURCE_DOMAIN_ENABLED')){
-		$domains = Domain::findAll();
+		$domains = Domain::getByLimitedDomains();
 
 		foreach($inputSources as $email){
 			if(isset($emailErrors[$email])){
@@ -49,7 +53,7 @@ if(isset($_POST['savemode'])){
 			);
 
 			if(is_null($searchResult)){
-				$emailErrors[$email] = "Domain of source address \"{$email}\" not in domains.";
+				$emailErrors[$email] = "Domain of source address \"{$email}\" not in your domains.";
 			}
 		}
 	}
@@ -80,21 +84,21 @@ if(isset($_POST['savemode'])){
 				}
 
 				$emailsToCheck = $inputSources;
-				foreach($existingRedirectsToEdit as $r) {
+				foreach($existingRedirectsToEdit as $r){
 					$key = array_search($r->getSource(), $emailsToCheck);
-					if($key !== false) {
+					if($key !== false){
 						unset($emailsToCheck[$key]);
 					}
 				}
 
-				if (count($emailsToCheck) > 0) {
+				if(count($emailsToCheck) > 0){
 					$existingRedirectsOther = AbstractRedirect::findWhere(
 						array(
 							array(DBC_ALIASES_SOURCE, 'IN', $emailsToCheck)
 						)
 					);
 				}
-				else {
+				else{
 					$existingRedirectsOther = null;
 				}
 
@@ -157,7 +161,7 @@ if(isset($_POST['savemode'])){
 				}
 			}
 			else{
-				add_message("fail", "Redirect could not be edited. Fill out all fields.");
+				add_message("fail", "Redirect couldn't be edited. Fill out all fields.");
 			}
 		}
 
@@ -205,7 +209,7 @@ if(isset($_POST['savemode'])){
 				}
 			}
 			else{
-				add_message("fail", "Redirect could not be created. Fill out all fields.");
+				add_message("fail", "Redirect couldn't be created. Fill out all fields.");
 			}
 		}
 	}
@@ -217,13 +221,15 @@ $mode = "create";
 if(isset($_GET['id'])){
 	$mode = "edit";
 }
+
+$domains = Domain::getByLimitedDomains();
 ?>
 
-<h1><?php echo ($mode === "create") ? 'Create' : 'Edit'; ?> Redirect</h1>
+	<h1><?php echo ($mode === "create") ? 'Create' : 'Edit'; ?> Redirect</h1>
 
-<div class="buttons">
-	<a class="button" href="<?php echo url('admin/listredirects'); ?>">&#10092; Back to redirects list</a>
-</div>
+	<div class="buttons">
+		<a class="button" href="<?php echo url('admin/listredirects'); ?>">&#10092; Back to redirects list</a>
+	</div>
 
 <div class="notification">
 	Please note that mailservers will prefer to deliver mails to redirects over mailboxes.<br>
@@ -232,32 +238,55 @@ if(isset($_GET['id'])){
 
 <?php output_messages(); ?>
 
-<form class="form" action="" method="post" autocomplete="off">
-	<input name="savemode" type="hidden" value="<?php echo $mode; ?>"/>
-
-	<div class="input-group">
-		<div class="input-info">Enter single or multiple addresses separated by comma, semicolon or newline.</div>
+<?php if(defined('VALIDATE_ALIASES_SOURCE_DOMAIN_ENABLED') && Auth::getUser()->isDomainLimited() && $domains->count() === 0): ?>
+	<div class="notification notification-fail">
+		You are listed for limited access to domains, but it seems there are no domains listed you can access.
 	</div>
+<?php else: ?>
+	<form class="form" action="" method="post" autocomplete="off">
+		<input name="savemode" type="hidden" value="<?php echo $mode; ?>"/>
 
-	<div class="input-group">
-		<label for="source">Source</label>
-		<div class="input">
-			<?php if(defined('DBC_ALIASES_MULTI_SOURCE')): ?>
-				<textarea name="source" placeholder="Source" required autofocus><?php echo formatEmails(isset($_POST['source']) ? strip_tags($_POST['source']) : (is_null($redirect) ? '' : $redirect->getSource()), FRONTEND_EMAIL_SEPARATOR_FORM); ?></textarea>
-			<?php else: ?>
-				<input type="text" name="source" placeholder="Source (single address)" required autofocus value="<?php echo strip_tags(formatEmails(isset($_POST['source']) ? $_POST['source'] : (is_null($redirect) ? '' : $redirect->getSource()), FRONTEND_EMAIL_SEPARATOR_FORM)); ?>"/>
-			<?php endif; ?>
+		<div class="input-group">
+			<div class="input-info">Enter single or multiple addresses separated by comma, semicolon or newline.</div>
 		</div>
-	</div>
 
-	<div class="input-group">
-		<label for="destination">Destination</label>
-		<div class="input">
-			<textarea name="destination" placeholder="Destination" required><?php echo formatEmails(isset($_POST['destination']) ? strip_tags($_POST['destination']) : (is_null($redirect) ? '' : $redirect->getDestination()), FRONTEND_EMAIL_SEPARATOR_FORM); ?></textarea>
+		<div class="input-group">
+			<label for="source">Source</label>
+			<div class="input-info">
+				<?php if($domains->count() > 0): ?>
+					<?php if(Auth::getUser()->isDomainLimited()): ?>
+						You can create redirects for source addresses from these domains only:
+					<?php else: ?>
+						You can create redirects for every domain you want,<br>
+						but here's a list of domains managed by WebMUM:
+					<?php endif; ?>
+					<ul>
+						<?php foreach($domains as $domain): /** @var Domain $domain */ ?>
+							<li><?php echo $domain->getDomain(); ?></li>
+						<?php endforeach; ?>
+					</ul>
+				<?php else: ?>
+					There are no domains managed by in WebMUM yet.
+				<?php endif; ?>
+			</div>
+			<div class="input">
+				<?php if(defined('DBC_ALIASES_MULTI_SOURCE')): ?>
+					<textarea name="source" placeholder="Source" required autofocus><?php echo formatEmails(isset($_POST['source']) ? strip_tags($_POST['source']) : (is_null($redirect) ? '' : $redirect->getSource()), FRONTEND_EMAIL_SEPARATOR_FORM); ?></textarea>
+				<?php else: ?>
+					<input type="text" name="source" placeholder="Source (single address)" required autofocus value="<?php echo strip_tags(formatEmails(isset($_POST['source']) ? $_POST['source'] : (is_null($redirect) ? '' : $redirect->getSource()), FRONTEND_EMAIL_SEPARATOR_FORM)); ?>"/>
+				<?php endif; ?>
+			</div>
 		</div>
-	</div>
 
-	<div class="buttons">
-		<button type="submit" class="button button-primary">Save settings</button>
-	</div>
-</form>
+		<div class="input-group">
+			<label for="destination">Destination</label>
+			<div class="input">
+				<textarea name="destination" placeholder="Destination" required><?php echo formatEmails(isset($_POST['destination']) ? strip_tags($_POST['destination']) : (is_null($redirect) ? '' : $redirect->getDestination()), FRONTEND_EMAIL_SEPARATOR_FORM); ?></textarea>
+			</div>
+		</div>
+
+		<div class="buttons">
+			<button type="submit" class="button button-primary">Save settings</button>
+		</div>
+	</form>
+<?php endif; ?>

+ 93 - 71
include/php/pages/admin/edituser.php

@@ -1,4 +1,4 @@
-<?php 
+<?php
 
 $mailboxLimitDefault = User::getMailboxLimitDefault();
 
@@ -33,10 +33,14 @@ if(!is_null($saveMode)){
 		$userToEdit = User::find($inputId);
 
 		if(is_null($userToEdit)){
-			// User does not exist, redirect to overview
+			// User doesn't exist, redirect to overview
 			redirect("admin/listusers");
 		}
 
+		if(!$userToEdit->isInLimitedDomains()){
+			redirect("admin/listusers/?missing-permission=1");
+		}
+
 		if(defined('DBC_USERS_MAILBOXLIMIT') && !is_null($inputMailboxLimit)){
 			$userToEdit->setMailboxLimit($inputMailboxLimit);
 		}
@@ -73,43 +77,57 @@ if(!is_null($saveMode)){
 			&& (!empty($inputPassword) || !empty($inputPasswordRepeated))
 		){
 
-			/** @var User $user */
-			$user = User::findWhereFirst(
-				array(
-					array(DBC_USERS_USERNAME, $inputUsername),
-					array(DBC_USERS_DOMAIN, $inputDomain),
-				)
+			/** @var Domain $selectedDomain */
+			$selectedDomain = Domain::findWhereFirst(
+				array(DBC_DOMAINS_DOMAIN, $inputDomain)
 			);
 
-			// Check if user already exists
-			if(is_null($user)){
-				try{
-					// Check password then go on an insert user first
-					Auth::validateNewPassword($inputPassword, $inputPasswordRepeated);
-
+			if(!is_null($selectedDomain)){
 
-					$data = array(
-						DBC_USERS_USERNAME => $inputUsername,
-						DBC_USERS_DOMAIN => $inputDomain,
-						DBC_USERS_PASSWORD => Auth::generatePasswordHash($inputPassword)
-					);
+				if(!$selectedDomain->isInLimitedDomains()){
+					redirect("admin/listusers/?missing-permission=1");
+				}
 
-					if(defined('DBC_USERS_MAILBOXLIMIT') && !is_null($inputMailboxLimit)){
-						$data[DBC_USERS_MAILBOXLIMIT] = $inputMailboxLimit;
+				/** @var User $user */
+				$user = User::findWhereFirst(
+					array(
+						array(DBC_USERS_USERNAME, $inputUsername),
+						array(DBC_USERS_DOMAIN, $selectedDomain->getDomain()),
+					)
+				);
+
+				// Check if user already exists
+				if(is_null($user)){
+					try{
+						// Check password then go on an insert user first
+						Auth::validateNewPassword($inputPassword, $inputPasswordRepeated);
+
+						$data = array(
+							DBC_USERS_USERNAME => $inputUsername,
+							DBC_USERS_DOMAIN => $selectedDomain->getDomain(),
+							DBC_USERS_PASSWORD => Auth::generatePasswordHash($inputPassword)
+						);
+
+						if(defined('DBC_USERS_MAILBOXLIMIT') && !is_null($inputMailboxLimit)){
+							$data[DBC_USERS_MAILBOXLIMIT] = $inputMailboxLimit;
+						}
+
+						/** @var User $user */
+						$user = User::createAndSave($data);
+
+						// Redirect user to user list
+						redirect("admin/listusers/?created=1");
+					}
+					catch(Exception $passwordInvalidException){
+						add_message("fail", $passwordInvalidException->getMessage());
 					}
-
-					/** @var User $user */
-					$user = User::createAndSave($data);
-
-					// Redirect user to user list
-					redirect("admin/listusers/?created=1");
 				}
-				catch(Exception $passwordInvalidException){
-					add_message("fail", $passwordInvalidException->getMessage());
+				else{
+					add_message("fail", "User already exists in database.");
 				}
 			}
 			else{
-				add_message("fail", "User already exists in database.");
+				add_message("fail", "The selected domain doesn't exist.");
 			}
 		}
 		else{
@@ -130,13 +148,14 @@ if(isset($_GET['id'])){
 	$user = User::find($id);
 
 	if(is_null($user)){
-		// User does not exist, redirect to overview
+		// User doesn't exist, redirect to overview
 		redirect("admin/listusers");
 	}
-}
 
-/** @var ModelCollection $domains */
-$domains = Domain::findAll();
+	if(!$user->isInLimitedDomains()){
+		redirect("admin/listusers/?missing-permission=1");
+	}
+}
 
 ?>
 
@@ -146,41 +165,44 @@ $domains = Domain::findAll();
 	<a class="button" href="<?php echo url('admin/listusers'); ?>">&#10092; Back to user list</a>
 </div>
 
-<form class="form" action="" method="post">
+<form class="form" action="" method="post" autocomplete="off">
 	<input type="hidden" name="savemode" value="<?php echo $mode; ?>"/>
-<?php if($mode === "edit"): ?>
-	<input type="hidden" name="id" value="<?php echo $user->getId(); ?>"/>
-<?php endif; ?>
+	<?php if($mode === "edit"): ?>
+		<input type="hidden" name="id" value="<?php echo $user->getId(); ?>"/>
+	<?php endif; ?>
 
 	<?php output_messages(); ?>
 
-<?php if($mode === "edit"): ?>
-	<div class="input-group">
-		<label>Username and Group cannot be edited</label>
-		<div class="input-info">To rename or move a mailbox, you have to move in the filesystem first and create a new user here after.</div>
-	</div>
-<?php else: ?>
-	<div class="input-group">
-		<label for="username">Username</label>
-		<div class="input">
-			<input type="text" name="username" placeholder="Username" value="<?php echo isset($_POST['username']) ? strip_tags($_POST['username']) : (isset($user) ? $user->getUsername() : ''); ?>" autofocus required/>
+	<?php if($mode === "edit"): ?>
+		<div class="input-group">
+			<label>Username and Group cannot be edited</label>
+			<div class="input-info">To rename or move a mailbox, you have to move in the filesystem first and create a new user here after.</div>
+		</div>
+	<?php else:
+		/** @var ModelCollection $domains */
+		$domains = Domain::getByLimitedDomains();
+		?>
+		<div class="input-group">
+			<label for="username">Username</label>
+			<div class="input">
+				<input type="text" name="username" placeholder="Username" value="<?php echo isset($_POST['username']) ? strip_tags($_POST['username']) : ''; ?>" autofocus required/>
+			</div>
 		</div>
-	</div>
 
-	<div class="input-group">
-		<label for="domain">Domain</label>
-		<div class="input">
-			<select name="domain" required>
-				<option value="">-- Select a domain --</option>
-			<?php foreach($domains as $domain): /** @var Domain $domain */ ?>
-				<option value="<?php echo $domain->getDomain(); ?>" <?php echo ((isset($_POST['domain']) && $_POST['domain'] === $domain->getDomain()) || (isset($user) && $user->getDomain() == $domain->getDomain())) ? 'selected' : ''; ?>>
-					<?php echo $domain->getDomain(); ?>
-				</option>
-			<?php endforeach; ?>
-			</select>
+		<div class="input-group">
+			<label for="domain">Domain</label>
+			<div class="input">
+				<select name="domain" required>
+					<option value="">-- Select a domain --</option>
+					<?php foreach($domains as $domain): /** @var Domain $domain */ ?>
+						<option value="<?php echo $domain->getDomain(); ?>" <?php echo ((isset($_POST['domain']) && $_POST['domain'] === $domain->getDomain()) || ($mode === "create" && $domains->count() === 1)) ? 'selected' : ''; ?>>
+							<?php echo $domain->getDomain(); ?>
+						</option>
+					<?php endforeach; ?>
+				</select>
+			</div>
 		</div>
-	</div>
-<?php endif; ?>
+	<?php endif; ?>
 
 	<div class="input-group">
 		<label for="password">Password</label>
@@ -194,16 +216,16 @@ $domains = Domain::findAll();
 		</div>
 	</div>
 
-<?php if(defined('DBC_USERS_MAILBOXLIMIT')): ?>
-	<div class="input-group">
-		<label>Mailbox limit</label>
-		<div class="input-info">The default limit is <?php echo $mailboxLimitDefault; ?> MB. Limit set to 0 means no limit in size.</div>
-		<div class="input input-labeled input-labeled-right">
-			<input name="mailbox_limit" type="number" value="<?php echo isset($_POST['mailbox_limit']) ? strip_tags($_POST['mailbox_limit']) : ((isset($user) && defined('DBC_USERS_MAILBOXLIMIT')) ? $user->getMailboxLimit() : $mailboxLimitDefault); ?>" placeholder="Mailbox limit in MB" min="0" required/>
-			<span class="input-label">MB</span>
+	<?php if(defined('DBC_USERS_MAILBOXLIMIT')): ?>
+		<div class="input-group">
+			<label>Mailbox limit</label>
+			<div class="input-info">The default limit is <?php echo $mailboxLimitDefault; ?> MB. Limit set to 0 means no limit in size.</div>
+			<div class="input input-labeled input-labeled-right">
+				<input name="mailbox_limit" type="number" value="<?php echo isset($_POST['mailbox_limit']) ? strip_tags($_POST['mailbox_limit']) : ((isset($user) && defined('DBC_USERS_MAILBOXLIMIT')) ? $user->getMailboxLimit() : $mailboxLimitDefault); ?>" placeholder="Mailbox limit in MB" min="0" required/>
+				<span class="input-label">MB</span>
+			</div>
 		</div>
-	</div>
-<?php endif; ?>
+	<?php endif; ?>
 
 	<div class="buttons">
 		<button type="submit" class="button button-primary">Save settings</button>

+ 40 - 26
include/php/pages/admin/listdomains.php

@@ -1,4 +1,9 @@
 <?php
+
+if(Auth::getUser()->isDomainLimited()){
+	Router::displayError(403);
+}
+
 if(isset($_GET['deleted']) && $_GET['deleted'] == "1"){
 	add_message("success", "Domain deleted successfully.");
 }
@@ -6,47 +11,56 @@ else if(isset($_GET['created']) && $_GET['created'] == "1"){
 	add_message("success", "Domain created successfully.");
 }
 else if(isset($_GET['adm_del']) && $_GET['adm_del'] == "1"){
-	add_message("fail", "Domain could not be deleted because admin account would be affected.");
+	add_message("fail", "Domain couldn't be deleted because admin account would be affected.");
+}
+else if(isset($_GET['missing-permission']) && $_GET['missing-permission'] == "1"){
+	add_message("fail", "You don't have the permission to delete that domain.");
 }
 
 $domains = Domain::findAll();
 
 ?>
 
-<h1>Domains</h1>
+	<h1>Domains</h1>
 
-<div class="buttons">
-	<a class="button" href="<?php echo url('admin/createdomain'); ?>">Create new domain</a>
-</div>
+<?php if(!Auth::getUser()->isDomainLimited()): ?>
+	<div class="buttons">
+		<a class="button" href="<?php echo url('admin/createdomain'); ?>">Create new domain</a>
+	</div>
+<?php endif; ?>
 
 <?php output_messages(); ?>
 
-<table class="table">
-	<thead>
+<?php if($domains->count() > 0): ?>
+	<table class="table">
+		<thead>
 		<tr>
 			<th>Domain</th>
 			<th>User count</th>
 			<th>Redirect count</th>
 			<th></th>
 		<tr>
-	</thead>
-	<tbody>
-	<?php foreach($domains as $domain): /** @var Domain $domain */ ?>
+		</thead>
+		<tbody>
+		<?php foreach($domains as $domain): /** @var Domain $domain */ ?>
+			<tr>
+				<td><?php echo $domain->getDomain(); ?></td>
+				<td><?php echo $domain->countUsers(); ?></td>
+				<td><?php echo $domain->countRedirects(); ?></td>
+				<td>
+					<a href="<?php echo url('admin/deletedomain/?id='.$domain->getId()); ?>">[Delete]</a>
+				</td>
+			</tr>
+		<?php endforeach; ?>
+		</tbody>
+		<tfoot>
 		<tr>
-			<td><?php echo $domain->getDomain(); ?></td>
-			<td><?php echo $domain->countUsers(); ?></td>
-			<td><?php echo $domain->countRedirects(); ?></td>
-			<td>
-				<a href="<?php echo url('admin/deletedomain/?id='.$domain->getId()); ?>">[Delete]</a>
-			</td>
+			<th><?php echo ($domains->count() > 1) ? $domains->count().' Domains' : '1 Domain'; ?></th>
 		</tr>
-	<?php endforeach; ?>
-	</tbody>
-<?php if ($domains->count() > 0): ?>
-	<tfoot>
-	<tr>
-		<th><?php echo $domains->count();?> Domains</th>
-	</tr>
-	</tfoot>
-<?php endif; ?>
-</table>
+		</tfoot>
+	</table>
+<?php else: ?>
+	<div class="notification notification-warning">
+		There are currently no domains created you can manage.
+	</div>
+<?php endif; ?>

+ 47 - 34
include/php/pages/admin/listredirects.php

@@ -9,52 +9,65 @@ else if(isset($_GET['created']) && $_GET['created'] == "1"){
 else if(isset($_GET['edited']) && $_GET['edited'] == "1"){
 	add_message("success", "Redirect edited successfully.");
 }
+else if(isset($_GET['missing-permission']) && $_GET['missing-permission'] == "1"){
+	add_message("fail", "You don't have the permission to edit/delete redirects of that domain.");
+}
 
-$redirects = AbstractRedirect::findMultiAll();
+$redirects = AbstractRedirect::getMultiByLimitedDomains();
 
 ?>
 
-<h1>Redirects</h1>
+	<h1>Redirects</h1>
 
-<?php output_messages(); ?>
+<?php if(!(Auth::getUser()->isDomainLimited() && count(Domain::getByLimitedDomains()) === 0)): ?>
+	<div class="buttons">
+		<a class="button" href="<?php echo url('admin/editredirect'); ?>">Create new redirect</a>
+	</div>
+<?php else: ?>
+	<div class="notification notification-warning">
+		You are listed for limited access to domains, but it seems there are no domains listed you can access.
+	</div>
+<?php endif; ?>
 
-<div class="buttons">
-	<a class="button" href="<?php echo url('admin/editredirect'); ?>">Create new redirect</a>
-</div>
+<?php output_messages(); ?>
 
-<table class="table">
-	<thead>
+<?php if($redirects->count() > 0): ?>
+	<table class="table">
+		<thead>
 		<tr>
 			<th>Source</th>
 			<th>Destination</th>
 			<th></th>
 			<th></th>
 		<tr>
-	</thead>
-	<tbody>
-	<?php foreach($redirects as $redirect): /** @var AbstractRedirect $redirect */ ?>
-		<tr<?php echo $redirect->getConflictingUsers()->count() > 0 ? ' class="warning"' : ''; ?>>
-			<td>
-			<?php if($redirect->getConflictingUsers()->count() > 0): ?>
-				<strong><?php echo $redirect->getConflictingUsers()->count() === 1 ? 'The marked redirect overrides a mailbox.' : 'The marked redirects override mailboxes.'; ?></strong><br>
-			<?php endif; ?>
-				<?php echo formatEmails($redirect->getConflictingMarkedSource(), str_replace(PHP_EOL, '<br>', FRONTEND_EMAIL_SEPARATOR_TEXT)); ?>
-			</td>
-			<td><?php echo formatEmails($redirect->getDestination(), str_replace(PHP_EOL, '<br>', FRONTEND_EMAIL_SEPARATOR_TEXT)); ?></td>
-			<td>
-				<a href="<?php echo url('admin/editredirect/?id='.$redirect->getId()); ?>">[Edit]</a>
-			</td>
-			<td>
-				<a href="<?php echo url('admin/deleteredirect/?id='.$redirect->getId()); ?>">[Delete]</a>
-			</td>
-		</tr>
-	<?php endforeach; ?>
-	</tbody>
-<?php if($redirects->count() > 0): ?>
-	<tfoot>
+		</thead>
+		<tbody>
+		<?php foreach($redirects as $redirect): /** @var AbstractRedirect $redirect */ ?>
+			<tr<?php echo $redirect->getConflictingUsers()->count() > 0 ? ' class="warning"' : ''; ?>>
+				<td>
+					<?php if($redirect->getConflictingUsers()->count() > 0): ?>
+						<strong><?php echo $redirect->getConflictingUsers()->count() === 1 ? 'The marked redirect overrides a mailbox.' : 'The marked redirects override mailboxes.'; ?></strong><br>
+					<?php endif; ?>
+					<?php echo formatEmails($redirect->getConflictingMarkedSource(), str_replace(PHP_EOL, '<br>', FRONTEND_EMAIL_SEPARATOR_TEXT)); ?>
+				</td>
+				<td><?php echo formatEmails($redirect->getDestination(), str_replace(PHP_EOL, '<br>', FRONTEND_EMAIL_SEPARATOR_TEXT)); ?></td>
+				<td>
+					<a href="<?php echo url('admin/editredirect/?id='.$redirect->getId()); ?>">[Edit]</a>
+				</td>
+				<td>
+					<a href="<?php echo url('admin/deleteredirect/?id='.$redirect->getId()); ?>">[Delete]</a>
+				</td>
+			</tr>
+		<?php endforeach; ?>
+		</tbody>
+		<tfoot>
 		<tr>
-			<th><?php echo $redirects->count(); ?> Redirects</th>
+			<th><?php echo ($redirects->count() > 1) ? $redirects->count().' Redirects' : '1 Redirect'; ?></th>
 		</tr>
-	</tfoot>
-<?php endif; ?>
-</table>
+		</tfoot>
+	</table>
+<?php elseif(!(Auth::getUser()->isDomainLimited() && count(Domain::getByLimitedDomains()) === 0)): ?>
+	<div class="notification notification-warning">
+		There are currently no redirects created you can manage.
+	</div>
+<?php endif; ?>

+ 52 - 39
include/php/pages/admin/listusers.php

@@ -1,7 +1,7 @@
-<?php 
+<?php
 
 if(isset($_GET['deleted']) && $_GET['deleted'] == "1"){
-	add_message("success", "User deleted successfully."); 
+	add_message("success", "User deleted successfully.");
 }
 else if(isset($_GET['created']) && $_GET['created'] == "1"){
 	add_message("success", "User created successfully.");
@@ -12,21 +12,31 @@ else if(isset($_GET['edited']) && $_GET['edited'] == "1"){
 else if(isset($_GET['adm_del']) && $_GET['adm_del'] == "1"){
 	add_message("fail", "Admin user cannot be deleted.");
 }
+else if(isset($_GET['missing-permission']) && $_GET['missing-permission'] == "1"){
+	add_message("fail", "You don't have the permission to edit/delete users of that domain.");
+}
 
-$users = User::findAll();
+$users = User::getByLimitedDomains();
 
 ?>
 
 <h1>List of all mailbox accounts</h1>
 
-<div class="buttons">
-	<a class="button button-small" href="<?php echo url('admin/edituser'); ?>">Create new user</a>
-</div>
+<?php if(!(Auth::getUser()->isDomainLimited() && count(Domain::getByLimitedDomains()) === 0)): ?>
+	<div class="buttons">
+		<a class="button button-small" href="<?php echo url('admin/edituser'); ?>">Create new user</a>
+	</div>
+<?php else: ?>
+	<div class="notification notification-warning">
+		You are listed for limited access to domains, but it seems there are no domains listed you can access.
+	</div>
+<?php endif; ?>
 
 <?php output_messages(); ?>
 
-<table class="table">
-	<thead>
+<?php if($users->count() > 0): ?>
+	<table class="table">
+		<thead>
 		<tr>
 			<th>Username</th>
 			<th>Domain</th>
@@ -37,35 +47,38 @@ $users = User::findAll();
 			<th></th>
 			<th></th>
 		<tr>
-	</thead>
-	<tbody>
-	<?php foreach($users as $user): /** @var User $user */ ?>
-		<tr<?php echo !is_null($user->getConflictingRedirect()) ? ' class="warning"' : ''; ?>>
-			<td>
-			<?php if(!is_null($user->getConflictingRedirect())): ?>
-				<strong>This mailbox is overridden by a redirect.</strong><br>
-			<?php endif; ?>
-				<?php echo $user->getUsername(); ?>
-			</td>
-			<td><?php echo $user->getDomain(); ?></td>
-		<?php if(defined('DBC_USERS_MAILBOXLIMIT')): ?>
-			<td style="text-align: right"><?php echo ($user->getMailboxLimit() > 0) ? $user->getMailboxLimit().' MB' : 'No limit'; ?></td>
-		<?php endif; ?>
-			<td><?php echo ($user->getRole() === User::ROLE_ADMIN) ? 'Admin' : 'User'; ?></td>
-			<td>
-				<a href="<?php echo url('admin/edituser/?id='.$user->getId()); ?>">[Edit]</a>
-			</td>
-			<td>
-				<a href="<?php echo url('admin/deleteuser/?id='.$user->getId()); ?>">[Delete]</a>
-			</td>
-		</tr>
-	<?php endforeach; ?>
-	</tbody>
-<?php if ($users->count() > 0): ?>
-	<tfoot>
-		<tr>
-			<th><?php echo $users->count();?> User</th>
-		</tr>
-	</tfoot>
+		</thead>
+		<tbody>
+		<?php foreach($users as $user): /** @var User $user */ ?>
+			<tr<?php echo !is_null($user->getConflictingRedirect()) ? ' class="warning"' : ''; ?>>
+				<td>
+					<?php if(!is_null($user->getConflictingRedirect())): ?>
+						<strong>This mailbox is overridden by a redirect.</strong><br>
+					<?php endif; ?>
+					<?php echo $user->getUsername(); ?>
+				</td>
+				<td><?php echo $user->getDomain(); ?></td>
+				<?php if(defined('DBC_USERS_MAILBOXLIMIT')): ?>
+					<td style="text-align: right"><?php echo ($user->getMailboxLimit() > 0) ? $user->getMailboxLimit().' MB' : 'No limit'; ?></td>
+				<?php endif; ?>
+				<td><?php echo ($user->getRole() === User::ROLE_ADMIN) ? 'Admin' : 'User'; ?></td>
+				<td>
+					<a href="<?php echo url('admin/edituser/?id='.$user->getId()); ?>">[Edit]</a>
+				</td>
+				<td>
+					<a href="<?php echo url('admin/deleteuser/?id='.$user->getId()); ?>">[Delete]</a>
+				</td>
+			</tr>
+		<?php endforeach; ?>
+		</tbody>
+		<tfoot>
+			<tr>
+				<th><?php echo ($users->count() > 1) ? $users->count().' Users' : '1 User'; ?></th>
+			</tr>
+		</tfoot>
+	</table>
+<?php elseif(!(Auth::getUser()->isDomainLimited() && count(Domain::getByLimitedDomains()) === 0)): ?>
+	<div class="notification notification-warning">
+		There are currently no users created you can manage.
+	</div>
 <?php endif; ?>
-</table>

+ 3 - 1
include/php/pages/admin/start.php

@@ -3,7 +3,9 @@
 <div class="buttons buttons-horizontal button-large">
 	<a class="button" href="<?php echo url('admin/listusers'); ?>">Manage users</a>
 
-	<a class="button" href="<?php echo url('admin/listdomains'); ?>">Manage domains</a>
+	<?php if(!Auth::getUser()->isDomainLimited()): ?>
+		<a class="button" href="<?php echo url('admin/listdomains'); ?>">Manage domains</a>
+	<?php endif; ?>
 
 	<a class="button" href="<?php echo url('admin/listredirects'); ?>">Manage redirects</a>
 </div>

+ 1 - 1
include/php/pages/login.php

@@ -14,7 +14,7 @@ if(isset($_POST['email']) && isset($_POST['password'])){
 		if(Auth::login($_POST['email'], $_POST['password'])){
 			redirect("private");
 		}
-		// If login is not successful
+		// If login isn't successful
 		else{
 			//Log error message
 			writeLog("WebMUM login failed for IP ".$_SERVER['REMOTE_ADDR']);

+ 0 - 6
include/php/pages/logout.php

@@ -1,6 +0,0 @@
-<?php
-require_once 'include/php/default.inc.php';
-
-session_destroy();
-redirect('/');
-?>

+ 1 - 1
include/php/pages/not-allowed.php

@@ -1,5 +1,5 @@
 <h1>Not allowed!</h1>
 
 <p>
-	Sorry, you are not allowed to access this page.
+	Sorry, you aren't allowed to access this page.
 </p>

+ 5 - 1
include/php/routes.inc.php

@@ -7,7 +7,11 @@ Router::addGet('/', 'include/php/pages/start.php');
  * Auth
  */
 Router::addMixed('/login', 'include/php/pages/login.php');
-Router::addGet('/logout', 'include/php/pages/logout.php');
+Router::addGet('/logout', function(){
+	Auth::logout();
+	redirect('/');
+	return;
+});
 
 /**
  * Private area