1+ <?php
2+
3+ if (PHP_SAPI !== 'cli ' ) {
4+ exit ("Use the console for running this script " );
5+ }
6+
7+ include_once '../system/autoload.php ' ;
8+
9+ use Aurora \Api ;
10+ use Symfony \Component \Console \Input \InputArgument ;
11+ use Symfony \Component \Console \Input \InputInterface ;
12+ use Symfony \Component \Console \Output \OutputInterface ;
13+ use Symfony \Component \Console \SingleCommandApplication ;
14+ use Symfony \Component \Console \Question \ConfirmationQuestion ;
15+ use Symfony \Component \Console \Helper \ProgressBar ;
16+ use Illuminate \Database \Capsule \Manager as Capsule ;
17+
18+ Api::Init ();
19+
20+ abstract class Enums
21+ {
22+ public const file = 1 ;
23+ public const console = 2 ;
24+ public const both = 3 ;
25+ }
26+ function logMessage ($ output , $ message , $ mode = Enums::both) {
27+ if ($ mode === Enums::console || $ mode === Enums::both) {
28+ $ output ->writeln ($ message );
29+ }
30+ if ($ mode === Enums::file || $ mode === Enums::both) {
31+ Api::Log ($ message , \Aurora \System \Enums \LogLevel::Full, 'update-encryption-key- ' );
32+ }
33+ }
34+
35+ function logObjectResults ($ output , $ data , $ title ) {
36+ logMessage ($ output , " $ title: " );
37+ foreach ($ data as $ propName => $ ids ) {
38+ logMessage ($ output , " Property: $ propName " );
39+ logMessage ($ output , " Ids: " . implode (', ' , $ ids ));
40+ }
41+ logMessage ($ output , "" );
42+ }
43+
44+ function updateEncryptedProp ($ class , $ shortClassName , $ propNames , $ count , $ output ) {
45+ $ progressBar = new ProgressBar ($ output , $ count );
46+ $ progressBar ->setFormat ('verbose ' );
47+ $ progressBar ->setBarCharacter ('<info>=</info> ' );
48+
49+ $ progressBar ->start ();
50+ $ aObjectResults = [
51+ 'missing ' => [],
52+ 'updated ' => [],
53+ 'error ' => [],
54+ 'empty ' => [],
55+ ];
56+
57+ foreach ($ propNames as $ propName ) {
58+ $ aObjectResults ['missing ' ][$ propName ] = [];
59+ $ aObjectResults ['updated ' ][$ propName ] = [];
60+ $ aObjectResults ['error ' ][$ propName ] = [];
61+ $ aObjectResults ['empty ' ][$ propName ] = [];
62+ }
63+
64+ $ class ::where ('Properties->EncryptionKeyIsUpdatedV2 ' , false )->orWhere ('Properties->EncryptionKeyIsUpdatedV2 ' , null )->chunk (10000 , function ($ items ) use ($ propNames , $ progressBar , &$ aObjectResults , $ output ) {
65+ foreach ($ items as $ item ) {
66+ $ bObjectError = false ;
67+ foreach ($ propNames as $ propName ) {
68+ if (strpos ($ propName , ':: ' ) !== false ) {
69+ $ propValue = $ item ->getExtendedProp ($ propName );
70+
71+ $ decryptedValue = \Aurora \System \Utils::DecryptValue ($ propValue );
72+
73+ if ($ decryptedValue ) {
74+ $ item ->setExtendedProp ($ propName , \Aurora \System \Utils::EncryptValue ($ decryptedValue ));
75+ //store updated item id
76+ $ aObjectResults ['updated ' ][$ propName ][] = $ item ->Id ;
77+ } else {
78+ //store failed item id
79+ $ bObjectError = true ;
80+ $ aObjectResults ['error ' ][$ propName ][] = $ item ->Id ;
81+ }
82+ } else {
83+ $ rawValue = trim ($ item ->getRawOriginal ($ propName ));
84+ if ($ rawValue !== '' ) {
85+ // Most of model properties are decrypted automatically when they are read.
86+ $ propValue = $ item ->{$ propName };
87+
88+ if ($ propValue ) {
89+ $ item ->{$ propName } = $ propValue ;
90+ //store updated item id
91+ $ aObjectResults ['updated ' ][$ propName ][] = $ item ->Id ;
92+ } elseif ($ propValue === false || $ rawValue !== '' && trim ($ propValue ) === '' ) {
93+ // false means decryption error, but currently auto encrypted fields return empty strings when value cannot be decrypted
94+ $ bObjectError = true ;
95+ $ aObjectResults ['error ' ][$ propName ][] = $ item ->Id ;
96+ } elseif ($ propValue === null ) {
97+ $ aObjectResults ['missing ' ][$ propName ][] = $ item ->Id ;
98+ } elseif (trim ($ propValue ) === '' ) {
99+ $ aObjectResults ['empty ' ][$ propName ][] = $ item ->Id ;
100+ }
101+ } else {
102+ $ aObjectResults ['empty ' ][$ propName ][] = $ item ->Id ;
103+ }
104+ }
105+ }
106+ $ item ->setExtendedProp ('EncryptionKeyIsUpdatedV2 ' , true );
107+ logMessage ($ output , "EncryptionKeyIsUpdatedV2: $ item ->Id : " . (!$ bObjectError ? "true " : "false " ));
108+ if ($ item ->save ()) {
109+ $ progressBar ->advance ();
110+ } else {
111+ logMessage ($ output , "Object saving error: $ item ->getName (): $ item ->Id " );
112+ }
113+ }
114+ });
115+
116+ $ progressBar ->finish ();
117+ logMessage ($ output , "" );
118+ logMessage ($ output , "' $ shortClassName' objects updating results: " );
119+
120+ logObjectResults ($ output , $ aObjectResults ['updated ' ], "Updated " );
121+ logObjectResults ($ output , $ aObjectResults ['error ' ], "Errors " );
122+ logObjectResults ($ output , $ aObjectResults ['missing ' ], "Missing " );
123+ logObjectResults ($ output , $ aObjectResults ['empty ' ], "Empty " );
124+ }
125+
126+ function updateEncryptedConfig ($ moduleName , $ configName , $ output ) {
127+ if (Api::$ oModuleManager ->isModuleLoaded ($ moduleName )) {
128+
129+ logMessage ($ output , "Processing $ moduleName-> $ configName: " );
130+
131+ $ configValue = Api::$ oModuleManager ->getModuleConfigValue ($ moduleName , $ configName );
132+ if ($ configValue ) {
133+ $ value = \Aurora \System \Utils::DecryptValue ($ configValue );
134+
135+ if ($ value ) {
136+ $ value = \Aurora \System \Utils::EncryptValue ($ value );
137+ Api::$ oModuleManager ->setModuleConfigValue ($ moduleName , $ configName , $ value );
138+ Api::$ oModuleManager ->saveModuleConfigValue ($ moduleName );
139+
140+ logMessage ($ output , "Config file updated " );
141+ } else {
142+ logMessage ($ output , "Can't decrypt config value " );
143+ }
144+ } else {
145+ logMessage ($ output , "Config value not found " );
146+ }
147+ }
148+ }
149+
150+ function processObject ($ class , $ props , $ input , $ output , $ helper , $ force ) {
151+
152+ $ classParts = explode ('\\' , $ class );
153+ $ shortClassName = end ($ classParts );
154+
155+ logMessage ($ output , "Processing $ class objects " );
156+
157+ if (class_exists ($ class )) {
158+ $ classTablename = with (new $ class )->getTable ();
159+ if (Capsule::schema ()->hasTable ($ classTablename )) {
160+ if ($ force ) {
161+ $ class ::where ('Properties->EncryptionKeyIsUpdatedV2 ' , true )->update (['Properties->EncryptionKeyIsUpdatedV2 ' => false ]);
162+ }
163+
164+ $ allObjectsCount = $ class ::count ();
165+ $ objectsCount = $ class ::where ('Properties->EncryptionKeyIsUpdatedV2 ' , false )->orWhere ('Properties->EncryptionKeyIsUpdatedV2 ' , null )->count ();
166+
167+ logMessage ($ output , $ allObjectsCount . ' object(s) found, ' . $ objectsCount . ' of them have not yet been updated ' );
168+ if ($ objectsCount > 0 ) {
169+ $ question = new ConfirmationQuestion ('Update encrypted properties for them? [yes] ' , true );
170+ if ($ helper ->ask ($ input , $ output , $ question )) {
171+ updateEncryptedProp ($ class , $ shortClassName , $ props , $ objectsCount , $ output );
172+ }
173+ } else {
174+ logMessage ($ output , 'No objects found ' );
175+ }
176+ } else {
177+ logMessage ($ output , "$ classTablename table not found " );
178+ }
179+ } else {
180+ logMessage ($ output , "$ shortClassName class not found " );
181+ }
182+ }
183+
184+ (new SingleCommandApplication ())
185+ ->setName ('Update encryption key script V2 ' ) // Optional
186+ ->setVersion ('2.0.0 ' ) // Optional
187+ ->addArgument ('force ' , InputArgument::OPTIONAL , 'Force reset EncryptionKeyIsUpdatedV2 flag for all objects ' )
188+ ->setCode (function (InputInterface $ input , OutputInterface $ output ) {
189+ $ helper = $ this ->getHelper ('question ' );
190+ $ force = $ input ->getArgument ('force ' );
191+
192+ // update encrypted data for classes
193+ $ objects = [
194+ "\Aurora\Modules\Mail\Models\MailAccount " => ['IncomingPassword ' ],
195+ "\Aurora\Modules\Mail\Models\Fetcher " => ['IncomingPassword ' ],
196+ "\Aurora\Modules\Mail\Models\Server " => ['SmtpPassword ' ],
197+ "\Aurora\Modules\StandardAuth\Models\Account " => ['Password ' ],
198+ "\Aurora\Modules\Core\Models\User " => ['TwoFactorAuth::BackupCodes ' , 'TwoFactorAuth::Secret ' , 'IframeAppWebclient::Password ' ]
199+ ];
200+
201+ foreach ($ objects as $ class => $ props ) {
202+ processObject ($ class , $ props , $ input , $ output , $ helper , $ force );
203+ logMessage ($ output , "" );
204+ }
205+
206+ // update encrypted data in configs
207+ $ question = new ConfirmationQuestion ('Update encrypted data in config files? [no] ' , false );
208+ if ($ helper ->ask ($ input , $ output , $ question )) {
209+ $ settings = [
210+ 'CpanelIntegrator ' => 'CpanelPassword ' ,
211+ 'LdapChangePasswordPlugin ' => 'BindPassword ' ,
212+ 'MailChangePasswordFastpanelPlugin ' => 'FastpanelAdminPass ' ,
213+ 'MailChangePasswordHmailserverPlugin ' => 'AdminPass ' ,
214+ 'MailChangePasswordIredmailPlugin ' => 'DbPass ' ,
215+ 'MailChangePasswordIspconfigPlugin ' => 'DbPass ' ,
216+ 'MailChangePasswordIspmanagerPlugin ' => 'ISPmanagerPass ' ,
217+ 'MailChangePasswordVirtualminPlugin ' => 'VirtualminAdminPass ' ,
218+ 'MailSignupDirectadmin ' => 'AdminPassword ' ,
219+ 'MailSignupFastpanel ' => 'FastpanelAdminPass ' ,
220+ 'MailSignupPlesk ' => 'PleskAdminPassword ' ,
221+ 'RocketChatWebclient ' => 'AdminPassword ' ,
222+ 'StandardResetPassword ' => 'NotificationPassword ' ,
223+ 'TeamContactsLdap ' => 'BindPassword ' ,
224+ ];
225+
226+ foreach ($ settings as $ moduleName => $ configName ) {
227+ updateEncryptedConfig ($ moduleName , $ configName , $ output );
228+ }
229+ logMessage ($ output , "" );
230+ }
231+ })->run ();
0 commit comments