diff --git a/src/Dev/Install/Installer.php b/src/Dev/Install/Installer.php index 80df8afb0..f0447396e 100644 --- a/src/Dev/Install/Installer.php +++ b/src/Dev/Install/Installer.php @@ -4,11 +4,10 @@ namespace SilverStripe\Dev\Install; use Exception; use SilverStripe\Control\Cookie; +use SilverStripe\Control\HTTPApplication; use SilverStripe\Control\HTTPRequest; use SilverStripe\Control\HTTPRequestBuilder; -use SilverStripe\Control\Session; use SilverStripe\Core\CoreKernel; -use SilverStripe\Control\HTTPApplication; use SilverStripe\Core\Kernel; use SilverStripe\Core\Startup\ParameterConfirmationToken; use SilverStripe\ORM\DatabaseAdmin; @@ -229,9 +228,15 @@ PHP // Create default administrator user and group in database // (not using Security::setDefaultAdmin()) - $adminMember = DefaultAdminService::singleton()->findOrCreateDefaultAdmin(); - $adminMember->Email = $config['admin']['username']; - $adminMember->Password = $config['admin']['password']; + $username = $config['admin']['username']; + $password = $config['admin']['password']; + $adminMember = DefaultAdminService::singleton() + ->findOrCreateAdmin( + $username, + _t(DefaultAdminService::class . '.DefaultAdminFirstname', 'Default Admin') + ); + $adminMember->Email = $username; + $adminMember->Password = $password; $adminMember->PasswordEncryption = Security::config()->get('encryption_algorithm'); try { @@ -243,8 +248,8 @@ PHP ); } - $request->getSession()->set('username', $config['admin']['username']); - $request->getSession()->set('password', $config['admin']['password']); + $request->getSession()->set('username', $username); + $request->getSession()->set('password', $password); $request->getSession()->save($request); }, true); diff --git a/src/Security/DefaultAdminService.php b/src/Security/DefaultAdminService.php index 005de5df6..95d151f63 100644 --- a/src/Security/DefaultAdminService.php +++ b/src/Security/DefaultAdminService.php @@ -129,33 +129,53 @@ class DefaultAdminService return null; } - // Find or create ADMIN group - Group::singleton()->requireDefaultRecords(); - $adminGroup = Permission::get_groups_by_permission('ADMIN')->first(); + // Create admin with default admin username + $admin = $this->findOrCreateAdmin( + static::getDefaultAdminUsername(), + _t(__CLASS__ . '.DefaultAdminFirstname', 'Default Admin') + ); - if (!$adminGroup) { - Group::singleton()->requireDefaultRecords(); - $adminGroup = Permission::get_groups_by_permission('ADMIN')->first(); - } + $this->extend('afterFindOrCreateDefaultAdmin', $admin); + + return $admin; + } + + /** + * Find or create a Member with admin permissions + * + * @skipUpgrade + * @param string $email + * @param string $name + * @return Member + */ + public function findOrCreateAdmin($email, $name = null) + { + $this->extend('beforeFindOrCreateAdmin', $email, $name); // Find member - /** @skipUpgrade */ + /** @var Member $admin */ $admin = Member::get() - ->filter('Email', static::getDefaultAdminUsername()) + ->filter('Email', $email) ->first(); + + // Find or create admin group + $adminGroup = $this->findOrCreateAdminGroup(); + // If no admin is found, create one - if (!$admin) { - // 'Password' is not set to avoid creating - // persistent logins in the database. See Security::setDefaultAdmin(). + if ($admin) { + $inGroup = $admin->inGroup($adminGroup); + } else { + // Note: This user won't be able to login until a password is set // Set 'Email' to identify this as the default admin + $inGroup = false; $admin = Member::create(); - $admin->FirstName = _t(__CLASS__ . '.DefaultAdminFirstname', 'Default Admin'); - $admin->Email = static::getDefaultAdminUsername(); + $admin->FirstName = $name ?: $email; + $admin->Email = $email; $admin->write(); } - // Ensure this user is in the admin group - if (!$admin->inGroup($adminGroup)) { + // Ensure this user is in an admin group + if (!$inGroup) { // Add member to group instead of adding group to member // This bypasses the privilege escallation code in Member_GroupSet $adminGroup @@ -163,11 +183,41 @@ class DefaultAdminService ->add($admin); } - $this->extend('afterFindOrCreateDefaultAdmin', $admin); + $this->extend('afterFindOrCreateAdmin', $admin); return $admin; } + /** + * Ensure a Group exists with admin permission + * + * @return Group + */ + protected function findOrCreateAdminGroup() + { + // Check pre-existing group + $adminGroup = Permission::get_groups_by_permission('ADMIN')->first(); + if ($adminGroup) { + return $adminGroup; + } + + // Check if default records create the group + Group::singleton()->requireDefaultRecords(); + $adminGroup = Permission::get_groups_by_permission('ADMIN')->first(); + if ($adminGroup) { + return $adminGroup; + } + + // Create new admin group directly + $adminGroup = Group::create(); + $adminGroup->Code = 'administrators'; + $adminGroup->Title = _t('SilverStripe\\Security\\Group.DefaultGroupTitleAdministrators', 'Administrators'); + $adminGroup->Sort = 0; + $adminGroup->write(); + Permission::grant($adminGroup->ID, 'ADMIN'); + return $adminGroup; + } + /** * Check if the user is a default admin. * Returns false if there is no default admin. diff --git a/tests/php/Security/SecurityDefaultAdminTest.php b/tests/php/Security/SecurityDefaultAdminTest.php index b34ab1851..401ba9313 100644 --- a/tests/php/Security/SecurityDefaultAdminTest.php +++ b/tests/php/Security/SecurityDefaultAdminTest.php @@ -79,6 +79,20 @@ class SecurityDefaultAdminTest extends SapphireTest $this->assertNull($admin->Password); } + public function testFindOrCreateAdmin() + { + $adminMembers = Permission::get_members_by_permission('ADMIN'); + $this->assertEquals(0, $adminMembers->count()); + + $admin = DefaultAdminService::singleton()->findOrCreateAdmin('newadmin@example.com', 'Admin Name'); + + $this->assertInstanceOf(Member::class, $admin); + $this->assertTrue(Permission::checkMember($admin, 'ADMIN')); + $this->assertEquals('newadmin@example.com', $admin->Email); + $this->assertEquals('Admin Name', $admin->FirstName); + $this->assertNull($admin->Password); + } + public function testFindAnAdministratorWithoutDefaultAdmin() { // Clear default admin