2018-04-13 12:36:34 -04:00
< ? php
2020-12-01 08:33:22 -05:00
declare ( strict_types = 1 );
2020-12-16 08:54:15 -05:00
2019-12-03 13:57:53 -05:00
/**
* @ copyright Copyright ( c ) 2016 , ownCloud , Inc .
*
* @ author Arthur Schiwon < blizzz @ arthur - schiwon . de >
* @ author Bjoern Schiessle < bjoern @ schiessle . org >
* @ author Christoph Wurst < christoph @ winzerhof - wurst . at >
2021-06-04 15:52:51 -04:00
* @ author Daniel Calviño Sánchez < danxuliu @ gmail . com >
2020-03-31 04:49:10 -04:00
* @ author Daniel Kesselberg < mail @ danielkesselberg . de >
2020-08-24 08:54:25 -04:00
* @ author GretaD < gretadoci @ gmail . com >
* @ author Joas Schilling < coding @ schilljs . com >
2021-06-04 15:52:51 -04:00
* @ author John Molakvoæ < skjnldsv @ protonmail . com >
2020-08-24 08:54:25 -04:00
* @ author Morris Jobke < hey @ morrisjobke . de >
2019-12-03 13:57:53 -05:00
* @ author Roeland Jago Douma < roeland @ famdouma . nl >
2021-06-04 15:52:51 -04:00
* @ author Vincent Petry < vincent @ nextcloud . com >
2023-03-21 02:43:38 -04:00
* @ author Kate Döen < kate . doeen @ nextcloud . com >
2019-12-03 13:57:53 -05:00
*
* @ license AGPL - 3.0
*
* This code is free software : you can redistribute it and / or modify
* it under the terms of the GNU Affero General Public License , version 3 ,
* as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU Affero General Public License for more details .
*
* You should have received a copy of the GNU Affero General Public License , version 3 ,
* along with this program . If not , see < http :// www . gnu . org / licenses />
*
*/
2018-04-13 12:36:34 -04:00
2019-09-17 10:33:27 -04:00
namespace OCA\Settings\Controller ;
2018-04-13 12:36:34 -04:00
2021-05-19 18:41:00 -04:00
use InvalidArgumentException ;
2018-04-13 12:36:34 -04:00
use OC\AppFramework\Http ;
2018-12-20 05:11:04 -05:00
use OC\Encryption\Exceptions\ModuleDoesNotExistsException ;
2018-04-13 12:36:34 -04:00
use OC\ForbiddenException ;
2021-03-10 13:37:10 -05:00
use OC\KnownUser\KnownUserService ;
2018-04-13 12:36:34 -04:00
use OC\Security\IdentityProof\Manager ;
2020-07-08 07:49:11 -04:00
use OC\User\Manager as UserManager ;
2019-11-22 14:52:10 -05:00
use OCA\Settings\BackgroundJobs\VerifyUserData ;
2020-09-07 11:51:07 -04:00
use OCA\Settings\Events\BeforeTemplateRenderedEvent ;
2018-08-09 07:22:32 -04:00
use OCA\User_LDAP\User_Proxy ;
2021-05-19 18:41:00 -04:00
use OCP\Accounts\IAccount ;
2020-12-01 08:33:22 -05:00
use OCP\Accounts\IAccountManager ;
2021-05-19 18:41:00 -04:00
use OCP\Accounts\PropertyDoesNotExistException ;
2018-04-13 12:36:34 -04:00
use OCP\App\IAppManager ;
use OCP\AppFramework\Controller ;
2024-01-18 04:38:37 -05:00
use OCP\AppFramework\Http\Attribute\OpenAPI ;
2018-04-13 12:36:34 -04:00
use OCP\AppFramework\Http\DataResponse ;
2020-02-26 12:04:43 -05:00
use OCP\AppFramework\Http\JSONResponse ;
2018-04-13 12:36:34 -04:00
use OCP\AppFramework\Http\TemplateResponse ;
2024-02-22 11:18:41 -05:00
use OCP\AppFramework\Services\IInitialState ;
2018-04-13 12:36:34 -04:00
use OCP\BackgroundJob\IJobList ;
2018-04-24 10:20:01 -04:00
use OCP\Encryption\IManager ;
2020-09-07 11:51:07 -04:00
use OCP\EventDispatcher\IEventDispatcher ;
2018-04-13 12:36:34 -04:00
use OCP\IConfig ;
use OCP\IGroupManager ;
use OCP\IL10N ;
use OCP\IRequest ;
use OCP\IUser ;
use OCP\IUserSession ;
use OCP\L10N\IFactory ;
use OCP\Mail\IMailer ;
2020-02-26 12:04:43 -05:00
use function in_array ;
2018-04-13 12:36:34 -04:00
2024-01-18 04:38:37 -05:00
#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]
2018-04-13 12:36:34 -04:00
class UsersController extends Controller {
2020-09-07 11:51:07 -04:00
public function __construct (
string $appName ,
IRequest $request ,
2024-02-22 11:18:41 -05:00
private UserManager $userManager ,
private IGroupManager $groupManager ,
private IUserSession $userSession ,
private IConfig $config ,
private IL10N $l10n ,
private IMailer $mailer ,
private IFactory $l10nFactory ,
private IAppManager $appManager ,
private IAccountManager $accountManager ,
private Manager $keyManager ,
private IJobList $jobList ,
private IManager $encryptionManager ,
private KnownUserService $knownUserService ,
private IEventDispatcher $dispatcher ,
private IInitialState $initialState ,
2020-09-07 11:51:07 -04:00
) {
2018-04-13 12:36:34 -04:00
parent :: __construct ( $appName , $request );
}
/**
* @ NoCSRFRequired
* @ NoAdminRequired
2018-12-20 05:11:04 -05:00
*
2018-04-13 12:36:34 -04:00
* Display users list template
2018-12-20 05:11:04 -05:00
*
2018-04-13 12:36:34 -04:00
* @ return TemplateResponse
*/
2020-12-01 08:33:22 -05:00
public function usersListByGroup () : TemplateResponse {
2018-04-13 12:36:34 -04:00
return $this -> usersList ();
}
2018-04-14 04:34:40 -04:00
/**
* @ NoCSRFRequired
* @ NoAdminRequired
2018-12-20 05:11:04 -05:00
*
2018-04-14 04:34:40 -04:00
* Display users list template
2018-12-20 05:11:04 -05:00
*
2018-04-14 04:34:40 -04:00
* @ return TemplateResponse
*/
2020-12-01 08:33:22 -05:00
public function usersList () : TemplateResponse {
2018-04-13 12:36:34 -04:00
$user = $this -> userSession -> getUser ();
$uid = $user -> getUID ();
2024-01-11 07:28:25 -05:00
$isAdmin = $this -> groupManager -> isAdmin ( $uid );
2018-04-13 12:36:34 -04:00
\OC :: $server -> getNavigationManager () -> setActiveEntry ( 'core_users' );
2018-12-20 05:11:04 -05:00
2018-04-13 12:36:34 -04:00
/* SORT OPTION: SORT_USERCOUNT or SORT_GROUPNAME */
$sortGroupsBy = \OC\Group\MetaData :: SORT_USERCOUNT ;
2018-08-09 07:22:32 -04:00
$isLDAPUsed = false ;
2024-03-25 18:45:39 -04:00
if ( $this -> config -> getSystemValueBool ( 'sort_groups_by_name' , false )) {
2018-04-13 12:36:34 -04:00
$sortGroupsBy = \OC\Group\MetaData :: SORT_GROUPNAME ;
} else {
if ( $this -> appManager -> isEnabledForUser ( 'user_ldap' )) {
$isLDAPUsed =
2018-08-09 07:22:32 -04:00
$this -> groupManager -> isBackendUsed ( '\OCA\User_LDAP\Group_Proxy' );
2018-04-13 12:36:34 -04:00
if ( $isLDAPUsed ) {
// LDAP user count can be slow, so we sort by group name here
$sortGroupsBy = \OC\Group\MetaData :: SORT_GROUPNAME ;
}
}
}
2018-12-20 05:11:04 -05:00
$canChangePassword = $this -> canAdminChangeUserPasswords ();
/* GROUPS */
2018-04-13 12:36:34 -04:00
$groupsInfo = new \OC\Group\MetaData (
$uid ,
2024-01-11 07:28:25 -05:00
$isAdmin ,
2018-04-13 12:36:34 -04:00
$this -> groupManager ,
$this -> userSession
);
2018-12-20 05:11:04 -05:00
2018-04-13 12:36:34 -04:00
$groupsInfo -> setSorting ( $sortGroupsBy );
2021-01-12 04:15:48 -05:00
[ $adminGroup , $groups ] = $groupsInfo -> get ();
2018-08-09 07:22:32 -04:00
2020-04-10 08:19:56 -04:00
if ( ! $isLDAPUsed && $this -> appManager -> isEnabledForUser ( 'user_ldap' )) {
2018-08-09 07:22:32 -04:00
$isLDAPUsed = ( bool ) array_reduce ( $this -> userManager -> getBackends (), function ( $ldapFound , $backend ) {
return $ldapFound || $backend instanceof User_Proxy ;
});
}
2018-12-20 05:11:04 -05:00
2019-10-07 12:25:29 -04:00
$disabledUsers = - 1 ;
$userCount = 0 ;
2020-04-10 08:19:56 -04:00
if ( ! $isLDAPUsed ) {
2024-01-11 07:28:25 -05:00
if ( $isAdmin ) {
2019-10-07 12:25:29 -04:00
$disabledUsers = $this -> userManager -> countDisabledUsers ();
2020-04-09 07:53:40 -04:00
$userCount = array_reduce ( $this -> userManager -> countUsers (), function ( $v , $w ) {
2019-10-07 12:25:29 -04:00
return $v + ( int ) $w ;
}, 0 );
} else {
// User is subadmin !
// Map group list to names to retrieve the countDisabledUsersOfGroups
$userGroups = $this -> groupManager -> getUserGroups ( $user );
$groupsNames = [];
2020-04-10 08:19:56 -04:00
foreach ( $groups as $key => $group ) {
2019-10-07 12:25:29 -04:00
// $userCount += (int)$group['usercount'];
2023-07-05 14:06:45 -04:00
$groupsNames [] = $group [ 'name' ];
2019-10-07 12:25:29 -04:00
// we prevent subadmins from looking up themselves
// so we lower the count of the groups he belongs to
if ( array_key_exists ( $group [ 'id' ], $userGroups )) {
$groups [ $key ][ 'usercount' ] -- ;
$userCount -= 1 ; // we also lower from one the total count
}
2020-09-16 10:18:43 -04:00
}
2024-02-22 11:18:41 -05:00
2019-10-07 12:25:29 -04:00
$userCount += $this -> userManager -> countUsersOfGroups ( $groupsInfo -> getGroups ());
$disabledUsers = $this -> userManager -> countDisabledUsersOfGroups ( $groupsNames );
}
$userCount -= $disabledUsers ;
2018-04-13 12:36:34 -04:00
}
2019-10-07 12:25:29 -04:00
2018-04-13 12:36:34 -04:00
$disabledUsersGroup = [
2018-04-14 04:34:40 -04:00
'id' => 'disabled' ,
2022-09-21 11:44:32 -04:00
'name' => 'Disabled accounts' ,
2018-04-13 12:36:34 -04:00
'usercount' => $disabledUsers
];
2018-12-20 05:11:04 -05:00
2018-04-13 12:36:34 -04:00
/* QUOTAS PRESETS */
2020-02-22 14:02:56 -05:00
$quotaPreset = $this -> parseQuotaPreset ( $this -> config -> getAppValue ( 'files' , 'quota_preset' , '1 GB, 5 GB, 10 GB' ));
2021-07-29 07:04:41 -04:00
$allowUnlimitedQuota = $this -> config -> getAppValue ( 'files' , 'allow_unlimited_quota' , '1' ) === '1' ;
if ( ! $allowUnlimitedQuota && count ( $quotaPreset ) > 0 ) {
$defaultQuota = $this -> config -> getAppValue ( 'files' , 'default_quota' , $quotaPreset [ 0 ]);
} else {
$defaultQuota = $this -> config -> getAppValue ( 'files' , 'default_quota' , 'none' );
}
2018-12-20 05:11:04 -05:00
2020-09-07 11:51:07 -04:00
$event = new BeforeTemplateRenderedEvent ();
$this -> dispatcher -> dispatch ( 'OC\Settings\Users::loadAdditionalScripts' , $event );
$this -> dispatcher -> dispatchTyped ( $event );
2018-12-20 05:11:04 -05:00
2018-04-13 12:36:34 -04:00
/* LANGUAGES */
$languages = $this -> l10nFactory -> getLanguages ();
2018-12-20 05:11:04 -05:00
2024-03-25 18:45:39 -04:00
/** Using LDAP or admins (system config) can enfore sorting by group name, in this case the frontend setting is overwritten */
$forceSortGroupByName = $sortGroupsBy === \OC\Group\MetaData :: SORT_GROUPNAME ;
2018-04-13 12:36:34 -04:00
/* FINAL DATA */
2020-03-26 04:30:18 -04:00
$serverData = [];
2018-04-13 12:36:34 -04:00
// groups
$serverData [ 'groups' ] = array_merge_recursive ( $adminGroup , [ $disabledUsersGroup ], $groups );
// Various data
2024-01-11 07:28:25 -05:00
$serverData [ 'isAdmin' ] = $isAdmin ;
2024-03-25 18:45:39 -04:00
$serverData [ 'sortGroups' ] = $forceSortGroupByName
? \OC\Group\MetaData :: SORT_GROUPNAME
: ( int ) $this -> config -> getAppValue ( 'core' , 'group.sortBy' , ( string ) \OC\Group\MetaData :: SORT_USERCOUNT );
$serverData [ 'forceSortGroupByName' ] = $forceSortGroupByName ;
2018-04-13 12:36:34 -04:00
$serverData [ 'quotaPreset' ] = $quotaPreset ;
2021-07-29 07:04:41 -04:00
$serverData [ 'allowUnlimitedQuota' ] = $allowUnlimitedQuota ;
2019-10-07 12:25:29 -04:00
$serverData [ 'userCount' ] = $userCount ;
2018-04-13 12:36:34 -04:00
$serverData [ 'languages' ] = $languages ;
$serverData [ 'defaultLanguage' ] = $this -> config -> getSystemValue ( 'default_language' , 'en' );
2020-04-29 08:42:07 -04:00
$serverData [ 'forceLanguage' ] = $this -> config -> getSystemValue ( 'force_language' , false );
2018-04-13 12:36:34 -04:00
// Settings
$serverData [ 'defaultQuota' ] = $defaultQuota ;
$serverData [ 'canChangePassword' ] = $canChangePassword ;
2019-06-21 04:18:44 -04:00
$serverData [ 'newUserGenerateUserID' ] = $this -> config -> getAppValue ( 'core' , 'newUser.generateUserID' , 'no' ) === 'yes' ;
$serverData [ 'newUserRequireEmail' ] = $this -> config -> getAppValue ( 'core' , 'newUser.requireEmail' , 'no' ) === 'yes' ;
2020-02-26 12:04:43 -05:00
$serverData [ 'newUserSendEmail' ] = $this -> config -> getAppValue ( 'core' , 'newUser.sendEmail' , 'yes' ) === 'yes' ;
2018-04-13 12:36:34 -04:00
2024-02-22 11:18:41 -05:00
$this -> initialState -> provideInitialState ( 'usersSettings' , $serverData );
\OCP\Util :: addStyle ( 'settings' , 'settings' );
\OCP\Util :: addScript ( 'settings' , 'vue-settings-apps-users-management' );
2024-02-25 07:48:38 -05:00
return new TemplateResponse ( 'settings' , 'settings/empty' , [ 'pageTitle' => $this -> l10n -> t ( 'Settings' )]);
2018-04-13 12:36:34 -04:00
}
2020-02-26 12:04:43 -05:00
/**
* @ param string $key
* @ param string $value
*
* @ return JSONResponse
*/
public function setPreference ( string $key , string $value ) : JSONResponse {
2024-03-25 18:45:39 -04:00
$allowed = [ 'newUser.sendEmail' , 'group.sortBy' ];
2020-02-26 12:04:43 -05:00
if ( ! in_array ( $key , $allowed , true )) {
return new JSONResponse ([], Http :: STATUS_FORBIDDEN );
}
$this -> config -> setAppValue ( 'core' , $key , $value );
return new JSONResponse ([]);
}
2020-02-22 14:02:56 -05:00
/**
* Parse the app value for quota_present
*
* @ param string $quotaPreset
* @ return array
*/
protected function parseQuotaPreset ( string $quotaPreset ) : array {
// 1 GB, 5 GB, 10 GB => [1 GB, 5 GB, 10 GB]
$presets = array_filter ( array_map ( 'trim' , explode ( ',' , $quotaPreset )));
// Drop default and none, Make array indexes numerically
return array_values ( array_diff ( $presets , [ 'default' , 'none' ]));
}
2018-12-20 05:11:04 -05:00
/**
* check if the admin can change the users password
*
* The admin can change the passwords if :
*
* - no encryption module is loaded and encryption is disabled
* - encryption module is loaded but it doesn ' t require per user keys
*
* The admin can not change the passwords if :
*
* - an encryption module is loaded and it uses per - user keys
* - encryption is enabled but no encryption modules are loaded
*
* @ return bool
*/
2020-12-01 08:33:22 -05:00
protected function canAdminChangeUserPasswords () : bool {
2018-12-20 05:11:04 -05:00
$isEncryptionEnabled = $this -> encryptionManager -> isEnabled ();
try {
2020-10-05 09:12:57 -04:00
$noUserSpecificEncryptionKeys = ! $this -> encryptionManager -> getEncryptionModule () -> needDetailedAccessList ();
2018-12-20 05:11:04 -05:00
$isEncryptionModuleLoaded = true ;
} catch ( ModuleDoesNotExistsException $e ) {
$noUserSpecificEncryptionKeys = true ;
$isEncryptionModuleLoaded = false ;
}
2020-11-05 07:21:19 -05:00
$canChangePassword = ( $isEncryptionModuleLoaded && $noUserSpecificEncryptionKeys )
|| ( ! $isEncryptionModuleLoaded && ! $isEncryptionEnabled );
2018-12-20 05:11:04 -05:00
return $canChangePassword ;
}
2018-04-13 12:36:34 -04:00
/**
* @ NoAdminRequired
2020-06-23 14:18:38 -04:00
* @ NoSubAdminRequired
2018-04-13 12:36:34 -04:00
* @ PasswordConfirmationRequired
*
2020-12-30 04:15:35 -05:00
* @ param string | null $avatarScope
* @ param string | null $displayname
* @ param string | null $displaynameScope
* @ param string | null $phone
* @ param string | null $phoneScope
* @ param string | null $email
* @ param string | null $emailScope
* @ param string | null $website
* @ param string | null $websiteScope
* @ param string | null $address
* @ param string | null $addressScope
* @ param string | null $twitter
* @ param string | null $twitterScope
2022-11-21 10:44:55 -05:00
* @ param string | null $fediverse
* @ param string | null $fediverseScope
2023-11-28 11:01:52 -05:00
* @ param string | null $birthdate
* @ param string | null $birthdateScope
2020-12-30 04:15:35 -05:00
*
2018-04-13 12:36:34 -04:00
* @ return DataResponse
*/
2020-12-30 04:15:35 -05:00
public function setUserSettings ( ? string $avatarScope = null ,
2023-11-23 04:22:34 -05:00
? string $displayname = null ,
? string $displaynameScope = null ,
? string $phone = null ,
? string $phoneScope = null ,
? string $email = null ,
? string $emailScope = null ,
? string $website = null ,
? string $websiteScope = null ,
? string $address = null ,
? string $addressScope = null ,
? string $twitter = null ,
? string $twitterScope = null ,
? string $fediverse = null ,
2023-11-28 11:01:52 -05:00
? string $fediverseScope = null ,
? string $birthdate = null ,
? string $birthdateScope = null ,
2018-04-13 12:36:34 -04:00
) {
2021-03-10 13:37:10 -05:00
$user = $this -> userSession -> getUser ();
if ( ! $user instanceof IUser ) {
return new DataResponse (
[
'status' => 'error' ,
'data' => [
2022-09-21 11:44:32 -04:00
'message' => $this -> l10n -> t ( 'Invalid account' )
2021-03-10 13:37:10 -05:00
]
],
Http :: STATUS_UNAUTHORIZED
);
}
2021-01-31 08:08:24 -05:00
$email = ! is_null ( $email ) ? strtolower ( $email ) : $email ;
2018-04-13 12:36:34 -04:00
if ( ! empty ( $email ) && ! $this -> mailer -> validateMailAddress ( $email )) {
return new DataResponse (
[
'status' => 'error' ,
'data' => [
'message' => $this -> l10n -> t ( 'Invalid mail address' )
]
],
Http :: STATUS_UNPROCESSABLE_ENTITY
);
}
2021-03-10 13:37:10 -05:00
2021-05-19 18:41:00 -04:00
$userAccount = $this -> accountManager -> getAccount ( $user );
$oldPhoneValue = $userAccount -> getProperty ( IAccountManager :: PROPERTY_PHONE ) -> getValue ();
$updatable = [
IAccountManager :: PROPERTY_AVATAR => [ 'value' => null , 'scope' => $avatarScope ],
IAccountManager :: PROPERTY_DISPLAYNAME => [ 'value' => $displayname , 'scope' => $displaynameScope ],
IAccountManager :: PROPERTY_EMAIL => [ 'value' => $email , 'scope' => $emailScope ],
IAccountManager :: PROPERTY_WEBSITE => [ 'value' => $website , 'scope' => $websiteScope ],
IAccountManager :: PROPERTY_ADDRESS => [ 'value' => $address , 'scope' => $addressScope ],
IAccountManager :: PROPERTY_PHONE => [ 'value' => $phone , 'scope' => $phoneScope ],
IAccountManager :: PROPERTY_TWITTER => [ 'value' => $twitter , 'scope' => $twitterScope ],
2022-11-21 10:44:55 -05:00
IAccountManager :: PROPERTY_FEDIVERSE => [ 'value' => $fediverse , 'scope' => $fediverseScope ],
2023-11-28 11:01:52 -05:00
IAccountManager :: PROPERTY_BIRTHDATE => [ 'value' => $birthdate , 'scope' => $birthdateScope ],
2021-05-19 18:41:00 -04:00
];
$allowUserToChangeDisplayName = $this -> config -> getSystemValueBool ( 'allow_user_to_change_display_name' , true );
foreach ( $updatable as $property => $data ) {
if ( $allowUserToChangeDisplayName === false
&& in_array ( $property , [ IAccountManager :: PROPERTY_DISPLAYNAME , IAccountManager :: PROPERTY_EMAIL ], true )) {
continue ;
2021-01-31 16:53:48 -05:00
}
2021-05-19 18:41:00 -04:00
$property = $userAccount -> getProperty ( $property );
2024-03-28 11:13:19 -04:00
if ( $data [ 'value' ] !== null ) {
2021-05-19 18:41:00 -04:00
$property -> setValue ( $data [ 'value' ]);
2021-01-31 16:53:48 -05:00
}
2024-03-28 11:13:19 -04:00
if ( $data [ 'scope' ] !== null ) {
2021-05-19 18:41:00 -04:00
$property -> setScope ( $data [ 'scope' ]);
2021-01-31 16:53:48 -05:00
}
}
2021-03-23 11:59:05 -04:00
2018-04-13 12:36:34 -04:00
try {
2021-05-19 18:41:00 -04:00
$this -> saveUserSettings ( $userAccount );
if ( $oldPhoneValue !== $userAccount -> getProperty ( IAccountManager :: PROPERTY_PHONE ) -> getValue ()) {
2021-03-10 14:30:29 -05:00
$this -> knownUserService -> deleteByContactUserId ( $user -> getUID ());
2021-03-10 13:37:10 -05:00
}
2018-04-13 12:36:34 -04:00
return new DataResponse (
[
'status' => 'success' ,
'data' => [
'userId' => $user -> getUID (),
2021-05-19 18:41:00 -04:00
'avatarScope' => $userAccount -> getProperty ( IAccountManager :: PROPERTY_AVATAR ) -> getScope (),
'displayname' => $userAccount -> getProperty ( IAccountManager :: PROPERTY_DISPLAYNAME ) -> getValue (),
'displaynameScope' => $userAccount -> getProperty ( IAccountManager :: PROPERTY_DISPLAYNAME ) -> getScope (),
'phone' => $userAccount -> getProperty ( IAccountManager :: PROPERTY_PHONE ) -> getValue (),
'phoneScope' => $userAccount -> getProperty ( IAccountManager :: PROPERTY_PHONE ) -> getScope (),
'email' => $userAccount -> getProperty ( IAccountManager :: PROPERTY_EMAIL ) -> getValue (),
'emailScope' => $userAccount -> getProperty ( IAccountManager :: PROPERTY_EMAIL ) -> getScope (),
'website' => $userAccount -> getProperty ( IAccountManager :: PROPERTY_WEBSITE ) -> getValue (),
'websiteScope' => $userAccount -> getProperty ( IAccountManager :: PROPERTY_WEBSITE ) -> getScope (),
'address' => $userAccount -> getProperty ( IAccountManager :: PROPERTY_ADDRESS ) -> getValue (),
'addressScope' => $userAccount -> getProperty ( IAccountManager :: PROPERTY_ADDRESS ) -> getScope (),
'twitter' => $userAccount -> getProperty ( IAccountManager :: PROPERTY_TWITTER ) -> getValue (),
'twitterScope' => $userAccount -> getProperty ( IAccountManager :: PROPERTY_TWITTER ) -> getScope (),
2022-11-21 10:44:55 -05:00
'fediverse' => $userAccount -> getProperty ( IAccountManager :: PROPERTY_FEDIVERSE ) -> getValue (),
'fediverseScope' => $userAccount -> getProperty ( IAccountManager :: PROPERTY_FEDIVERSE ) -> getScope (),
2023-11-28 11:01:52 -05:00
'birthdate' => $userAccount -> getProperty ( IAccountManager :: PROPERTY_BIRTHDATE ) -> getValue (),
'birthdateScope' => $userAccount -> getProperty ( IAccountManager :: PROPERTY_BIRTHDATE ) -> getScope (),
2021-05-19 18:41:00 -04:00
'message' => $this -> l10n -> t ( 'Settings saved' ),
],
2018-04-13 12:36:34 -04:00
],
Http :: STATUS_OK
);
2021-05-19 18:41:00 -04:00
} catch ( ForbiddenException | InvalidArgumentException | PropertyDoesNotExistException $e ) {
2020-12-01 09:38:43 -05:00
return new DataResponse ([
'status' => 'error' ,
'data' => [
'message' => $e -> getMessage ()
],
]);
2018-04-13 12:36:34 -04:00
}
}
/**
* update account manager with new user data
*
* @ throws ForbiddenException
2021-05-19 18:41:00 -04:00
* @ throws InvalidArgumentException
2018-04-13 12:36:34 -04:00
*/
2021-05-19 18:41:00 -04:00
protected function saveUserSettings ( IAccount $userAccount ) : void {
2018-04-13 12:36:34 -04:00
// keep the user back-end up-to-date with the latest display name and email
// address
2021-05-19 18:41:00 -04:00
$oldDisplayName = $userAccount -> getUser () -> getDisplayName ();
if ( $oldDisplayName !== $userAccount -> getProperty ( IAccountManager :: PROPERTY_DISPLAYNAME ) -> getValue ()) {
$result = $userAccount -> getUser () -> setDisplayName ( $userAccount -> getProperty ( IAccountManager :: PROPERTY_DISPLAYNAME ) -> getValue ());
2018-04-13 12:36:34 -04:00
if ( $result === false ) {
throw new ForbiddenException ( $this -> l10n -> t ( 'Unable to change full name' ));
}
}
2020-12-01 09:38:43 -05:00
2021-09-01 09:41:02 -04:00
$oldEmailAddress = $userAccount -> getUser () -> getSystemEMailAddress ();
2021-05-19 18:41:00 -04:00
$oldEmailAddress = strtolower (( string ) $oldEmailAddress );
2022-10-17 06:41:38 -04:00
if ( $oldEmailAddress !== strtolower ( $userAccount -> getProperty ( IAccountManager :: PROPERTY_EMAIL ) -> getValue ())) {
2018-04-13 12:36:34 -04:00
// this is the only permission a backend provides and is also used
// for the permission of setting a email address
2021-05-19 18:41:00 -04:00
if ( ! $userAccount -> getUser () -> canChangeDisplayName ()) {
2018-04-13 12:36:34 -04:00
throw new ForbiddenException ( $this -> l10n -> t ( 'Unable to change email address' ));
}
2021-09-01 09:41:02 -04:00
$userAccount -> getUser () -> setSystemEMailAddress ( $userAccount -> getProperty ( IAccountManager :: PROPERTY_EMAIL ) -> getValue ());
2018-04-13 12:36:34 -04:00
}
2020-12-01 09:38:43 -05:00
2020-12-02 10:03:08 -05:00
try {
2021-05-19 18:41:00 -04:00
$this -> accountManager -> updateAccount ( $userAccount );
} catch ( InvalidArgumentException $e ) {
2020-12-02 10:03:08 -05:00
if ( $e -> getMessage () === IAccountManager :: PROPERTY_PHONE ) {
2021-05-19 18:41:00 -04:00
throw new InvalidArgumentException ( $this -> l10n -> t ( 'Unable to set invalid phone number' ));
2020-12-01 09:38:43 -05:00
}
2021-03-23 09:52:04 -04:00
if ( $e -> getMessage () === IAccountManager :: PROPERTY_WEBSITE ) {
2021-05-19 18:41:00 -04:00
throw new InvalidArgumentException ( $this -> l10n -> t ( 'Unable to set invalid website' ));
2021-03-23 09:52:04 -04:00
}
2021-05-19 18:41:00 -04:00
throw new InvalidArgumentException ( $this -> l10n -> t ( 'Some account data was invalid' ));
2020-12-01 09:38:43 -05:00
}
2018-04-13 12:36:34 -04:00
}
/**
* Set the mail address of a user
*
* @ NoAdminRequired
2020-06-23 14:18:38 -04:00
* @ NoSubAdminRequired
2018-04-13 12:36:34 -04:00
* @ PasswordConfirmationRequired
*
* @ param string $account
* @ param bool $onlyVerificationCode only return verification code without updating the data
* @ return DataResponse
*/
public function getVerificationCode ( string $account , bool $onlyVerificationCode ) : DataResponse {
$user = $this -> userSession -> getUser ();
if ( $user === null ) {
return new DataResponse ([], Http :: STATUS_BAD_REQUEST );
}
2021-05-19 18:41:00 -04:00
$userAccount = $this -> accountManager -> getAccount ( $user );
2018-04-13 12:36:34 -04:00
$cloudId = $user -> getCloudId ();
$message = 'Use my Federated Cloud ID to share with me: ' . $cloudId ;
$signature = $this -> signMessage ( $user , $message );
$code = $message . ' ' . $signature ;
$codeMd5 = $message . ' ' . md5 ( $signature );
switch ( $account ) {
case 'verify-twitter' :
$msg = $this -> l10n -> t ( 'In order to verify your Twitter account, post the following tweet on Twitter (please make sure to post it without any line breaks):' );
$code = $codeMd5 ;
2020-12-01 08:33:22 -05:00
$type = IAccountManager :: PROPERTY_TWITTER ;
2018-04-13 12:36:34 -04:00
break ;
case 'verify-website' :
$msg = $this -> l10n -> t ( 'In order to verify your Website, store the following content in your web-root at \'.well-known/CloudIdVerificationCode.txt\' (please make sure that the complete text is in one line):' );
2020-12-01 08:33:22 -05:00
$type = IAccountManager :: PROPERTY_WEBSITE ;
2018-04-13 12:36:34 -04:00
break ;
default :
return new DataResponse ([], Http :: STATUS_BAD_REQUEST );
}
2021-05-19 18:41:00 -04:00
$userProperty = $userAccount -> getProperty ( $type );
$userProperty
-> setVerified ( IAccountManager :: VERIFICATION_IN_PROGRESS )
-> setVerificationData ( $signature );
2018-04-13 12:36:34 -04:00
if ( $onlyVerificationCode === false ) {
2021-05-19 18:41:00 -04:00
$this -> accountManager -> updateAccount ( $userAccount );
2018-04-13 12:36:34 -04:00
$this -> jobList -> add ( VerifyUserData :: class ,
[
'verificationCode' => $code ,
2021-05-19 18:41:00 -04:00
'data' => $userProperty -> getValue (),
2018-04-13 12:36:34 -04:00
'type' => $type ,
'uid' => $user -> getUID (),
'try' => 0 ,
'lastRun' => $this -> getCurrentTime ()
]
);
}
return new DataResponse ([ 'msg' => $msg , 'code' => $code ]);
}
/**
* get current timestamp
*
* @ return int
*/
protected function getCurrentTime () : int {
return time ();
}
/**
* sign message with users private key
*
* @ param IUser $user
* @ param string $message
*
* @ return string base64 encoded signature
*/
protected function signMessage ( IUser $user , string $message ) : string {
$privateKey = $this -> keyManager -> getKey ( $user ) -> getPrivate ();
openssl_sign ( json_encode ( $message ), $signature , $privateKey , OPENSSL_ALGO_SHA512 );
return base64_encode ( $signature );
}
}