diff --git a/include/class-Domain.php b/include/class-Domain.php index 6e7a6a9..c782f21 100644 --- a/include/class-Domain.php +++ b/include/class-Domain.php @@ -1,101 +1,124 @@ . /** - * A domain handled by the user + * Methods for a Domain class */ -class Domain extends Queried { - - const T = 'domain'; - - const UID = 'domain_name'; - - public function __construct() { - $this->integers( 'domain_ID' ); - $this->booleans( 'domain_active' ); - $this->dates( 'domain_born', 'domain_expiration' ); - } +trait DomainTrait { /** * Get domain ID * * @return int */ public function getDomainID() { return $this->get( 'domain_ID' ); } - /** + /* * Get domain name * * @return string */ public function getDomainName() { return $this->get( 'domain_name' ); } /** * Get the domain edit URl * * @param boolean $absolute True for an absolute URL * @return string */ public function getDomainPermalink( $absolute = false ) { return Domain::permalink( $this->get( 'domain_name' ), $absolute ); } /** * Factory mailbox from this domain * * @return MailboxFullAPI */ public function factoryMailbox() { return ( new MailboxFullAPI() )->whereDomain( $this ); } /** * Factory e-mail forward from this domain * * @return MailforwardFullAPI */ public function factoryMailforwardfrom() { return ( new MailforwardfromAPI() )->whereDomain( $this ); } /** * Factory FTP users from this domain * * @return FTPAPI */ public function factoryFTP() { return ( new FTPAPI() )->whereDomain( $this ); } + /** + * Normalize a Domain object after being retrieved from database + */ + protected function normalizeDomain() { + $this->integers( 'domain_ID' ); + $this->booleans( 'domain_active' ); + $this->dates( 'domain_born', 'domain_expiration' ); + } + +} + +/** + * Describe the 'domain' table + */ +class Domain extends Queried { + + use DomainTrait; + + /** + * Table name + */ + const T = 'domain'; + + const UID = 'domain_name'; + + /** + * Constructor + */ + public function __construct() { + $this->normalizeDomain(); + } + /** * Get the domain permalink * * @param string $domain_name Domain name * @param boolean $absolute True for an absolute URL */ public static function permalink( $domain_name = null, $absolute = false ) { $url = 'domain.php'; if( $domain_name ) { $url .= _ . $domain_name; } return site_page( $url, $absolute ); } + } diff --git a/include/class-DomainAPI.php b/include/class-DomainAPI.php index 63a7395..3e9c26a 100644 --- a/include/class-DomainAPI.php +++ b/include/class-DomainAPI.php @@ -1,145 +1,155 @@ . /** * Methods related to a Domain class */ trait DomainAPITrait { /** * Where the domains are editable by me * * @return self */ public function whereDomainIsEditable() { if( ! has_permission( 'edit-domain-all' ) ) { $this->whereDomainUser(); } return $this; } /** * Where the Domain is Active (or not) * * @param boolean $active If you want the active, or the inactive * @return self */ public function whereDomainIsActive( $active = true ) { return $this->wheerInt( 'domain_active', $active ); } /** * Limit to a certain user (or yourself) * * @param $user_ID int * @return self */ public function whereDomainUser( $user_ID = false ) { if( $user_ID === false ) { $user_ID = get_user( 'user_ID' ); } $this->joinDomainUser(); return $this->whereInt( 'domain_user.user_ID', $user_ID ); } /** * Limit to a certian domain name * * @param $domain_name string * @return self */ public function whereDomainName( $domain_name ) { return $this->whereStr( 'domain_name', $domain_name ); } /** * Constructor from a domain ID * * @param $domain_ID int * @return self */ public function whereDomainID( $domain_ID ) { return $this->whereInt( static::DOMAIN_ID, $domain_ID ); } /** * Constructor from a Domain object * * @param $domain object * @return self */ public function whereDomain( $domain ) { return $this->whereDomainID( $domain->getDomainID() ); } + /** + * Order by the Domain name + * + * @param string $direction DESC|ASC + * @return self + */ + public function orderByDomainName( $direction = null ) { + return $this->orderBy( 'domain_name', $direction ); + } + /** * Join domain and users (once) * * @return self */ public function joinDomainUser() { if( empty( $this->joinedDomainUser ) ) { $this->from( 'domain_user' ); $this->equals( 'domain_user.domain_ID', 'domain.domain_ID' ); $this->joinedDomainUser = true; } return $this; } /** * Join whatever table with the domain table * * @return self */ public function joinDomain() { - return $this->joinOn( 'INNER', static::DOMAIN_ID, 'domain.domain_ID' ); + return $this->joinOn( 'INNER', 'domain', static::DOMAIN_ID, 'domain.domain_ID' ); } } /** * Domain API */ class DomainAPI extends Query { use DomainAPITrait; /** * @TODO: what is this shit? Is this used? */ const UID = 'name'; /** * Domain ID column name */ const DOMAIN_ID = 'domain.domain_ID'; /** * Constructor * * @param object $db Database (or NULL for the current one) */ public function __construct( $db = null ) { // set database and class name parent::__construct( $db, 'Domain' ); // set database table $this->from( Domain::T ); } } diff --git a/include/class-DomainUser.php b/include/class-DomainUser.php new file mode 100644 index 0000000..29ff517 --- /dev/null +++ b/include/class-DomainUser.php @@ -0,0 +1,60 @@ +. + +// assure load of dependent traits +class_exists( 'Domain' ); +class_exists( 'User' ); + +/** + * Trait for a DomainUser class + */ +trait DomainUserTrait { + + use DomainTrait; + use UserTrait; + + /** + * Normalize a DomainUser object after being retrieved from database + */ + protected function normalizeDomainUser() { + $this->normalizeDomain(); + $this->normalizeUser(); + } + +} + +/** + * Describe the 'domain_user' table + */ +class DomainUser extends Queried { + + use DomainUserTrait; + + /** + * Database table + */ + const T = 'domain_user'; + + /** + * Constructor + */ + public function __construct() { + $this->normalizeDomainUser(); + } + + +} diff --git a/include/class-DomainUserAPI.php b/include/class-DomainUserAPI.php new file mode 100644 index 0000000..bf0332a --- /dev/null +++ b/include/class-DomainUserAPI.php @@ -0,0 +1,67 @@ +. + +// assure load of dependent traits +class_exists( 'DomainAPI' ); +class_exists( 'UserAPI' ); + +/** + * Trait for a DomainUserAPI class + */ +trait DomainUserAPITrait { + + use DomainAPITrait; + use UserAPITrait; + +} + +/** + * Query the 'domain_user' table + */ +class DomainUserAPI extends Query { + + use DomainUserAPITrait; + + /** + * Univoque Domain ID column name + * + * Used by DomainApi + */ + const DOMAIN_ID = 'domain_user.domain_ID'; + + /** + * Univoque User ID column name + * + * Used by DomainApi + */ + const USER_ID = 'domain_user.user_ID'; + + /** + * Constructor + * + * @param object $db Database (or NULL for the current one) + */ + public function __construct( $db = null ) { + + // set database and class name + parent::__construct( $db, DomainUser::class ); + + // set database table + $this->from( DomainUser::T ); + } + +} diff --git a/include/class-Mailbox.php b/include/class-Mailbox.php index 55895b0..75b5d37 100644 --- a/include/class-Mailbox.php +++ b/include/class-Mailbox.php @@ -1,110 +1,116 @@ . +// load dependent traits +class_exists( 'Domain' ); + /** * A mailbox */ -class Mailbox extends Domain { +class Mailbox extends Queried { + + use DomainTrait; const T = 'mailbox'; public function __construct() { + $this->normalizeDomain(); $this->booleans( 'mailbox_receive' ); } /** * Get the mailbox username * * @return string */ public function getMailboxUsername() { return $this->get( 'mailbox_username' ); } /** * Get the mailbox address * * @return string E-mail */ public function getMailboxAddress() { return sprintf( "%s@%s", $this->get( 'mailbox_username' ), $this->get( 'domain_name' ) ); } /** * Get the mailbox permalink * * @return string */ public function getMailboxPermalink( $absolute = false ) { return Mailbox::permalink( $this->get( 'domain_name' ), $this->get( 'mailbox_username' ) ); } /** * Update this mailbox password * * @param string $password * @return string */ public function updateMailboxPassword( $password = null ) { if( ! $password ) { $password = generate_password(); } $enc_password = Mailbox::encryptPassword( $password ); // update ( new MailboxAPI() ) ->whereMailbox( $this ) ->update( [ new DBCol( 'mailbox_password', $enc_password, 's' ), ] ); return $password; } /** * Get the mailbox permalink * * @param $domain string * @param $mailbox string * @param $absolute boolean * @return string */ public static function permalink( $domain, $mailbox = null, $absolute = false ) { $part = site_page( 'mailbox.php', $absolute ) . _ . $domain; if( $mailbox ) { $part .= _ . $mailbox; } return $part; } /** * Encrypt a password * * TODO: do not hardcode to my Dovecot configuration */ public static function encryptPassword( $password ) { $salt = bin2hex( openssl_random_pseudo_bytes( 3 ) ); return '{SHA512-CRYPT}' . crypt( $password, "$6$$salt" ); } } diff --git a/include/class-UserAPI.php b/include/class-UserAPI.php index 2aa663a..3fcd1e5 100644 --- a/include/class-UserAPI.php +++ b/include/class-UserAPI.php @@ -1,103 +1,117 @@ . /** - * User users API + * Methods for an UserAPI class */ -class UserAPI extends DomainAPI { - - /** - * Column name of the User ID - */ - const USER_ID = 'user.user_ID'; - - /** - * Constructor - */ - public function __construct() { - Query::__construct(); - $this->from( User::T ); - $this->defaultClass( User::class ); - } +trait UserAPITrait { /** * Filter to a certain User UID * * @param string $uid User UID * @return self */ public function whereUserUID( $uid ) { return $this->whereStr( 'user_uid', $uid ); } /** * Filter to a certain User E-mail * * @param string $email User E-mail * @return self */ public function whereUserEmail( $email ) { return $this->whereStr( 'user_email', $email ); } /** * Filter to a certain User ID * * @param string $uid User ID * @return self */ public function whereUserID( $id ) { return $this->whereInt( static::USER_ID, $id ); } /** * Filter to myself * * @return self */ public function whereUserIsMe() { $id = get_user()->getUserID(); return $this->whereUserID( $id ); } /** * WHere the User(s) is editable * * @return Query */ public function whereUserIsEditable() { // if I can't see everyone, just see myself if( !has_permission( 'edit-user-all' ) ) { $this->whereUserIsMe(); } return $this; } /** * Limit to a specific User * * @param object $user User * @return self */ public function whereUser( $user ) { $id = $user->getSessionuserID(); return $this->whereUserID( $id ); } } + +/** + * Query the 'user' database table + */ +class UserAPI extends Query { + + use UserAPITrait; + + /** + * Univoque column name of the User ID + */ + const USER_ID = 'user.user_ID'; + + /** + * Constructor + * + * @param object $db Database (or NULL for the current one) + */ + public function __construct( $db = null ) { + + // set database and class name + parent::__construct( $db, User::class ); + + // set database table + $this->from( User::T ); + } + +} diff --git a/template/user.php b/template/user.php index 3df0bce..339bcfe 100644 --- a/template/user.php +++ b/template/user.php @@ -1,60 +1,98 @@ . /* * This is the template for an User * * Called from: * user.php * * Available variables: * $user object|null * $new_password string|null + * $user_domains object|null (generator) */ // unuseful when load directly defined( 'BOZ_PHP' ) or die; ?> +
getUserEmail() ) : '' ?> class="form-control" />
getUserUID() ) : '' ?> class="form-control" />
+

/>
+ + + +
+

+ +
+ + + + + +
+
+

+ + + +
+ + +
+ + +
+
+ + diff --git a/www/user.php b/www/user.php index fc3c89e..a0c39cd 100644 --- a/www/user.php +++ b/www/user.php @@ -1,82 +1,172 @@ . /* * This is the single User creation/edit page */ // load framework require '../load.php'; +// require the permission to see the backend +require_permission( 'backend' ); + // wanted informations $user = null; // URL paramenters (user_uid) list( $user_uid ) = url_parts( 1, 0 ); // eventually retrieve mailforward from database if( $user_uid ) { $user = ( new UserAPI() ) ->whereUserUID( $user_uid ) ->whereUserIsEditable() ->queryRow(); // 404 if( !$user || !$user->isUserEditable() ) { PageNotFound::spawn(); } } else { // to create an FTP user, must edit all FTP users require_permission( 'edit-user-all' ); } // save destination action if( is_action( 'user-save' ) ) { } +// add a Domain to the user +if( is_action( 'add-domain' ) ){ + + // check for permissions + if( !has_permission( 'edit-user-all' ) ) { + error_die( "Not authorized to add a Domain" ); + } + + // get the Domain by name + $domain_name = $_POST['domain_name'] ?? null; + if( !$domain_name ) { + die( "Please fill that damn Domain name" ); + } + + // search the Domain name + $domain = + ( new DomainAPI() ) + ->whereDomainName( $domain_name ) + ->queryRow(); + + query( 'START TRANSACTION' ); + + // domain ID to be assigned to the User + $domain_ID = null; + + // does the Domain already exist? + if( $domain ) { + $domain_ID = $domain->getDomainID(); + } else { + // can I add this Domain? + if( has_permission( 'edit-domain-all' ) ) { + + // add this Domain + ( new DomainAPI() ) + ->insertRow( [ + 'domain_name' => $domain_name, + 'domain_active' => 1, + new DBCol( 'domain_born', 'NOW()', '-' ), + ] ); + + $domain_ID = last_inserted_ID(); + } + } + + if( $domain_ID ) { + + $is_domain_mine = + ( new DomainUserAPI() ) + ->whereUserIsMe() + ->whereDomainID( $domain_ID ) + ->queryRow(); + + // is it already mine? + if( !$is_domain_mine ) { + + // associate this domain to myself + ( new DomainUserAPI() ) + ->insertRow( [ + 'domain_ID' => $domain_ID, + 'user_ID' => $user->getUserID(), + new DBCol( 'domain_user_creation_date', 'NOW()', '-' ), + ] ); + } + + } else { + die( "this Domain is not registered and can't be added" ); + } + + query( 'COMMIT' ); + + // end add Domain to User +} + // register action to generate a new password $new_password = null; if( is_action( 'change-password' ) && $user ) { // generate a new password and save $new_password = generate_password(); $encrypted = User::encryptPassword( $new_password ); ( new UserAPI() ) ->whereUser( $user ) ->update( [ new DBCol( User::PASSWORD, $encrypted, 's' ), ] ); } +// expose the User domains +$user_domains = []; +if( $user ) { + + // get User domains + $user_domains = + ( new DomainUserAPI() ) + ->joinDomain() + ->whereUser( $user ) + ->orderByDomainName() + ->queryGenerator(); +} + // spawn header Header::spawn( [ 'uid' => false, 'title-prefix' => __( "User" ), 'title' => $user ? $user->getUserUID() : __( "create" ), ] ); // spawn the page content template( 'user', [ 'user' => $user, 'new_password' => $new_password, + 'user_domains' => $user_domains, ] ); // spawn the footer Footer::spawn();