Protection.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. <?php
  2. namespace PhpOffice\PhpSpreadsheet\Worksheet;
  3. use PhpOffice\PhpSpreadsheet\Shared\PasswordHasher;
  4. class Protection
  5. {
  6. const ALGORITHM_MD2 = 'MD2';
  7. const ALGORITHM_MD4 = 'MD4';
  8. const ALGORITHM_MD5 = 'MD5';
  9. const ALGORITHM_SHA_1 = 'SHA-1';
  10. const ALGORITHM_SHA_256 = 'SHA-256';
  11. const ALGORITHM_SHA_384 = 'SHA-384';
  12. const ALGORITHM_SHA_512 = 'SHA-512';
  13. const ALGORITHM_RIPEMD_128 = 'RIPEMD-128';
  14. const ALGORITHM_RIPEMD_160 = 'RIPEMD-160';
  15. const ALGORITHM_WHIRLPOOL = 'WHIRLPOOL';
  16. /**
  17. * Autofilters are locked when sheet is protected, default true.
  18. *
  19. * @var ?bool
  20. */
  21. private $autoFilter;
  22. /**
  23. * Deleting columns is locked when sheet is protected, default true.
  24. *
  25. * @var ?bool
  26. */
  27. private $deleteColumns;
  28. /**
  29. * Deleting rows is locked when sheet is protected, default true.
  30. *
  31. * @var ?bool
  32. */
  33. private $deleteRows;
  34. /**
  35. * Formatting cells is locked when sheet is protected, default true.
  36. *
  37. * @var ?bool
  38. */
  39. private $formatCells;
  40. /**
  41. * Formatting columns is locked when sheet is protected, default true.
  42. *
  43. * @var ?bool
  44. */
  45. private $formatColumns;
  46. /**
  47. * Formatting rows is locked when sheet is protected, default true.
  48. *
  49. * @var ?bool
  50. */
  51. private $formatRows;
  52. /**
  53. * Inserting columns is locked when sheet is protected, default true.
  54. *
  55. * @var ?bool
  56. */
  57. private $insertColumns;
  58. /**
  59. * Inserting hyperlinks is locked when sheet is protected, default true.
  60. *
  61. * @var ?bool
  62. */
  63. private $insertHyperlinks;
  64. /**
  65. * Inserting rows is locked when sheet is protected, default true.
  66. *
  67. * @var ?bool
  68. */
  69. private $insertRows;
  70. /**
  71. * Objects are locked when sheet is protected, default false.
  72. *
  73. * @var ?bool
  74. */
  75. private $objects;
  76. /**
  77. * Pivot tables are locked when the sheet is protected, default true.
  78. *
  79. * @var ?bool
  80. */
  81. private $pivotTables;
  82. /**
  83. * Scenarios are locked when sheet is protected, default false.
  84. *
  85. * @var ?bool
  86. */
  87. private $scenarios;
  88. /**
  89. * Selection of locked cells is locked when sheet is protected, default false.
  90. *
  91. * @var ?bool
  92. */
  93. private $selectLockedCells;
  94. /**
  95. * Selection of unlocked cells is locked when sheet is protected, default false.
  96. *
  97. * @var ?bool
  98. */
  99. private $selectUnlockedCells;
  100. /**
  101. * Sheet is locked when sheet is protected, default false.
  102. *
  103. * @var ?bool
  104. */
  105. private $sheet;
  106. /**
  107. * Sorting is locked when sheet is protected, default true.
  108. *
  109. * @var ?bool
  110. */
  111. private $sort;
  112. /**
  113. * Hashed password.
  114. *
  115. * @var string
  116. */
  117. private $password = '';
  118. /**
  119. * Algorithm name.
  120. *
  121. * @var string
  122. */
  123. private $algorithm = '';
  124. /**
  125. * Salt value.
  126. *
  127. * @var string
  128. */
  129. private $salt = '';
  130. /**
  131. * Spin count.
  132. *
  133. * @var int
  134. */
  135. private $spinCount = 10000;
  136. /**
  137. * Create a new Protection.
  138. */
  139. public function __construct()
  140. {
  141. }
  142. /**
  143. * Is some sort of protection enabled?
  144. */
  145. public function isProtectionEnabled(): bool
  146. {
  147. return
  148. $this->password !== '' ||
  149. isset($this->sheet) ||
  150. isset($this->objects) ||
  151. isset($this->scenarios) ||
  152. isset($this->formatCells) ||
  153. isset($this->formatColumns) ||
  154. isset($this->formatRows) ||
  155. isset($this->insertColumns) ||
  156. isset($this->insertRows) ||
  157. isset($this->insertHyperlinks) ||
  158. isset($this->deleteColumns) ||
  159. isset($this->deleteRows) ||
  160. isset($this->selectLockedCells) ||
  161. isset($this->sort) ||
  162. isset($this->autoFilter) ||
  163. isset($this->pivotTables) ||
  164. isset($this->selectUnlockedCells);
  165. }
  166. public function getSheet(): ?bool
  167. {
  168. return $this->sheet;
  169. }
  170. public function setSheet(?bool $sheet): self
  171. {
  172. $this->sheet = $sheet;
  173. return $this;
  174. }
  175. public function getObjects(): ?bool
  176. {
  177. return $this->objects;
  178. }
  179. public function setObjects(?bool $objects): self
  180. {
  181. $this->objects = $objects;
  182. return $this;
  183. }
  184. public function getScenarios(): ?bool
  185. {
  186. return $this->scenarios;
  187. }
  188. public function setScenarios(?bool $scenarios): self
  189. {
  190. $this->scenarios = $scenarios;
  191. return $this;
  192. }
  193. public function getFormatCells(): ?bool
  194. {
  195. return $this->formatCells;
  196. }
  197. public function setFormatCells(?bool $formatCells): self
  198. {
  199. $this->formatCells = $formatCells;
  200. return $this;
  201. }
  202. public function getFormatColumns(): ?bool
  203. {
  204. return $this->formatColumns;
  205. }
  206. public function setFormatColumns(?bool $formatColumns): self
  207. {
  208. $this->formatColumns = $formatColumns;
  209. return $this;
  210. }
  211. public function getFormatRows(): ?bool
  212. {
  213. return $this->formatRows;
  214. }
  215. public function setFormatRows(?bool $formatRows): self
  216. {
  217. $this->formatRows = $formatRows;
  218. return $this;
  219. }
  220. public function getInsertColumns(): ?bool
  221. {
  222. return $this->insertColumns;
  223. }
  224. public function setInsertColumns(?bool $insertColumns): self
  225. {
  226. $this->insertColumns = $insertColumns;
  227. return $this;
  228. }
  229. public function getInsertRows(): ?bool
  230. {
  231. return $this->insertRows;
  232. }
  233. public function setInsertRows(?bool $insertRows): self
  234. {
  235. $this->insertRows = $insertRows;
  236. return $this;
  237. }
  238. public function getInsertHyperlinks(): ?bool
  239. {
  240. return $this->insertHyperlinks;
  241. }
  242. public function setInsertHyperlinks(?bool $insertHyperLinks): self
  243. {
  244. $this->insertHyperlinks = $insertHyperLinks;
  245. return $this;
  246. }
  247. public function getDeleteColumns(): ?bool
  248. {
  249. return $this->deleteColumns;
  250. }
  251. public function setDeleteColumns(?bool $deleteColumns): self
  252. {
  253. $this->deleteColumns = $deleteColumns;
  254. return $this;
  255. }
  256. public function getDeleteRows(): ?bool
  257. {
  258. return $this->deleteRows;
  259. }
  260. public function setDeleteRows(?bool $deleteRows): self
  261. {
  262. $this->deleteRows = $deleteRows;
  263. return $this;
  264. }
  265. public function getSelectLockedCells(): ?bool
  266. {
  267. return $this->selectLockedCells;
  268. }
  269. public function setSelectLockedCells(?bool $selectLockedCells): self
  270. {
  271. $this->selectLockedCells = $selectLockedCells;
  272. return $this;
  273. }
  274. public function getSort(): ?bool
  275. {
  276. return $this->sort;
  277. }
  278. public function setSort(?bool $sort): self
  279. {
  280. $this->sort = $sort;
  281. return $this;
  282. }
  283. public function getAutoFilter(): ?bool
  284. {
  285. return $this->autoFilter;
  286. }
  287. public function setAutoFilter(?bool $autoFilter): self
  288. {
  289. $this->autoFilter = $autoFilter;
  290. return $this;
  291. }
  292. public function getPivotTables(): ?bool
  293. {
  294. return $this->pivotTables;
  295. }
  296. public function setPivotTables(?bool $pivotTables): self
  297. {
  298. $this->pivotTables = $pivotTables;
  299. return $this;
  300. }
  301. public function getSelectUnlockedCells(): ?bool
  302. {
  303. return $this->selectUnlockedCells;
  304. }
  305. public function setSelectUnlockedCells(?bool $selectUnlockedCells): self
  306. {
  307. $this->selectUnlockedCells = $selectUnlockedCells;
  308. return $this;
  309. }
  310. /**
  311. * Get hashed password.
  312. *
  313. * @return string
  314. */
  315. public function getPassword()
  316. {
  317. return $this->password;
  318. }
  319. /**
  320. * Set Password.
  321. *
  322. * @param string $password
  323. * @param bool $alreadyHashed If the password has already been hashed, set this to true
  324. *
  325. * @return $this
  326. */
  327. public function setPassword($password, $alreadyHashed = false)
  328. {
  329. if (!$alreadyHashed) {
  330. $salt = $this->generateSalt();
  331. $this->setSalt($salt);
  332. $password = PasswordHasher::hashPassword($password, $this->getAlgorithm(), $this->getSalt(), $this->getSpinCount());
  333. }
  334. $this->password = $password;
  335. return $this;
  336. }
  337. public function setHashValue(string $password): self
  338. {
  339. return $this->setPassword($password, true);
  340. }
  341. /**
  342. * Create a pseudorandom string.
  343. */
  344. private function generateSalt(): string
  345. {
  346. return base64_encode(random_bytes(16));
  347. }
  348. /**
  349. * Get algorithm name.
  350. */
  351. public function getAlgorithm(): string
  352. {
  353. return $this->algorithm;
  354. }
  355. /**
  356. * Set algorithm name.
  357. */
  358. public function setAlgorithm(string $algorithm): self
  359. {
  360. return $this->setAlgorithmName($algorithm);
  361. }
  362. /**
  363. * Set algorithm name.
  364. */
  365. public function setAlgorithmName(string $algorithm): self
  366. {
  367. $this->algorithm = $algorithm;
  368. return $this;
  369. }
  370. public function getSalt(): string
  371. {
  372. return $this->salt;
  373. }
  374. public function setSalt(string $salt): self
  375. {
  376. return $this->setSaltValue($salt);
  377. }
  378. public function setSaltValue(string $salt): self
  379. {
  380. $this->salt = $salt;
  381. return $this;
  382. }
  383. /**
  384. * Get spin count.
  385. */
  386. public function getSpinCount(): int
  387. {
  388. return $this->spinCount;
  389. }
  390. /**
  391. * Set spin count.
  392. */
  393. public function setSpinCount(int $spinCount): self
  394. {
  395. $this->spinCount = $spinCount;
  396. return $this;
  397. }
  398. /**
  399. * Verify that the given non-hashed password can "unlock" the protection.
  400. */
  401. public function verify(string $password): bool
  402. {
  403. if ($this->password === '') {
  404. return true;
  405. }
  406. $hash = PasswordHasher::hashPassword($password, $this->getAlgorithm(), $this->getSalt(), $this->getSpinCount());
  407. return $this->getPassword() === $hash;
  408. }
  409. /**
  410. * Implement PHP __clone to create a deep clone, not just a shallow copy.
  411. */
  412. public function __clone()
  413. {
  414. $vars = get_object_vars($this);
  415. foreach ($vars as $key => $value) {
  416. if (is_object($value)) {
  417. $this->$key = clone $value;
  418. } else {
  419. $this->$key = $value;
  420. }
  421. }
  422. }
  423. }