Compare commits
886 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ac33c865b | ||
|
|
431638742c | ||
|
|
7b432cbbf7 | ||
|
|
01cede4511 | ||
|
|
73954c44c6 | ||
|
|
e345ae7b53 | ||
|
|
9fd53ef84f | ||
|
|
3e5a1ad76b | ||
|
|
a45c606b96 | ||
|
|
fcad7252bd | ||
|
|
c11edb9138 | ||
|
|
61852536fe | ||
|
|
d9d7774cc1 | ||
|
|
63e5282c41 | ||
|
|
0c3204c7a6 | ||
|
|
c2a72ce6b0 | ||
|
|
cbf8dd439c | ||
|
|
c658ec658a | ||
|
|
9db5323a92 | ||
|
|
c58501ee87 | ||
|
|
3e9d8cce10 | ||
|
|
6897be7a93 | ||
|
|
f0b0e462ea | ||
|
|
2b83afdd7e | ||
|
|
a81b4c54d8 | ||
|
|
7975b74816 | ||
|
|
361b70e4d7 | ||
|
|
8305ae6b09 | ||
|
|
91fc933432 | ||
|
|
ed60bdeac9 | ||
|
|
bd14af5806 | ||
|
|
234f33e01e | ||
|
|
441f807bce | ||
|
|
80560e70b7 | ||
|
|
56c51d481c | ||
|
|
2d2e024cfa | ||
|
|
491c714f54 | ||
|
|
8459b9f6fb | ||
|
|
153ff1b766 | ||
|
|
11efe732f9 | ||
|
|
f03a1868ab | ||
|
|
1c000b799b | ||
|
|
18d46dfdeb | ||
|
|
9d761fcaee | ||
|
|
5f70ed188b | ||
|
|
616fd4b54a | ||
|
|
6d87922cea | ||
|
|
0753514e5b | ||
|
|
7ef006e1c9 | ||
|
|
5a58d142e5 | ||
|
|
db51d9aacb | ||
|
|
d534f262aa | ||
|
|
0f04bfc319 | ||
|
|
4d42485bf5 | ||
|
|
713f2b3a52 | ||
|
|
0d39a63b05 | ||
|
|
bd1bf9e9ac | ||
|
|
6552fec113 | ||
|
|
d5190b5481 | ||
|
|
06bc987bd9 | ||
|
|
344606b2c8 | ||
|
|
a5574e885c | ||
|
|
bf1f9df590 | ||
|
|
2fd2b182e7 | ||
|
|
8778af681c | ||
|
|
fb63e75743 | ||
|
|
4c0af1b2a2 | ||
|
|
9c38baac9e | ||
|
|
dd18f963d4 | ||
|
|
5d46dfd2b4 | ||
|
|
1d54735d5d | ||
|
|
cb5f9d2164 | ||
|
|
a26ddb33a2 | ||
|
|
dd8ba68e07 | ||
|
|
bfe4ee6e47 | ||
|
|
a429fa74d9 | ||
|
|
f0a0e44d2e | ||
|
|
533e8e14b6 | ||
|
|
60099b91c4 | ||
|
|
5fd1d54607 | ||
|
|
0eb3496ab6 | ||
|
|
bd06b4cf42 | ||
|
|
72c0cc2267 | ||
|
|
43b6676d0e | ||
|
|
36e7d3b7da | ||
|
|
7a60651037 | ||
|
|
50705cfaf8 | ||
|
|
3720ba9318 | ||
|
|
a05fc9dc73 | ||
|
|
cf0a12f999 | ||
|
|
5fcc3401e2 | ||
|
|
60b2dd6ab4 | ||
|
|
ed96f8c628 | ||
|
|
77bcc5b2f3 | ||
|
|
1b1951a64f | ||
|
|
a33dcd359a | ||
|
|
55ebf024a7 | ||
|
|
21f467dd36 | ||
|
|
26e504e096 | ||
|
|
5d56eba398 | ||
|
|
0b80c5e18e | ||
|
|
be26cccd8a | ||
|
|
71bd33da98 | ||
|
|
3c673717b0 | ||
|
|
01a176f2a3 | ||
|
|
af1c47d1e8 | ||
|
|
913ad8634f | ||
|
|
79711c32b7 | ||
|
|
ddd832c2dc | ||
|
|
d30059d76c | ||
|
|
5fe7781452 | ||
|
|
8fc4f67f14 | ||
|
|
f5d9513957 | ||
|
|
3917383cef | ||
|
|
46a4af1dc2 | ||
|
|
2954169512 | ||
|
|
b122b285c9 | ||
|
|
85b7ee349b | ||
|
|
7484c2f0d8 | ||
|
|
2944f952dc | ||
|
|
9b414d81d8 | ||
|
|
5ac96c0a87 | ||
|
|
a60e113fa8 | ||
|
|
392e36d4e4 | ||
|
|
8b3728ed9b | ||
|
|
b96b601017 | ||
|
|
cdf37a061b | ||
|
|
b1f838814d | ||
|
|
161e58b1a2 | ||
|
|
d815638f7b | ||
|
|
384d1892aa | ||
|
|
32a90911b0 | ||
|
|
6967512c5f | ||
|
|
8ea0187ecd | ||
|
|
abc7f143da | ||
|
|
d7779f9209 | ||
|
|
4c5aa43079 | ||
|
|
11ca5588ea | ||
|
|
3ff1f879e0 | ||
|
|
74b68e10e4 | ||
|
|
ec853da5ad | ||
|
|
c494887d98 | ||
|
|
f64c6c9c9c | ||
|
|
56fabf8adf | ||
|
|
42ada6cb15 | ||
|
|
54607a2142 | ||
|
|
6a6b645b83 | ||
|
|
2f1270278d | ||
|
|
f235e8b803 | ||
|
|
baf8d8433f | ||
|
|
f5bd8e4947 | ||
|
|
d49e3d0ceb | ||
|
|
bc76998cd2 | ||
|
|
f371f14fc2 | ||
|
|
4e1799759c | ||
|
|
122f3ff594 | ||
|
|
a19fd8880d | ||
|
|
e3a9a19d95 | ||
|
|
75e8d2ad6e | ||
|
|
2dc688dcdf | ||
|
|
ce79974888 | ||
|
|
060a40ad43 | ||
|
|
ce3e5a8538 | ||
|
|
9384fa65b5 | ||
|
|
e6e0573756 | ||
|
|
fcde4c70a2 | ||
|
|
b7cf8fac34 | ||
|
|
0271e7539d | ||
|
|
3acd98f331 | ||
|
|
74a625155d | ||
|
|
643835e2b3 | ||
|
|
a843129a06 | ||
|
|
0ab77083c6 | ||
|
|
32401b42f1 | ||
|
|
4fbd6023f0 | ||
|
|
b35a5b5a85 | ||
|
|
135479cae2 | ||
|
|
29fd95967b | ||
|
|
c8a48272c5 | ||
|
|
2b3b4272f8 | ||
|
|
786007c78c | ||
|
|
1e5a39ea6c | ||
|
|
d859d1b6c7 | ||
|
|
778efcb054 | ||
|
|
f52662ab26 | ||
|
|
fea444b941 | ||
|
|
27c29c0c58 | ||
|
|
afc4224aff | ||
|
|
042bfabf64 | ||
|
|
e9ebbcb47e | ||
|
|
35a71cc9a5 | ||
|
|
ddccbb9530 | ||
|
|
5ef6c4e03d | ||
|
|
b6c868ec88 | ||
|
|
32625aff30 | ||
|
|
2fb1ed84f2 | ||
|
|
9255da9856 | ||
|
|
2db69a5328 | ||
|
|
4c46a2f162 | ||
|
|
6d7a5bf943 | ||
|
|
8d87a2c4a6 | ||
|
|
42197e2ce3 | ||
|
|
61c802935b | ||
|
|
b6334a2da5 | ||
|
|
acc547c5ff | ||
|
|
b509fdb30f | ||
|
|
3f35822e24 | ||
|
|
d5316623dc | ||
|
|
8339aeab9a | ||
|
|
f718e88aa1 | ||
|
|
38783b1ca3 | ||
|
|
56fade9a04 | ||
|
|
70e68280b8 | ||
|
|
840f5487aa | ||
|
|
920e991c82 | ||
|
|
67c3ab92cf | ||
|
|
3bc748d43f | ||
|
|
aeb9e172a9 | ||
|
|
1025c30885 | ||
|
|
4aae60a375 | ||
|
|
05a15a9669 | ||
|
|
43e01439d6 | ||
|
|
2261db62f5 | ||
|
|
496b62f2eb | ||
|
|
b5ab3d7fc0 | ||
|
|
321a631d92 | ||
|
|
a1ded6e9d7 | ||
|
|
68dd41a4c9 | ||
|
|
0d02aa7910 | ||
|
|
77a533a29d | ||
|
|
84e942f121 | ||
|
|
96c092ce14 | ||
|
|
ec3f92e102 | ||
|
|
15e357fa94 | ||
|
|
3ba1441c2d | ||
|
|
9c24dbbac7 | ||
|
|
9b908c3d05 | ||
|
|
dbfe5ef28a | ||
|
|
a3c33585e1 | ||
|
|
6a8c41754f | ||
|
|
3399930355 | ||
|
|
09d80ac9fc | ||
|
|
6995b96589 | ||
|
|
5624968d1e | ||
|
|
a5d6e6eb28 | ||
|
|
098b4e9e81 | ||
|
|
d0b3fe2414 | ||
|
|
3e02e79154 | ||
|
|
841998bdcb | ||
|
|
ff93c5859b | ||
|
|
7ffe80cf25 | ||
|
|
86daab2800 | ||
|
|
174ab07a4b | ||
|
|
9998861402 | ||
|
|
5b6ba39734 | ||
|
|
fb9c2b5362 | ||
|
|
a93e424738 | ||
|
|
d27db8cc61 | ||
|
|
0c6a58a883 | ||
|
|
d2b766ddee | ||
|
|
da10518278 | ||
|
|
9ed35a377f | ||
|
|
c8c182b36e | ||
|
|
f2dadc7104 | ||
|
|
ada93bab0c | ||
|
|
b3acb2a1e7 | ||
|
|
af72528570 | ||
|
|
5f69b2095a | ||
|
|
6e3a7ea140 | ||
|
|
38c817c7dd | ||
|
|
61873e4e21 | ||
|
|
6d4d5400d7 | ||
|
|
92d069ab6c | ||
|
|
d0101668ce | ||
|
|
73a5e3810f | ||
|
|
6d81789a66 | ||
|
|
e1e501b17f | ||
|
|
f53ecea372 | ||
|
|
15b39a6566 | ||
|
|
508ef23486 | ||
|
|
ff8d755702 | ||
|
|
9159598d32 | ||
|
|
361d6892c9 | ||
|
|
149b55f8ae | ||
|
|
e7a46ed9e7 | ||
|
|
ad06cd4b1f | ||
|
|
f03142f3d6 | ||
|
|
0497136d29 | ||
|
|
c7f26fa969 | ||
|
|
ad6e281586 | ||
|
|
595d43ab35 | ||
|
|
45738b6b7a | ||
|
|
ba33fd6a07 | ||
|
|
19585f9c3b | ||
|
|
da150afcc5 | ||
|
|
496b68e2ad | ||
|
|
d6a380613f | ||
|
|
c38454a5b2 | ||
|
|
e4fa642e54 | ||
|
|
697f461ded | ||
|
|
48a1e69f5e | ||
|
|
d71fae51f8 | ||
|
|
372676ee17 | ||
|
|
686a43ccf3 | ||
|
|
3ce828b4b7 | ||
|
|
b297ddf349 | ||
|
|
ecc7161611 | ||
|
|
f4c91f0e82 | ||
|
|
5a37703b3f | ||
|
|
088879c4ec | ||
|
|
7097201eab | ||
|
|
b089b85753 | ||
|
|
34c1760e9f | ||
|
|
140f3a7aa9 | ||
|
|
f1a99ebb6d | ||
|
|
bd205c58c7 | ||
|
|
a036cc56c4 | ||
|
|
1203580848 | ||
|
|
f534574b30 | ||
|
|
a11a75faba | ||
|
|
0d8d5cb3e6 | ||
|
|
4858b5e94a | ||
|
|
3d68d172af | ||
|
|
fe652258be | ||
|
|
24dbdd2b8e | ||
|
|
f8675f60e8 | ||
|
|
d6ee378564 | ||
|
|
335ad56ff8 | ||
|
|
7479a6ebf9 | ||
|
|
f0c4347330 | ||
|
|
d222d2b73f | ||
|
|
5b6f866756 | ||
|
|
cdeb7cb9b3 | ||
|
|
a7398bcfa8 | ||
|
|
f4e1e5f414 | ||
|
|
c9875e3959 | ||
|
|
a3c6b7a662 | ||
|
|
063d2032a7 | ||
|
|
47c05e048f | ||
|
|
c83a17e6c8 | ||
|
|
3e7bc72281 | ||
|
|
251e4d78b8 | ||
|
|
6b89f4b44d | ||
|
|
a766f5b633 | ||
|
|
7ee3ad411c | ||
|
|
bdfd3ca2bf | ||
|
|
4dcd1ed34f | ||
|
|
a045f8cd3a | ||
|
|
4e74983676 | ||
|
|
aedd64b5a6 | ||
|
|
b6e81b517f | ||
|
|
77b5e2e661 | ||
|
|
9411a6ea53 | ||
|
|
e0d1fa554f | ||
|
|
532f65116a | ||
|
|
30398370b8 | ||
|
|
413bd5406c | ||
|
|
056b97834d | ||
|
|
1f5f278501 | ||
|
|
2b91e628f5 | ||
|
|
c8dab5829c | ||
|
|
8089e0f9b8 | ||
|
|
7fd925e96d | ||
|
|
3a4ac2caa1 | ||
|
|
67b9edeeb7 | ||
|
|
25d003a52d | ||
|
|
a47f965b87 | ||
|
|
0484939fe0 | ||
|
|
69296befa6 | ||
|
|
bc5c608e4e | ||
|
|
02374e595a | ||
|
|
f788dffce8 | ||
|
|
d102cb4ffa | ||
|
|
83a06950f4 | ||
|
|
6c09c0e082 | ||
|
|
2376d69115 | ||
|
|
7eb0d6866c | ||
|
|
5ddacaed7f | ||
|
|
bd74ab2886 | ||
|
|
424cc31fd6 | ||
|
|
d388ef5c0a | ||
|
|
7d389b26f9 | ||
|
|
9254073786 | ||
|
|
bb703006da | ||
|
|
1371fecf26 | ||
|
|
1e5c786392 | ||
|
|
6d85cf2a0b | ||
|
|
3b1715c3fc | ||
|
|
6d747e9721 | ||
|
|
c5db1ef3cc | ||
|
|
27a630b0bc | ||
|
|
e25593db4e | ||
|
|
7edf912ef8 | ||
|
|
aab44694ad | ||
|
|
98268078c0 | ||
|
|
df97d8299a | ||
|
|
97dba2e022 | ||
|
|
b52154b306 | ||
|
|
64a9315b4a | ||
|
|
3fb55d9d2e | ||
|
|
094f5bcc78 | ||
|
|
79f5346105 | ||
|
|
5c0139fdff | ||
|
|
c1c70b306e | ||
|
|
4d261d9e15 | ||
|
|
257dbe61e2 | ||
|
|
d660aab116 | ||
|
|
e37f44e56a | ||
|
|
bdc26c6749 | ||
|
|
6d8fdcd7d2 | ||
|
|
0044c3bb6b | ||
|
|
1ec756d125 | ||
|
|
c31990e936 | ||
|
|
4e850128e6 | ||
|
|
00bb0e122f | ||
|
|
2013404e0a | ||
|
|
734112a5c1 | ||
|
|
fbcd80ac08 | ||
|
|
435b9c3028 | ||
|
|
838c849a5a | ||
|
|
85d7492398 | ||
|
|
a6eb638380 | ||
|
|
cb3bc5ad31 | ||
|
|
d9384cd959 | ||
|
|
bd7f6c9e18 | ||
|
|
bb69eebde4 | ||
|
|
cb39c55073 | ||
|
|
33737f9526 | ||
|
|
b3ddd39438 | ||
|
|
2b297567b5 | ||
|
|
d6276beac0 | ||
|
|
03bb3d2bdc | ||
|
|
44c7562c7b | ||
|
|
e556698db7 | ||
|
|
2bdf5a8b1a | ||
|
|
dfc6bfe131 | ||
|
|
c0d1b6bfb2 | ||
|
|
387638e317 | ||
|
|
2d3ea08e19 | ||
|
|
3ebd0b8983 | ||
|
|
984b6a28ac | ||
|
|
744b20bc0d | ||
|
|
8905e77d60 | ||
|
|
c16c680e32 | ||
|
|
13cee302f7 | ||
|
|
2aaad09062 | ||
|
|
996c68aa2e | ||
|
|
7f030eedde | ||
|
|
2e93300313 | ||
|
|
a385a367b0 | ||
|
|
97b83b9098 | ||
|
|
ce69e83b33 | ||
|
|
a2e9056832 | ||
|
|
260229b027 | ||
|
|
0c014409e6 | ||
|
|
2068eaf16b | ||
|
|
645e7a0c9e | ||
|
|
0afd79a22c | ||
|
|
6946962609 | ||
|
|
f8ecef2917 | ||
|
|
6c544b95e8 | ||
|
|
5637a581d2 | ||
|
|
83b9a89569 | ||
|
|
8580e2a697 | ||
|
|
7f45226b8e | ||
|
|
37632e428d | ||
|
|
7930440b31 | ||
|
|
749c519759 | ||
|
|
35cb14c95f | ||
|
|
28f462095d | ||
|
|
19a7aa081e | ||
|
|
82ccb04182 | ||
|
|
ebbd40385a | ||
|
|
03f9b14a23 | ||
|
|
8ed1762dfb | ||
|
|
76ff7ab007 | ||
|
|
9bf1a18512 | ||
|
|
86400ea4c8 | ||
|
|
ff6deb809a | ||
|
|
7f1416e62a | ||
|
|
a23396452f | ||
|
|
1981d220c5 | ||
|
|
572720fb7a | ||
|
|
0732131cd9 | ||
|
|
2ffcff228c | ||
|
|
bec08955b2 | ||
|
|
f040529e6d | ||
|
|
5ac620cb04 | ||
|
|
e26a5ff715 | ||
|
|
b7c7ec26c7 | ||
|
|
7f1e32764d | ||
|
|
3f5cefb07f | ||
|
|
463ad5a50d | ||
|
|
ec9517f98b | ||
|
|
36680a5a81 | ||
|
|
04597aedcb | ||
|
|
1a9e08ae4d | ||
|
|
dedb47390b | ||
|
|
95eb9bf8ae | ||
|
|
cc0a855103 | ||
|
|
19dd866c7e | ||
|
|
8e89c51e87 | ||
|
|
2a859f84a8 | ||
|
|
8869dcf1af | ||
|
|
ca121d8438 | ||
|
|
f5bac5fb2d | ||
|
|
c52f66cc73 | ||
|
|
5e5d9e0a80 | ||
|
|
b98c22d39b | ||
|
|
b609d36e3c | ||
|
|
a7968604d8 | ||
|
|
9b99c81a9d | ||
|
|
96bb1f7f45 | ||
|
|
85cde16feb | ||
|
|
1e4431ba01 | ||
|
|
994f36e498 | ||
|
|
640abbe099 | ||
|
|
1f2550e3da | ||
|
|
cfca88f20e | ||
|
|
8e77ca6201 | ||
|
|
41d8157937 | ||
|
|
6534304196 | ||
|
|
7bd4f6cac5 | ||
|
|
69cc2044f0 | ||
|
|
eb682a3022 | ||
|
|
e638a9a444 | ||
|
|
f268eee86c | ||
|
|
28e1480d1c | ||
|
|
81c4043d61 | ||
|
|
bd5796a0d1 | ||
|
|
919d19c906 | ||
|
|
459c78106d | ||
|
|
444e21ab15 | ||
|
|
b59d58fdc1 | ||
|
|
b26dd3c76b | ||
|
|
3d35c7edcc | ||
|
|
6eb1f7a474 | ||
|
|
f3f1d1ee73 | ||
|
|
11b2835cc3 | ||
|
|
8b86df308b | ||
|
|
98ac45fa78 | ||
|
|
617636d5a7 | ||
|
|
4d9fd199ef | ||
|
|
cc820eb824 | ||
|
|
135d40203c | ||
|
|
30cc4b9293 | ||
|
|
04e14ab5a6 | ||
|
|
a133833fb9 | ||
|
|
2b001adce0 | ||
|
|
5a8db83c48 | ||
|
|
a3dcbe5bce | ||
|
|
dac8dead07 | ||
|
|
09ab1f16c0 | ||
|
|
0a2e471163 | ||
|
|
a70fe184e7 | ||
|
|
e552573bed | ||
|
|
ffdcab6cd5 | ||
|
|
c38fcb520a | ||
|
|
af17de9467 | ||
|
|
6c1e19b386 | ||
|
|
2307a5a6b5 | ||
|
|
a3a97cf1f5 | ||
|
|
4e4b299ceb | ||
|
|
e9497182ba | ||
|
|
02ac579eae | ||
|
|
b94c88266b | ||
|
|
1f52e975fa | ||
|
|
a3c6d20ccb | ||
|
|
9caff0be96 | ||
|
|
c6e87acb96 | ||
|
|
992bd6dbf2 | ||
|
|
f74bf923ca | ||
|
|
899035bfd3 | ||
|
|
e311535ae1 | ||
|
|
b1b745c052 | ||
|
|
a9ce0edecb | ||
|
|
dce2e5e3b7 | ||
|
|
80d3f30ada | ||
|
|
7842014a68 | ||
|
|
2facba644e | ||
|
|
75236b0ee6 | ||
|
|
4d2ab79392 | ||
|
|
322cd65b9e | ||
|
|
c47a32d515 | ||
|
|
d9008f8ae4 | ||
|
|
22387b8346 | ||
|
|
9173a661bd | ||
|
|
c69215c115 | ||
|
|
efca0ab4d4 | ||
|
|
c4fd3dbd2a | ||
|
|
67399901dc | ||
|
|
23f4e7c1cb | ||
|
|
29e9cb51dc | ||
|
|
1519f018a4 | ||
|
|
2b449dd4fd | ||
|
|
be7cb7298a | ||
|
|
86009a564e | ||
|
|
9d5d0cca7a | ||
|
|
e0c62bbd64 | ||
|
|
f82467f845 | ||
|
|
cac56279c2 | ||
|
|
5e8733a9f6 | ||
|
|
e7cea79ee7 | ||
|
|
1a2bae347a | ||
|
|
ed6365af4a | ||
|
|
8cd5e652de | ||
|
|
5dd950bc0a | ||
|
|
e3cf107e1d | ||
|
|
4c6c22c4e3 | ||
|
|
35c133143a | ||
|
|
34e0259fa8 | ||
|
|
fe119300c7 | ||
|
|
9a83dbec76 | ||
|
|
bc1848933d | ||
|
|
e2f23b24f7 | ||
|
|
4593d9f819 | ||
|
|
0711ab5de7 | ||
|
|
1266ebdb10 | ||
|
|
1b4793db66 | ||
|
|
eb68b0d5c6 | ||
|
|
ca3d09c940 | ||
|
|
f979a0c09d | ||
|
|
09ed6b5ad4 | ||
|
|
113049b8f2 | ||
|
|
c8f81e6241 | ||
|
|
7aa35e9f24 | ||
|
|
9f9c18bd74 | ||
|
|
6af1a2d914 | ||
|
|
3432b2e386 | ||
|
|
5b27b01525 | ||
|
|
3bcca4f344 | ||
|
|
8972f74ecf | ||
|
|
1051460afc | ||
|
|
6907eb3eb0 | ||
|
|
d1658c1d2c | ||
|
|
cb0cdd1d74 | ||
|
|
5c89d9c9ee | ||
|
|
493b724935 | ||
|
|
b50a4e918b | ||
|
|
db7b245800 | ||
|
|
b3e2f6b457 | ||
|
|
db117bd468 | ||
|
|
8c892638f5 | ||
|
|
a8f24ed408 | ||
|
|
41fe3f2652 | ||
|
|
592a2dc82f | ||
|
|
18c023da16 | ||
|
|
68ba31fd4c | ||
|
|
0c057bf310 | ||
|
|
4748923dbc | ||
|
|
44ae0868fd | ||
|
|
8f8923a714 | ||
|
|
9ba8f5b604 | ||
|
|
2213127094 | ||
|
|
9598efc7cd | ||
|
|
8c56be5722 | ||
|
|
34963b0d89 | ||
|
|
2b9c093510 | ||
|
|
3c68defac2 | ||
|
|
8417f55b01 | ||
|
|
1ca1e1d4d1 | ||
|
|
5b88d3d3fc | ||
|
|
12c7ddc18f | ||
|
|
cc1d95cbe5 | ||
|
|
ef6a0254f9 | ||
|
|
d59b94fa4c | ||
|
|
079390c037 | ||
|
|
8a038eeeef | ||
|
|
7540a58ce7 | ||
|
|
97a62f6bc4 | ||
|
|
cc531fc905 | ||
|
|
2e04c5e956 | ||
|
|
ec79e5470a | ||
|
|
9b9fbc60c0 | ||
|
|
a1656abb4c | ||
|
|
8f8f3d1e43 | ||
|
|
3ebdb3593a | ||
|
|
5bc562d2ce | ||
|
|
039b1e3715 | ||
|
|
b21a1de07a | ||
|
|
aba52ff02f | ||
|
|
3e103f5bee | ||
|
|
5a57d2bd42 | ||
|
|
ed89a746e2 | ||
|
|
78af01393f | ||
|
|
f359e3432e | ||
|
|
c0f0e79b5e | ||
|
|
727374a6f3 | ||
|
|
a0f024fec2 | ||
|
|
32cec6cae4 | ||
|
|
57c954a345 | ||
|
|
24c8774b7f | ||
|
|
f68fa072c7 | ||
|
|
4f40cde66a | ||
|
|
19bad71da2 | ||
|
|
2e8f993299 | ||
|
|
5eca22d229 | ||
|
|
78ea700752 | ||
|
|
60b1a6e75f | ||
|
|
0b8135dcc4 | ||
|
|
8f9deb118a | ||
|
|
dc41f63930 | ||
|
|
185e41afdd | ||
|
|
eb665bf3c8 | ||
|
|
64094c5384 | ||
|
|
1cd1214b93 | ||
|
|
d677040bdc | ||
|
|
72459ca50a | ||
|
|
b4a379b7e3 | ||
|
|
59aa54ddb8 | ||
|
|
7287789588 | ||
|
|
bfb6b7ba90 | ||
|
|
a50c33a91f | ||
|
|
53c1e3d41b | ||
|
|
3b760141d6 | ||
|
|
8757a99f78 | ||
|
|
663e8bc5e2 | ||
|
|
2e28b7fff9 | ||
|
|
1831ae9c9b | ||
|
|
51cbe2a0ee | ||
|
|
02a61c0b6a | ||
|
|
7cd1a48222 | ||
|
|
0cabafb513 | ||
|
|
df0d00c8c6 | ||
|
|
2d7379da2c | ||
|
|
9cd741417a | ||
|
|
05301825e2 | ||
|
|
87ec3fbf1d | ||
|
|
c72f0e692b | ||
|
|
d28b4caa2f | ||
|
|
06bcf3db8d | ||
|
|
d6e61745c8 | ||
|
|
f33c49e2be | ||
|
|
c152ab4572 | ||
|
|
f75f1b4412 | ||
|
|
127aa309fb | ||
|
|
303e504fcb | ||
|
|
3c0f5d02ba | ||
|
|
faf0bfb29b | ||
|
|
c7d7ca455a | ||
|
|
555fcbdd7d | ||
|
|
69065ceecb | ||
|
|
61d9967221 | ||
|
|
a393670f7d | ||
|
|
9be9e777c2 | ||
|
|
0e732982ae | ||
|
|
508fd15975 | ||
|
|
e482ba60bc | ||
|
|
bad2d4d408 | ||
|
|
a77044da9e | ||
|
|
9c6e87849b | ||
|
|
f80f8d9cc4 | ||
|
|
00c0495703 | ||
|
|
e7f7ac38c9 | ||
|
|
6094da6c76 | ||
|
|
22c957d475 | ||
|
|
06158966f1 | ||
|
|
47d2e963be | ||
|
|
7403476489 | ||
|
|
981bd7da2a | ||
|
|
bab5de8e8f | ||
|
|
acf80ba7cc | ||
|
|
abe2c8ce76 | ||
|
|
0b8de8087b | ||
|
|
34cb09b777 | ||
|
|
ef202509f3 | ||
|
|
cce2cb578f | ||
|
|
8d851435b6 | ||
|
|
b1575eda3a | ||
|
|
fc6d1177b7 | ||
|
|
a66ee26187 | ||
|
|
e2f2313eb5 | ||
|
|
7886b900f1 | ||
|
|
b020e6b308 | ||
|
|
4de6896028 | ||
|
|
f5aa292587 | ||
|
|
0cde504e80 | ||
|
|
7e3a4b59a2 | ||
|
|
36039ca0c3 | ||
|
|
8af6b723bf | ||
|
|
0895324553 | ||
|
|
e0a69d4c0a | ||
|
|
198a30d964 | ||
|
|
a163b185ab | ||
|
|
4fa39283f6 | ||
|
|
ad249155ec | ||
|
|
ec30cc4f21 | ||
|
|
ca34fb9ea0 | ||
|
|
f40554fb2e | ||
|
|
cca04f3514 | ||
|
|
8ebe6ce7bb | ||
|
|
292ce3f484 | ||
|
|
eaa5c530de | ||
|
|
ebce1e1c41 | ||
|
|
d3b6a6333e | ||
|
|
c5c600bd7b | ||
|
|
078637130e | ||
|
|
3113e99c90 | ||
|
|
9fa495cf70 | ||
|
|
5a36841144 | ||
|
|
b51d54a84c | ||
|
|
a4a0ebf9db | ||
|
|
a58f85d3ee | ||
|
|
3de1d2cfff | ||
|
|
75d45c4e49 | ||
|
|
0acb76c97f | ||
|
|
8e8c6c9f72 | ||
|
|
334ace9080 | ||
|
|
8ec00dcb66 | ||
|
|
b7d717e47c | ||
|
|
9d3336002b | ||
|
|
c5278a421a | ||
|
|
36360a6e8a | ||
|
|
169b328d41 | ||
|
|
1a01debe68 | ||
|
|
1cc8be0701 | ||
|
|
bae4579d60 | ||
|
|
fae2a00922 | ||
|
|
9ebb037fc2 | ||
|
|
5bb42bedec | ||
|
|
e3d50804f7 | ||
|
|
14ea0ea3bc | ||
|
|
66130ad336 | ||
|
|
a310415b09 | ||
|
|
10bac56551 | ||
|
|
32c6afba90 | ||
|
|
31d8bce383 | ||
|
|
2fb022fbc3 | ||
|
|
5911188211 | ||
|
|
9ccadcfe80 | ||
|
|
9ee46bbe91 | ||
|
|
bc1ff4744b | ||
|
|
6043a90a71 | ||
|
|
0e62a7dc59 | ||
|
|
7a6b76f96e | ||
|
|
a78293dd3f | ||
|
|
1ce9ba1ebc | ||
|
|
2f15ae988f | ||
|
|
c02e95ff40 | ||
|
|
03c13021db | ||
|
|
01c0601d39 | ||
|
|
8bd5c6e04d | ||
|
|
76b310de9d | ||
|
|
ee2886331e | ||
|
|
ed1c918d9e | ||
|
|
18f5f85160 | ||
|
|
19dedf3d61 | ||
|
|
efadfedbaa | ||
|
|
60e3195600 | ||
|
|
db72e90504 | ||
|
|
cf982e9818 | ||
|
|
dccab5d20f | ||
|
|
f5e4ebf2ba | ||
|
|
2bfdd02c2a | ||
|
|
8b97073e13 | ||
|
|
d2fd78a0c9 | ||
|
|
59fd9d7517 | ||
|
|
5344d9beab | ||
|
|
127ce3c5d9 | ||
|
|
04604dae0d | ||
|
|
0fe1f25a9e | ||
|
|
a5d34b435f | ||
|
|
67cf1d61e1 | ||
|
|
5549148039 | ||
|
|
0319ee3894 | ||
|
|
2bd3aa6b21 | ||
|
|
36d2aab945 | ||
|
|
0a4e95cc07 | ||
|
|
b429a71660 | ||
|
|
b33c61798c | ||
|
|
11a2c0249d | ||
|
|
86545a90d0 | ||
|
|
71261decf1 | ||
|
|
f390ae53ba | ||
|
|
195cf273f8 | ||
|
|
3a1c7182e6 | ||
|
|
c272338897 | ||
|
|
74edbd5df0 | ||
|
|
c28fb2de4e | ||
|
|
b343b18cd9 | ||
|
|
2224d1c0d3 | ||
|
|
b42215a3c9 | ||
|
|
2115a9bf1a | ||
|
|
843ad18bf3 | ||
|
|
ed4ba00b77 | ||
|
|
4a7aaa8914 |
2
3rdparty
2
3rdparty
Submodule 3rdparty updated: 6ece897f44...4a46bb61a2
@@ -26,7 +26,10 @@ $success = true;
|
||||
|
||||
//Now delete
|
||||
foreach ($files as $file) {
|
||||
if (($dir === '' && $file === 'Shared') || !\OC\Files\Filesystem::unlink($dir . '/' . $file)) {
|
||||
if (\OC\Files\Filesystem::file_exists($dir . '/' . $file) &&
|
||||
!(\OC\Files\Filesystem::isDeletable($dir . '/' . $file) &&
|
||||
\OC\Files\Filesystem::unlink($dir . '/' . $file))
|
||||
) {
|
||||
$filesWithError .= $file . "\n";
|
||||
$success = false;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ try {
|
||||
|
||||
OCP\JSON::success(array('data' => $data));
|
||||
} catch (\OCP\Files\StorageNotAvailableException $e) {
|
||||
\OCP\Util::logException('files', $e);
|
||||
OCP\JSON::error(array(
|
||||
'data' => array(
|
||||
'exception' => '\OCP\Files\StorageNotAvailableException',
|
||||
@@ -39,6 +40,7 @@ try {
|
||||
)
|
||||
));
|
||||
} catch (\OCP\Files\StorageInvalidException $e) {
|
||||
\OCP\Util::logException('files', $e);
|
||||
OCP\JSON::error(array(
|
||||
'data' => array(
|
||||
'exception' => '\OCP\Files\StorageInvalidException',
|
||||
@@ -46,6 +48,7 @@ try {
|
||||
)
|
||||
));
|
||||
} catch (\Exception $e) {
|
||||
\OCP\Util::logException('files', $e);
|
||||
OCP\JSON::error(array(
|
||||
'data' => array(
|
||||
'exception' => '\Exception',
|
||||
|
||||
@@ -19,10 +19,16 @@ if(\OC\Files\Filesystem::file_exists($target . '/' . $file)) {
|
||||
if ($target != '' || strtolower($file) != 'shared') {
|
||||
$targetFile = \OC\Files\Filesystem::normalizePath($target . '/' . $file);
|
||||
$sourceFile = \OC\Files\Filesystem::normalizePath($dir . '/' . $file);
|
||||
if(\OC\Files\Filesystem::rename($sourceFile, $targetFile)) {
|
||||
OCP\JSON::success(array("data" => array( "dir" => $dir, "files" => $file )));
|
||||
} else {
|
||||
OCP\JSON::error(array("data" => array( "message" => $l->t("Could not move %s", array($file)) )));
|
||||
try {
|
||||
if(\OC\Files\Filesystem::rename($sourceFile, $targetFile)) {
|
||||
OCP\JSON::success(array("data" => array( "dir" => $dir, "files" => $file )));
|
||||
} else {
|
||||
OCP\JSON::error(array("data" => array( "message" => $l->t("Could not move %s", array($file)) )));
|
||||
}
|
||||
} catch (\OCP\Files\NotPermittedException $e) {
|
||||
OCP\JSON::error(array("data" => array( "message" => $l->t("Permission denied") )));
|
||||
} catch (\Exception $e) {
|
||||
OCP\JSON::error(array("data" => array( "message" => $e->getMessage())));
|
||||
}
|
||||
}else{
|
||||
OCP\JSON::error(array("data" => array( "message" => $l->t("Could not move %s", array($file)) )));
|
||||
|
||||
@@ -93,7 +93,8 @@ if (\OC\Files\Filesystem::file_exists($target)) {
|
||||
}
|
||||
|
||||
if($source) {
|
||||
if(substr($source, 0, 8)!='https://' and substr($source, 0, 7)!='http://') {
|
||||
$httpHelper = \OC::$server->getHTTPHelper();
|
||||
if(!$httpHelper->isHTTPURL($source)) {
|
||||
OCP\JSON::error(array('data' => array('message' => $l10n->t('Not a valid source'))));
|
||||
exit();
|
||||
}
|
||||
@@ -104,10 +105,39 @@ if($source) {
|
||||
exit();
|
||||
}
|
||||
|
||||
$ctx = stream_context_create(null, array('notification' =>'progress'));
|
||||
$source = $httpHelper->getFinalLocationOfURL($source);
|
||||
|
||||
$ctx = stream_context_create(\OC::$server->getHTTPHelper()->getDefaultContextArray(), array('notification' =>'progress'));
|
||||
|
||||
$sourceStream=@fopen($source, 'rb', false, $ctx);
|
||||
$result = 0;
|
||||
if (is_resource($sourceStream)) {
|
||||
$meta = stream_get_meta_data($sourceStream);
|
||||
if (isset($meta['wrapper_data']) && is_array($meta['wrapper_data'])) {
|
||||
//check stream size
|
||||
$storageStats = \OCA\Files\Helper::buildFileStorageStatistics($dir);
|
||||
$freeSpace = $storageStats['freeSpace'];
|
||||
|
||||
foreach($meta['wrapper_data'] as $header) {
|
||||
if (strpos($header, ':') === false){
|
||||
continue;
|
||||
}
|
||||
list($name, $value) = explode(':', $header);
|
||||
if ('content-length' === strtolower(trim($name))) {
|
||||
$length = (int) trim($value);
|
||||
|
||||
if ($length > $freeSpace) {
|
||||
$delta = $length - $freeSpace;
|
||||
$humanDelta = OCP\Util::humanFileSize($delta);
|
||||
|
||||
$eventSource->send('error', array('message' => (string)$l10n->t('The file exceeds your quota by %s', array($humanDelta))));
|
||||
$eventSource->close();
|
||||
fclose($sourceStream);
|
||||
exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$result=\OC\Files\Filesystem::file_put_contents($target, $sourceStream);
|
||||
}
|
||||
if($result) {
|
||||
|
||||
@@ -24,6 +24,8 @@ if (empty($_POST['dirToken'])) {
|
||||
// and the upload/file transfer code needs to be refactored into a utility method
|
||||
// that could be used there
|
||||
|
||||
\OC_User::setIncognitoMode(true);
|
||||
|
||||
// return only read permissions for public upload
|
||||
$allowedPermissions = OCP\PERMISSION_READ;
|
||||
$publicDirectory = !empty($_POST['subdir']) ? $_POST['subdir'] : '/';
|
||||
@@ -175,7 +177,7 @@ if (strpos($dir, '..') === false) {
|
||||
} catch(Exception $ex) {
|
||||
$error = $ex->getMessage();
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
// file already exists
|
||||
$meta = \OC\Files\Filesystem::getFileInfo($target);
|
||||
|
||||
@@ -17,4 +17,7 @@
|
||||
<webdav>appinfo/remote.php</webdav>
|
||||
<filesync>appinfo/filesync.php</filesync>
|
||||
</remote>
|
||||
<documentation>
|
||||
<user>user-files</user>
|
||||
</documentation>
|
||||
</info>
|
||||
|
||||
@@ -38,7 +38,7 @@ $server->setBaseUri($baseuri);
|
||||
$defaults = new OC_Defaults();
|
||||
$server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend, $defaults->getName()));
|
||||
$server->addPlugin(new \Sabre\DAV\Locks\Plugin($lockBackend));
|
||||
$server->addPlugin(new \Sabre\DAV\Browser\Plugin(false)); // Show something in the Browser, but no upload
|
||||
$server->addPlugin(new \Sabre\DAV\Browser\Plugin(false, false)); // Show something in the Browser, but no upload
|
||||
$server->addPlugin(new OC_Connector_Sabre_FilesPlugin());
|
||||
$server->addPlugin(new OC_Connector_Sabre_MaintenancePlugin());
|
||||
$server->addPlugin(new OC_Connector_Sabre_ExceptionLoggerPlugin('webdav'));
|
||||
@@ -53,7 +53,6 @@ $server->subscribeEvent('beforeMethod', function () use ($server, $objectTree) {
|
||||
$rootDir = new OC_Connector_Sabre_Directory($view, $rootInfo);
|
||||
$objectTree->init($rootDir, $view, $mountManager);
|
||||
|
||||
$server->addPlugin(new OC_Connector_Sabre_AbortedUploadDetectionPlugin($view));
|
||||
$server->addPlugin(new OC_Connector_Sabre_QuotaPlugin($view));
|
||||
}, 30); // priority 30: after auth (10) and acl(20), before lock(50) and handling the request
|
||||
|
||||
|
||||
@@ -77,7 +77,11 @@ class Scan extends Command {
|
||||
if (is_object($user)) {
|
||||
$user = $user->getUID();
|
||||
}
|
||||
$this->scanFiles($user, $output);
|
||||
if ($this->userManager->userExists($user)) {
|
||||
$this->scanFiles($user, $output);
|
||||
} else {
|
||||
$output->writeln("<error>Unknown user $user</error>");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,16 +152,24 @@ table th .columntitle.name {
|
||||
padding-right: 80px;
|
||||
margin-left: 50px;
|
||||
}
|
||||
/* hover effect on sortable column */
|
||||
table th a.columntitle:hover {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
table th .sort-indicator {
|
||||
width: 10px;
|
||||
height: 8px;
|
||||
margin-left: 10px;
|
||||
margin-left: 5px;
|
||||
display: inline-block;
|
||||
vertical-align: text-bottom;
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=30)";
|
||||
filter: alpha(opacity=30);
|
||||
opacity: .3;
|
||||
}
|
||||
.sort-indicator.hidden {
|
||||
visibility: hidden;
|
||||
}
|
||||
table th:hover .sort-indicator.hidden {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
table th, table td { border-bottom:1px solid #ddd; text-align:left; font-weight:normal; }
|
||||
table td {
|
||||
padding: 0 15px;
|
||||
@@ -231,8 +239,8 @@ table td.filename a.name {
|
||||
table tr[data-type="dir"] td.filename a.name span.nametext {font-weight:bold; }
|
||||
table td.filename input.filename {
|
||||
width: 70%;
|
||||
margin-top: 1px;
|
||||
margin-left: 48px;
|
||||
margin-top: 6px;
|
||||
margin-left: 7px;
|
||||
cursor: text;
|
||||
}
|
||||
table td.filename a, table td.login, table td.logout, table td.download, table td.upload, table td.create, table td.delete { padding:3px 8px 8px 3px; }
|
||||
@@ -345,14 +353,13 @@ table td.filename .uploadtext {
|
||||
#fileList tr td.filename>input[type="checkbox"] + label,
|
||||
.select-all + label {
|
||||
height: 50px;
|
||||
position: absolute;
|
||||
position: relative;
|
||||
width: 50px;
|
||||
z-index: 5;
|
||||
}
|
||||
#fileList tr td.filename>input[type="checkbox"]{
|
||||
/* sometimes checkbox height is bigger (KDE/Qt), so setting to absolute
|
||||
* to prevent it to increase the height */
|
||||
position: absolute;
|
||||
position: relative;
|
||||
z-index: 4;
|
||||
}
|
||||
#fileList tr td.filename>input[type="checkbox"] + label {
|
||||
left: 0;
|
||||
@@ -367,7 +374,6 @@ table td.filename .uploadtext {
|
||||
left: 18px;
|
||||
}
|
||||
|
||||
|
||||
#fileList tr td.filename {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
@@ -432,7 +438,6 @@ a.action>img {
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
|
||||
|
||||
#fileList a.action {
|
||||
display: inline;
|
||||
padding: 18px 8px;
|
||||
@@ -474,7 +479,7 @@ a.action>img {
|
||||
|
||||
.summary td {
|
||||
padding-top: 20px;
|
||||
padding-bottom: 250px;
|
||||
padding-bottom: 150px;
|
||||
border-bottom: none;
|
||||
}
|
||||
.summary .info {
|
||||
|
||||
@@ -137,3 +137,27 @@
|
||||
.oc-dialog .oc-dialog-buttonrow .cancel {
|
||||
float:left;
|
||||
}
|
||||
|
||||
.highlightUploaded {
|
||||
-webkit-animation: highlightAnimation 2s 1;
|
||||
-moz-animation: highlightAnimation 2s 1;
|
||||
-o-animation: highlightAnimation 2s 1;
|
||||
animation: highlightAnimation 2s 1;
|
||||
}
|
||||
|
||||
@-webkit-keyframes highlightAnimation {
|
||||
0% { background-color: rgba(255, 255, 140, 1); }
|
||||
100% { background-color: rgba(0, 0, 0, 0); }
|
||||
}
|
||||
@-moz-keyframes highlightAnimation {
|
||||
0% { background-color: rgba(255, 255, 140, 1); }
|
||||
100% { background-color: rgba(0, 0, 0, 0); }
|
||||
}
|
||||
@-o-keyframes highlightAnimation {
|
||||
0% { background-color: rgba(255, 255, 140, 1); }
|
||||
100% { background-color: rgba(0, 0, 0, 0); }
|
||||
}
|
||||
@keyframes highlightAnimation {
|
||||
0% { background-color: rgba(255, 255, 140, 1); }
|
||||
100% { background-color: rgba(0, 0, 0, 0); }
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ if(!\OC\Files\Filesystem::file_exists($filename)) {
|
||||
exit;
|
||||
}
|
||||
|
||||
$ftype=\OC\Files\Filesystem::getMimeType( $filename );
|
||||
$ftype=\OC_Helper::getSecureMimeType(\OC\Files\Filesystem::getMimeType( $filename ));
|
||||
|
||||
header('Content-Type:'.$ftype);
|
||||
OCP\Response::setContentDispositionHeader(basename($filename), 'attachment');
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
initialize: function() {
|
||||
this.navigation = new OCA.Files.Navigation($('#app-navigation'));
|
||||
|
||||
var urlParams = OC.Util.History.parseUrlQuery();
|
||||
var fileActions = new OCA.Files.FileActions();
|
||||
// default actions
|
||||
fileActions.registerDefaultActions();
|
||||
@@ -32,9 +33,11 @@
|
||||
// regular actions
|
||||
fileActions.merge(OCA.Files.fileActions);
|
||||
|
||||
// in case apps would decide to register file actions later,
|
||||
// replace the global object with this one
|
||||
OCA.Files.fileActions = fileActions;
|
||||
this._onActionsUpdated = _.bind(this._onActionsUpdated, this);
|
||||
OCA.Files.fileActions.on('setDefault.app-files', this._onActionsUpdated);
|
||||
OCA.Files.fileActions.on('registerAction.app-files', this._onActionsUpdated);
|
||||
window.FileActions.on('setDefault.app-files', this._onActionsUpdated);
|
||||
window.FileActions.on('registerAction.app-files', this._onActionsUpdated);
|
||||
|
||||
this.files = OCA.Files.Files;
|
||||
|
||||
@@ -45,7 +48,8 @@
|
||||
dragOptions: dragOptions,
|
||||
folderDropOptions: folderDropOptions,
|
||||
fileActions: fileActions,
|
||||
allowLegacyActions: true
|
||||
allowLegacyActions: true,
|
||||
scrollTo: urlParams.scrollto
|
||||
}
|
||||
);
|
||||
this.files.initialize();
|
||||
@@ -56,7 +60,33 @@
|
||||
|
||||
this._setupEvents();
|
||||
// trigger URL change event handlers
|
||||
this._onPopState(OC.Util.History.parseUrlQuery());
|
||||
this._onPopState(urlParams);
|
||||
},
|
||||
|
||||
/**
|
||||
* Destroy the app
|
||||
*/
|
||||
destroy: function() {
|
||||
this.navigation = null;
|
||||
this.fileList.destroy();
|
||||
this.fileList = null;
|
||||
this.files = null;
|
||||
OCA.Files.fileActions.off('setDefault.app-files', this._onActionsUpdated);
|
||||
OCA.Files.fileActions.off('registerAction.app-files', this._onActionsUpdated);
|
||||
window.FileActions.off('setDefault.app-files', this._onActionsUpdated);
|
||||
window.FileActions.off('registerAction.app-files', this._onActionsUpdated);
|
||||
},
|
||||
|
||||
_onActionsUpdated: function(ev, newAction) {
|
||||
// forward new action to the file list
|
||||
if (ev.action) {
|
||||
this.fileList.fileActions.registerAction(ev.action);
|
||||
} else if (ev.defaultAction) {
|
||||
this.fileList.fileActions.setDefault(
|
||||
ev.defaultAction.mime,
|
||||
ev.defaultAction.name
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -233,7 +233,8 @@ OC.Upload = {
|
||||
data.originalFiles.selection = {
|
||||
uploads: [],
|
||||
filesToUpload: data.originalFiles.length,
|
||||
totalBytes: 0
|
||||
totalBytes: 0,
|
||||
biggestFileBytes: 0
|
||||
};
|
||||
}
|
||||
var selection = data.originalFiles.selection;
|
||||
@@ -273,13 +274,15 @@ OC.Upload = {
|
||||
|
||||
// add size
|
||||
selection.totalBytes += file.size;
|
||||
// update size of biggest file
|
||||
selection.biggestFileBytes = Math.max(selection.biggestFileBytes, file.size);
|
||||
|
||||
// check PHP upload limit
|
||||
if (selection.totalBytes > $('#upload_limit').val()) {
|
||||
// check PHP upload limit against biggest file
|
||||
if (selection.biggestFileBytes > $('#upload_limit').val()) {
|
||||
data.textStatus = 'sizeexceedlimit';
|
||||
data.errorThrown = t('files',
|
||||
'Total file size {size1} exceeds upload limit {size2}', {
|
||||
'size1': humanFileSize(selection.totalBytes),
|
||||
'size1': humanFileSize(selection.biggestFileBytes),
|
||||
'size2': humanFileSize($('#upload_limit').val())
|
||||
});
|
||||
}
|
||||
@@ -424,6 +427,14 @@ OC.Upload = {
|
||||
data.textStatus = 'servererror';
|
||||
data.errorThrown = result[0].data.message; // error message has been translated on server
|
||||
fu._trigger('fail', e, data);
|
||||
} else { // Successful upload
|
||||
// Checking that the uploaded file is the last one and contained in the current directory
|
||||
if (data.files[0] === data.originalFiles[data.originalFiles.length - 1] &&
|
||||
result[0].directory === FileList.getCurrentDirectory()) {
|
||||
// Scroll to the last uploaded file and highlight all of them
|
||||
var fileList = _.pluck(data.originalFiles, 'name');
|
||||
FileList.highlightFiles(fileList);
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
@@ -479,6 +490,21 @@ OC.Upload = {
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
// for all browsers that don't support the progress bar
|
||||
// IE 8 & 9
|
||||
|
||||
// show a spinner
|
||||
fileupload.on('fileuploadstart', function() {
|
||||
$('#upload').addClass('icon-loading');
|
||||
$('#upload .icon-upload').hide();
|
||||
});
|
||||
|
||||
// hide a spinner
|
||||
fileupload.on('fileuploadstop fileuploadfail', function() {
|
||||
$('#upload').removeClass('icon-loading');
|
||||
$('#upload .icon-upload').show();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -618,7 +644,7 @@ OC.Upload = {
|
||||
},
|
||||
function(result) {
|
||||
if (result.status === 'success') {
|
||||
FileList.add(result.data, {hidden: hidden, animate: true});
|
||||
FileList.add(result.data, {hidden: hidden, animate: true, scrollTo: true});
|
||||
} else {
|
||||
OC.dialogs.alert(result.data.message, t('core', 'Could not create file'));
|
||||
}
|
||||
@@ -634,7 +660,7 @@ OC.Upload = {
|
||||
},
|
||||
function(result) {
|
||||
if (result.status === 'success') {
|
||||
FileList.add(result.data, {hidden: hidden, animate: true});
|
||||
FileList.add(result.data, {hidden: hidden, animate: true, scrollTo: true});
|
||||
} else {
|
||||
OC.dialogs.alert(result.data.message, t('core', 'Could not create folder'));
|
||||
}
|
||||
|
||||
@@ -23,48 +23,52 @@
|
||||
icons: {},
|
||||
currentFile: null,
|
||||
|
||||
/**
|
||||
* Dummy jquery element, for events
|
||||
*/
|
||||
$el: null,
|
||||
|
||||
/**
|
||||
* List of handlers to be notified whenever a register() or
|
||||
* setDefault() was called.
|
||||
*/
|
||||
_updateListeners: [],
|
||||
_updateListeners: {},
|
||||
|
||||
initialize: function() {
|
||||
this.clear();
|
||||
// abusing jquery for events until we get a real event lib
|
||||
this.$el = $('<div class="dummy-fileactions hidden"></div>');
|
||||
$('body').append(this.$el);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds an update listener to be notified whenever register()
|
||||
* or setDefault() has been called.
|
||||
* Adds an event handler
|
||||
*
|
||||
* @param {String} eventName event name
|
||||
* @param Function callback
|
||||
*/
|
||||
addUpdateListener: function(callback) {
|
||||
if (!_.isFunction(callback)) {
|
||||
throw 'Argument passed to FileActions.addUpdateListener must be a function';
|
||||
}
|
||||
this._updateListeners.push(callback);
|
||||
on: function(eventName, callback) {
|
||||
this.$el.on(eventName, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes an update listener.
|
||||
* Removes an event handler
|
||||
*
|
||||
* @param {String} eventName event name
|
||||
* @param Function callback
|
||||
*/
|
||||
removeUpdateListener: function(callback) {
|
||||
if (!_.isFunction(callback)) {
|
||||
throw 'Argument passed to FileActions.removeUpdateListener must be a function';
|
||||
}
|
||||
this._updateListeners = _.without(this._updateListeners, callback);
|
||||
off: function(eventName, callback) {
|
||||
this.$el.off(eventName, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Notifies the registered update listeners
|
||||
* Notifies the event handlers
|
||||
*
|
||||
* @param {String} eventName event name
|
||||
* @param {Object} data data
|
||||
*/
|
||||
_notifyUpdateListeners: function() {
|
||||
for (var i = 0; i < this._updateListeners.length; i++) {
|
||||
this._updateListeners[i](this);
|
||||
}
|
||||
_notifyUpdateListeners: function(eventName, data) {
|
||||
this.$el.trigger(new $.Event(eventName, data));
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -87,21 +91,44 @@
|
||||
this.defaults = _.extend(this.defaults, fileActions.defaults);
|
||||
this.icons = _.extend(this.icons, fileActions.icons);
|
||||
},
|
||||
register: function (mime, name, permissions, icon, action, displayName) {
|
||||
/**
|
||||
* @deprecated use #registerAction() instead
|
||||
*/
|
||||
register: function(mime, name, permissions, icon, action, displayName) {
|
||||
return this.registerAction({
|
||||
name: name,
|
||||
mime: mime,
|
||||
permissions: permissions,
|
||||
icon: icon,
|
||||
actionHandler: action,
|
||||
displayName: displayName
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Register action
|
||||
*
|
||||
* @param {Object} action action object
|
||||
* @param {String} action.name identifier of the action
|
||||
* @param {String} action.displayName display name of the action, defaults
|
||||
* to the name given in action.name
|
||||
* @param {String} action.mime mime type
|
||||
* @param {int} action.permissions permissions
|
||||
* @param {(Function|String)} action.icon icon
|
||||
* @param {Function} action.actionHandler function that performs the action
|
||||
*/
|
||||
registerAction: function (action) {
|
||||
var mime = action.mime;
|
||||
var name = action.name;
|
||||
if (!this.actions[mime]) {
|
||||
this.actions[mime] = {};
|
||||
}
|
||||
if (!this.actions[mime][name]) {
|
||||
this.actions[mime][name] = {};
|
||||
}
|
||||
if (!displayName) {
|
||||
displayName = t('files', name);
|
||||
}
|
||||
this.actions[mime][name]['action'] = action;
|
||||
this.actions[mime][name]['permissions'] = permissions;
|
||||
this.actions[mime][name]['displayName'] = displayName;
|
||||
this.icons[name] = icon;
|
||||
this._notifyUpdateListeners();
|
||||
this.actions[mime][name] = {
|
||||
action: action.actionHandler,
|
||||
permissions: action.permissions,
|
||||
displayName: action.displayName || t('files', name)
|
||||
};
|
||||
this.icons[name] = action.icon;
|
||||
this._notifyUpdateListeners('registerAction', {action: action});
|
||||
},
|
||||
clear: function() {
|
||||
this.actions = {};
|
||||
@@ -112,7 +139,7 @@
|
||||
},
|
||||
setDefault: function (mime, name) {
|
||||
this.defaults[mime] = name;
|
||||
this._notifyUpdateListeners();
|
||||
this._notifyUpdateListeners('setDefault', {defaultAction: {mime: mime, name: name}});
|
||||
},
|
||||
get: function (mime, type, permissions) {
|
||||
var actions = this.getActions(mime, type, permissions);
|
||||
@@ -264,14 +291,20 @@
|
||||
if (actions['Delete']) {
|
||||
var img = self.icons['Delete'];
|
||||
var html;
|
||||
var mountType = $tr.attr('data-mounttype');
|
||||
var deleteTitle = t('files', 'Delete');
|
||||
if (mountType === 'external-root') {
|
||||
deleteTitle = t('files', 'Disconnect storage');
|
||||
} else if (mountType === 'shared-root') {
|
||||
deleteTitle = t('files', 'Unshare');
|
||||
} else if (fileList.id === 'trashbin') {
|
||||
deleteTitle = t('files', 'Delete permanently');
|
||||
}
|
||||
|
||||
if (img.call) {
|
||||
img = img(file);
|
||||
}
|
||||
if (typeof trashBinApp !== 'undefined' && trashBinApp) {
|
||||
html = '<a href="#" original-title="' + t('files', 'Delete permanently') + '" class="action delete delete-icon" />';
|
||||
} else {
|
||||
html = '<a href="#" original-title="' + t('files', 'Delete') + '" class="action delete delete-icon" />';
|
||||
}
|
||||
html = '<a href="#" original-title="' + escapeHTML(deleteTitle) + '" class="action delete delete-icon" />';
|
||||
var element = $(html);
|
||||
element.data('action', actions['Delete']);
|
||||
element.on('click', {a: null, elem: parent, actionFunc: actions['Delete'].action}, actionHandler);
|
||||
@@ -314,7 +347,7 @@
|
||||
});
|
||||
|
||||
this.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename, context) {
|
||||
var dir = context.fileList.getCurrentDirectory();
|
||||
var dir = context.$file.attr('data-path') || context.fileList.getCurrentDirectory();
|
||||
if (dir !== '/') {
|
||||
dir = dir + '/';
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
this.initialize($el, options);
|
||||
};
|
||||
FileList.prototype = {
|
||||
SORT_INDICATOR_ASC_CLASS: 'icon-triangle-s',
|
||||
SORT_INDICATOR_DESC_CLASS: 'icon-triangle-n',
|
||||
SORT_INDICATOR_ASC_CLASS: 'icon-triangle-n',
|
||||
SORT_INDICATOR_DESC_CLASS: 'icon-triangle-s',
|
||||
|
||||
id: 'files',
|
||||
appName: t('files', 'Files'),
|
||||
@@ -49,8 +49,10 @@
|
||||
fileSummary: null,
|
||||
initialized: false,
|
||||
|
||||
// number of files per page
|
||||
pageSize: 20,
|
||||
// number of files per page, calculated dynamically
|
||||
pageSize: function() {
|
||||
return Math.ceil(this.$container.height() / 50);
|
||||
},
|
||||
|
||||
/**
|
||||
* Array of files in the current folder.
|
||||
@@ -103,9 +105,10 @@
|
||||
* @param $el container element with existing markup for the #controls
|
||||
* and a table
|
||||
* @param options map of options, see other parameters
|
||||
* @param scrollContainer scrollable container, defaults to $(window)
|
||||
* @param dragOptions drag options, disabled by default
|
||||
* @param folderDropOptions folder drop options, disabled by default
|
||||
* @param options.scrollContainer scrollable container, defaults to $(window)
|
||||
* @param options.dragOptions drag options, disabled by default
|
||||
* @param options.folderDropOptions folder drop options, disabled by default
|
||||
* @param options.scrollTo name of file to scroll to after the first load
|
||||
*/
|
||||
initialize: function($el, options) {
|
||||
var self = this;
|
||||
@@ -165,6 +168,12 @@
|
||||
this.setupUploadEvents();
|
||||
|
||||
this.$container.on('scroll', _.bind(this._onScroll, this));
|
||||
|
||||
if (options.scrollTo) {
|
||||
this.$fileList.one('updated', function() {
|
||||
self.scrollTo(options.scrollTo);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -172,7 +181,8 @@
|
||||
*/
|
||||
destroy: function() {
|
||||
// TODO: also unregister other event handlers
|
||||
this.fileActions.removeUpdateListener(this._onFileActionsUpdated);
|
||||
this.fileActions.off('registerAction', this._onFileActionsUpdated);
|
||||
this.fileActions.off('setDefault', this._onFileActionsUpdated);
|
||||
},
|
||||
|
||||
_initFileActions: function(fileActions) {
|
||||
@@ -182,7 +192,8 @@
|
||||
this.fileActions.registerDefaultActions();
|
||||
}
|
||||
this._onFileActionsUpdated = _.debounce(_.bind(this._onFileActionsUpdated, this), 100);
|
||||
this.fileActions.addUpdateListener(this._onFileActionsUpdated);
|
||||
this.fileActions.on('registerAction', this._onFileActionsUpdated);
|
||||
this.fileActions.on('setDefault', this._onFileActionsUpdated);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -336,7 +347,6 @@
|
||||
else {
|
||||
files = _.pluck(this.getSelectedFiles(), 'name');
|
||||
}
|
||||
OC.Notification.show(t('files','Your download is being prepared. This might take some time if the files are big.'));
|
||||
OC.redirect(this.getDownloadUrl(files, dir));
|
||||
return false;
|
||||
},
|
||||
@@ -369,7 +379,12 @@
|
||||
this.setSort(sort, (this._sortDirection === 'desc')?'asc':'desc');
|
||||
}
|
||||
else {
|
||||
this.setSort(sort, 'asc');
|
||||
if ( sort === 'name' ) { //default sorting of name is opposite to size and mtime
|
||||
this.setSort(sort, 'asc');
|
||||
}
|
||||
else {
|
||||
this.setSort(sort, 'desc');
|
||||
}
|
||||
}
|
||||
this.reload();
|
||||
}
|
||||
@@ -393,7 +408,7 @@
|
||||
* This appends/renders the next page of entries when reaching the bottom.
|
||||
*/
|
||||
_onScroll: function(e) {
|
||||
if (this.$container.scrollTop() + this.$container.height() > this.$el.height() - 100) {
|
||||
if (this.$container.scrollTop() + this.$container.height() > this.$el.height() - 300) {
|
||||
this._nextPage(true);
|
||||
}
|
||||
},
|
||||
@@ -466,7 +481,8 @@
|
||||
mimetype: $el.attr('data-mime'),
|
||||
type: $el.attr('data-type'),
|
||||
size: parseInt($el.attr('data-size'), 10),
|
||||
etag: $el.attr('data-etag')
|
||||
etag: $el.attr('data-etag'),
|
||||
permissions: parseInt($el.attr('data-permissions'), 10)
|
||||
};
|
||||
},
|
||||
|
||||
@@ -477,7 +493,7 @@
|
||||
*/
|
||||
_nextPage: function(animate) {
|
||||
var index = this.$fileList.children().length,
|
||||
count = this.pageSize,
|
||||
count = this.pageSize(),
|
||||
tr,
|
||||
fileData,
|
||||
newTrs = [],
|
||||
@@ -598,6 +614,10 @@
|
||||
"data-permissions": fileData.permissions || this.getDirectoryPermissions()
|
||||
});
|
||||
|
||||
if (fileData.mountType) {
|
||||
tr.attr('data-mounttype', fileData.mountType);
|
||||
}
|
||||
|
||||
if (!_.isUndefined(path)) {
|
||||
tr.attr('data-path', path);
|
||||
}
|
||||
@@ -699,8 +719,10 @@
|
||||
*
|
||||
* @param fileData map of file attributes
|
||||
* @param options map of attributes:
|
||||
* - "updateSummary": true to update the summary after adding (default), false otherwise
|
||||
* - "silent": true to prevent firing events like "fileActionsReady"
|
||||
* @param options.updateSummary true to update the summary after adding (default), false otherwise
|
||||
* @param options.silent true to prevent firing events like "fileActionsReady"
|
||||
* @param options.animate true to animate preview loading (defaults to true here)
|
||||
* @param options.scrollTo true to automatically scroll to the file's location
|
||||
* @return new tr element (not appended to the table)
|
||||
*/
|
||||
add: function(fileData, options) {
|
||||
@@ -708,7 +730,7 @@
|
||||
var $tr;
|
||||
var $rows;
|
||||
var $insertionPoint;
|
||||
options = options || {};
|
||||
options = _.extend({animate: true}, options || {});
|
||||
|
||||
// there are three situations to cover:
|
||||
// 1) insertion point is visible on the current page
|
||||
@@ -749,6 +771,10 @@
|
||||
});
|
||||
}
|
||||
|
||||
if (options.scrollTo) {
|
||||
this.scrollTo(fileData.name);
|
||||
}
|
||||
|
||||
// defaults to true if not defined
|
||||
if (typeof(options.updateSummary) === 'undefined' || !!options.updateSummary) {
|
||||
this.fileSummary.add(fileData, true);
|
||||
@@ -766,6 +792,7 @@
|
||||
* @param options map of attributes:
|
||||
* - "index" optional index at which to insert the element
|
||||
* - "updateSummary" true to update the summary after adding (default), false otherwise
|
||||
* - "animate" true to animate the preview rendering
|
||||
* @return new tr element (not appended to the table)
|
||||
*/
|
||||
_renderRow: function(fileData, options) {
|
||||
@@ -807,7 +834,7 @@
|
||||
|
||||
if (fileData.isPreviewAvailable) {
|
||||
// lazy load / newly inserted td ?
|
||||
if (!fileData.icon) {
|
||||
if (options.animate) {
|
||||
this.lazyLoadPreview({
|
||||
path: path + '/' + fileData.name,
|
||||
mime: mime,
|
||||
@@ -908,18 +935,29 @@
|
||||
this._sort = sort;
|
||||
this._sortDirection = (direction === 'desc')?'desc':'asc';
|
||||
this._sortComparator = comparator;
|
||||
|
||||
if (direction === 'desc') {
|
||||
this._sortComparator = function(fileInfo1, fileInfo2) {
|
||||
return -comparator(fileInfo1, fileInfo2);
|
||||
};
|
||||
}
|
||||
this.$el.find('thead th .sort-indicator')
|
||||
.removeClass(this.SORT_INDICATOR_ASC_CLASS + ' ' + this.SORT_INDICATOR_DESC_CLASS);
|
||||
.removeClass(this.SORT_INDICATOR_ASC_CLASS)
|
||||
.removeClass(this.SORT_INDICATOR_DESC_CLASS)
|
||||
.toggleClass('hidden', true)
|
||||
.addClass(this.SORT_INDICATOR_DESC_CLASS);
|
||||
|
||||
this.$el.find('thead th.column-' + sort + ' .sort-indicator')
|
||||
.removeClass(this.SORT_INDICATOR_ASC_CLASS)
|
||||
.removeClass(this.SORT_INDICATOR_DESC_CLASS)
|
||||
.toggleClass('hidden', false)
|
||||
.addClass(direction === 'desc' ? this.SORT_INDICATOR_DESC_CLASS : this.SORT_INDICATOR_ASC_CLASS);
|
||||
},
|
||||
|
||||
/**
|
||||
* @brief Reloads the file list using ajax call
|
||||
* Reloads the file list using ajax call
|
||||
*
|
||||
* @return ajax call object
|
||||
*/
|
||||
reload: function() {
|
||||
this._selectedFiles = {};
|
||||
@@ -945,6 +983,13 @@
|
||||
this.hideMask();
|
||||
|
||||
if (!result || result.status === 'error') {
|
||||
// if the error is not related to folder we're trying to load, reload the page to handle logout etc
|
||||
if (result.data.error === 'authentication_error' ||
|
||||
result.data.error === 'token_expired' ||
|
||||
result.data.error === 'application_not_enabled'
|
||||
) {
|
||||
OC.redirect(OC.generateUrl('apps/files'));
|
||||
}
|
||||
OC.Notification.show(result.data.message);
|
||||
return false;
|
||||
}
|
||||
@@ -968,7 +1013,7 @@
|
||||
}
|
||||
|
||||
this.setFiles(result.data.files);
|
||||
return true
|
||||
return true;
|
||||
},
|
||||
|
||||
updateStorageStatistics: function(force) {
|
||||
@@ -1125,7 +1170,7 @@
|
||||
// if there are less elements visible than one page
|
||||
// but there are still pending elements in the array,
|
||||
// then directly append the next page
|
||||
if (lastIndex < this.files.length && lastIndex < this.pageSize) {
|
||||
if (lastIndex < this.files.length && lastIndex < this.pageSize()) {
|
||||
this._nextPage(true);
|
||||
}
|
||||
|
||||
@@ -1291,6 +1336,10 @@
|
||||
if (!result || result.status === 'error') {
|
||||
OC.dialogs.alert(result.data.message, t('core', 'Could not rename file'));
|
||||
fileInfo = oldFileInfo;
|
||||
if (result.data.code === 'sourcenotfound') {
|
||||
self.remove(result.data.newname, {updateSummary: true});
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
fileInfo = result.data;
|
||||
@@ -1473,16 +1522,15 @@
|
||||
this.$table.removeClass('hidden');
|
||||
},
|
||||
scrollTo:function(file) {
|
||||
//scroll to and highlight preselected file
|
||||
var $scrollToRow = this.findFileEl(file);
|
||||
if ($scrollToRow.exists()) {
|
||||
$scrollToRow.addClass('searchresult');
|
||||
$(window).scrollTop($scrollToRow.position().top);
|
||||
//remove highlight when hovered over
|
||||
$scrollToRow.one('hover', function() {
|
||||
$scrollToRow.removeClass('searchresult');
|
||||
});
|
||||
if (!_.isArray(file)) {
|
||||
file = [file];
|
||||
}
|
||||
this.highlightFiles(file, function($tr) {
|
||||
$tr.addClass('searchresult');
|
||||
$tr.one('hover', function() {
|
||||
$tr.removeClass('searchresult');
|
||||
});
|
||||
});
|
||||
},
|
||||
filter:function(query) {
|
||||
this.$fileList.find('tr').each(function(i,e) {
|
||||
@@ -1517,7 +1565,7 @@
|
||||
this.$el.find('.selectedActions').addClass('hidden');
|
||||
}
|
||||
else {
|
||||
canDelete = (this.getDirectoryPermissions() & OC.PERMISSION_DELETE);
|
||||
canDelete = (this.getDirectoryPermissions() & OC.PERMISSION_DELETE) && this.isSelectedDeletable();
|
||||
this.$el.find('.selectedActions').removeClass('hidden');
|
||||
this.$el.find('#headerSize a>span:first').text(OC.Util.humanFileSize(summary.totalSize));
|
||||
var selection = '';
|
||||
@@ -1537,6 +1585,15 @@
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Check whether all selected files are deletable
|
||||
*/
|
||||
isSelectedDeletable: function() {
|
||||
return _.reduce(this.getSelectedFiles(), function(deletable, file) {
|
||||
return deletable && (file.permissions & OC.PERMISSION_DELETE);
|
||||
}, true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns whether all files are selected
|
||||
* @return true if all files are selected, false otherwise
|
||||
@@ -1566,7 +1623,7 @@
|
||||
numMatch=base.match(/\((\d+)\)/);
|
||||
var num=2;
|
||||
if (numMatch && numMatch.length>0) {
|
||||
num=parseInt(numMatch[numMatch.length-1])+1;
|
||||
num=parseInt(numMatch[numMatch.length-1], 10)+1;
|
||||
base=base.split('(');
|
||||
base.pop();
|
||||
base=$.trim(base.join('('));
|
||||
@@ -1581,6 +1638,18 @@
|
||||
return name;
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows a "permission denied" notification
|
||||
*/
|
||||
_showPermissionDeniedNotification: function() {
|
||||
var message = t('core', 'You don’t have permission to upload or create files here');
|
||||
OC.Notification.show(message);
|
||||
//hide notification after 10 sec
|
||||
setTimeout(function() {
|
||||
OC.Notification.hide();
|
||||
}, 5000);
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup file upload events related to the file-upload plugin
|
||||
*/
|
||||
@@ -1612,6 +1681,12 @@
|
||||
// remember as context
|
||||
data.context = dropTarget;
|
||||
|
||||
// if permissions are specified, only allow if create permission is there
|
||||
var permissions = dropTarget.data('permissions');
|
||||
if (!_.isUndefined(permissions) && (permissions & OC.PERMISSION_CREATE) === 0) {
|
||||
self._showPermissionDeniedNotification();
|
||||
return false;
|
||||
}
|
||||
var dir = dropTarget.data('file');
|
||||
// if from file list, need to prepend parent dir
|
||||
if (dir) {
|
||||
@@ -1636,6 +1711,7 @@
|
||||
// cancel uploads to current dir if no permission
|
||||
var isCreatable = (self.getDirectoryPermissions() & OC.PERMISSION_CREATE) !== 0;
|
||||
if (!isCreatable) {
|
||||
self._showPermissionDeniedNotification();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1806,6 +1882,68 @@
|
||||
self.updateStorageStatistics();
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Scroll to the last file of the given list
|
||||
* Highlight the list of files
|
||||
* @param files array of filenames,
|
||||
* @param {Function} [highlightFunction] optional function
|
||||
* to be called after the scrolling is finished
|
||||
*/
|
||||
highlightFiles: function(files, highlightFunction) {
|
||||
// Detection of the uploaded element
|
||||
var filename = files[files.length - 1];
|
||||
var $fileRow = this.findFileEl(filename);
|
||||
|
||||
while(!$fileRow.exists() && this._nextPage(false) !== false) { // Checking element existence
|
||||
$fileRow = this.findFileEl(filename);
|
||||
}
|
||||
|
||||
if (!$fileRow.exists()) { // Element not present in the file list
|
||||
return;
|
||||
}
|
||||
|
||||
var currentOffset = this.$container.scrollTop();
|
||||
var additionalOffset = this.$el.find("#controls").height()+this.$el.find("#controls").offset().top;
|
||||
|
||||
// Animation
|
||||
var _this = this;
|
||||
var $scrollContainer = this.$container;
|
||||
if ($scrollContainer[0] === window) {
|
||||
// need to use "body" to animate scrolling
|
||||
// when the scroll container is the window
|
||||
$scrollContainer = $('body');
|
||||
}
|
||||
$scrollContainer.animate({
|
||||
// Scrolling to the top of the new element
|
||||
scrollTop: currentOffset + $fileRow.offset().top - $fileRow.height() * 2 - additionalOffset
|
||||
}, {
|
||||
duration: 500,
|
||||
complete: function() {
|
||||
// Highlighting function
|
||||
var highlightRow = highlightFunction;
|
||||
|
||||
if (!highlightRow) {
|
||||
highlightRow = function($fileRow) {
|
||||
$fileRow.addClass("highlightUploaded");
|
||||
setTimeout(function() {
|
||||
$fileRow.removeClass("highlightUploaded");
|
||||
}, 2500);
|
||||
};
|
||||
}
|
||||
|
||||
// Loop over uploaded files
|
||||
for(var i=0; i<files.length; i++) {
|
||||
var $fileRow = _this.findFileEl(files[i]);
|
||||
|
||||
if($fileRow.length !== 0) { // Checking element existence
|
||||
highlightRow($fileRow);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -350,7 +350,7 @@ var createDragShadow = function(event) {
|
||||
}
|
||||
|
||||
// do not show drag shadow for too many files
|
||||
var selectedFiles = _.first(FileList.getSelectedFiles(), FileList.pageSize);
|
||||
var selectedFiles = _.first(FileList.getSelectedFiles(), FileList.pageSize());
|
||||
selectedFiles = _.sortBy(selectedFiles, FileList._fileInfoCompare);
|
||||
|
||||
if (!isDragSelected && selectedFiles.length === 1) {
|
||||
@@ -433,7 +433,12 @@ var folderDropOptions = {
|
||||
return false;
|
||||
}
|
||||
|
||||
var targetPath = FileList.getCurrentDirectory() + '/' + $(this).closest('tr').data('file');
|
||||
var $tr = $(this).closest('tr');
|
||||
if (($tr.data('permissions') & OC.PERMISSION_CREATE) === 0) {
|
||||
FileList._showPermissionDeniedNotification();
|
||||
return false;
|
||||
}
|
||||
var targetPath = FileList.getCurrentDirectory() + '/' + $tr.data('file');
|
||||
|
||||
var files = FileList.getSelectedFiles();
|
||||
if (files.length === 0) {
|
||||
|
||||
@@ -17,7 +17,7 @@ $TRANSLATIONS = array(
|
||||
"Invalid Token" => "Jeton non valide",
|
||||
"No file was uploaded. Unknown error" => "Aucun fichier n'a été envoyé. Erreur inconnue",
|
||||
"There is no error, the file uploaded with success" => "Aucune erreur, le fichier a été envoyé avec succès.",
|
||||
"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Le fichier envoyé dépasse l'instruction upload_max_filesize située dans le fichier php.ini:",
|
||||
"The uploaded file exceeds the upload_max_filesize directive in php.ini: " => "Le fichier envoyé dépasse l'instruction upload_max_filesize située dans le fichier php.ini :",
|
||||
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Le fichier envoyé dépasse l'instruction MAX_FILE_SIZE qui est spécifiée dans le formulaire HTML.",
|
||||
"The uploaded file was only partially uploaded" => "Le fichier n'a été que partiellement envoyé.",
|
||||
"No file was uploaded" => "Pas de fichier envoyé.",
|
||||
@@ -62,7 +62,7 @@ $TRANSLATIONS = array(
|
||||
"Your storage is almost full ({usedSpacePercent}%)" => "Votre espace de stockage est presque plein ({usedSpacePercent}%)",
|
||||
"Encryption App is enabled but your keys are not initialized, please log-out and log-in again" => "L'application de chiffrement est activée mais vos clés ne sont pas initialisées, veuillez vous déconnecter et ensuite vous reconnecter.",
|
||||
"Invalid private key for Encryption App. Please update your private key password in your personal settings to recover access to your encrypted files." => "Votre clef privée pour l'application de chiffrement est invalide ! Veuillez mettre à jour le mot de passe de votre clef privée dans vos paramètres personnels pour récupérer l'accès à vos fichiers chiffrés.",
|
||||
"Encryption was disabled but your files are still encrypted. Please go to your personal settings to decrypt your files." => "Le chiffrement était désactivé mais vos fichiers sont toujours chiffrés. Veuillez vous rendre sur vos Paramètres personnels pour déchiffrer vos fichiers.",
|
||||
"Encryption was disabled but your files are still encrypted. Please go to your personal settings to decrypt your files." => "Le chiffrement a été désactivé mais vos fichiers sont toujours chiffrés. Veuillez vous rendre sur vos paramètres personnels pour déchiffrer vos fichiers.",
|
||||
"{dirs} and {files}" => "{dirs} et {files}",
|
||||
"%s could not be renamed" => "%s ne peut être renommé",
|
||||
"Upload (max. %s)" => "Envoi (max. %s)",
|
||||
@@ -77,13 +77,13 @@ $TRANSLATIONS = array(
|
||||
"Text file" => "Fichier texte",
|
||||
"New folder" => "Nouveau dossier",
|
||||
"Folder" => "Dossier",
|
||||
"From link" => "Depuis le lien",
|
||||
"From link" => "Depuis un lien",
|
||||
"You don’t have permission to upload or create files here" => "Vous n'avez pas la permission de téléverser ou de créer des fichiers ici",
|
||||
"Nothing in here. Upload something!" => "Il n'y a rien ici ! Envoyez donc quelque chose :)",
|
||||
"Download" => "Télécharger",
|
||||
"Upload too large" => "Téléversement trop volumineux",
|
||||
"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Les fichiers que vous essayez d'envoyer dépassent la taille maximale permise par ce serveur.",
|
||||
"Files are being scanned, please wait." => "Les fichiers sont en cours d'analyse, veuillez patienter.",
|
||||
"Currently scanning" => "Analyse en cours de traitement"
|
||||
"Currently scanning" => "Analyse en cours",
|
||||
);
|
||||
$PLURAL_FORMS = "nplurals=2; plural=(n > 1);";
|
||||
|
||||
@@ -71,15 +71,25 @@ class App {
|
||||
'data' => NULL
|
||||
);
|
||||
|
||||
$normalizedOldPath = \OC\Files\Filesystem::normalizePath($dir . '/' . $oldname);
|
||||
$normalizedNewPath = \OC\Files\Filesystem::normalizePath($dir . '/' . $newname);
|
||||
|
||||
// rename to non-existing folder is denied
|
||||
if (!$this->view->file_exists($dir)) {
|
||||
if (!$this->view->file_exists($normalizedOldPath)) {
|
||||
$result['data'] = array(
|
||||
'message' => $this->l10n->t('%s could not be renamed as it has been deleted', array($oldname)),
|
||||
'code' => 'sourcenotfound',
|
||||
'oldname' => $oldname,
|
||||
'newname' => $newname,
|
||||
);
|
||||
}else if (!$this->view->file_exists($dir)) {
|
||||
$result['data'] = array('message' => (string)$this->l10n->t(
|
||||
'The target folder has been moved or deleted.',
|
||||
array($dir)),
|
||||
'code' => 'targetnotfound'
|
||||
);
|
||||
// rename to existing file is denied
|
||||
} else if ($this->view->file_exists($dir . '/' . $newname)) {
|
||||
} else if ($this->view->file_exists($normalizedNewPath)) {
|
||||
|
||||
$result['data'] = array(
|
||||
'message' => $this->l10n->t(
|
||||
@@ -90,10 +100,10 @@ class App {
|
||||
// rename to "." is denied
|
||||
$newname !== '.' and
|
||||
// THEN try to rename
|
||||
$this->view->rename($dir . '/' . $oldname, $dir . '/' . $newname)
|
||||
$this->view->rename($normalizedOldPath, $normalizedNewPath)
|
||||
) {
|
||||
// successful rename
|
||||
$meta = $this->view->getFileInfo($dir . '/' . $newname);
|
||||
$meta = $this->view->getFileInfo($normalizedNewPath);
|
||||
$fileinfo = \OCA\Files\Helper::formatFileInfo($meta);
|
||||
$result['success'] = true;
|
||||
$result['data'] = $fileinfo;
|
||||
|
||||
@@ -37,6 +37,7 @@ class Helper
|
||||
public static function determineIcon($file) {
|
||||
if($file['type'] === 'dir') {
|
||||
$icon = \OC_Helper::mimetypeIcon('dir');
|
||||
// TODO: move this part to the client side, using mountType
|
||||
if ($file->isShared()) {
|
||||
$icon = \OC_Helper::mimetypeIcon('dir-shared');
|
||||
} elseif ($file->isMounted()) {
|
||||
@@ -92,7 +93,7 @@ class Helper
|
||||
public static function compareSize($a, $b) {
|
||||
$aSize = $a->getSize();
|
||||
$bSize = $b->getSize();
|
||||
return $aSize - $bSize;
|
||||
return ($aSize < $bSize) ? -1 : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,6 +126,18 @@ class Helper
|
||||
if (isset($i['is_share_mount_point'])) {
|
||||
$entry['isShareMountPoint'] = $i['is_share_mount_point'];
|
||||
}
|
||||
$mountType = null;
|
||||
if ($i->isShared()) {
|
||||
$mountType = 'shared';
|
||||
} else if ($i->isMounted()) {
|
||||
$mountType = 'external';
|
||||
}
|
||||
if ($mountType !== null) {
|
||||
if ($i->getInternalPath() === '') {
|
||||
$mountType .= '-root';
|
||||
}
|
||||
$entry['mountType'] = $mountType;
|
||||
}
|
||||
return $entry;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,14 @@ class Test_OC_Files_App_Rename extends \PHPUnit_Framework_TestCase {
|
||||
*/
|
||||
private $files;
|
||||
|
||||
function setUp() {
|
||||
/** @var \OC\Files\Storage\Storage */
|
||||
private $originalStorage;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->originalStorage = \OC\Files\Filesystem::getStorage('/');
|
||||
|
||||
// mock OC_L10n
|
||||
if (!self::$user) {
|
||||
self::$user = uniqid();
|
||||
@@ -59,10 +66,13 @@ class Test_OC_Files_App_Rename extends \PHPUnit_Framework_TestCase {
|
||||
$this->files = new \OCA\Files\App($viewMock, $l10nMock);
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
protected function tearDown() {
|
||||
$result = \OC_User::deleteUser(self::$user);
|
||||
$this->assertTrue($result);
|
||||
\OC\Files\Filesystem::tearDown();
|
||||
\OC\Files\Filesystem::mount($this->originalStorage, array(), '/');
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -73,10 +83,14 @@ class Test_OC_Files_App_Rename extends \PHPUnit_Framework_TestCase {
|
||||
$oldname = 'oldname';
|
||||
$newname = 'newname';
|
||||
|
||||
$this->viewMock->expects($this->at(0))
|
||||
$this->viewMock->expects($this->any())
|
||||
->method('file_exists')
|
||||
->with('/')
|
||||
->will($this->returnValue(true));
|
||||
->with($this->anything())
|
||||
->will($this->returnValueMap(array(
|
||||
array('/', true),
|
||||
array('/oldname', true)
|
||||
)));
|
||||
|
||||
|
||||
$this->viewMock->expects($this->any())
|
||||
->method('getFileInfo')
|
||||
@@ -119,7 +133,7 @@ class Test_OC_Files_App_Rename extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
$this->viewMock->expects($this->at(0))
|
||||
->method('file_exists')
|
||||
->with('/unexist')
|
||||
->with('/unexist/oldname')
|
||||
->will($this->returnValue(false));
|
||||
|
||||
$this->viewMock->expects($this->any())
|
||||
@@ -136,6 +150,40 @@ class Test_OC_Files_App_Rename extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
$result = $this->files->rename($dir, $oldname, $newname);
|
||||
|
||||
$this->assertFalse($result['success']);
|
||||
$this->assertEquals('sourcenotfound', $result['data']['code']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test move to a folder that doesn't exist any more
|
||||
*/
|
||||
function testRenameToNonExistingFolder() {
|
||||
$dir = '/';
|
||||
$oldname = 'oldname';
|
||||
$newname = '/unexist/newname';
|
||||
|
||||
$this->viewMock->expects($this->any())
|
||||
->method('file_exists')
|
||||
->with($this->anything())
|
||||
->will($this->returnValueMap(array(
|
||||
array('/oldname', true),
|
||||
array('/unexist', false)
|
||||
)));
|
||||
|
||||
$this->viewMock->expects($this->any())
|
||||
->method('getFileInfo')
|
||||
->will($this->returnValue(array(
|
||||
'fileid' => 123,
|
||||
'type' => 'dir',
|
||||
'mimetype' => 'httpd/unix-directory',
|
||||
'size' => 18,
|
||||
'etag' => 'abcdef',
|
||||
'directory' => '/unexist',
|
||||
'name' => 'new_name',
|
||||
)));
|
||||
|
||||
$result = $this->files->rename($dir, $oldname, $newname);
|
||||
|
||||
$this->assertFalse($result['success']);
|
||||
$this->assertEquals('targetnotfound', $result['data']['code']);
|
||||
}
|
||||
|
||||
@@ -52,9 +52,7 @@ describe('OCA.Files.App tests', function() {
|
||||
App.initialize();
|
||||
});
|
||||
afterEach(function() {
|
||||
App.navigation = null;
|
||||
App.fileList = null;
|
||||
App.files = null;
|
||||
App.destroy();
|
||||
|
||||
pushStateStub.restore();
|
||||
parseUrlQueryStub.restore();
|
||||
|
||||
@@ -193,6 +193,156 @@ describe('OCA.Files.FileActions tests', function() {
|
||||
context = actionStub.getCall(0).args[1];
|
||||
expect(context.dir).toEqual('/somepath');
|
||||
});
|
||||
describe('merging', function() {
|
||||
var $tr;
|
||||
beforeEach(function() {
|
||||
var fileData = {
|
||||
id: 18,
|
||||
type: 'file',
|
||||
name: 'testName.txt',
|
||||
path: '/anotherpath/there',
|
||||
mimetype: 'text/plain',
|
||||
size: '1234',
|
||||
etag: 'a01234c',
|
||||
mtime: '123456'
|
||||
};
|
||||
$tr = fileList.add(fileData);
|
||||
});
|
||||
afterEach(function() {
|
||||
$tr = null;
|
||||
});
|
||||
it('copies all actions to target file actions', function() {
|
||||
var actions1 = new OCA.Files.FileActions();
|
||||
var actions2 = new OCA.Files.FileActions();
|
||||
var actionStub1 = sinon.stub();
|
||||
var actionStub2 = sinon.stub();
|
||||
actions1.register(
|
||||
'all',
|
||||
'Test',
|
||||
OC.PERMISSION_READ,
|
||||
OC.imagePath('core', 'actions/test'),
|
||||
actionStub1
|
||||
);
|
||||
actions2.register(
|
||||
'all',
|
||||
'Test2',
|
||||
OC.PERMISSION_READ,
|
||||
OC.imagePath('core', 'actions/test'),
|
||||
actionStub2
|
||||
);
|
||||
actions2.merge(actions1);
|
||||
|
||||
actions2.display($tr.find('td.filename'), true, fileList);
|
||||
|
||||
expect($tr.find('.action-test').length).toEqual(1);
|
||||
expect($tr.find('.action-test2').length).toEqual(1);
|
||||
|
||||
$tr.find('.action-test').click();
|
||||
expect(actionStub1.calledOnce).toEqual(true);
|
||||
expect(actionStub2.notCalled).toEqual(true);
|
||||
|
||||
actionStub1.reset();
|
||||
|
||||
$tr.find('.action-test2').click();
|
||||
expect(actionStub1.notCalled).toEqual(true);
|
||||
expect(actionStub2.calledOnce).toEqual(true);
|
||||
});
|
||||
it('overrides existing actions on merge', function() {
|
||||
var actions1 = new OCA.Files.FileActions();
|
||||
var actions2 = new OCA.Files.FileActions();
|
||||
var actionStub1 = sinon.stub();
|
||||
var actionStub2 = sinon.stub();
|
||||
actions1.register(
|
||||
'all',
|
||||
'Test',
|
||||
OC.PERMISSION_READ,
|
||||
OC.imagePath('core', 'actions/test'),
|
||||
actionStub1
|
||||
);
|
||||
actions2.register(
|
||||
'all',
|
||||
'Test', // override
|
||||
OC.PERMISSION_READ,
|
||||
OC.imagePath('core', 'actions/test'),
|
||||
actionStub2
|
||||
);
|
||||
actions1.merge(actions2);
|
||||
|
||||
actions1.display($tr.find('td.filename'), true, fileList);
|
||||
|
||||
expect($tr.find('.action-test').length).toEqual(1);
|
||||
|
||||
$tr.find('.action-test').click();
|
||||
expect(actionStub1.notCalled).toEqual(true);
|
||||
expect(actionStub2.calledOnce).toEqual(true);
|
||||
});
|
||||
it('overrides existing action when calling register after merge', function() {
|
||||
var actions1 = new OCA.Files.FileActions();
|
||||
var actions2 = new OCA.Files.FileActions();
|
||||
var actionStub1 = sinon.stub();
|
||||
var actionStub2 = sinon.stub();
|
||||
actions1.register(
|
||||
'all',
|
||||
'Test',
|
||||
OC.PERMISSION_READ,
|
||||
OC.imagePath('core', 'actions/test'),
|
||||
actionStub1
|
||||
);
|
||||
|
||||
actions1.merge(actions2);
|
||||
|
||||
// late override
|
||||
actions1.register(
|
||||
'all',
|
||||
'Test', // override
|
||||
OC.PERMISSION_READ,
|
||||
OC.imagePath('core', 'actions/test'),
|
||||
actionStub2
|
||||
);
|
||||
|
||||
actions1.display($tr.find('td.filename'), true, fileList);
|
||||
|
||||
expect($tr.find('.action-test').length).toEqual(1);
|
||||
|
||||
$tr.find('.action-test').click();
|
||||
expect(actionStub1.notCalled).toEqual(true);
|
||||
expect(actionStub2.calledOnce).toEqual(true);
|
||||
});
|
||||
it('leaves original file actions untouched (clean copy)', function() {
|
||||
var actions1 = new OCA.Files.FileActions();
|
||||
var actions2 = new OCA.Files.FileActions();
|
||||
var actionStub1 = sinon.stub();
|
||||
var actionStub2 = sinon.stub();
|
||||
actions1.register(
|
||||
'all',
|
||||
'Test',
|
||||
OC.PERMISSION_READ,
|
||||
OC.imagePath('core', 'actions/test'),
|
||||
actionStub1
|
||||
);
|
||||
|
||||
// copy the Test action to actions2
|
||||
actions2.merge(actions1);
|
||||
|
||||
// late override
|
||||
actions2.register(
|
||||
'all',
|
||||
'Test', // override
|
||||
OC.PERMISSION_READ,
|
||||
OC.imagePath('core', 'actions/test'),
|
||||
actionStub2
|
||||
);
|
||||
|
||||
// check if original actions still call the correct handler
|
||||
actions1.display($tr.find('td.filename'), true, fileList);
|
||||
|
||||
expect($tr.find('.action-test').length).toEqual(1);
|
||||
|
||||
$tr.find('.action-test').click();
|
||||
expect(actionStub1.calledOnce).toEqual(true);
|
||||
expect(actionStub2.notCalled).toEqual(true);
|
||||
});
|
||||
});
|
||||
describe('events', function() {
|
||||
var clock;
|
||||
beforeEach(function() {
|
||||
@@ -204,7 +354,7 @@ describe('OCA.Files.FileActions tests', function() {
|
||||
it('notifies update event handlers once after multiple changes', function() {
|
||||
var actionStub = sinon.stub();
|
||||
var handler = sinon.stub();
|
||||
FileActions.addUpdateListener(handler);
|
||||
FileActions.on('registerAction', handler);
|
||||
FileActions.register(
|
||||
'all',
|
||||
'Test',
|
||||
@@ -224,8 +374,8 @@ describe('OCA.Files.FileActions tests', function() {
|
||||
it('does not notifies update event handlers after unregistering', function() {
|
||||
var actionStub = sinon.stub();
|
||||
var handler = sinon.stub();
|
||||
FileActions.addUpdateListener(handler);
|
||||
FileActions.removeUpdateListener(handler);
|
||||
FileActions.on('registerAction', handler);
|
||||
FileActions.off('registerAction', handler);
|
||||
FileActions.register(
|
||||
'all',
|
||||
'Test',
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
*/
|
||||
|
||||
describe('OCA.Files.FileList tests', function() {
|
||||
var testFiles, alertStub, notificationStub, fileList;
|
||||
var testFiles, alertStub, notificationStub, fileList, pageSizeStub;
|
||||
var bcResizeStub;
|
||||
|
||||
/**
|
||||
@@ -97,7 +97,8 @@ describe('OCA.Files.FileList tests', function() {
|
||||
name: 'One.txt',
|
||||
mimetype: 'text/plain',
|
||||
size: 12,
|
||||
etag: 'abc'
|
||||
etag: 'abc',
|
||||
permissions: OC.PERMISSION_ALL
|
||||
}, {
|
||||
id: 2,
|
||||
type: 'file',
|
||||
@@ -105,6 +106,7 @@ describe('OCA.Files.FileList tests', function() {
|
||||
mimetype: 'image/jpeg',
|
||||
size: 12049,
|
||||
etag: 'def',
|
||||
permissions: OC.PERMISSION_ALL
|
||||
}, {
|
||||
id: 3,
|
||||
type: 'file',
|
||||
@@ -112,15 +114,17 @@ describe('OCA.Files.FileList tests', function() {
|
||||
mimetype: 'application/pdf',
|
||||
size: 58009,
|
||||
etag: '123',
|
||||
permissions: OC.PERMISSION_ALL
|
||||
}, {
|
||||
id: 4,
|
||||
type: 'dir',
|
||||
name: 'somedir',
|
||||
mimetype: 'httpd/unix-directory',
|
||||
size: 250,
|
||||
etag: '456'
|
||||
etag: '456',
|
||||
permissions: OC.PERMISSION_ALL
|
||||
}];
|
||||
|
||||
pageSizeStub = sinon.stub(OCA.Files.FileList.prototype, 'pageSize').returns(20);
|
||||
fileList = new OCA.Files.FileList($('#app-content-files'));
|
||||
});
|
||||
afterEach(function() {
|
||||
@@ -130,6 +134,7 @@ describe('OCA.Files.FileList tests', function() {
|
||||
notificationStub.restore();
|
||||
alertStub.restore();
|
||||
bcResizeStub.restore();
|
||||
pageSizeStub.restore();
|
||||
});
|
||||
describe('Getters', function() {
|
||||
it('Returns the current directory', function() {
|
||||
@@ -218,13 +223,13 @@ describe('OCA.Files.FileList tests', function() {
|
||||
|
||||
expect($tr).toBeDefined();
|
||||
expect($tr[0].tagName.toLowerCase()).toEqual('tr');
|
||||
expect($tr.attr('data-id')).toEqual(null);
|
||||
expect($tr.attr('data-id')).toBeUndefined();
|
||||
expect($tr.attr('data-type')).toEqual('file');
|
||||
expect($tr.attr('data-file')).toEqual('testFile.txt');
|
||||
expect($tr.attr('data-size')).toEqual(null);
|
||||
expect($tr.attr('data-etag')).toEqual(null);
|
||||
expect($tr.attr('data-size')).toBeUndefined();
|
||||
expect($tr.attr('data-etag')).toBeUndefined();
|
||||
expect($tr.attr('data-permissions')).toEqual('31');
|
||||
expect($tr.attr('data-mime')).toEqual(null);
|
||||
expect($tr.attr('data-mime')).toBeUndefined();
|
||||
expect($tr.attr('data-mtime')).toEqual('123456');
|
||||
|
||||
expect($tr.find('.filesize').text()).toEqual('Pending');
|
||||
@@ -239,11 +244,11 @@ describe('OCA.Files.FileList tests', function() {
|
||||
|
||||
expect($tr).toBeDefined();
|
||||
expect($tr[0].tagName.toLowerCase()).toEqual('tr');
|
||||
expect($tr.attr('data-id')).toEqual(null);
|
||||
expect($tr.attr('data-id')).toBeUndefined();
|
||||
expect($tr.attr('data-type')).toEqual('dir');
|
||||
expect($tr.attr('data-file')).toEqual('testFolder');
|
||||
expect($tr.attr('data-size')).toEqual(null);
|
||||
expect($tr.attr('data-etag')).toEqual(null);
|
||||
expect($tr.attr('data-size')).toBeUndefined();
|
||||
expect($tr.attr('data-etag')).toBeUndefined();
|
||||
expect($tr.attr('data-permissions')).toEqual('31');
|
||||
expect($tr.attr('data-mime')).toEqual('httpd/unix-directory');
|
||||
expect($tr.attr('data-mtime')).toEqual('123456');
|
||||
@@ -814,7 +819,7 @@ describe('OCA.Files.FileList tests', function() {
|
||||
fileList.$fileList.on('fileActionsReady', handler);
|
||||
fileList._nextPage();
|
||||
expect(handler.calledOnce).toEqual(true);
|
||||
expect(handler.getCall(0).args[0].$files.length).toEqual(fileList.pageSize);
|
||||
expect(handler.getCall(0).args[0].$files.length).toEqual(fileList.pageSize());
|
||||
});
|
||||
it('does not trigger "fileActionsReady" event after single add with silent argument', function() {
|
||||
var handler = sinon.stub();
|
||||
@@ -938,16 +943,6 @@ describe('OCA.Files.FileList tests', function() {
|
||||
describe('file previews', function() {
|
||||
var previewLoadStub;
|
||||
|
||||
function getImageUrl($el) {
|
||||
// might be slightly different cross-browser
|
||||
var url = $el.css('background-image');
|
||||
var r = url.match(/url\(['"]?([^'")]*)['"]?\)/);
|
||||
if (!r) {
|
||||
return url;
|
||||
}
|
||||
return r[1];
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
previewLoadStub = sinon.stub(OCA.Files.FileList.prototype, 'lazyLoadPreview');
|
||||
});
|
||||
@@ -961,7 +956,7 @@ describe('OCA.Files.FileList tests', function() {
|
||||
};
|
||||
var $tr = fileList.add(fileData);
|
||||
var $td = $tr.find('td.filename');
|
||||
expect(getImageUrl($td)).toEqual(OC.webroot + '/core/img/filetypes/file.svg');
|
||||
expect(OC.TestUtil.getImageUrl($td)).toEqual(OC.webroot + '/core/img/filetypes/file.svg');
|
||||
expect(previewLoadStub.notCalled).toEqual(true);
|
||||
});
|
||||
it('renders default icon for dir when none provided and no preview is available', function() {
|
||||
@@ -971,7 +966,7 @@ describe('OCA.Files.FileList tests', function() {
|
||||
};
|
||||
var $tr = fileList.add(fileData);
|
||||
var $td = $tr.find('td.filename');
|
||||
expect(getImageUrl($td)).toEqual(OC.webroot + '/core/img/filetypes/folder.svg');
|
||||
expect(OC.TestUtil.getImageUrl($td)).toEqual(OC.webroot + '/core/img/filetypes/folder.svg');
|
||||
expect(previewLoadStub.notCalled).toEqual(true);
|
||||
});
|
||||
it('renders provided icon for file when provided', function() {
|
||||
@@ -982,7 +977,7 @@ describe('OCA.Files.FileList tests', function() {
|
||||
};
|
||||
var $tr = fileList.add(fileData);
|
||||
var $td = $tr.find('td.filename');
|
||||
expect(getImageUrl($td)).toEqual(OC.webroot + '/core/img/filetypes/application-pdf.svg');
|
||||
expect(OC.TestUtil.getImageUrl($td)).toEqual(OC.webroot + '/core/img/filetypes/application-pdf.svg');
|
||||
expect(previewLoadStub.notCalled).toEqual(true);
|
||||
});
|
||||
it('renders preview when no icon was provided and preview is available', function() {
|
||||
@@ -993,11 +988,11 @@ describe('OCA.Files.FileList tests', function() {
|
||||
};
|
||||
var $tr = fileList.add(fileData);
|
||||
var $td = $tr.find('td.filename');
|
||||
expect(getImageUrl($td)).toEqual(OC.webroot + '/core/img/filetypes/file.svg');
|
||||
expect(OC.TestUtil.getImageUrl($td)).toEqual(OC.webroot + '/core/img/filetypes/file.svg');
|
||||
expect(previewLoadStub.calledOnce).toEqual(true);
|
||||
// third argument is callback
|
||||
previewLoadStub.getCall(0).args[0].callback(OC.webroot + '/somepath.png');
|
||||
expect(getImageUrl($td)).toEqual(OC.webroot + '/somepath.png');
|
||||
expect(OC.TestUtil.getImageUrl($td)).toEqual(OC.webroot + '/somepath.png');
|
||||
});
|
||||
it('renders default file type icon when no icon was provided and no preview is available', function() {
|
||||
var fileData = {
|
||||
@@ -1007,7 +1002,7 @@ describe('OCA.Files.FileList tests', function() {
|
||||
};
|
||||
var $tr = fileList.add(fileData);
|
||||
var $td = $tr.find('td.filename');
|
||||
expect(getImageUrl($td)).toEqual(OC.webroot + '/core/img/filetypes/file.svg');
|
||||
expect(OC.TestUtil.getImageUrl($td)).toEqual(OC.webroot + '/core/img/filetypes/file.svg');
|
||||
expect(previewLoadStub.notCalled).toEqual(true);
|
||||
});
|
||||
});
|
||||
@@ -1488,6 +1483,17 @@ describe('OCA.Files.FileList tests', function() {
|
||||
$('.select-all').click();
|
||||
expect(fileList.$el.find('.delete-selected').hasClass('hidden')).toEqual(true);
|
||||
});
|
||||
it('show doesnt show the delete action if one or more files are not deletable', function () {
|
||||
fileList.setFiles(testFiles);
|
||||
$('#permissions').val(OC.PERMISSION_READ | OC.PERMISSION_DELETE);
|
||||
$('.select-all').click();
|
||||
expect(fileList.$el.find('.delete-selected').hasClass('hidden')).toEqual(false);
|
||||
testFiles[0].permissions = OC.PERMISSION_READ;
|
||||
$('.select-all').click();
|
||||
fileList.setFiles(testFiles);
|
||||
$('.select-all').click();
|
||||
expect(fileList.$el.find('.delete-selected').hasClass('hidden')).toEqual(true);
|
||||
});
|
||||
});
|
||||
describe('Actions', function() {
|
||||
beforeEach(function() {
|
||||
@@ -1504,7 +1510,8 @@ describe('OCA.Files.FileList tests', function() {
|
||||
mimetype: 'text/plain',
|
||||
type: 'file',
|
||||
size: 12,
|
||||
etag: 'abc'
|
||||
etag: 'abc',
|
||||
permissions: OC.PERMISSION_ALL
|
||||
});
|
||||
expect(files[1]).toEqual({
|
||||
id: 3,
|
||||
@@ -1512,7 +1519,8 @@ describe('OCA.Files.FileList tests', function() {
|
||||
name: 'Three.pdf',
|
||||
mimetype: 'application/pdf',
|
||||
size: 58009,
|
||||
etag: '123'
|
||||
etag: '123',
|
||||
permissions: OC.PERMISSION_ALL
|
||||
});
|
||||
expect(files[2]).toEqual({
|
||||
id: 4,
|
||||
@@ -1520,7 +1528,8 @@ describe('OCA.Files.FileList tests', function() {
|
||||
name: 'somedir',
|
||||
mimetype: 'httpd/unix-directory',
|
||||
size: 250,
|
||||
etag: '456'
|
||||
etag: '456',
|
||||
permissions: OC.PERMISSION_ALL
|
||||
});
|
||||
});
|
||||
it('Removing a file removes it from the selection', function() {
|
||||
@@ -1533,7 +1542,8 @@ describe('OCA.Files.FileList tests', function() {
|
||||
mimetype: 'text/plain',
|
||||
type: 'file',
|
||||
size: 12,
|
||||
etag: 'abc'
|
||||
etag: 'abc',
|
||||
permissions: OC.PERMISSION_ALL
|
||||
});
|
||||
expect(files[1]).toEqual({
|
||||
id: 4,
|
||||
@@ -1541,7 +1551,8 @@ describe('OCA.Files.FileList tests', function() {
|
||||
name: 'somedir',
|
||||
mimetype: 'httpd/unix-directory',
|
||||
size: 250,
|
||||
etag: '456'
|
||||
etag: '456',
|
||||
permissions: OC.PERMISSION_ALL
|
||||
});
|
||||
});
|
||||
describe('Download', function() {
|
||||
@@ -1696,7 +1707,7 @@ describe('OCA.Files.FileList tests', function() {
|
||||
var url = fakeServer.requests[0].url;
|
||||
var query = OC.parseQueryString(url.substr(url.indexOf('?') + 1));
|
||||
expect(query.sort).toEqual('size');
|
||||
expect(query.sortdirection).toEqual('asc');
|
||||
expect(query.sortdirection).toEqual('desc');
|
||||
});
|
||||
it('Toggles sort direction when clicking on already sorted column', function() {
|
||||
fileList.$el.find('.column-name .columntitle').click();
|
||||
@@ -1710,37 +1721,51 @@ describe('OCA.Files.FileList tests', function() {
|
||||
var ASC_CLASS = fileList.SORT_INDICATOR_ASC_CLASS;
|
||||
var DESC_CLASS = fileList.SORT_INDICATOR_DESC_CLASS;
|
||||
fileList.$el.find('.column-size .columntitle').click();
|
||||
// moves triangle to size column
|
||||
// moves triangle to size column, check indicator on name is hidden
|
||||
expect(
|
||||
fileList.$el.find('.column-name .sort-indicator').hasClass(ASC_CLASS + ' ' + DESC_CLASS)
|
||||
).toEqual(false);
|
||||
expect(
|
||||
fileList.$el.find('.column-size .sort-indicator').hasClass(ASC_CLASS)
|
||||
fileList.$el.find('.column-name .sort-indicator').hasClass('hidden')
|
||||
).toEqual(true);
|
||||
|
||||
// click again on size column, reverses direction
|
||||
fileList.$el.find('.column-size .columntitle').click();
|
||||
// check indicator on size is visible and defaults to descending
|
||||
expect(
|
||||
fileList.$el.find('.column-size .sort-indicator').hasClass('hidden')
|
||||
).toEqual(false);
|
||||
expect(
|
||||
fileList.$el.find('.column-size .sort-indicator').hasClass(DESC_CLASS)
|
||||
).toEqual(true);
|
||||
|
||||
// click again on size column, reverses direction
|
||||
fileList.$el.find('.column-size .columntitle').click();
|
||||
expect(
|
||||
fileList.$el.find('.column-size .sort-indicator').hasClass('hidden')
|
||||
).toEqual(false);
|
||||
expect(
|
||||
fileList.$el.find('.column-size .sort-indicator').hasClass(ASC_CLASS)
|
||||
).toEqual(true);
|
||||
|
||||
// click again on size column, reverses direction
|
||||
fileList.$el.find('.column-size .columntitle').click();
|
||||
expect(
|
||||
fileList.$el.find('.column-size .sort-indicator').hasClass('hidden')
|
||||
).toEqual(false);
|
||||
expect(
|
||||
fileList.$el.find('.column-size .sort-indicator').hasClass(DESC_CLASS)
|
||||
).toEqual(true);
|
||||
|
||||
// click on mtime column, moves indicator there
|
||||
fileList.$el.find('.column-mtime .columntitle').click();
|
||||
expect(
|
||||
fileList.$el.find('.column-size .sort-indicator').hasClass(ASC_CLASS + ' ' + DESC_CLASS)
|
||||
fileList.$el.find('.column-size .sort-indicator').hasClass('hidden')
|
||||
).toEqual(true);
|
||||
expect(
|
||||
fileList.$el.find('.column-mtime .sort-indicator').hasClass('hidden')
|
||||
).toEqual(false);
|
||||
expect(
|
||||
fileList.$el.find('.column-mtime .sort-indicator').hasClass(ASC_CLASS)
|
||||
fileList.$el.find('.column-mtime .sort-indicator').hasClass(DESC_CLASS)
|
||||
).toEqual(true);
|
||||
});
|
||||
it('Uses correct sort comparator when inserting files', function() {
|
||||
testFiles.sort(OCA.Files.FileList.Comparators.size);
|
||||
testFiles.reverse(); //default is descending
|
||||
// this will make it reload the testFiles with the correct sorting
|
||||
fileList.$el.find('.column-size .columntitle').click();
|
||||
expect(fakeServer.requests.length).toEqual(1);
|
||||
@@ -1764,17 +1789,16 @@ describe('OCA.Files.FileList tests', function() {
|
||||
etag: '999'
|
||||
};
|
||||
fileList.add(newFileData);
|
||||
expect(fileList.findFileEl('Three.pdf').index()).toEqual(0);
|
||||
expect(fileList.findFileEl('new file.txt').index()).toEqual(1);
|
||||
expect(fileList.findFileEl('Two.jpg').index()).toEqual(2);
|
||||
expect(fileList.findFileEl('somedir').index()).toEqual(3);
|
||||
expect(fileList.findFileEl('One.txt').index()).toEqual(4);
|
||||
expect(fileList.files.length).toEqual(5);
|
||||
expect(fileList.$fileList.find('tr').length).toEqual(5);
|
||||
expect(fileList.findFileEl('One.txt').index()).toEqual(0);
|
||||
expect(fileList.findFileEl('somedir').index()).toEqual(1);
|
||||
expect(fileList.findFileEl('Two.jpg').index()).toEqual(2);
|
||||
expect(fileList.findFileEl('new file.txt').index()).toEqual(3);
|
||||
expect(fileList.findFileEl('Three.pdf').index()).toEqual(4);
|
||||
});
|
||||
it('Uses correct reversed sort comparator when inserting files', function() {
|
||||
testFiles.sort(OCA.Files.FileList.Comparators.size);
|
||||
testFiles.reverse();
|
||||
// this will make it reload the testFiles with the correct sorting
|
||||
fileList.$el.find('.column-size .columntitle').click();
|
||||
expect(fakeServer.requests.length).toEqual(1);
|
||||
@@ -1811,13 +1835,13 @@ describe('OCA.Files.FileList tests', function() {
|
||||
etag: '999'
|
||||
};
|
||||
fileList.add(newFileData);
|
||||
expect(fileList.findFileEl('One.txt').index()).toEqual(0);
|
||||
expect(fileList.findFileEl('somedir').index()).toEqual(1);
|
||||
expect(fileList.findFileEl('Two.jpg').index()).toEqual(2);
|
||||
expect(fileList.findFileEl('new file.txt').index()).toEqual(3);
|
||||
expect(fileList.findFileEl('Three.pdf').index()).toEqual(4);
|
||||
expect(fileList.files.length).toEqual(5);
|
||||
expect(fileList.$fileList.find('tr').length).toEqual(5);
|
||||
expect(fileList.findFileEl('One.txt').index()).toEqual(4);
|
||||
expect(fileList.findFileEl('somedir').index()).toEqual(3);
|
||||
expect(fileList.findFileEl('Two.jpg').index()).toEqual(2);
|
||||
expect(fileList.findFileEl('new file.txt').index()).toEqual(1);
|
||||
expect(fileList.findFileEl('Three.pdf').index()).toEqual(0);
|
||||
});
|
||||
});
|
||||
/**
|
||||
@@ -1832,7 +1856,6 @@ describe('OCA.Files.FileList tests', function() {
|
||||
// but it makes it possible to simulate the event triggering to
|
||||
// test the response of the handlers
|
||||
$uploader = $('#file_upload_start');
|
||||
fileList.setupUploadEvents();
|
||||
fileList.setFiles(testFiles);
|
||||
});
|
||||
|
||||
@@ -1909,6 +1932,16 @@ describe('OCA.Files.FileList tests', function() {
|
||||
ev = dropOn(fileList.$fileList.find('th:first'));
|
||||
|
||||
expect(ev.result).toEqual(false);
|
||||
expect(notificationStub.calledOnce).toEqual(true);
|
||||
});
|
||||
it('drop on an folder does not trigger upload if no upload permission on that folder', function() {
|
||||
var $tr = fileList.findFileEl('somedir');
|
||||
var ev;
|
||||
$tr.data('permissions', OC.PERMISSION_READ);
|
||||
ev = dropOn($tr);
|
||||
|
||||
expect(ev.result).toEqual(false);
|
||||
expect(notificationStub.calledOnce).toEqual(true);
|
||||
});
|
||||
it('drop on a file row inside the table triggers upload to current folder', function() {
|
||||
var ev;
|
||||
@@ -1933,4 +1966,30 @@ describe('OCA.Files.FileList tests', function() {
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('Handeling errors', function () {
|
||||
beforeEach(function () {
|
||||
redirectStub = sinon.stub(OC, 'redirect');
|
||||
|
||||
fileList = new OCA.Files.FileList($('#app-content-files'));
|
||||
});
|
||||
afterEach(function () {
|
||||
fileList = undefined;
|
||||
|
||||
redirectStub.restore();
|
||||
});
|
||||
it('reloads the page on authentication errors', function () {
|
||||
fileList.reload();
|
||||
fakeServer.requests[0].respond(
|
||||
200,
|
||||
{ 'Content-Type': 'application/json' },
|
||||
JSON.stringify({
|
||||
status: 'error',
|
||||
data: {
|
||||
'error': 'authentication_error'
|
||||
}
|
||||
})
|
||||
);
|
||||
expect(redirectStub.calledWith(OC.generateUrl('apps/files'))).toEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,8 +16,28 @@ use OCA\Encryption;
|
||||
$l = OC_L10N::get('files_encryption');
|
||||
|
||||
$return = false;
|
||||
// Enable recoveryAdmin
|
||||
$errorMessage = $l->t("Unknown error");
|
||||
|
||||
//check if both passwords are the same
|
||||
if (empty($_POST['recoveryPassword'])) {
|
||||
$errorMessage = $l->t('Missing recovery key password');
|
||||
\OCP\JSON::error(array('data' => array('message' => $errorMessage)));
|
||||
exit();
|
||||
}
|
||||
|
||||
if (empty($_POST['confirmPassword'])) {
|
||||
$errorMessage = $l->t('Please repeat the recovery key password');
|
||||
\OCP\JSON::error(array('data' => array('message' => $errorMessage)));
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($_POST['recoveryPassword'] !== $_POST['confirmPassword']) {
|
||||
$errorMessage = $l->t('Repeated recovery key password does not match the provided recovery key password');
|
||||
\OCP\JSON::error(array('data' => array('message' => $errorMessage)));
|
||||
exit();
|
||||
}
|
||||
|
||||
// Enable recoveryAdmin
|
||||
$recoveryKeyId = \OC::$server->getAppConfig()->getValue('files_encryption', 'recoveryKeyId');
|
||||
|
||||
if (isset($_POST['adminEnableRecovery']) && $_POST['adminEnableRecovery'] === '1') {
|
||||
@@ -26,14 +46,9 @@ if (isset($_POST['adminEnableRecovery']) && $_POST['adminEnableRecovery'] === '1
|
||||
|
||||
// Return success or failure
|
||||
if ($return) {
|
||||
\OCP\JSON::success(array('data' => array('message' => $l->t('Recovery key successfully enabled'))));
|
||||
$successMessage = $l->t('Recovery key successfully enabled');
|
||||
} else {
|
||||
\OCP\JSON::error(array(
|
||||
'data' => array(
|
||||
'message' => $l->t(
|
||||
'Could not enable recovery key. Please check your recovery key password!')
|
||||
)
|
||||
));
|
||||
$errorMessage = $l->t('Could not disable recovery key. Please check your recovery key password!');
|
||||
}
|
||||
|
||||
// Disable recoveryAdmin
|
||||
@@ -43,17 +58,16 @@ if (isset($_POST['adminEnableRecovery']) && $_POST['adminEnableRecovery'] === '1
|
||||
) {
|
||||
$return = \OCA\Encryption\Helper::adminDisableRecovery($_POST['recoveryPassword']);
|
||||
|
||||
// Return success or failure
|
||||
if ($return) {
|
||||
\OCP\JSON::success(array('data' => array('message' => $l->t('Recovery key successfully disabled'))));
|
||||
$successMessage = $l->t('Recovery key successfully disabled');
|
||||
} else {
|
||||
\OCP\JSON::error(array(
|
||||
'data' => array(
|
||||
'message' => $l->t(
|
||||
'Could not disable recovery key. Please check your recovery key password!')
|
||||
)
|
||||
));
|
||||
$errorMessage = $l->t('Could not disable recovery key. Please check your recovery key password!');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Return success or failure
|
||||
if ($return) {
|
||||
\OCP\JSON::success(array('data' => array('message' => $successMessage)));
|
||||
} else {
|
||||
\OCP\JSON::error(array('data' => array('message' => $errorMessage)));
|
||||
}
|
||||
|
||||
@@ -21,6 +21,32 @@ $return = false;
|
||||
|
||||
$oldPassword = $_POST['oldPassword'];
|
||||
$newPassword = $_POST['newPassword'];
|
||||
$confirmPassword = $_POST['confirmPassword'];
|
||||
|
||||
//check if both passwords are the same
|
||||
if (empty($_POST['oldPassword'])) {
|
||||
$errorMessage = $l->t('Please provide the old recovery password');
|
||||
\OCP\JSON::error(array('data' => array('message' => $errorMessage)));
|
||||
exit();
|
||||
}
|
||||
|
||||
if (empty($_POST['newPassword'])) {
|
||||
$errorMessage = $l->t('Please provide a new recovery password');
|
||||
\OCP\JSON::error(array('data' => array('message' => $errorMessage)));
|
||||
exit();
|
||||
}
|
||||
|
||||
if (empty($_POST['confirmPassword'])) {
|
||||
$errorMessage = $l->t('Please repeat the new recovery password');
|
||||
\OCP\JSON::error(array('data' => array('message' => $errorMessage)));
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($_POST['newPassword'] !== $_POST['confirmPassword']) {
|
||||
$errorMessage = $l->t('Repeated recovery key password does not match the provided recovery key password');
|
||||
\OCP\JSON::error(array('data' => array('message' => $errorMessage)));
|
||||
exit();
|
||||
}
|
||||
|
||||
$view = new \OC\Files\View('/');
|
||||
$util = new \OCA\Encryption\Util(new \OC\Files\View('/'), \OCP\User::getUser());
|
||||
@@ -35,11 +61,12 @@ $encryptedRecoveryKey = $view->file_get_contents($keyPath);
|
||||
$decryptedRecoveryKey = \OCA\Encryption\Crypt::decryptPrivateKey($encryptedRecoveryKey, $oldPassword);
|
||||
|
||||
if ($decryptedRecoveryKey) {
|
||||
|
||||
$encryptedRecoveryKey = \OCA\Encryption\Crypt::symmetricEncryptFileContent($decryptedRecoveryKey, $newPassword);
|
||||
$view->file_put_contents($keyPath, $encryptedRecoveryKey);
|
||||
|
||||
$return = true;
|
||||
$cipher = \OCA\Encryption\Helper::getCipher();
|
||||
$encryptedKey = \OCA\Encryption\Crypt::symmetricEncryptFileContent($decryptedRecoveryKey, $newPassword, $cipher);
|
||||
if ($encryptedKey) {
|
||||
\OCA\Encryption\Keymanager::setPrivateSystemKey($encryptedKey, $keyId . '.private.key');
|
||||
$return = true;
|
||||
}
|
||||
}
|
||||
|
||||
\OC_FileProxy::$enabled = $proxyStatus;
|
||||
|
||||
@@ -18,6 +18,7 @@ use OCA\Encryption;
|
||||
$l = OC_L10N::get('core');
|
||||
|
||||
$return = false;
|
||||
$errorMessage = $l->t('Could not update the private key password.');
|
||||
|
||||
$oldPassword = $_POST['oldPassword'];
|
||||
$newPassword = $_POST['newPassword'];
|
||||
@@ -25,31 +26,45 @@ $newPassword = $_POST['newPassword'];
|
||||
$view = new \OC\Files\View('/');
|
||||
$session = new \OCA\Encryption\Session($view);
|
||||
$user = \OCP\User::getUser();
|
||||
$loginName = \OC::$server->getUserSession()->getLoginName();
|
||||
|
||||
$proxyStatus = \OC_FileProxy::$enabled;
|
||||
\OC_FileProxy::$enabled = false;
|
||||
// check new password
|
||||
$passwordCorrect = \OCP\User::checkPassword($loginName, $newPassword);
|
||||
|
||||
$keyPath = '/' . $user . '/files_encryption/' . $user . '.private.key';
|
||||
if ($passwordCorrect !== false) {
|
||||
|
||||
$encryptedKey = $view->file_get_contents($keyPath);
|
||||
$decryptedKey = \OCA\Encryption\Crypt::decryptPrivateKey($encryptedKey, $oldPassword);
|
||||
$proxyStatus = \OC_FileProxy::$enabled;
|
||||
\OC_FileProxy::$enabled = false;
|
||||
|
||||
if ($decryptedKey) {
|
||||
$keyPath = '/' . $user . '/files_encryption/' . $user . '.private.key';
|
||||
|
||||
$encryptedKey = \OCA\Encryption\Crypt::symmetricEncryptFileContent($decryptedKey, $newPassword);
|
||||
$view->file_put_contents($keyPath, $encryptedKey);
|
||||
$encryptedKey = $view->file_get_contents($keyPath);
|
||||
$decryptedKey = \OCA\Encryption\Crypt::decryptPrivateKey($encryptedKey, $oldPassword);
|
||||
|
||||
$session->setPrivateKey($decryptedKey);
|
||||
if ($decryptedKey) {
|
||||
$cipher = \OCA\Encryption\Helper::getCipher();
|
||||
$encryptedKey = \OCA\Encryption\Crypt::symmetricEncryptFileContent($decryptedKey, $newPassword, $cipher);
|
||||
if ($encryptedKey) {
|
||||
\OCA\Encryption\Keymanager::setPrivateKey($encryptedKey, $user);
|
||||
$session->setPrivateKey($decryptedKey);
|
||||
$return = true;
|
||||
}
|
||||
} else {
|
||||
$result = false;
|
||||
$errorMessage = $l->t('The old password was not correct, please try again.');
|
||||
}
|
||||
|
||||
$return = true;
|
||||
\OC_FileProxy::$enabled = $proxyStatus;
|
||||
|
||||
} else {
|
||||
$result = false;
|
||||
$errorMessage = $l->t('The current log-in password was not correct, please try again.');
|
||||
}
|
||||
|
||||
\OC_FileProxy::$enabled = $proxyStatus;
|
||||
|
||||
// success or failure
|
||||
if ($return) {
|
||||
$session->setInitialized(\OCA\Encryption\Session::INIT_SUCCESSFUL);
|
||||
\OCP\JSON::success(array('data' => array('message' => $l->t('Private key password successfully updated.'))));
|
||||
} else {
|
||||
\OCP\JSON::error(array('data' => array('message' => $l->t('Could not update the private key password. Maybe the old password was not correct.'))));
|
||||
\OCP\JSON::error(array('data' => array('message' => $errorMessage)));
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ use OCA\Encryption;
|
||||
\OCP\JSON::checkAppEnabled('files_encryption');
|
||||
\OCP\JSON::callCheck();
|
||||
|
||||
$l = \OC::$server->getL10N('files_encryption');
|
||||
|
||||
if (
|
||||
isset($_POST['userEnableRecovery'])
|
||||
&& (0 == $_POST['userEnableRecovery'] || '1' === $_POST['userEnableRecovery'])
|
||||
@@ -38,4 +40,8 @@ if (
|
||||
}
|
||||
|
||||
// Return success or failure
|
||||
($return) ? \OCP\JSON::success() : \OCP\JSON::error();
|
||||
if ($return) {
|
||||
\OCP\JSON::success(array('data' => array('message' => $l->t('File recovery settings updated'))));
|
||||
} else {
|
||||
\OCP\JSON::error(array('data' => array('message' => $l->t('Could not update file recovery'))));
|
||||
}
|
||||
|
||||
@@ -10,6 +10,10 @@ OC::$CLASSPATH['OCA\Encryption\Session'] = 'files_encryption/lib/session.php';
|
||||
OC::$CLASSPATH['OCA\Encryption\Capabilities'] = 'files_encryption/lib/capabilities.php';
|
||||
OC::$CLASSPATH['OCA\Encryption\Helper'] = 'files_encryption/lib/helper.php';
|
||||
|
||||
// Exceptions
|
||||
OC::$CLASSPATH['OCA\Encryption\Exceptions\MultiKeyEncryptException'] = 'files_encryption/lib/exceptions.php';
|
||||
OC::$CLASSPATH['OCA\Encryption\Exceptions\MultiKeyDecryptException'] = 'files_encryption/lib/exceptions.php';
|
||||
|
||||
\OCP\Util::addscript('files_encryption', 'encryption');
|
||||
\OCP\Util::addscript('files_encryption', 'detect-migration');
|
||||
|
||||
@@ -31,22 +35,6 @@ if (!OC_Config::getValue('maintenance', false)) {
|
||||
if(!in_array('crypt', stream_get_wrappers())) {
|
||||
stream_wrapper_register('crypt', 'OCA\Encryption\Stream');
|
||||
}
|
||||
|
||||
// check if we are logged in
|
||||
if (OCP\User::isLoggedIn()) {
|
||||
|
||||
// ensure filesystem is loaded
|
||||
if (!\OC\Files\Filesystem::$loaded) {
|
||||
\OC_Util::setupFS();
|
||||
}
|
||||
|
||||
$view = new OC\Files\View('/');
|
||||
|
||||
$sessionReady = OCA\Encryption\Helper::checkRequirements();
|
||||
if($sessionReady) {
|
||||
$session = new \OCA\Encryption\Session($view);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// logout user if we are in maintenance to force re-login
|
||||
OCP\User::logout();
|
||||
|
||||
@@ -2,17 +2,22 @@
|
||||
<info>
|
||||
<id>files_encryption</id>
|
||||
<name>Encryption</name>
|
||||
<description>The ownCloud files encryption system provides server side-encryption. After the app is enabled you need to re-login to initialize your encryption keys. Please note that server side encryption requires that the ownCloud server admin can be trusted. The main purpose of this app is the encryption of files that are stored on externally mounted storages.</description>
|
||||
<description>
|
||||
This application encrypts all files accessed by ownCloud at rest, wherever they are stored. As an example, with this application enabled, external cloud based Amazon S3 storage will be encrypted, protecting this data on storage outside of the control of the Admin. When this application is enabled for the first time, all files are encrypted as users log in and are prompted for their password. The recommended recovery key option enables recovery of files in case the key is lost.
|
||||
Note that this app encrypts all files that are touched by ownCloud, so external storage providers and applications such as SharePoint will see new files encrypted when they are accessed. Encryption is based on AES 128 or 256 bit keys. More information is available in the Encryption documentation.
|
||||
|
||||
</description>
|
||||
<licence>AGPL</licence>
|
||||
<author>Sam Tuke, Bjoern Schiessle, Florin Peter</author>
|
||||
<requiremin>4</requiremin>
|
||||
<shipped>true</shipped>
|
||||
<documentation>
|
||||
<user>http://doc.owncloud.org/server/6.0/user_manual/files/encryption.html</user>
|
||||
<admin>http://doc.owncloud.org/server/6.0/admin_manual/configuration/configuration_encryption.html</admin>
|
||||
<user>user-encryption</user>
|
||||
<admin>admin-encryption</admin>
|
||||
</documentation>
|
||||
<rememberlogin>false</rememberlogin>
|
||||
<types>
|
||||
<filesystem/>
|
||||
</types>
|
||||
<ocsid>166047</ocsid>
|
||||
</info>
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.6
|
||||
0.6.1
|
||||
|
||||
@@ -2,9 +2,13 @@
|
||||
|
||||
if (!isset($_)) { //also provide standalone error page
|
||||
require_once __DIR__ . '/../../../lib/base.php';
|
||||
require_once __DIR__ . '/../lib/crypt.php';
|
||||
|
||||
$l = OC_L10N::get('files_encryption');
|
||||
|
||||
OC_JSON::checkAppEnabled('files_encryption');
|
||||
OC_App::loadApp('files_encryption');
|
||||
|
||||
if (isset($_GET['errorCode'])) {
|
||||
$errorCode = $_GET['errorCode'];
|
||||
switch ($errorCode) {
|
||||
|
||||
@@ -136,6 +136,14 @@ class Hooks {
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* remove keys from session during logout
|
||||
*/
|
||||
public static function logout() {
|
||||
$session = new \OCA\Encryption\Session(new \OC\Files\View());
|
||||
$session->removeKeys();
|
||||
}
|
||||
|
||||
/**
|
||||
* setup encryption backend upon user created
|
||||
* @note This method should never be called for users using client side encryption
|
||||
@@ -187,7 +195,6 @@ class Hooks {
|
||||
* @param array $params keys: uid, password
|
||||
*/
|
||||
public static function setPassphrase($params) {
|
||||
|
||||
if (\OCP\App::isEnabled('files_encryption') === false) {
|
||||
return true;
|
||||
}
|
||||
@@ -198,19 +205,22 @@ class Hooks {
|
||||
if (Crypt::mode() === 'server') {
|
||||
|
||||
$view = new \OC\Files\View('/');
|
||||
$session = new \OCA\Encryption\Session($view);
|
||||
|
||||
if ($params['uid'] === \OCP\User::getUser()) {
|
||||
// Get existing decrypted private key
|
||||
$privateKey = $session->getPrivateKey();
|
||||
|
||||
$session = new \OCA\Encryption\Session($view);
|
||||
|
||||
// Get existing decrypted private key
|
||||
$privateKey = $session->getPrivateKey();
|
||||
if ($params['uid'] === \OCP\User::getUser() && $privateKey) {
|
||||
|
||||
// Encrypt private key with new user pwd as passphrase
|
||||
$encryptedPrivateKey = Crypt::symmetricEncryptFileContent($privateKey, $params['password']);
|
||||
$encryptedPrivateKey = Crypt::symmetricEncryptFileContent($privateKey, $params['password'], Helper::getCipher());
|
||||
|
||||
// Save private key
|
||||
Keymanager::setPrivateKey($encryptedPrivateKey);
|
||||
if ($encryptedPrivateKey) {
|
||||
Keymanager::setPrivateKey($encryptedPrivateKey, \OCP\User::getUser());
|
||||
} else {
|
||||
\OCP\Util::writeLog('files_encryption', 'Could not update users encryption password', \OCP\Util::ERROR);
|
||||
}
|
||||
|
||||
// NOTE: Session does not need to be updated as the
|
||||
// private key has not changed, only the passphrase
|
||||
@@ -231,6 +241,9 @@ class Hooks {
|
||||
|| !$util->userKeysExists()
|
||||
|| !$view->file_exists($user . '/files')) {
|
||||
|
||||
// backup old keys
|
||||
$util->backupAllKeys('recovery');
|
||||
|
||||
$newUserPassword = $params['password'];
|
||||
|
||||
// make sure that the users home is mounted
|
||||
@@ -245,16 +258,17 @@ class Hooks {
|
||||
// Save public key
|
||||
$view->file_put_contents('/public-keys/' . $user . '.public.key', $keypair['publicKey']);
|
||||
|
||||
// Encrypt private key empty passphrase
|
||||
$encryptedPrivateKey = Crypt::symmetricEncryptFileContent($keypair['privateKey'], $newUserPassword);
|
||||
// Encrypt private key with new password
|
||||
$encryptedKey = \OCA\Encryption\Crypt::symmetricEncryptFileContent($keypair['privateKey'], $newUserPassword, Helper::getCipher());
|
||||
if ($encryptedKey) {
|
||||
Keymanager::setPrivateKey($encryptedKey, $user);
|
||||
|
||||
// Save private key
|
||||
$view->file_put_contents(
|
||||
'/' . $user . '/files_encryption/' . $user . '.private.key', $encryptedPrivateKey);
|
||||
|
||||
if ($recoveryPassword) { // if recovery key is set we can re-encrypt the key files
|
||||
$util = new Util($view, $user);
|
||||
$util->recoverUsersFiles($recoveryPassword);
|
||||
if ($recoveryPassword) { // if recovery key is set we can re-encrypt the key files
|
||||
$util = new Util($view, $user);
|
||||
$util->recoverUsersFiles($recoveryPassword);
|
||||
}
|
||||
} else {
|
||||
\OCP\Util::writeLog('files_encryption', 'Could not update users encryption password', \OCP\Util::ERROR);
|
||||
}
|
||||
|
||||
\OC_FileProxy::$enabled = $proxyStatus;
|
||||
@@ -303,7 +317,7 @@ class Hooks {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* update share keys if a file was shared
|
||||
*/
|
||||
public static function postShared($params) {
|
||||
|
||||
@@ -313,34 +327,44 @@ class Hooks {
|
||||
|
||||
if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') {
|
||||
|
||||
$view = new \OC\Files\View('/');
|
||||
$session = new \OCA\Encryption\Session($view);
|
||||
$userId = \OCP\User::getUser();
|
||||
$util = new Util($view, $userId);
|
||||
$path = \OC\Files\Filesystem::getPath($params['fileSource']);
|
||||
|
||||
$sharingEnabled = \OCP\Share::isEnabled();
|
||||
|
||||
$mountManager = \OC\Files\Filesystem::getMountManager();
|
||||
$mount = $mountManager->find('/' . $userId . '/files' . $path);
|
||||
$mountPoint = $mount->getMountPoint();
|
||||
|
||||
// if a folder was shared, get a list of all (sub-)folders
|
||||
if ($params['itemType'] === 'folder') {
|
||||
$allFiles = $util->getAllFiles($path, $mountPoint);
|
||||
} else {
|
||||
$allFiles = array($path);
|
||||
}
|
||||
|
||||
foreach ($allFiles as $path) {
|
||||
$usersSharing = $util->getSharingUsersArray($sharingEnabled, $path);
|
||||
$util->setSharedFileKeyfiles($session, $usersSharing, $path);
|
||||
}
|
||||
self::updateKeyfiles($path, $params['itemType']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* update keyfiles and share keys recursively
|
||||
*
|
||||
* @param string $path to the file/folder
|
||||
* @param string $type 'file' or 'folder'
|
||||
*/
|
||||
private static function updateKeyfiles($path, $type) {
|
||||
$view = new \OC\Files\View('/');
|
||||
$userId = \OCP\User::getUser();
|
||||
$session = new \OCA\Encryption\Session($view);
|
||||
$util = new Util($view, $userId);
|
||||
$sharingEnabled = \OCP\Share::isEnabled();
|
||||
|
||||
$mountManager = \OC\Files\Filesystem::getMountManager();
|
||||
$mount = $mountManager->find('/' . $userId . '/files' . $path);
|
||||
$mountPoint = $mount->getMountPoint();
|
||||
|
||||
// if a folder was shared, get a list of all (sub-)folders
|
||||
if ($type === 'folder') {
|
||||
$allFiles = $util->getAllFiles($path, $mountPoint);
|
||||
} else {
|
||||
$allFiles = array($path);
|
||||
}
|
||||
|
||||
foreach ($allFiles as $path) {
|
||||
$usersSharing = $util->getSharingUsersArray($sharingEnabled, $path);
|
||||
$util->setSharedFileKeyfiles($session, $usersSharing, $path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* unshare file/folder from a user with whom you shared the file before
|
||||
*/
|
||||
public static function postUnshare($params) {
|
||||
|
||||
@@ -385,8 +409,10 @@ class Hooks {
|
||||
// Unshare every user who no longer has access to the file
|
||||
$delUsers = array_diff($userIds, $sharingUsers);
|
||||
|
||||
list($owner, $ownerPath) = $util->getUidAndFilename($path);
|
||||
|
||||
// delete share key
|
||||
Keymanager::delShareKey($view, $delUsers, $path);
|
||||
Keymanager::delShareKey($view, $delUsers, $ownerPath, $owner);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -397,6 +423,18 @@ class Hooks {
|
||||
* @param array $params with the old path and the new path
|
||||
*/
|
||||
public static function preRename($params) {
|
||||
self::preRenameOrCopy($params, 'rename');
|
||||
}
|
||||
|
||||
/**
|
||||
* mark file as copied so that we know the original source after the file was copied
|
||||
* @param array $params with the old path and the new path
|
||||
*/
|
||||
public static function preCopy($params) {
|
||||
self::preRenameOrCopy($params, 'copy');
|
||||
}
|
||||
|
||||
private static function preRenameOrCopy($params, $operation) {
|
||||
$user = \OCP\User::getUser();
|
||||
$view = new \OC\Files\View('/');
|
||||
$util = new Util($view, $user);
|
||||
@@ -407,21 +445,39 @@ class Hooks {
|
||||
$mp1 = $view->getMountPoint('/' . $user . '/files/' . $params['oldpath']);
|
||||
$mp2 = $view->getMountPoint('/' . $user . '/files/' . $params['newpath']);
|
||||
|
||||
$type = $view->is_dir('/' . $user . '/files/' . $params['oldpath']) ? 'folder' : 'file';
|
||||
|
||||
if ($mp1 === $mp2) {
|
||||
if ($util->isSystemWideMountPoint($pathOld)) {
|
||||
$oldShareKeyPath = 'files_encryption/share-keys/' . $pathOld;
|
||||
} else {
|
||||
$oldShareKeyPath = $ownerOld . '/' . 'files_encryption/share-keys/' . $pathOld;
|
||||
}
|
||||
// gather share keys here because in postRename() the file will be moved already
|
||||
$oldShareKeys = Helper::findShareKeys($pathOld, $oldShareKeyPath, $view);
|
||||
if (count($oldShareKeys) === 0) {
|
||||
\OC_Log::write(
|
||||
'Encryption library', 'No share keys found for "' . $pathOld . '"',
|
||||
\OC_Log::WARN
|
||||
);
|
||||
}
|
||||
self::$renamedFiles[$params['oldpath']] = array(
|
||||
'uid' => $ownerOld,
|
||||
'path' => $pathOld);
|
||||
'path' => $pathOld,
|
||||
'type' => $type,
|
||||
'operation' => $operation,
|
||||
'sharekeys' => $oldShareKeys
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* after a file is renamed, rename its keyfile and share-keys also fix the file size and fix also the sharing
|
||||
* @param array $params array with oldpath and newpath
|
||||
* after a file is renamed/copied, rename/copy its keyfile and share-keys also fix the file size and fix also the sharing
|
||||
*
|
||||
* This function is connected to the rename signal of OC_Filesystem and adjust the name and location
|
||||
* of the stored versions along the actual file
|
||||
* @param array $params array with oldpath and newpath
|
||||
*/
|
||||
public static function postRename($params) {
|
||||
public static function postRenameOrCopy($params) {
|
||||
|
||||
if (\OCP\App::isEnabled('files_encryption') === false) {
|
||||
return true;
|
||||
@@ -432,16 +488,21 @@ class Hooks {
|
||||
\OC_FileProxy::$enabled = false;
|
||||
|
||||
$view = new \OC\Files\View('/');
|
||||
$session = new \OCA\Encryption\Session($view);
|
||||
$userId = \OCP\User::getUser();
|
||||
$util = new Util($view, $userId);
|
||||
$oldShareKeys = null;
|
||||
|
||||
if (isset(self::$renamedFiles[$params['oldpath']]['uid']) &&
|
||||
isset(self::$renamedFiles[$params['oldpath']]['path'])) {
|
||||
$ownerOld = self::$renamedFiles[$params['oldpath']]['uid'];
|
||||
$pathOld = self::$renamedFiles[$params['oldpath']]['path'];
|
||||
$type = self::$renamedFiles[$params['oldpath']]['type'];
|
||||
$operation = self::$renamedFiles[$params['oldpath']]['operation'];
|
||||
$oldShareKeys = self::$renamedFiles[$params['oldpath']]['sharekeys'];
|
||||
unset(self::$renamedFiles[$params['oldpath']]);
|
||||
} else {
|
||||
\OCP\Util::writeLog('Encryption library', "can't get path and owner from the file before it was renamed", \OCP\Util::ERROR);
|
||||
\OCP\Util::writeLog('Encryption library', "can't get path and owner from the file before it was renamed", \OCP\Util::DEBUG);
|
||||
\OC_FileProxy::$enabled = $proxyStatus;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -473,35 +534,28 @@ class Hooks {
|
||||
}
|
||||
|
||||
// handle share keys
|
||||
if (!$view->is_dir($oldKeyfilePath)) {
|
||||
if ($type === 'file') {
|
||||
$oldKeyfilePath .= '.key';
|
||||
$newKeyfilePath .= '.key';
|
||||
|
||||
// handle share-keys
|
||||
$matches = Helper::findShareKeys($oldShareKeyPath, $view);
|
||||
foreach ($matches as $src) {
|
||||
foreach ($oldShareKeys as $src) {
|
||||
$dst = \OC\Files\Filesystem::normalizePath(str_replace($pathOld, $pathNew, $src));
|
||||
$view->rename($src, $dst);
|
||||
$view->$operation($src, $dst);
|
||||
}
|
||||
|
||||
} else {
|
||||
// handle share-keys folders
|
||||
$view->rename($oldShareKeyPath, $newShareKeyPath);
|
||||
$view->$operation($oldShareKeyPath, $newShareKeyPath);
|
||||
}
|
||||
|
||||
// Rename keyfile so it isn't orphaned
|
||||
if ($view->file_exists($oldKeyfilePath)) {
|
||||
$view->rename($oldKeyfilePath, $newKeyfilePath);
|
||||
$view->$operation($oldKeyfilePath, $newKeyfilePath);
|
||||
}
|
||||
|
||||
// update share keys
|
||||
$sharingEnabled = \OCP\Share::isEnabled();
|
||||
|
||||
// get users
|
||||
$usersSharing = $util->getSharingUsersArray($sharingEnabled, $pathNew);
|
||||
|
||||
// update sharing-keys
|
||||
$util->setSharedFileKeyfiles($session, $usersSharing, $pathNew);
|
||||
self::updateKeyfiles($params['newpath'], $type);
|
||||
|
||||
\OC_FileProxy::$enabled = $proxyStatus;
|
||||
}
|
||||
@@ -595,6 +649,7 @@ class Hooks {
|
||||
}
|
||||
|
||||
/**
|
||||
* unmount file from yourself
|
||||
* remember files/folders which get unmounted
|
||||
*/
|
||||
public static function preUmount($params) {
|
||||
@@ -613,6 +668,9 @@ class Hooks {
|
||||
'itemType' => $itemType);
|
||||
}
|
||||
|
||||
/**
|
||||
* unmount file from yourself
|
||||
*/
|
||||
public static function postUmount($params) {
|
||||
|
||||
if (!isset(self::$umountedFiles[$params[\OC\Files\Filesystem::signal_param_path]])) {
|
||||
@@ -642,7 +700,7 @@ class Hooks {
|
||||
// check if the user still has access to the file, otherwise delete share key
|
||||
$sharingUsers = \OCP\Share::getUsersSharingFile($path, $user);
|
||||
if (!in_array(\OCP\User::getUser(), $sharingUsers['users'])) {
|
||||
Keymanager::delShareKey($view, array(\OCP\User::getUser()), $path);
|
||||
Keymanager::delShareKey($view, array(\OCP\User::getUser()), $path, $user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,32 +9,21 @@
|
||||
|
||||
$(document).ready(function(){
|
||||
|
||||
$('input:password[name="encryptionRecoveryPassword"]').keyup(function(event) {
|
||||
var recoveryPassword = $( '#encryptionRecoveryPassword' ).val();
|
||||
var recoveryPasswordRepeated = $( '#repeatEncryptionRecoveryPassword' ).val();
|
||||
var checkedButton = $('input:radio[name="adminEnableRecovery"]:checked').val();
|
||||
var uncheckedValue = (1+parseInt(checkedButton)) % 2;
|
||||
if (recoveryPassword !== '' && recoveryPassword === recoveryPasswordRepeated) {
|
||||
$('input:radio[name="adminEnableRecovery"][value="'+uncheckedValue.toString()+'"]').removeAttr("disabled");
|
||||
} else {
|
||||
$('input:radio[name="adminEnableRecovery"][value="'+uncheckedValue.toString()+'"]').attr("disabled", "true");
|
||||
}
|
||||
});
|
||||
|
||||
$( 'input:radio[name="adminEnableRecovery"]' ).change(
|
||||
function() {
|
||||
var recoveryStatus = $( this ).val();
|
||||
var oldStatus = (1+parseInt(recoveryStatus)) % 2;
|
||||
var recoveryPassword = $( '#encryptionRecoveryPassword' ).val();
|
||||
var confirmPassword = $( '#repeatEncryptionRecoveryPassword' ).val();
|
||||
OC.msg.startSaving('#encryptionSetRecoveryKey .msg');
|
||||
$.post(
|
||||
OC.filePath( 'files_encryption', 'ajax', 'adminrecovery.php' )
|
||||
, { adminEnableRecovery: recoveryStatus, recoveryPassword: recoveryPassword }
|
||||
, { adminEnableRecovery: recoveryStatus, recoveryPassword: recoveryPassword, confirmPassword: confirmPassword }
|
||||
, function( result ) {
|
||||
OC.msg.finishedSaving('#encryptionSetRecoveryKey .msg', result);
|
||||
if (result.status === "error") {
|
||||
OC.Notification.show(t('admin', result.data.message));
|
||||
$('input:radio[name="adminEnableRecovery"][value="'+oldStatus.toString()+'"]').attr("checked", "true");
|
||||
} else {
|
||||
OC.Notification.hide();
|
||||
if (recoveryStatus === "0") {
|
||||
$('p[name="changeRecoveryPasswordBlock"]').addClass("hidden");
|
||||
} else {
|
||||
@@ -49,33 +38,17 @@ $(document).ready(function(){
|
||||
|
||||
// change recovery password
|
||||
|
||||
$('input:password[name="changeRecoveryPassword"]').keyup(function(event) {
|
||||
var oldRecoveryPassword = $('#oldEncryptionRecoveryPassword').val();
|
||||
var newRecoveryPassword = $('#newEncryptionRecoveryPassword').val();
|
||||
var newRecoveryPasswordRepeated = $('#repeatedNewEncryptionRecoveryPassword').val();
|
||||
|
||||
if (newRecoveryPassword !== '' && oldRecoveryPassword !== '' && newRecoveryPassword === newRecoveryPasswordRepeated) {
|
||||
$('button:button[name="submitChangeRecoveryKey"]').removeAttr("disabled");
|
||||
} else {
|
||||
$('button:button[name="submitChangeRecoveryKey"]').attr("disabled", "true");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
$('button:button[name="submitChangeRecoveryKey"]').click(function() {
|
||||
var oldRecoveryPassword = $('#oldEncryptionRecoveryPassword').val();
|
||||
var newRecoveryPassword = $('#newEncryptionRecoveryPassword').val();
|
||||
OC.msg.startSaving('#encryption .msg');
|
||||
var confirmNewPassword = $('#repeatedNewEncryptionRecoveryPassword').val();
|
||||
OC.msg.startSaving('#encryptionChangeRecoveryKey .msg');
|
||||
$.post(
|
||||
OC.filePath( 'files_encryption', 'ajax', 'changeRecoveryPassword.php' )
|
||||
, { oldPassword: oldRecoveryPassword, newPassword: newRecoveryPassword }
|
||||
, { oldPassword: oldRecoveryPassword, newPassword: newRecoveryPassword, confirmPassword: confirmNewPassword }
|
||||
, function( data ) {
|
||||
if (data.status == "error") {
|
||||
OC.msg.finishedSaving('#encryption .msg', data);
|
||||
} else {
|
||||
OC.msg.finishedSaving('#encryption .msg', data);
|
||||
OC.msg.finishedSaving('#encryptionChangeRecoveryKey .msg', data);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -26,36 +26,27 @@ $(document).ready(function(){
|
||||
// Trigger ajax on recoveryAdmin status change
|
||||
$( 'input:radio[name="userEnableRecovery"]' ).change(
|
||||
function() {
|
||||
|
||||
// Hide feedback messages in case they're already visible
|
||||
$('#recoveryEnabledSuccess').hide();
|
||||
$('#recoveryEnabledError').hide();
|
||||
|
||||
var recoveryStatus = $( this ).val();
|
||||
|
||||
OC.msg.startAction('#userEnableRecovery .msg', 'Updating recovery keys. This can take some time...');
|
||||
$.post(
|
||||
OC.filePath( 'files_encryption', 'ajax', 'userrecovery.php' )
|
||||
, { userEnableRecovery: recoveryStatus }
|
||||
, function( data ) {
|
||||
if ( data.status == "success" ) {
|
||||
$('#recoveryEnabledSuccess').show();
|
||||
} else {
|
||||
$('#recoveryEnabledError').show();
|
||||
}
|
||||
OC.msg.finishedAction('#userEnableRecovery .msg', data);
|
||||
}
|
||||
);
|
||||
// Ensure page is not reloaded on form submit
|
||||
return false;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
$("#encryptAll").click(
|
||||
function(){
|
||||
|
||||
|
||||
// Hide feedback messages in case they're already visible
|
||||
$('#encryptAllSuccess').hide();
|
||||
$('#encryptAllError').hide();
|
||||
|
||||
|
||||
var userPassword = $( '#userPassword' ).val();
|
||||
var encryptAll = $( '#encryptAll' ).val();
|
||||
|
||||
@@ -73,7 +64,7 @@ $(document).ready(function(){
|
||||
// Ensure page is not reloaded on form submit
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
);
|
||||
|
||||
// update private key password
|
||||
|
||||
@@ -4,8 +4,8 @@ $TRANSLATIONS = array(
|
||||
"Could not enable recovery key. Please check your recovery key password!" => "Impossible d'activer la clé de récupération. Veuillez vérifier votre mot de passe de clé de récupération !",
|
||||
"Recovery key successfully disabled" => "Clé de récupération désactivée avec succès",
|
||||
"Could not disable recovery key. Please check your recovery key password!" => "Impossible de désactiver la clé de récupération. Veuillez vérifier votre mot de passe de clé de récupération !",
|
||||
"Password successfully changed." => "Mot de passe changé avec succès ",
|
||||
"Could not change the password. Maybe the old password was not correct." => "Ne peut pas changer le mot de passe. L'ancien mot de passe est peut-être incorrect.",
|
||||
"Password successfully changed." => "Mot de passe changé avec succès.",
|
||||
"Could not change the password. Maybe the old password was not correct." => "Erreur lors du changement de mot de passe. L'ancien mot de passe est peut-être incorrect.",
|
||||
"Private key password successfully updated." => "Mot de passe de la clé privé mis à jour avec succès.",
|
||||
"Could not update the private key password. Maybe the old password was not correct." => "Impossible de mettre à jour le mot de passe de la clé privé. Peut-être que l'ancien mot de passe n'était pas correcte.",
|
||||
"Encryption app not initialized! Maybe the encryption app was re-enabled during your session. Please try to log out and log back in to initialize the encryption app." => "L'application de chiffrement n'est pas initialisée ! Peut-être que cette application a été réactivée pendant votre session. Veuillez essayer de vous déconnecter et ensuite de vous reconnecter pour initialiser l'application de chiffrement.",
|
||||
@@ -22,8 +22,8 @@ $TRANSLATIONS = array(
|
||||
"Enable recovery key (allow to recover users files in case of password loss):" => "Activer la clef de récupération (permet de récupérer les fichiers des utilisateurs en cas de perte de mot de passe).",
|
||||
"Recovery key password" => "Mot de passe de la clef de récupération",
|
||||
"Repeat Recovery key password" => "Répétez le mot de passe de la clé de récupération",
|
||||
"Enabled" => "Activer",
|
||||
"Disabled" => "Désactiver",
|
||||
"Enabled" => "Activé",
|
||||
"Disabled" => "Désactivé",
|
||||
"Change recovery key password:" => "Modifier le mot de passe de la clef de récupération :",
|
||||
"Old Recovery key password" => "Ancien mot de passe de la clef de récupération",
|
||||
"New Recovery key password" => "Nouveau mot de passe de la clef de récupération",
|
||||
|
||||
155
apps/files_encryption/lib/crypt.php
Executable file → Normal file
155
apps/files_encryption/lib/crypt.php
Executable file → Normal file
@@ -24,6 +24,7 @@
|
||||
*/
|
||||
|
||||
namespace OCA\Encryption;
|
||||
use OCA\Encryption\Exceptions\EncryptionException;
|
||||
|
||||
require_once __DIR__ . '/../3rdparty/Crypt_Blowfish/Blowfish.php';
|
||||
|
||||
@@ -38,6 +39,11 @@ class Crypt {
|
||||
const ENCRYPTION_PRIVATE_KEY_NOT_VALID_ERROR = 2;
|
||||
const ENCRYPTION_NO_SHARE_KEY_FOUND = 3;
|
||||
|
||||
const BLOCKSIZE = 8192; // block size will always be 8192 for a PHP stream https://bugs.php.net/bug.php?id=21641
|
||||
const DEFAULT_CIPHER = 'AES-256-CFB';
|
||||
|
||||
const HEADERSTART = 'HBEGIN';
|
||||
const HEADEREND = 'HEND';
|
||||
|
||||
/**
|
||||
* return encryption mode client or server side encryption
|
||||
@@ -213,19 +219,22 @@ class Crypt {
|
||||
* @param string $plainContent
|
||||
* @param string $iv
|
||||
* @param string $passphrase
|
||||
* @param string $cypher used for encryption, currently we support AES-128-CFB and AES-256-CFB
|
||||
* @return string encrypted file content
|
||||
* @throws \OCA\Encryption\Exceptions\EncryptionException
|
||||
*/
|
||||
private static function encrypt($plainContent, $iv, $passphrase = '') {
|
||||
private static function encrypt($plainContent, $iv, $passphrase = '', $cipher = Crypt::DEFAULT_CIPHER) {
|
||||
|
||||
if ($encryptedContent = openssl_encrypt($plainContent, 'AES-128-CFB', $passphrase, false, $iv)) {
|
||||
return $encryptedContent;
|
||||
} else {
|
||||
\OCP\Util::writeLog('Encryption library', 'Encryption (symmetric) of content failed', \OCP\Util::ERROR);
|
||||
\OCP\Util::writeLog('Encryption library', openssl_error_string(), \OCP\Util::ERROR);
|
||||
return false;
|
||||
$encryptedContent = openssl_encrypt($plainContent, $cipher, $passphrase, false, $iv);
|
||||
|
||||
if (!$encryptedContent) {
|
||||
$error = "Encryption (symmetric) of content failed: " . openssl_error_string();
|
||||
\OCP\Util::writeLog('Encryption library', $error, \OCP\Util::ERROR);
|
||||
throw new Exceptions\EncryptionException($error, 50);
|
||||
}
|
||||
|
||||
return $encryptedContent;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -233,19 +242,18 @@ class Crypt {
|
||||
* @param string $encryptedContent
|
||||
* @param string $iv
|
||||
* @param string $passphrase
|
||||
* @param string $cipher cipher user for decryption, currently we support aes128 and aes256
|
||||
* @throws \Exception
|
||||
* @return string decrypted file content
|
||||
*/
|
||||
private static function decrypt($encryptedContent, $iv, $passphrase) {
|
||||
private static function decrypt($encryptedContent, $iv, $passphrase, $cipher = Crypt::DEFAULT_CIPHER) {
|
||||
|
||||
if ($plainContent = openssl_decrypt($encryptedContent, 'AES-128-CFB', $passphrase, false, $iv)) {
|
||||
$plainContent = openssl_decrypt($encryptedContent, $cipher, $passphrase, false, $iv);
|
||||
|
||||
if ($plainContent) {
|
||||
return $plainContent;
|
||||
|
||||
} else {
|
||||
|
||||
throw new \Exception('Encryption library: Decryption (symmetric) of content failed');
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -293,11 +301,12 @@ class Crypt {
|
||||
* Symmetrically encrypts a string and returns keyfile content
|
||||
* @param string $plainContent content to be encrypted in keyfile
|
||||
* @param string $passphrase
|
||||
* @param string $cypher used for encryption, currently we support AES-128-CFB and AES-256-CFB
|
||||
* @return false|string encrypted content combined with IV
|
||||
* @note IV need not be specified, as it will be stored in the returned keyfile
|
||||
* and remain accessible therein.
|
||||
*/
|
||||
public static function symmetricEncryptFileContent($plainContent, $passphrase = '') {
|
||||
public static function symmetricEncryptFileContent($plainContent, $passphrase = '', $cipher = Crypt::DEFAULT_CIPHER) {
|
||||
|
||||
if (!$plainContent) {
|
||||
\OCP\Util::writeLog('Encryption library', 'symmetrically encryption failed, no content given.', \OCP\Util::ERROR);
|
||||
@@ -306,15 +315,16 @@ class Crypt {
|
||||
|
||||
$iv = self::generateIv();
|
||||
|
||||
if ($encryptedContent = self::encrypt($plainContent, $iv, $passphrase)) {
|
||||
try {
|
||||
$encryptedContent = self::encrypt($plainContent, $iv, $passphrase, $cipher);
|
||||
// Combine content to encrypt with IV identifier and actual IV
|
||||
$catfile = self::concatIv($encryptedContent, $iv);
|
||||
$padded = self::addPadding($catfile);
|
||||
|
||||
return $padded;
|
||||
|
||||
} else {
|
||||
\OCP\Util::writeLog('Encryption library', 'Encryption (symmetric) of keyfile content failed', \OCP\Util::ERROR);
|
||||
} catch (EncryptionException $e) {
|
||||
$message = 'Could not encrypt file content (code: ' . $e->getCode() . '): ';
|
||||
\OCP\Util::writeLog('files_encryption', $message . $e->getMessage(), \OCP\Util::ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -325,6 +335,7 @@ class Crypt {
|
||||
* Symmetrically decrypts keyfile content
|
||||
* @param string $keyfileContent
|
||||
* @param string $passphrase
|
||||
* @param string $cipher cipher used for decryption, currently aes128 and aes256 is supported.
|
||||
* @throws \Exception
|
||||
* @return string|false
|
||||
* @internal param string $source
|
||||
@@ -334,7 +345,7 @@ class Crypt {
|
||||
*
|
||||
* This function decrypts a file
|
||||
*/
|
||||
public static function symmetricDecryptFileContent($keyfileContent, $passphrase = '') {
|
||||
public static function symmetricDecryptFileContent($keyfileContent, $passphrase = '', $cipher = Crypt::DEFAULT_CIPHER) {
|
||||
|
||||
if (!$keyfileContent) {
|
||||
|
||||
@@ -348,7 +359,7 @@ class Crypt {
|
||||
// Split into enc data and catfile
|
||||
$catfile = self::splitIv($noPadding);
|
||||
|
||||
if ($plainContent = self::decrypt($catfile['encrypted'], $catfile['iv'], $passphrase)) {
|
||||
if ($plainContent = self::decrypt($catfile['encrypted'], $catfile['iv'], $passphrase, $cipher)) {
|
||||
|
||||
return $plainContent;
|
||||
|
||||
@@ -360,6 +371,7 @@ class Crypt {
|
||||
|
||||
/**
|
||||
* Decrypt private key and check if the result is a valid keyfile
|
||||
*
|
||||
* @param string $encryptedKey encrypted keyfile
|
||||
* @param string $passphrase to decrypt keyfile
|
||||
* @return string|false encrypted private key or false
|
||||
@@ -368,7 +380,15 @@ class Crypt {
|
||||
*/
|
||||
public static function decryptPrivateKey($encryptedKey, $passphrase) {
|
||||
|
||||
$plainKey = self::symmetricDecryptFileContent($encryptedKey, $passphrase);
|
||||
$header = self::parseHeader($encryptedKey);
|
||||
$cipher = self::getCipher($header);
|
||||
|
||||
// if we found a header we need to remove it from the key we want to decrypt
|
||||
if (!empty($header)) {
|
||||
$encryptedKey = substr($encryptedKey, strpos($encryptedKey, self::HEADEREND) + strlen(self::HEADEREND));
|
||||
}
|
||||
|
||||
$plainKey = self::symmetricDecryptFileContent($encryptedKey, $passphrase, $cipher);
|
||||
|
||||
// check if this a valid private key
|
||||
$res = openssl_pkey_get_private($plainKey);
|
||||
@@ -390,6 +410,7 @@ class Crypt {
|
||||
* @param string $plainContent content to be encrypted
|
||||
* @param array $publicKeys array keys must be the userId of corresponding user
|
||||
* @return array keys: keys (array, key = userId), data
|
||||
* @throws \OCA\Encryption\Exceptions\\MultiKeyEncryptException if encryption failed
|
||||
* @note symmetricDecryptFileContent() can decrypt files created using this method
|
||||
*/
|
||||
public static function multiKeyEncrypt($plainContent, array $publicKeys) {
|
||||
@@ -397,9 +418,7 @@ class Crypt {
|
||||
// openssl_seal returns false without errors if $plainContent
|
||||
// is empty, so trigger our own error
|
||||
if (empty($plainContent)) {
|
||||
|
||||
throw new \Exception('Cannot mutliKeyEncrypt empty plain content');
|
||||
|
||||
throw new Exceptions\MultiKeyEncryptException('Cannot mutliKeyEncrypt empty plain content', 10);
|
||||
}
|
||||
|
||||
// Set empty vars to be set by openssl by reference
|
||||
@@ -426,9 +445,7 @@ class Crypt {
|
||||
);
|
||||
|
||||
} else {
|
||||
|
||||
return false;
|
||||
|
||||
throw new Exceptions\MultiKeyEncryptException('multi key encryption failed: ' . openssl_error_string(), 20);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -438,8 +455,8 @@ class Crypt {
|
||||
* @param string $encryptedContent
|
||||
* @param string $shareKey
|
||||
* @param mixed $privateKey
|
||||
* @return false|string
|
||||
* @internal param string $plainContent content to be encrypted
|
||||
* @throws \OCA\Encryption\Exceptions\\MultiKeyDecryptException if decryption failed
|
||||
* @internal param string $plainContent contains decrypted content
|
||||
* @return string $plainContent decrypted string
|
||||
* @note symmetricDecryptFileContent() can be used to decrypt files created using this method
|
||||
*
|
||||
@@ -448,9 +465,7 @@ class Crypt {
|
||||
public static function multiKeyDecrypt($encryptedContent, $shareKey, $privateKey) {
|
||||
|
||||
if (!$encryptedContent) {
|
||||
|
||||
return false;
|
||||
|
||||
throw new Exceptions\MultiKeyDecryptException('Cannot mutliKeyDecrypt empty plain content', 10);
|
||||
}
|
||||
|
||||
if (openssl_open($encryptedContent, $plainContent, $shareKey, $privateKey)) {
|
||||
@@ -458,11 +473,7 @@ class Crypt {
|
||||
return $plainContent;
|
||||
|
||||
} else {
|
||||
|
||||
\OCP\Util::writeLog('Encryption library', 'Decryption (asymmetric) of sealed content with share-key "'.$shareKey.'" failed', \OCP\Util::ERROR);
|
||||
|
||||
return false;
|
||||
|
||||
throw new Exceptions\MultiKeyDecryptException('multiKeyDecrypt with share-key' . $shareKey . 'failed: ' . openssl_error_string(), 20);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -580,4 +591,76 @@ class Crypt {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* read header into array
|
||||
*
|
||||
* @param string $data
|
||||
* @return array
|
||||
*/
|
||||
public static function parseHeader($data) {
|
||||
|
||||
$result = array();
|
||||
|
||||
if (substr($data, 0, strlen(self::HEADERSTART)) === self::HEADERSTART) {
|
||||
$endAt = strpos($data, self::HEADEREND);
|
||||
$header = substr($data, 0, $endAt + strlen(self::HEADEREND));
|
||||
|
||||
// +1 to not start with an ':' which would result in empty element at the beginning
|
||||
$exploded = explode(':', substr($header, strlen(self::HEADERSTART)+1));
|
||||
|
||||
$element = array_shift($exploded);
|
||||
while ($element !== self::HEADEREND) {
|
||||
|
||||
$result[$element] = array_shift($exploded);
|
||||
|
||||
$element = array_shift($exploded);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if data block is the header
|
||||
*
|
||||
* @param string $data
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isHeader($data) {
|
||||
|
||||
if (substr($data, 0, strlen(self::HEADERSTART)) === self::HEADERSTART) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* get chiper from header
|
||||
*
|
||||
* @param array $header
|
||||
* @throws \OCA\Encryption\Exceptions\EncryptionException
|
||||
*/
|
||||
public static function getCipher($header) {
|
||||
$cipher = isset($header['cipher']) ? $header['cipher'] : 'AES-128-CFB';
|
||||
|
||||
if ($cipher !== 'AES-256-CFB' && $cipher !== 'AES-128-CFB') {
|
||||
|
||||
throw new \OCA\Encryption\Exceptions\EncryptionException('file header broken, no supported cipher defined', 40);
|
||||
}
|
||||
|
||||
return $cipher;
|
||||
}
|
||||
|
||||
/**
|
||||
* generate header for encrypted file
|
||||
*/
|
||||
public static function generateHeader() {
|
||||
$cipher = Helper::getCipher();
|
||||
$header = self::HEADERSTART . ':cipher:' . $cipher . ':' . self::HEADEREND;
|
||||
|
||||
return $header;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
55
apps/files_encryption/lib/exceptions.php
Normal file
55
apps/files_encryption/lib/exceptions.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/**
|
||||
* ownCloud
|
||||
*
|
||||
* @author Bjoern Schiessle
|
||||
* @copyright 2014 Bjoern Schiessle <schiessle@owncloud.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library 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 along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Encryption\Exceptions;
|
||||
|
||||
/**
|
||||
* General encryption exception
|
||||
* Possible Error Codes:
|
||||
* 10 - unexpected end of encryption header
|
||||
* 20 - unexpected blog size
|
||||
* 30 - encryption header to large
|
||||
* 40 - unknown cipher
|
||||
* 50 - encryption failed
|
||||
*/
|
||||
class EncryptionException extends \Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw this exception if multi key encrytion fails
|
||||
*
|
||||
* Possible error codes:
|
||||
* 10 - empty plain content was given
|
||||
* 20 - openssl_seal failed
|
||||
*/
|
||||
class MultiKeyEncryptException extends EncryptionException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw this encryption if multi key decryption failed
|
||||
*
|
||||
* Possible error codes:
|
||||
* 10 - empty encrypted content was given
|
||||
* 20 - openssl_open failed
|
||||
*/
|
||||
class MultiKeyDecryptException extends EncryptionException {
|
||||
}
|
||||
88
apps/files_encryption/lib/helper.php
Executable file → Normal file
88
apps/files_encryption/lib/helper.php
Executable file → Normal file
@@ -49,6 +49,7 @@ class Helper {
|
||||
public static function registerUserHooks() {
|
||||
|
||||
\OCP\Util::connectHook('OC_User', 'post_login', 'OCA\Encryption\Hooks', 'login');
|
||||
\OCP\Util::connectHook('OC_User', 'logout', 'OCA\Encryption\Hooks', 'logout');
|
||||
\OCP\Util::connectHook('OC_User', 'post_setPassword', 'OCA\Encryption\Hooks', 'setPassphrase');
|
||||
\OCP\Util::connectHook('OC_User', 'pre_setPassword', 'OCA\Encryption\Hooks', 'preSetPassphrase');
|
||||
\OCP\Util::connectHook('OC_User', 'post_createUser', 'OCA\Encryption\Hooks', 'postCreateUser');
|
||||
@@ -62,7 +63,9 @@ class Helper {
|
||||
public static function registerFilesystemHooks() {
|
||||
|
||||
\OCP\Util::connectHook('OC_Filesystem', 'rename', 'OCA\Encryption\Hooks', 'preRename');
|
||||
\OCP\Util::connectHook('OC_Filesystem', 'post_rename', 'OCA\Encryption\Hooks', 'postRename');
|
||||
\OCP\Util::connectHook('OC_Filesystem', 'post_rename', 'OCA\Encryption\Hooks', 'postRenameOrCopy');
|
||||
\OCP\Util::connectHook('OC_Filesystem', 'copy', 'OCA\Encryption\Hooks', 'preCopy');
|
||||
\OCP\Util::connectHook('OC_Filesystem', 'post_copy', 'OCA\Encryption\Hooks', 'postRenameOrCopy');
|
||||
\OCP\Util::connectHook('OC_Filesystem', 'post_delete', 'OCA\Encryption\Hooks', 'postDelete');
|
||||
\OCP\Util::connectHook('OC_Filesystem', 'delete', 'OCA\Encryption\Hooks', 'preDelete');
|
||||
\OCP\Util::connectHook('OC_Filesystem', 'post_umount', 'OCA\Encryption\Hooks', 'postUmount');
|
||||
@@ -141,19 +144,17 @@ class Helper {
|
||||
|
||||
$view->file_put_contents('/public-keys/' . $recoveryKeyId . '.public.key', $keypair['publicKey']);
|
||||
|
||||
// Encrypt private key empty passphrase
|
||||
$encryptedPrivateKey = \OCA\Encryption\Crypt::symmetricEncryptFileContent($keypair['privateKey'], $recoveryPassword);
|
||||
|
||||
// Save private key
|
||||
$view->file_put_contents('/owncloud_private_key/' . $recoveryKeyId . '.private.key', $encryptedPrivateKey);
|
||||
$cipher = \OCA\Encryption\Helper::getCipher();
|
||||
$encryptedKey = \OCA\Encryption\Crypt::symmetricEncryptFileContent($keypair['privateKey'], $recoveryPassword, $cipher);
|
||||
if ($encryptedKey) {
|
||||
Keymanager::setPrivateSystemKey($encryptedKey, $recoveryKeyId . '.private.key');
|
||||
// Set recoveryAdmin as enabled
|
||||
$appConfig->setValue('files_encryption', 'recoveryAdminEnabled', 1);
|
||||
$return = true;
|
||||
}
|
||||
|
||||
\OC_FileProxy::$enabled = true;
|
||||
|
||||
// Set recoveryAdmin as enabled
|
||||
$appConfig->setValue('files_encryption', 'recoveryAdminEnabled', 1);
|
||||
|
||||
$return = true;
|
||||
|
||||
} else { // get recovery key and check the password
|
||||
$util = new \OCA\Encryption\Util(new \OC\Files\View('/'), \OCP\User::getUser());
|
||||
$return = $util->checkRecoveryPassword($recoveryPassword);
|
||||
@@ -227,7 +228,6 @@ class Helper {
|
||||
return $return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* checks if access is public/anonymous user
|
||||
* @return bool
|
||||
@@ -431,24 +431,40 @@ class Helper {
|
||||
|
||||
/**
|
||||
* find all share keys for a given file
|
||||
* @param string $path to the file
|
||||
* @param \OC\Files\View $view view, relative to data/
|
||||
* @return array list of files, path relative to data/
|
||||
*
|
||||
* @param string $filePath path to the file name relative to the user's files dir
|
||||
* for example "subdir/filename.txt"
|
||||
* @param string $shareKeyPath share key prefix path relative to the user's data dir
|
||||
* for example "user1/files_encryption/share-keys/subdir/filename.txt"
|
||||
* @param \OC\Files\View $rootView root view, relative to data/
|
||||
* @return array list of share key files, path relative to data/$user
|
||||
*/
|
||||
public static function findShareKeys($path, $view) {
|
||||
public static function findShareKeys($filePath, $shareKeyPath, $rootView) {
|
||||
$result = array();
|
||||
$pathinfo = pathinfo($path);
|
||||
$dirContent = $view->opendir($pathinfo['dirname']);
|
||||
|
||||
if (is_resource($dirContent)) {
|
||||
while (($file = readdir($dirContent)) !== false) {
|
||||
if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
|
||||
if (preg_match("/" . $pathinfo['filename'] . ".(.*).shareKey/", $file)) {
|
||||
$result[] = $pathinfo['dirname'] . '/' . $file;
|
||||
}
|
||||
}
|
||||
$user = \OCP\User::getUser();
|
||||
$util = new Util($rootView, $user);
|
||||
// get current sharing state
|
||||
$sharingEnabled = \OCP\Share::isEnabled();
|
||||
|
||||
// get users sharing this file
|
||||
$usersSharing = $util->getSharingUsersArray($sharingEnabled, $filePath);
|
||||
|
||||
$pathinfo = pathinfo($shareKeyPath);
|
||||
|
||||
$baseDir = $pathinfo['dirname'] . '/';
|
||||
$fileName = $pathinfo['basename'];
|
||||
foreach ($usersSharing as $user) {
|
||||
$keyName = $fileName . '.' . $user . '.shareKey';
|
||||
if ($rootView->file_exists($baseDir . $keyName)) {
|
||||
$result[] = $baseDir . $keyName;
|
||||
} else {
|
||||
\OC_Log::write(
|
||||
'Encryption library',
|
||||
'No share key found for user "' . $user . '" for file "' . $fileName . '"',
|
||||
\OC_Log::WARN
|
||||
);
|
||||
}
|
||||
closedir($dirContent);
|
||||
}
|
||||
|
||||
return $result;
|
||||
@@ -475,5 +491,25 @@ class Helper {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* read the cipher used for encryption from the config.php
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getCipher() {
|
||||
|
||||
$cipher = \OCP\Config::getSystemValue('cipher', Crypt::DEFAULT_CIPHER);
|
||||
|
||||
if ($cipher !== 'AES-256-CFB' && $cipher !== 'AES-128-CFB') {
|
||||
\OCP\Util::writeLog('files_encryption',
|
||||
'wrong cipher defined in config.php, only AES-128-CFB and AES-256-CFB is supported. Fall back ' . Crypt::DEFAULT_CIPHER,
|
||||
\OCP\Util::WARN);
|
||||
|
||||
$cipher = Crypt::DEFAULT_CIPHER;
|
||||
}
|
||||
|
||||
return $cipher;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
104
apps/files_encryption/lib/keymanager.php
Executable file → Normal file
104
apps/files_encryption/lib/keymanager.php
Executable file → Normal file
@@ -258,9 +258,13 @@ class Keymanager {
|
||||
* @note Encryption of the private key must be performed by client code
|
||||
* as no encryption takes place here
|
||||
*/
|
||||
public static function setPrivateKey($key) {
|
||||
public static function setPrivateKey($key, $user = '') {
|
||||
|
||||
$user = \OCP\User::getUser();
|
||||
if ($user === '') {
|
||||
$user = \OCP\User::getUser();
|
||||
}
|
||||
|
||||
$header = Crypt::generateHeader();
|
||||
|
||||
$view = new \OC\Files\View('/' . $user . '/files_encryption');
|
||||
|
||||
@@ -271,7 +275,7 @@ class Keymanager {
|
||||
$view->mkdir('');
|
||||
}
|
||||
|
||||
$result = $view->file_put_contents($user . '.private.key', $key);
|
||||
$result = $view->file_put_contents($user . '.private.key', $header . $key);
|
||||
|
||||
\OC_FileProxy::$enabled = $proxyStatus;
|
||||
|
||||
@@ -279,6 +283,33 @@ class Keymanager {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* write private system key (recovery and public share key) to disk
|
||||
*
|
||||
* @param string $key encrypted key
|
||||
* @param string $keyName name of the key file
|
||||
* @return boolean
|
||||
*/
|
||||
public static function setPrivateSystemKey($key, $keyName) {
|
||||
|
||||
$header = Crypt::generateHeader();
|
||||
|
||||
$view = new \OC\Files\View('/owncloud_private_key');
|
||||
|
||||
$proxyStatus = \OC_FileProxy::$enabled;
|
||||
\OC_FileProxy::$enabled = false;
|
||||
|
||||
if (!$view->file_exists('')) {
|
||||
$view->mkdir('');
|
||||
}
|
||||
|
||||
$result = $view->file_put_contents($keyName, $header . $key);
|
||||
|
||||
\OC_FileProxy::$enabled = $proxyStatus;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* store share key
|
||||
*
|
||||
@@ -428,13 +459,17 @@ class Keymanager {
|
||||
\OCP\Util::writeLog('files_encryption', 'delAllShareKeys: delete share keys: ' . $baseDir . $filePath, \OCP\Util::DEBUG);
|
||||
$result = $view->unlink($baseDir . $filePath);
|
||||
} else {
|
||||
$parentDir = dirname($baseDir . $filePath);
|
||||
$filename = pathinfo($filePath, PATHINFO_BASENAME);
|
||||
foreach($view->getDirectoryContent($parentDir) as $content) {
|
||||
$path = $content['path'];
|
||||
if (self::getFilenameFromShareKey($content['name']) === $filename) {
|
||||
\OCP\Util::writeLog('files_encryption', 'dellAllShareKeys: delete share keys: ' . '/' . $userId . '/' . $path, \OCP\Util::DEBUG);
|
||||
$result &= $view->unlink('/' . $userId . '/' . $path);
|
||||
$sharingEnabled = \OCP\Share::isEnabled();
|
||||
$users = $util->getSharingUsersArray($sharingEnabled, $filePath);
|
||||
foreach($users as $user) {
|
||||
$keyName = $baseDir . $filePath . '.' . $user . '.shareKey';
|
||||
if ($view->file_exists($keyName)) {
|
||||
\OCP\Util::writeLog(
|
||||
'files_encryption',
|
||||
'dellAllShareKeys: delete share keys: "' . $keyName . '"',
|
||||
\OCP\Util::DEBUG
|
||||
);
|
||||
$result &= $view->unlink($keyName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -444,17 +479,18 @@ class Keymanager {
|
||||
|
||||
/**
|
||||
* Delete a single user's shareKey for a single file
|
||||
*
|
||||
* @param \OC\Files\View $view relative to data/
|
||||
* @param array $userIds list of users we want to remove
|
||||
* @param string $filename the owners name of the file for which we want to remove the users relative to data/user/files
|
||||
* @param string $owner owner of the file
|
||||
*/
|
||||
public static function delShareKey(\OC\Files\View $view, $userIds, $filePath) {
|
||||
public static function delShareKey($view, $userIds, $filename, $owner) {
|
||||
|
||||
$proxyStatus = \OC_FileProxy::$enabled;
|
||||
\OC_FileProxy::$enabled = false;
|
||||
|
||||
$userId = Helper::getUser($filePath);
|
||||
|
||||
$util = new Util($view, $userId);
|
||||
|
||||
list($owner, $filename) = $util->getUidAndFilename($filePath);
|
||||
$util = new Util($view, $owner);
|
||||
|
||||
if ($util->isSystemWideMountPoint($filename)) {
|
||||
$shareKeyPath = \OC\Files\Filesystem::normalizePath('/files_encryption/share-keys/' . $filename);
|
||||
@@ -507,17 +543,20 @@ class Keymanager {
|
||||
if ($view->is_dir($dir . '/' . $file)) {
|
||||
self::recursiveDelShareKeys($dir . '/' . $file, $userIds, $owner, $view);
|
||||
} else {
|
||||
$realFile = $realFileDir . self::getFilenameFromShareKey($file);
|
||||
foreach ($userIds as $userId) {
|
||||
if (preg_match("/(.*)." . $userId . ".shareKey/", $file)) {
|
||||
if ($userId === $owner &&
|
||||
$view->file_exists($realFile)) {
|
||||
\OCP\Util::writeLog('files_encryption', 'original file still exists, keep owners share key!', \OCP\Util::ERROR);
|
||||
continue;
|
||||
}
|
||||
\OCP\Util::writeLog('files_encryption', 'recursiveDelShareKey: delete share key: ' . $file, \OCP\Util::DEBUG);
|
||||
$view->unlink($dir . '/' . $file);
|
||||
$fileNameFromShareKey = self::getFilenameFromShareKey($file, $userId);
|
||||
if (!$fileNameFromShareKey) {
|
||||
continue;
|
||||
}
|
||||
$realFile = $realFileDir . $fileNameFromShareKey;
|
||||
|
||||
if ($userId === $owner &&
|
||||
$view->file_exists($realFile)) {
|
||||
\OCP\Util::writeLog('files_encryption', 'original file still exists, keep owners share key!', \OCP\Util::ERROR);
|
||||
continue;
|
||||
}
|
||||
\OCP\Util::writeLog('files_encryption', 'recursiveDelShareKey: delete share key: ' . $file, \OCP\Util::DEBUG);
|
||||
$view->unlink($dir . '/' . $file);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -559,16 +598,19 @@ class Keymanager {
|
||||
/**
|
||||
* extract filename from share key name
|
||||
* @param string $shareKey (filename.userid.sharekey)
|
||||
* @param string $userId
|
||||
* @return string|false filename or false
|
||||
*/
|
||||
protected static function getFilenameFromShareKey($shareKey) {
|
||||
$parts = explode('.', $shareKey);
|
||||
protected static function getFilenameFromShareKey($shareKey, $userId) {
|
||||
$expectedSuffix = '.' . $userId . '.' . 'shareKey';
|
||||
$suffixLen = strlen($expectedSuffix);
|
||||
|
||||
$filename = false;
|
||||
if(count($parts) > 2) {
|
||||
$filename = implode('.', array_slice($parts, 0, count($parts)-2));
|
||||
$suffix = substr($shareKey, -$suffixLen);
|
||||
|
||||
if ($suffix !== $expectedSuffix) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $filename;
|
||||
return substr($shareKey, 0, -$suffixLen);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,12 +49,17 @@ class Proxy extends \OC_FileProxy {
|
||||
* @param string $uid user
|
||||
* @return boolean
|
||||
*/
|
||||
private function isExcludedPath($path, $uid) {
|
||||
protected function isExcludedPath($path, $uid) {
|
||||
|
||||
$view = new \OC\Files\View();
|
||||
|
||||
// files outside of the files-folder are excluded
|
||||
if(strpos($path, '/' . $uid . '/files') !== 0) {
|
||||
$path = \OC\Files\Filesystem::normalizePath($path);
|
||||
|
||||
// we only encrypt/decrypt files in the files and files_versions folder
|
||||
if(
|
||||
strpos($path, '/' . $uid . '/files/') !== 0 &&
|
||||
strpos($path, '/' . $uid . '/files_versions/') !== 0) {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -157,8 +162,8 @@ class Proxy extends \OC_FileProxy {
|
||||
// store new unenecrypted size so that it can be updated
|
||||
// in the post proxy
|
||||
$tmpFileInfo = $view->getFileInfo($tmpPath);
|
||||
if ( isset($tmpFileInfo['size']) ) {
|
||||
self::$unencryptedSizes[\OC\Files\Filesystem::normalizePath($path)] = $tmpFileInfo['size'];
|
||||
if ( isset($tmpFileInfo['unencrypted_size']) ) {
|
||||
self::$unencryptedSizes[\OC\Files\Filesystem::normalizePath($path)] = $tmpFileInfo['unencrypted_size'];
|
||||
}
|
||||
|
||||
// remove our temp file
|
||||
@@ -262,7 +267,7 @@ class Proxy extends \OC_FileProxy {
|
||||
* @param resource $result
|
||||
* @return resource
|
||||
*/
|
||||
public function postFopen($path, &$result) {
|
||||
public function postFopen($path, $result) {
|
||||
|
||||
$path = \OC\Files\Filesystem::normalizePath($path);
|
||||
|
||||
|
||||
@@ -80,11 +80,13 @@ class Session {
|
||||
$this->view->file_put_contents('/public-keys/' . $publicShareKeyId . '.public.key', $keypair['publicKey']);
|
||||
|
||||
// Encrypt private key empty passphrase
|
||||
$encryptedPrivateKey = Crypt::symmetricEncryptFileContent($keypair['privateKey'], '');
|
||||
|
||||
// Save private key
|
||||
$this->view->file_put_contents(
|
||||
'/owncloud_private_key/' . $publicShareKeyId . '.private.key', $encryptedPrivateKey);
|
||||
$cipher = \OCA\Encryption\Helper::getCipher();
|
||||
$encryptedKey = \OCA\Encryption\Crypt::symmetricEncryptFileContent($keypair['privateKey'], '', $cipher);
|
||||
if ($encryptedKey) {
|
||||
Keymanager::setPrivateSystemKey($encryptedKey, $publicShareKeyId . '.private.key');
|
||||
} else {
|
||||
\OCP\Util::writeLog('files_encryption', 'Could not create public share keys', \OCP\Util::ERROR);
|
||||
}
|
||||
|
||||
\OC_FileProxy::$enabled = $proxyStatus;
|
||||
|
||||
@@ -121,6 +123,14 @@ class Session {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* remove keys from session
|
||||
*/
|
||||
public function removeKeys() {
|
||||
\OC::$session->remove('publicSharePrivateKey');
|
||||
\OC::$session->remove('privateKey');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets status of encryption app
|
||||
* @param string $init INIT_SUCCESSFUL, INIT_EXECUTED, NOT_INITIALIZED
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
/**
|
||||
* ownCloud
|
||||
*
|
||||
* @author Robin Appelman
|
||||
* @copyright 2012 Sam Tuke <samtuke@owncloud.com>, 2011 Robin Appelman
|
||||
* <icewind1991@gmail.com>
|
||||
* @author Bjoern Schiessle, Robin Appelman
|
||||
* @copyright 2014 Bjoern Schiessle <schiessle@owncloud.com>
|
||||
* 2012 Sam Tuke <samtuke@owncloud.com>,
|
||||
* 2011 Robin Appelman <icewind1991@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
@@ -49,9 +50,11 @@ namespace OCA\Encryption;
|
||||
* encryption proxies are used and keyfiles deleted.
|
||||
*/
|
||||
class Stream {
|
||||
|
||||
const PADDING_CHAR = '-';
|
||||
|
||||
private $plainKey;
|
||||
private $encKeyfiles;
|
||||
|
||||
private $rawPath; // The raw path relative to the data dir
|
||||
private $relPath; // rel path to users file dir
|
||||
private $userId;
|
||||
@@ -66,6 +69,9 @@ class Stream {
|
||||
private $newFile; // helper var, we only need to write the keyfile for new files
|
||||
private $isLocalTmpFile = false; // do we operate on a local tmp file
|
||||
private $localTmpFile; // path of local tmp file
|
||||
private $headerWritten = false;
|
||||
private $containHeader = false; // the file contain a header
|
||||
private $cipher; // cipher used for encryption/decryption
|
||||
|
||||
/**
|
||||
* @var \OC\Files\View
|
||||
@@ -87,6 +93,9 @@ class Stream {
|
||||
*/
|
||||
public function stream_open($path, $mode, $options, &$opened_path) {
|
||||
|
||||
// read default cipher from config
|
||||
$this->cipher = Helper::getCipher();
|
||||
|
||||
// assume that the file already exist before we decide it finally in getKey()
|
||||
$this->newFile = false;
|
||||
|
||||
@@ -150,6 +159,9 @@ class Stream {
|
||||
}
|
||||
|
||||
$this->size = $this->rootView->filesize($this->rawPath);
|
||||
|
||||
$this->readHeader();
|
||||
|
||||
}
|
||||
|
||||
if ($this->isLocalTmpFile) {
|
||||
@@ -178,6 +190,29 @@ class Stream {
|
||||
|
||||
}
|
||||
|
||||
private function readHeader() {
|
||||
|
||||
if ($this->isLocalTmpFile) {
|
||||
$handle = fopen($this->localTmpFile, 'r');
|
||||
} else {
|
||||
$handle = $this->rootView->fopen($this->rawPath, 'r');
|
||||
}
|
||||
|
||||
if (is_resource($handle)) {
|
||||
$data = fread($handle, Crypt::BLOCKSIZE);
|
||||
|
||||
$header = Crypt::parseHeader($data);
|
||||
$this->cipher = Crypt::getCipher($header);
|
||||
|
||||
// remeber that we found a header
|
||||
if (!empty($header)) {
|
||||
$this->containHeader = true;
|
||||
}
|
||||
|
||||
fclose($handle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current position of the file pointer
|
||||
* @return int position of the file pointer
|
||||
@@ -195,6 +230,11 @@ class Stream {
|
||||
|
||||
$this->flush();
|
||||
|
||||
// ignore the header and just overstep it
|
||||
if ($this->containHeader) {
|
||||
$offset += Crypt::BLOCKSIZE;
|
||||
}
|
||||
|
||||
// this wrapper needs to return "true" for success.
|
||||
// the fseek call itself returns 0 on succeess
|
||||
return !fseek($this->handle, $offset, $whence);
|
||||
@@ -204,25 +244,25 @@ class Stream {
|
||||
/**
|
||||
* @param int $count
|
||||
* @return bool|string
|
||||
* @throws \Exception
|
||||
* @throws \OCA\Encryption\Exceptions\EncryptionException
|
||||
*/
|
||||
public function stream_read($count) {
|
||||
|
||||
$this->writeCache = '';
|
||||
|
||||
if ($count !== 8192) {
|
||||
|
||||
// $count will always be 8192 https://bugs.php.net/bug.php?id=21641
|
||||
// This makes this function a lot simpler, but will break this class if the above 'bug' gets 'fixed'
|
||||
if ($count !== Crypt::BLOCKSIZE) {
|
||||
\OCP\Util::writeLog('Encryption library', 'PHP "bug" 21641 no longer holds, decryption system requires refactoring', \OCP\Util::FATAL);
|
||||
|
||||
die();
|
||||
|
||||
throw new \OCA\Encryption\Exceptions\EncryptionException('expected a blog size of 8192 byte', 20);
|
||||
}
|
||||
|
||||
// Get the data from the file handle
|
||||
$data = fread($this->handle, $count);
|
||||
|
||||
// if this block contained the header we move on to the next block
|
||||
if (Crypt::isHeader($data)) {
|
||||
$data = fread($this->handle, $count);
|
||||
}
|
||||
|
||||
$result = null;
|
||||
|
||||
if (strlen($data)) {
|
||||
@@ -236,7 +276,7 @@ class Stream {
|
||||
} else {
|
||||
|
||||
// Decrypt data
|
||||
$result = Crypt::symmetricDecryptFileContent($data, $this->plainKey);
|
||||
$result = Crypt::symmetricDecryptFileContent($data, $this->plainKey, $this->cipher);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -254,7 +294,7 @@ class Stream {
|
||||
public function preWriteEncrypt($plainData, $key) {
|
||||
|
||||
// Encrypt data to 'catfile', which includes IV
|
||||
if ($encrypted = Crypt::symmetricEncryptFileContent($plainData, $key)) {
|
||||
if ($encrypted = Crypt::symmetricEncryptFileContent($plainData, $key, $this->cipher)) {
|
||||
|
||||
return $encrypted;
|
||||
|
||||
@@ -317,6 +357,25 @@ class Stream {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* write header at beginning of encrypted file
|
||||
*
|
||||
* @throws Exceptions\EncryptionException
|
||||
*/
|
||||
private function writeHeader() {
|
||||
|
||||
$header = Crypt::generateHeader();
|
||||
|
||||
if (strlen($header) > Crypt::BLOCKSIZE) {
|
||||
throw new Exceptions\EncryptionException('max header size exceeded', 30);
|
||||
}
|
||||
|
||||
$paddedHeader = str_pad($header, Crypt::BLOCKSIZE, self::PADDING_CHAR, STR_PAD_RIGHT);
|
||||
|
||||
fwrite($this->handle, $paddedHeader);
|
||||
$this->headerWritten = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle plain data from the stream, and write it in 8192 byte blocks
|
||||
* @param string $data data to be written to disk
|
||||
@@ -334,6 +393,10 @@ class Stream {
|
||||
return strlen($data);
|
||||
}
|
||||
|
||||
if ($this->headerWritten === false) {
|
||||
$this->writeHeader();
|
||||
}
|
||||
|
||||
// Disable the file proxies so that encryption is not
|
||||
// automatically attempted when the file is written to disk -
|
||||
// we are handling that separately here and we don't want to
|
||||
@@ -510,6 +573,7 @@ class Stream {
|
||||
\OC_FileProxy::$enabled = false;
|
||||
|
||||
if ($this->rootView->file_exists($this->rawPath) && $this->size === 0) {
|
||||
fclose($this->handle);
|
||||
$this->rootView->unlink($this->rawPath);
|
||||
}
|
||||
|
||||
|
||||
@@ -167,11 +167,12 @@ class Util {
|
||||
\OC_FileProxy::$enabled = false;
|
||||
|
||||
// Encrypt private key with user pwd as passphrase
|
||||
$encryptedPrivateKey = Crypt::symmetricEncryptFileContent($keypair['privateKey'], $passphrase);
|
||||
$encryptedPrivateKey = Crypt::symmetricEncryptFileContent($keypair['privateKey'], $passphrase, Helper::getCipher());
|
||||
|
||||
// Save key-pair
|
||||
if ($encryptedPrivateKey) {
|
||||
$this->view->file_put_contents($this->privateKeyPath, $encryptedPrivateKey);
|
||||
$header = crypt::generateHeader();
|
||||
$this->view->file_put_contents($this->privateKeyPath, $header . $encryptedPrivateKey);
|
||||
$this->view->file_put_contents($this->publicKeyPath, $keypair['publicKey']);
|
||||
}
|
||||
|
||||
@@ -394,8 +395,14 @@ class Util {
|
||||
&& $this->isEncryptedPath($path)
|
||||
) {
|
||||
|
||||
// get the size from filesystem
|
||||
$size = $this->view->filesize($path);
|
||||
$offset = 0;
|
||||
if ($this->containHeader($path)) {
|
||||
$offset = Crypt::BLOCKSIZE;
|
||||
}
|
||||
|
||||
// get the size from filesystem if the file contains a encryption header we
|
||||
// we substract it
|
||||
$size = $this->view->filesize($path) - $offset;
|
||||
|
||||
// fast path, else the calculation for $lastChunkNr is bogus
|
||||
if ($size === 0) {
|
||||
@@ -406,15 +413,15 @@ class Util {
|
||||
// calculate last chunk nr
|
||||
// next highest is end of chunks, one subtracted is last one
|
||||
// we have to read the last chunk, we can't just calculate it (because of padding etc)
|
||||
$lastChunkNr = ceil($size/ 8192) - 1;
|
||||
$lastChunkSize = $size - ($lastChunkNr * 8192);
|
||||
$lastChunkNr = ceil($size/ Crypt::BLOCKSIZE) - 1;
|
||||
$lastChunkSize = $size - ($lastChunkNr * Crypt::BLOCKSIZE);
|
||||
|
||||
// open stream
|
||||
$stream = fopen('crypt://' . $path, "r");
|
||||
|
||||
if (is_resource($stream)) {
|
||||
// calculate last chunk position
|
||||
$lastChunckPos = ($lastChunkNr * 8192);
|
||||
$lastChunckPos = ($lastChunkNr * Crypt::BLOCKSIZE);
|
||||
|
||||
// seek to end
|
||||
if (@fseek($stream, $lastChunckPos) === -1) {
|
||||
@@ -448,6 +455,30 @@ class Util {
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if encrypted file contain a encryption header
|
||||
*
|
||||
* @param string $path
|
||||
* @return boolean
|
||||
*/
|
||||
private function containHeader($path) {
|
||||
// Disable encryption proxy to read the raw data
|
||||
$proxyStatus = \OC_FileProxy::$enabled;
|
||||
\OC_FileProxy::$enabled = false;
|
||||
|
||||
$isHeader = false;
|
||||
$handle = $this->view->fopen($path, 'r');
|
||||
|
||||
if (is_resource($handle)) {
|
||||
$firstBlock = fread($handle, Crypt::BLOCKSIZE);
|
||||
$isHeader = Crypt::isHeader($firstBlock);
|
||||
}
|
||||
|
||||
\OC_FileProxy::$enabled = $proxyStatus;
|
||||
|
||||
return $isHeader;
|
||||
}
|
||||
|
||||
/**
|
||||
* fix the file size of the encrypted file
|
||||
* @param string $path absolute path
|
||||
@@ -857,6 +888,25 @@ class Util {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given user is ready for encryption.
|
||||
* Also returns true if the given user is the public user
|
||||
* or the recovery key user.
|
||||
*
|
||||
* @param string $user user to check
|
||||
*
|
||||
* @return boolean true if the user is ready, false otherwise
|
||||
*/
|
||||
private function isUserReady($user) {
|
||||
if ($user === $this->publicShareKeyId
|
||||
|| $user === $this->recoveryKeyId
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
$util = new Util($this->view, $user);
|
||||
return $util->ready();
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter an array of UIDs to return only ones ready for sharing
|
||||
* @param array $unfilteredUsers users to be checked for sharing readiness
|
||||
@@ -869,16 +919,9 @@ class Util {
|
||||
|
||||
// Loop through users and create array of UIDs that need new keyfiles
|
||||
foreach ($unfilteredUsers as $user) {
|
||||
|
||||
$util = new Util($this->view, $user);
|
||||
|
||||
// Check that the user is encryption capable, or is the
|
||||
// public system user 'ownCloud' (for public shares)
|
||||
if (
|
||||
$user === $this->publicShareKeyId
|
||||
or $user === $this->recoveryKeyId
|
||||
or $util->ready()
|
||||
) {
|
||||
// public system user (for public shares)
|
||||
if ($this->isUserReady($user)) {
|
||||
|
||||
// Construct array of ready UIDs for Keymanager{}
|
||||
$readyIds[] = $user;
|
||||
@@ -956,19 +999,26 @@ class Util {
|
||||
// Get the current users's private key for decrypting existing keyfile
|
||||
$privateKey = $session->getPrivateKey();
|
||||
|
||||
$fileOwner = \OC\Files\Filesystem::getOwner($filePath);
|
||||
|
||||
// Decrypt keyfile
|
||||
$plainKeyfile = $this->decryptKeyfile($filePath, $privateKey);
|
||||
|
||||
// Re-enc keyfile to (additional) sharekeys
|
||||
$multiEncKey = Crypt::multiKeyEncrypt($plainKeyfile, $userPubKeys);
|
||||
try {
|
||||
// Decrypt keyfile
|
||||
$plainKeyfile = $this->decryptKeyfile($filePath, $privateKey);
|
||||
// Re-enc keyfile to (additional) sharekeys
|
||||
$multiEncKey = Crypt::multiKeyEncrypt($plainKeyfile, $userPubKeys);
|
||||
} catch (Exceptions\EncryptionException $e) {
|
||||
$msg = 'set shareFileKeyFailed (code: ' . $e->getCode() . '): ' . $e->getMessage();
|
||||
\OCP\Util::writeLog('files_encryption', $msg, \OCP\Util::FATAL);
|
||||
return false;
|
||||
} catch (\Exception $e) {
|
||||
$msg = 'set shareFileKeyFailed (unknown error): ' . $e->getMessage();
|
||||
\OCP\Util::writeLog('files_encryption', $msg, \OCP\Util::FATAL);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Save the recrypted key to it's owner's keyfiles directory
|
||||
// Save new sharekeys to all necessary user directory
|
||||
if (
|
||||
!Keymanager::setFileKey($this->view, $this, $filePath, $multiEncKey['data'])
|
||||
|| !Keymanager::setShareKeys($this->view, $this, $filePath, $multiEncKey['keys'])
|
||||
!Keymanager::setFileKey($this->view, $this, $filePath, $multiEncKey['data'])
|
||||
|| !Keymanager::setShareKeys($this->view, $this, $filePath, $multiEncKey['keys'])
|
||||
) {
|
||||
|
||||
\OCP\Util::writeLog('Encryption library',
|
||||
@@ -1034,10 +1084,10 @@ class Util {
|
||||
|
||||
// check if it is a group mount
|
||||
if (\OCP\App::isEnabled("files_external")) {
|
||||
$mount = \OC_Mount_Config::getSystemMountPoints();
|
||||
foreach ($mount as $mountPoint => $data) {
|
||||
if ($mountPoint == substr($ownerPath, 1, strlen($mountPoint))) {
|
||||
$userIds = array_merge($userIds, $this->getUserWithAccessToMountPoint($data['applicable']['users'], $data['applicable']['groups']));
|
||||
$mounts = \OC_Mount_Config::getSystemMountPoints();
|
||||
foreach ($mounts as $mount) {
|
||||
if ($mount['mountpoint'] == substr($ownerPath, 1, strlen($mount['mountpoint']))) {
|
||||
$userIds = array_merge($userIds, $this->getUserWithAccessToMountPoint($mount['applicable']['users'], $mount['applicable']['groups']));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1487,6 +1537,22 @@ class Util {
|
||||
$this->recoverAllFiles('/', $privateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* create a backup of all keys from the user
|
||||
*
|
||||
* @param string $purpose (optional) define the purpose of the backup, will be part of the backup folder
|
||||
*/
|
||||
public function backupAllKeys($purpose = '') {
|
||||
$this->userId;
|
||||
$backupDir = $this->encryptionDir . '/backup.';
|
||||
$backupDir .= ($purpose === '') ? date("Y-m-d_H-i-s") . '/' : $purpose . '.' . date("Y-m-d_H-i-s") . '/';
|
||||
$this->view->mkdir($backupDir);
|
||||
$this->view->copy($this->shareKeysPath, $backupDir . 'share-keys/');
|
||||
$this->view->copy($this->keyfilesPath, $backupDir . 'keyfiles/');
|
||||
$this->view->copy($this->privateKeyPath, $backupDir . $this->userId . '.private.key');
|
||||
$this->view->copy($this->publicKeyPath, $backupDir . $this->userId . '.public.key');
|
||||
}
|
||||
|
||||
/**
|
||||
* check if the file is stored on a system wide mount point
|
||||
* @param string $path relative to /data/user with leading '/'
|
||||
@@ -1495,16 +1561,41 @@ class Util {
|
||||
public function isSystemWideMountPoint($path) {
|
||||
$normalizedPath = ltrim($path, '/');
|
||||
if (\OCP\App::isEnabled("files_external")) {
|
||||
$mount = \OC_Mount_Config::getSystemMountPoints();
|
||||
foreach ($mount as $mountPoint => $data) {
|
||||
if ($mountPoint == substr($normalizedPath, 0, strlen($mountPoint))) {
|
||||
return true;
|
||||
$mounts = \OC_Mount_Config::getSystemMountPoints();
|
||||
foreach ($mounts as $mount) {
|
||||
if ($mount['mountpoint'] == substr($normalizedPath, 0, strlen($mount['mountpoint']))) {
|
||||
if ($this->isMountPointApplicableToUser($mount)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if mount point is applicable to user
|
||||
*
|
||||
* @param array $mount contains $mount['applicable']['users'], $mount['applicable']['groups']
|
||||
* @return boolean
|
||||
*/
|
||||
protected function isMountPointApplicableToUser($mount) {
|
||||
$uid = \OCP\User::getUser();
|
||||
$acceptedUids = array('all', $uid);
|
||||
// check if mount point is applicable for the user
|
||||
$intersection = array_intersect($acceptedUids, $mount['applicable']['users']);
|
||||
if (!empty($intersection)) {
|
||||
return true;
|
||||
}
|
||||
// check if mount point is applicable for group where the user is a member
|
||||
foreach ($mount['applicable']['groups'] as $gid) {
|
||||
if (\OC_Group::inGroup($uid, $gid)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* decrypt private key and add it to the current session
|
||||
* @param array $params with 'uid' and 'password'
|
||||
|
||||
@@ -12,8 +12,11 @@ $tmpl = new OCP\Template('files_encryption', 'settings-admin');
|
||||
|
||||
// Check if an adminRecovery account is enabled for recovering files after lost pwd
|
||||
$recoveryAdminEnabled = \OC::$server->getAppConfig()->getValue('files_encryption', 'recoveryAdminEnabled', '0');
|
||||
$session = new \OCA\Encryption\Session(new \OC\Files\View('/'));
|
||||
$initStatus = $session->getInitialized();
|
||||
|
||||
$tmpl->assign('recoveryEnabled', $recoveryAdminEnabled);
|
||||
$tmpl->assign('initStatus', $initStatus);
|
||||
|
||||
\OCP\Util::addscript('files_encryption', 'settings-admin');
|
||||
\OCP\Util::addscript('core', 'multiselect');
|
||||
|
||||
@@ -28,7 +28,6 @@ $result = false;
|
||||
if ($recoveryAdminEnabled || !$privateKeySet) {
|
||||
|
||||
\OCP\Util::addscript('files_encryption', 'settings-personal');
|
||||
\OCP\Util::addScript('settings', 'personal');
|
||||
|
||||
$tmpl->assign('recoveryEnabled', $recoveryAdminEnabled);
|
||||
$tmpl->assign('recoveryEnabledForUser', $recoveryEnabledForUser);
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
<form id="encryption" class="section">
|
||||
<h2><?php p($l->t('Encryption')); ?></h2>
|
||||
|
||||
<p>
|
||||
<?php if($_["initStatus"] === \OCA\Encryption\Session::NOT_INITIALIZED): ?>
|
||||
<?php p($l->t("Encryption App is enabled but your keys are not initialized, please log-out and log-in again")); ?>
|
||||
<?php else: ?>
|
||||
<p id="encryptionSetRecoveryKey">
|
||||
<?php p($l->t("Enable recovery key (allow to recover users files in case of password loss):")); ?>
|
||||
<span class="msg"></span>
|
||||
<br/>
|
||||
<br/>
|
||||
<input type="password" name="encryptionRecoveryPassword" id="encryptionRecoveryPassword"/>
|
||||
@@ -15,7 +19,7 @@
|
||||
type='radio'
|
||||
name='adminEnableRecovery'
|
||||
value='1'
|
||||
<?php echo($_["recoveryEnabled"] === '1' ? 'checked="checked"' : 'disabled'); ?> />
|
||||
<?php echo($_["recoveryEnabled"] === '1' ? 'checked="checked"' : ''); ?> />
|
||||
<?php p($l->t("Enabled")); ?>
|
||||
<br/>
|
||||
|
||||
@@ -23,13 +27,14 @@
|
||||
type='radio'
|
||||
name='adminEnableRecovery'
|
||||
value='0'
|
||||
<?php echo($_["recoveryEnabled"] === '0' ? 'checked="checked"' : 'disabled'); ?> />
|
||||
<?php echo($_["recoveryEnabled"] === '0' ? 'checked="checked"' : ''); ?> />
|
||||
<?php p($l->t("Disabled")); ?>
|
||||
</p>
|
||||
<br/><br/>
|
||||
|
||||
<p name="changeRecoveryPasswordBlock" <?php if ($_['recoveryEnabled'] === '0') print_unescaped('class="hidden"');?>>
|
||||
<p name="changeRecoveryPasswordBlock" id="encryptionChangeRecoveryKey" <?php if ($_['recoveryEnabled'] === '0') print_unescaped('class="hidden"');?>>
|
||||
<strong><?php p($l->t("Change recovery key password:")); ?></strong>
|
||||
<span class="msg"></span>
|
||||
<br/><br/>
|
||||
<input
|
||||
type="password"
|
||||
@@ -52,9 +57,9 @@
|
||||
<br/>
|
||||
<button
|
||||
type="button"
|
||||
name="submitChangeRecoveryKey"
|
||||
disabled><?php p($l->t("Change Password")); ?>
|
||||
name="submitChangeRecoveryKey">
|
||||
<?php p($l->t("Change Password")); ?>
|
||||
</button>
|
||||
<span class="msg"></span>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
</form>
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
<form id="encryption" class="section">
|
||||
<h2><?php p( $l->t( 'Encryption' ) ); ?></h2>
|
||||
|
||||
<?php if ( $_["initialized"] === '1' ): ?>
|
||||
<?php if ( $_["initialized"] === \OCA\Encryption\Session::NOT_INITIALIZED ): ?>
|
||||
|
||||
<?php p($l->t("Encryption App is enabled but your keys are not initialized, please log-out and log-in again")); ?>
|
||||
|
||||
<?php elseif ( $_["initialized"] === \OCA\Encryption\Session::INIT_EXECUTED ): ?>
|
||||
<p>
|
||||
<a name="changePKPasswd" />
|
||||
<label for="changePrivateKeyPasswd">
|
||||
<?php p( $l->t( "Your private key password no longer match your log-in password:" ) ); ?>
|
||||
<em><?php p( $l->t( "Your private key password no longer match your log-in password." ) ); ?></em>
|
||||
</label>
|
||||
<br />
|
||||
<em><?php p( $l->t( "Set your old private key password to your current log-in password." ) ); ?>
|
||||
<?php p( $l->t( "Set your old private key password to your current log-in password:" ) ); ?>
|
||||
<?php if ( $_["recoveryEnabledForUser"] ):
|
||||
p( $l->t( " If you don't remember your old password you can ask your administrator to recover your files." ) );
|
||||
endif; ?>
|
||||
</em>
|
||||
<br />
|
||||
<input
|
||||
type="password"
|
||||
@@ -33,12 +36,12 @@
|
||||
</button>
|
||||
<span class="msg"></span>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ( $_["recoveryEnabled"] && $_["privateKeySet"] ): ?>
|
||||
<?php elseif ( $_["recoveryEnabled"] && $_["privateKeySet"] && $_["initialized"] === \OCA\Encryption\Session::INIT_SUCCESSFUL ): ?>
|
||||
<br />
|
||||
<p>
|
||||
<p id="userEnableRecovery">
|
||||
<label for="userEnableRecovery"><?php p( $l->t( "Enable password recovery:" ) ); ?></label>
|
||||
<span class="msg"></span>
|
||||
<br />
|
||||
<em><?php p( $l->t( "Enabling this option will allow you to reobtain access to your encrypted files in case of password loss" ) ); ?></em>
|
||||
<br />
|
||||
@@ -56,8 +59,6 @@
|
||||
value='0'
|
||||
<?php echo ( $_["recoveryEnabledForUser"] === false ? 'checked="checked"' : '' ); ?> />
|
||||
<?php p( $l->t( "Disabled" ) ); ?>
|
||||
<div id="recoveryEnabledSuccess"><?php p( $l->t( 'File recovery settings updated' ) ); ?></div>
|
||||
<div id="recoveryEnabledError"><?php p( $l->t( 'Could not update file recovery' ) ); ?></div>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
</form>
|
||||
|
||||
@@ -23,7 +23,7 @@ use OCA\Encryption;
|
||||
/**
|
||||
* Class Test_Encryption_Crypt
|
||||
*/
|
||||
class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
class Test_Encryption_Crypt extends \OCA\Files_Encryption\Tests\TestCase {
|
||||
|
||||
const TEST_ENCRYPTION_CRYPT_USER1 = "test-crypt-user1";
|
||||
|
||||
@@ -42,6 +42,8 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
public $genPublicKey;
|
||||
|
||||
public static function setUpBeforeClass() {
|
||||
parent::setUpBeforeClass();
|
||||
|
||||
// reset backend
|
||||
\OC_User::clearBackends();
|
||||
\OC_User::useBackend('database');
|
||||
@@ -57,10 +59,12 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
\OC_FileProxy::register(new OCA\Encryption\Proxy());
|
||||
|
||||
// create test user
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Crypt::TEST_ENCRYPTION_CRYPT_USER1, true);
|
||||
self::loginHelper(\Test_Encryption_Crypt::TEST_ENCRYPTION_CRYPT_USER1, true);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// set user id
|
||||
\OC_User::setUserId(\Test_Encryption_Crypt::TEST_ENCRYPTION_CRYPT_USER1);
|
||||
$this->userId = \Test_Encryption_Crypt::TEST_ENCRYPTION_CRYPT_USER1;
|
||||
@@ -88,24 +92,39 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
\OC_App::disable('files_trashbin');
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
protected function tearDown() {
|
||||
// reset app files_trashbin
|
||||
if ($this->stateFilesTrashbin) {
|
||||
OC_App::enable('files_trashbin');
|
||||
} else {
|
||||
OC_App::disable('files_trashbin');
|
||||
}
|
||||
|
||||
$this->assertTrue(\OC_FileProxy::$enabled);
|
||||
\OCP\Config::deleteSystemValue('cipher');
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass() {
|
||||
// cleanup test user
|
||||
\OC_User::deleteUser(\Test_Encryption_Crypt::TEST_ENCRYPTION_CRYPT_USER1);
|
||||
|
||||
\OC_Hook::clear();
|
||||
\OC_FileProxy::clearProxies();
|
||||
|
||||
// Delete keys in /data/
|
||||
$view = new \OC\Files\View('/');
|
||||
$view->rmdir('public-keys');
|
||||
$view->rmdir('owncloud_private_key');
|
||||
|
||||
parent::tearDownAfterClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* @medium
|
||||
*/
|
||||
function testGenerateKey() {
|
||||
public function testGenerateKey() {
|
||||
|
||||
# TODO: use more accurate (larger) string length for test confirmation
|
||||
|
||||
@@ -115,12 +134,14 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
}
|
||||
|
||||
function testDecryptPrivateKey() {
|
||||
public function testDecryptPrivateKey() {
|
||||
|
||||
// test successful decrypt
|
||||
$crypted = Encryption\Crypt::symmetricEncryptFileContent($this->genPrivateKey, 'hat');
|
||||
|
||||
$decrypted = Encryption\Crypt::decryptPrivateKey($crypted, 'hat');
|
||||
$header = Encryption\Crypt::generateHeader();
|
||||
|
||||
$decrypted = Encryption\Crypt::decryptPrivateKey($header . $crypted, 'hat');
|
||||
|
||||
$this->assertEquals($this->genPrivateKey, $decrypted);
|
||||
|
||||
@@ -135,7 +156,7 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
/**
|
||||
* @medium
|
||||
*/
|
||||
function testSymmetricEncryptFileContent() {
|
||||
public function testSymmetricEncryptFileContent() {
|
||||
|
||||
# TODO: search in keyfile for actual content as IV will ensure this test always passes
|
||||
|
||||
@@ -153,11 +174,27 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
/**
|
||||
* @medium
|
||||
*/
|
||||
function testSymmetricStreamEncryptShortFileContent() {
|
||||
public function testSymmetricEncryptFileContentAes128() {
|
||||
|
||||
$filename = 'tmp-' . uniqid() . '.test';
|
||||
# TODO: search in keyfile for actual content as IV will ensure this test always passes
|
||||
|
||||
$util = new Encryption\Util(new \OC\Files\View(), $this->userId);
|
||||
$crypted = Encryption\Crypt::symmetricEncryptFileContent($this->dataShort, 'hat', 'AES-128-CFB');
|
||||
|
||||
$this->assertNotEquals($this->dataShort, $crypted);
|
||||
|
||||
|
||||
$decrypt = Encryption\Crypt::symmetricDecryptFileContent($crypted, 'hat', 'AES-128-CFB');
|
||||
|
||||
$this->assertEquals($this->dataShort, $decrypt);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @medium
|
||||
*/
|
||||
public function testSymmetricStreamEncryptShortFileContent() {
|
||||
|
||||
$filename = 'tmp-' . $this->getUniqueID() . '.test';
|
||||
|
||||
$cryptedFile = file_put_contents('crypt:///' . $this->userId . '/files/'. $filename, $this->dataShort);
|
||||
|
||||
@@ -177,26 +214,52 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
// Check that the file was encrypted before being written to disk
|
||||
$this->assertNotEquals($this->dataShort, $retreivedCryptedFile);
|
||||
|
||||
// Get the encrypted keyfile
|
||||
$encKeyfile = Encryption\Keymanager::getFileKey($this->view, $util, $filename);
|
||||
|
||||
// Attempt to fetch the user's shareKey
|
||||
$shareKey = Encryption\Keymanager::getShareKey($this->view, $this->userId, $util, $filename);
|
||||
|
||||
// get session
|
||||
$session = new \OCA\Encryption\Session($this->view);
|
||||
|
||||
// get private key
|
||||
$privateKey = $session->getPrivateKey($this->userId);
|
||||
|
||||
// Decrypt keyfile with shareKey
|
||||
$plainKeyfile = Encryption\Crypt::multiKeyDecrypt($encKeyfile, $shareKey, $privateKey);
|
||||
|
||||
// Manually decrypt
|
||||
$manualDecrypt = Encryption\Crypt::symmetricDecryptFileContent($retreivedCryptedFile, $plainKeyfile);
|
||||
// Get file contents with the encryption wrapper
|
||||
$decrypted = file_get_contents('crypt:///' . $this->userId . '/files/'. $filename);
|
||||
|
||||
// Check that decrypted data matches
|
||||
$this->assertEquals($this->dataShort, $manualDecrypt);
|
||||
$this->assertEquals($this->dataShort, $decrypted);
|
||||
|
||||
// Teardown
|
||||
$this->view->unlink($this->userId . '/files/' . $filename);
|
||||
|
||||
Encryption\Keymanager::deleteFileKey($this->view, $filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* @medium
|
||||
*/
|
||||
public function testSymmetricStreamEncryptShortFileContentAes128() {
|
||||
|
||||
$filename = 'tmp-' . $this->getUniqueID() . '.test';
|
||||
|
||||
\OCP\Config::setSystemValue('cipher', 'AES-128-CFB');
|
||||
|
||||
$cryptedFile = file_put_contents('crypt:///' . $this->userId . '/files/'. $filename, $this->dataShort);
|
||||
|
||||
// Test that data was successfully written
|
||||
$this->assertTrue(is_int($cryptedFile));
|
||||
|
||||
\OCP\Config::deleteSystemValue('cipher');
|
||||
|
||||
// Disable encryption proxy to prevent recursive calls
|
||||
$proxyStatus = \OC_FileProxy::$enabled;
|
||||
\OC_FileProxy::$enabled = false;
|
||||
|
||||
// Get file contents without using any wrapper to get it's actual contents on disk
|
||||
$retreivedCryptedFile = $this->view->file_get_contents($this->userId . '/files/' . $filename);
|
||||
|
||||
// Re-enable proxy - our work is done
|
||||
\OC_FileProxy::$enabled = $proxyStatus;
|
||||
|
||||
// Check that the file was encrypted before being written to disk
|
||||
$this->assertNotEquals($this->dataShort, $retreivedCryptedFile);
|
||||
|
||||
// Get file contents with the encryption wrapper
|
||||
$decrypted = file_get_contents('crypt:///' . $this->userId . '/files/'. $filename);
|
||||
|
||||
// Check that decrypted data matches
|
||||
$this->assertEquals($this->dataShort, $decrypted);
|
||||
|
||||
// Teardown
|
||||
$this->view->unlink($this->userId . '/files/' . $filename);
|
||||
@@ -211,12 +274,10 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
* @note If this test fails with truncate content, check that enough array slices are being rejoined to form $e, as the crypt.php file may have gotten longer and broken the manual
|
||||
* reassembly of its data
|
||||
*/
|
||||
function testSymmetricStreamEncryptLongFileContent() {
|
||||
public function testSymmetricStreamEncryptLongFileContent() {
|
||||
|
||||
// Generate a a random filename
|
||||
$filename = 'tmp-' . uniqid() . '.test';
|
||||
|
||||
$util = new Encryption\Util(new \OC\Files\View(), $this->userId);
|
||||
$filename = 'tmp-' . $this->getUniqueID() . '.test';
|
||||
|
||||
// Save long data as encrypted file using stream wrapper
|
||||
$cryptedFile = file_put_contents('crypt:///' . $this->userId . '/files/' . $filename, $this->dataLong . $this->dataLong);
|
||||
@@ -238,50 +299,9 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
// Check that the file was encrypted before being written to disk
|
||||
$this->assertNotEquals($this->dataLong . $this->dataLong, $retreivedCryptedFile);
|
||||
|
||||
// Manuallly split saved file into separate IVs and encrypted chunks
|
||||
$r = preg_split('/(00iv00.{16,18})/', $retreivedCryptedFile, NULL, PREG_SPLIT_DELIM_CAPTURE);
|
||||
$decrypted = file_get_contents('crypt:///' . $this->userId . '/files/'. $filename);
|
||||
|
||||
//print_r($r);
|
||||
|
||||
// Join IVs and their respective data chunks
|
||||
$e = array();
|
||||
$i = 0;
|
||||
while ($i < count($r)-1) {
|
||||
$e[] = $r[$i] . $r[$i+1];
|
||||
$i = $i + 2;
|
||||
}
|
||||
|
||||
//print_r($e);
|
||||
|
||||
// Get the encrypted keyfile
|
||||
$encKeyfile = Encryption\Keymanager::getFileKey($this->view, $util, $filename);
|
||||
|
||||
// Attempt to fetch the user's shareKey
|
||||
$shareKey = Encryption\Keymanager::getShareKey($this->view, $this->userId, $util, $filename);
|
||||
|
||||
// get session
|
||||
$session = new \OCA\Encryption\Session($this->view);
|
||||
|
||||
// get private key
|
||||
$privateKey = $session->getPrivateKey($this->userId);
|
||||
|
||||
// Decrypt keyfile with shareKey
|
||||
$plainKeyfile = Encryption\Crypt::multiKeyDecrypt($encKeyfile, $shareKey, $privateKey);
|
||||
|
||||
// Set var for reassembling decrypted content
|
||||
$decrypt = '';
|
||||
|
||||
// Manually decrypt chunk
|
||||
foreach ($e as $chunk) {
|
||||
|
||||
$chunkDecrypt = Encryption\Crypt::symmetricDecryptFileContent($chunk, $plainKeyfile);
|
||||
|
||||
// Assemble decrypted chunks
|
||||
$decrypt .= $chunkDecrypt;
|
||||
|
||||
}
|
||||
|
||||
$this->assertEquals($this->dataLong . $this->dataLong, $decrypt);
|
||||
$this->assertEquals($this->dataLong . $this->dataLong, $decrypted);
|
||||
|
||||
// Teardown
|
||||
|
||||
@@ -293,14 +313,20 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
/**
|
||||
* @medium
|
||||
* Test that data that is read by the crypto stream wrapper
|
||||
* Test that data that is written by the crypto stream wrapper with AES 128
|
||||
* @note Encrypted data is manually prepared and decrypted here to avoid dependency on success of stream_read
|
||||
* @note If this test fails with truncate content, check that enough array slices are being rejoined to form $e, as the crypt.php file may have gotten longer and broken the manual
|
||||
* reassembly of its data
|
||||
*/
|
||||
function testSymmetricStreamDecryptShortFileContent() {
|
||||
public function testSymmetricStreamEncryptLongFileContentAes128() {
|
||||
|
||||
$filename = 'tmp-' . uniqid();
|
||||
// Generate a a random filename
|
||||
$filename = 'tmp-' . $this->getUniqueID() . '.test';
|
||||
|
||||
\OCP\Config::setSystemValue('cipher', 'AES-128-CFB');
|
||||
|
||||
// Save long data as encrypted file using stream wrapper
|
||||
$cryptedFile = file_put_contents('crypt:///'. $this->userId . '/files/' . $filename, $this->dataShort);
|
||||
$cryptedFile = file_put_contents('crypt:///' . $this->userId . '/files/' . $filename, $this->dataLong . $this->dataLong);
|
||||
|
||||
// Test that data was successfully written
|
||||
$this->assertTrue(is_int($cryptedFile));
|
||||
@@ -309,51 +335,92 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
$proxyStatus = \OC_FileProxy::$enabled;
|
||||
\OC_FileProxy::$enabled = false;
|
||||
|
||||
$this->assertTrue(Encryption\Crypt::isEncryptedMeta($filename));
|
||||
\OCP\Config::deleteSystemValue('cipher');
|
||||
|
||||
// Get file contents without using any wrapper to get it's actual contents on disk
|
||||
$retreivedCryptedFile = $this->view->file_get_contents($this->userId . '/files/' . $filename);
|
||||
|
||||
// Re-enable proxy - our work is done
|
||||
\OC_FileProxy::$enabled = $proxyStatus;
|
||||
|
||||
// Get file decrypted contents
|
||||
$decrypt = file_get_contents('crypt:///' . $this->userId . '/files/' . $filename);
|
||||
|
||||
$this->assertEquals($this->dataShort, $decrypt);
|
||||
// Check that the file was encrypted before being written to disk
|
||||
$this->assertNotEquals($this->dataLong . $this->dataLong, $retreivedCryptedFile);
|
||||
|
||||
$decrypted = file_get_contents('crypt:///' . $this->userId . '/files/'. $filename);
|
||||
|
||||
$this->assertEquals($this->dataLong . $this->dataLong, $decrypted);
|
||||
|
||||
// Teardown
|
||||
|
||||
// tear down
|
||||
$this->view->unlink($this->userId . '/files/' . $filename);
|
||||
|
||||
Encryption\Keymanager::deleteFileKey($this->view, $filename);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @medium
|
||||
* Test that data that is written by the crypto stream wrapper with AES 128
|
||||
* @note Encrypted data is manually prepared and decrypted here to avoid dependency on success of stream_read
|
||||
* @note If this test fails with truncate content, check that enough array slices are being rejoined to form $e, as the crypt.php file may have gotten longer and broken the manual
|
||||
* reassembly of its data
|
||||
*/
|
||||
function testSymmetricStreamDecryptLongFileContent() {
|
||||
public function testStreamDecryptLongFileContentWithoutHeader() {
|
||||
|
||||
$filename = 'tmp-' . uniqid();
|
||||
// Generate a a random filename
|
||||
$filename = 'tmp-' . $this->getUniqueID() . '.test';
|
||||
|
||||
\OCP\Config::setSystemValue('cipher', 'AES-128-CFB');
|
||||
|
||||
// Save long data as encrypted file using stream wrapper
|
||||
$cryptedFile = file_put_contents('crypt:///' . $this->userId . '/files/' . $filename, $this->dataLong);
|
||||
$cryptedFile = file_put_contents('crypt:///' . $this->userId . '/files/' . $filename, $this->dataLong . $this->dataLong);
|
||||
|
||||
\OCP\Config::deleteSystemValue('cipher');
|
||||
|
||||
// Test that data was successfully written
|
||||
$this->assertTrue(is_int($cryptedFile));
|
||||
|
||||
// Get file decrypted contents
|
||||
$decrypt = file_get_contents('crypt:///' . $this->userId . '/files/' . $filename);
|
||||
// Disable encryption proxy to prevent recursive calls
|
||||
$proxyStatus = \OC_FileProxy::$enabled;
|
||||
\OC_FileProxy::$enabled = false;
|
||||
|
||||
$this->assertEquals($this->dataLong, $decrypt);
|
||||
// Get file contents without using any wrapper to get it's actual contents on disk
|
||||
$retreivedCryptedFile = $this->view->file_get_contents($this->userId . '/files/' . $filename);
|
||||
|
||||
// Check that the file was encrypted before being written to disk
|
||||
$this->assertNotEquals($this->dataLong . $this->dataLong, $retreivedCryptedFile);
|
||||
|
||||
// remove the header to check if we can also decrypt old files without a header,
|
||||
// this files should fall back to AES-128
|
||||
$cryptedWithoutHeader = substr($retreivedCryptedFile, Encryption\Crypt::BLOCKSIZE);
|
||||
$this->view->file_put_contents($this->userId . '/files/' . $filename, $cryptedWithoutHeader);
|
||||
|
||||
// Re-enable proxy - our work is done
|
||||
\OC_FileProxy::$enabled = $proxyStatus;
|
||||
|
||||
$decrypted = file_get_contents('crypt:///' . $this->userId . '/files/'. $filename);
|
||||
|
||||
$this->assertEquals($this->dataLong . $this->dataLong, $decrypted);
|
||||
|
||||
// Teardown
|
||||
|
||||
// tear down
|
||||
$this->view->unlink($this->userId . '/files/' . $filename);
|
||||
|
||||
Encryption\Keymanager::deleteFileKey($this->view, $filename);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @medium
|
||||
*/
|
||||
function testIsEncryptedContent() {
|
||||
public function testIsEncryptedContent() {
|
||||
|
||||
$this->assertFalse(Encryption\Crypt::isCatfileContent($this->dataUrl));
|
||||
|
||||
$this->assertFalse(Encryption\Crypt::isCatfileContent($this->legacyEncryptedData));
|
||||
|
||||
$keyfileContent = Encryption\Crypt::symmetricEncryptFileContent($this->dataUrl, 'hat');
|
||||
$keyfileContent = Encryption\Crypt::symmetricEncryptFileContent($this->dataUrl, 'hat', 'AES-128-CFB');
|
||||
|
||||
$this->assertTrue(Encryption\Crypt::isCatfileContent($keyfileContent));
|
||||
|
||||
@@ -362,7 +429,7 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
/**
|
||||
* @large
|
||||
*/
|
||||
function testMultiKeyEncrypt() {
|
||||
public function testMultiKeyEncrypt() {
|
||||
|
||||
# TODO: search in keyfile for actual content as IV will ensure this test always passes
|
||||
|
||||
@@ -416,9 +483,9 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
/**
|
||||
* @medium
|
||||
*/
|
||||
function testRenameFile() {
|
||||
public function testRenameFile() {
|
||||
|
||||
$filename = 'tmp-' . uniqid();
|
||||
$filename = 'tmp-' . $this->getUniqueID();
|
||||
|
||||
// Save long data as encrypted file using stream wrapper
|
||||
$cryptedFile = file_put_contents('crypt:///' . $this->userId . '/files/' . $filename, $this->dataLong);
|
||||
@@ -431,7 +498,7 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
$this->assertEquals($this->dataLong, $decrypt);
|
||||
|
||||
$newFilename = 'tmp-new-' . uniqid();
|
||||
$newFilename = 'tmp-new-' . $this->getUniqueID();
|
||||
$view = new \OC\Files\View('/' . $this->userId . '/files');
|
||||
$view->rename($filename, $newFilename);
|
||||
|
||||
@@ -447,9 +514,9 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
/**
|
||||
* @medium
|
||||
*/
|
||||
function testMoveFileIntoFolder() {
|
||||
public function testMoveFileIntoFolder() {
|
||||
|
||||
$filename = 'tmp-' . uniqid();
|
||||
$filename = 'tmp-' . $this->getUniqueID();
|
||||
|
||||
// Save long data as encrypted file using stream wrapper
|
||||
$cryptedFile = file_put_contents('crypt:///' . $this->userId . '/files/' . $filename, $this->dataLong);
|
||||
@@ -462,8 +529,8 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
$this->assertEquals($this->dataLong, $decrypt);
|
||||
|
||||
$newFolder = '/newfolder' . uniqid();
|
||||
$newFilename = 'tmp-new-' . uniqid();
|
||||
$newFolder = '/newfolder' . $this->getUniqueID();
|
||||
$newFilename = 'tmp-new-' . $this->getUniqueID();
|
||||
$view = new \OC\Files\View('/' . $this->userId . '/files');
|
||||
$view->mkdir($newFolder);
|
||||
$view->rename($filename, $newFolder . '/' . $newFilename);
|
||||
@@ -480,12 +547,12 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
/**
|
||||
* @medium
|
||||
*/
|
||||
function testMoveFolder() {
|
||||
public function testMoveFolder() {
|
||||
|
||||
$view = new \OC\Files\View('/' . $this->userId . '/files');
|
||||
|
||||
$filename = '/tmp-' . uniqid();
|
||||
$folder = '/folder' . uniqid();
|
||||
$filename = '/tmp-' . $this->getUniqueID();
|
||||
$folder = '/folder' . $this->getUniqueID();
|
||||
|
||||
$view->mkdir($folder);
|
||||
|
||||
@@ -500,7 +567,7 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
$this->assertEquals($this->dataLong, $decrypt);
|
||||
|
||||
$newFolder = '/newfolder/subfolder' . uniqid();
|
||||
$newFolder = '/newfolder/subfolder' . $this->getUniqueID();
|
||||
$view->mkdir('/newfolder');
|
||||
|
||||
$view->rename($folder, $newFolder);
|
||||
@@ -518,8 +585,8 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
/**
|
||||
* @medium
|
||||
*/
|
||||
function testChangePassphrase() {
|
||||
$filename = 'tmp-' . uniqid();
|
||||
public function testChangePassphrase() {
|
||||
$filename = 'tmp-' . $this->getUniqueID();
|
||||
|
||||
// Save long data as encrypted file using stream wrapper
|
||||
$cryptedFile = file_put_contents('crypt:///' . $this->userId . '/files/' . $filename, $this->dataLong);
|
||||
@@ -555,9 +622,9 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
/**
|
||||
* @medium
|
||||
*/
|
||||
function testViewFilePutAndGetContents() {
|
||||
public function testViewFilePutAndGetContents() {
|
||||
|
||||
$filename = '/tmp-' . uniqid();
|
||||
$filename = '/tmp-' . $this->getUniqueID();
|
||||
$view = new \OC\Files\View('/' . $this->userId . '/files');
|
||||
|
||||
// Save short data as encrypted file using stream wrapper
|
||||
@@ -589,8 +656,8 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
/**
|
||||
* @large
|
||||
*/
|
||||
function testTouchExistingFile() {
|
||||
$filename = '/tmp-' . uniqid();
|
||||
public function testTouchExistingFile() {
|
||||
$filename = '/tmp-' . $this->getUniqueID();
|
||||
$view = new \OC\Files\View('/' . $this->userId . '/files');
|
||||
|
||||
// Save short data as encrypted file using stream wrapper
|
||||
@@ -613,8 +680,8 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
/**
|
||||
* @medium
|
||||
*/
|
||||
function testTouchFile() {
|
||||
$filename = '/tmp-' . uniqid();
|
||||
public function testTouchFile() {
|
||||
$filename = '/tmp-' . $this->getUniqueID();
|
||||
$view = new \OC\Files\View('/' . $this->userId . '/files');
|
||||
|
||||
$view->touch($filename);
|
||||
@@ -637,8 +704,8 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
/**
|
||||
* @medium
|
||||
*/
|
||||
function testFopenFile() {
|
||||
$filename = '/tmp-' . uniqid();
|
||||
public function testFopenFile() {
|
||||
$filename = '/tmp-' . $this->getUniqueID();
|
||||
$view = new \OC\Files\View('/' . $this->userId . '/files');
|
||||
|
||||
// Save short data as encrypted file using stream wrapper
|
||||
@@ -655,6 +722,7 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
$this->assertEquals($this->dataShort, $decrypt);
|
||||
|
||||
// tear down
|
||||
fclose($handle);
|
||||
$view->unlink($filename);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,33 +5,49 @@
|
||||
* later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
|
||||
|
||||
require_once __DIR__ . '/../lib/helper.php';
|
||||
require_once __DIR__ . '/util.php';
|
||||
|
||||
use OCA\Encryption;
|
||||
|
||||
/**
|
||||
* Class Test_Encryption_Helper
|
||||
*/
|
||||
class Test_Encryption_Helper extends \PHPUnit_Framework_TestCase {
|
||||
class Test_Encryption_Helper extends \OCA\Files_Encryption\Tests\TestCase {
|
||||
|
||||
const TEST_ENCRYPTION_HELPER_USER1 = "test-helper-user1";
|
||||
const TEST_ENCRYPTION_HELPER_USER2 = "test-helper-user2";
|
||||
|
||||
public static function setUpBeforeClass() {
|
||||
protected function setUpUsers() {
|
||||
// create test user
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Helper::TEST_ENCRYPTION_HELPER_USER2, true);
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Helper::TEST_ENCRYPTION_HELPER_USER1, true);
|
||||
self::loginHelper(\Test_Encryption_Helper::TEST_ENCRYPTION_HELPER_USER2, true);
|
||||
self::loginHelper(\Test_Encryption_Helper::TEST_ENCRYPTION_HELPER_USER1, true);
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass() {
|
||||
protected function cleanUpUsers() {
|
||||
// cleanup test user
|
||||
\OC_User::deleteUser(\Test_Encryption_Helper::TEST_ENCRYPTION_HELPER_USER1);
|
||||
\OC_User::deleteUser(\Test_Encryption_Helper::TEST_ENCRYPTION_HELPER_USER2);
|
||||
}
|
||||
|
||||
public static function setupHooks() {
|
||||
// Filesystem related hooks
|
||||
\OCA\Encryption\Helper::registerFilesystemHooks();
|
||||
|
||||
// clear and register hooks
|
||||
\OC_FileProxy::clearProxies();
|
||||
\OC_FileProxy::register(new OCA\Encryption\Proxy());
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass() {
|
||||
\OC_Hook::clear();
|
||||
\OC_FileProxy::clearProxies();
|
||||
|
||||
// Delete keys in /data/
|
||||
$view = new \OC\Files\View('/');
|
||||
$view->rmdir('public-keys');
|
||||
$view->rmdir('owncloud_private_key');
|
||||
|
||||
parent::tearDownAfterClass();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,19 +99,20 @@ class Test_Encryption_Helper extends \PHPUnit_Framework_TestCase {
|
||||
}
|
||||
|
||||
function testGetUser() {
|
||||
self::setUpUsers();
|
||||
|
||||
$path1 = "/" . self::TEST_ENCRYPTION_HELPER_USER1 . "/files/foo/bar.txt";
|
||||
$path2 = "/" . self::TEST_ENCRYPTION_HELPER_USER1 . "/cache/foo/bar.txt";
|
||||
$path3 = "/" . self::TEST_ENCRYPTION_HELPER_USER2 . "/thumbnails/foo";
|
||||
$path4 ="/" . "/" . self::TEST_ENCRYPTION_HELPER_USER1;
|
||||
|
||||
\Test_Encryption_Util::loginHelper(self::TEST_ENCRYPTION_HELPER_USER1);
|
||||
self::loginHelper(self::TEST_ENCRYPTION_HELPER_USER1);
|
||||
|
||||
// if we are logged-in every path should return the currently logged-in user
|
||||
$this->assertEquals(self::TEST_ENCRYPTION_HELPER_USER1, Encryption\Helper::getUser($path3));
|
||||
|
||||
// now log out
|
||||
\Test_Encryption_Util::logoutHelper();
|
||||
self::logoutHelper();
|
||||
|
||||
// now we should only get the user from /user/files and user/cache paths
|
||||
$this->assertEquals(self::TEST_ENCRYPTION_HELPER_USER1, Encryption\Helper::getUser($path1));
|
||||
@@ -105,7 +122,60 @@ class Test_Encryption_Helper extends \PHPUnit_Framework_TestCase {
|
||||
$this->assertFalse(Encryption\Helper::getUser($path4));
|
||||
|
||||
// Log-in again
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Helper::TEST_ENCRYPTION_HELPER_USER1);
|
||||
self::loginHelper(\Test_Encryption_Helper::TEST_ENCRYPTION_HELPER_USER1);
|
||||
self::cleanUpUsers();
|
||||
}
|
||||
|
||||
function userNamesProvider() {
|
||||
return array(
|
||||
array('testuser' . $this->getUniqueID()),
|
||||
array('user.name.with.dots'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether share keys can be found
|
||||
*
|
||||
* @dataProvider userNamesProvider
|
||||
*/
|
||||
function testFindShareKeys($userName) {
|
||||
self::setUpUsers();
|
||||
// note: not using dataProvider as we want to make
|
||||
// sure that the correct keys are match and not any
|
||||
// other ones that might happen to have similar names
|
||||
self::setupHooks();
|
||||
self::loginHelper($userName, true);
|
||||
$testDir = 'testFindShareKeys' . $this->getUniqueID() . '/';
|
||||
$baseDir = $userName . '/files/' . $testDir;
|
||||
$fileList = array(
|
||||
't est.txt',
|
||||
't est_.txt',
|
||||
't est.doc.txt',
|
||||
't est(.*).txt', // make sure the regexp is escaped
|
||||
'multiple.dots.can.happen.too.txt',
|
||||
't est.' . $userName . '.txt',
|
||||
't est_.' . $userName . '.shareKey.txt',
|
||||
'who would upload their.shareKey',
|
||||
'user ones file.txt',
|
||||
'user ones file.txt.backup',
|
||||
'.t est.txt'
|
||||
);
|
||||
|
||||
$rootView = new \OC\Files\View('/');
|
||||
$rootView->mkdir($baseDir);
|
||||
foreach ($fileList as $fileName) {
|
||||
$rootView->file_put_contents($baseDir . $fileName, 'dummy');
|
||||
}
|
||||
|
||||
$shareKeysDir = $userName . '/files_encryption/share-keys/' . $testDir;
|
||||
foreach ($fileList as $fileName) {
|
||||
// make sure that every file only gets its correct respective keys
|
||||
$result = Encryption\Helper::findShareKeys($baseDir . $fileName, $shareKeysDir . $fileName, $rootView);
|
||||
$this->assertEquals(
|
||||
array($shareKeysDir . $fileName . '.' . $userName . '.shareKey'),
|
||||
$result
|
||||
);
|
||||
}
|
||||
self::cleanUpUsers();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ require_once __DIR__ . '/../lib/keymanager.php';
|
||||
require_once __DIR__ . '/../lib/stream.php';
|
||||
require_once __DIR__ . '/../lib/util.php';
|
||||
require_once __DIR__ . '/../appinfo/app.php';
|
||||
require_once __DIR__ . '/util.php';
|
||||
|
||||
use OCA\Encryption;
|
||||
|
||||
@@ -34,22 +33,43 @@ use OCA\Encryption;
|
||||
* Class Test_Encryption_Hooks
|
||||
* this class provide basic hook app tests
|
||||
*/
|
||||
class Test_Encryption_Hooks extends \PHPUnit_Framework_TestCase {
|
||||
class Test_Encryption_Hooks extends \OCA\Files_Encryption\Tests\TestCase {
|
||||
|
||||
const TEST_ENCRYPTION_HOOKS_USER1 = "test-encryption-hooks-user1";
|
||||
const TEST_ENCRYPTION_HOOKS_USER2 = "test-encryption-hooks-user2";
|
||||
const TEST_ENCRYPTION_HOOKS_USER1 = "test-encryption-hooks-user1.dot";
|
||||
const TEST_ENCRYPTION_HOOKS_USER2 = "test-encryption-hooks-user2.dot";
|
||||
|
||||
/**
|
||||
* @var \OC\Files\View
|
||||
*/
|
||||
/** @var \OC\Files\View */
|
||||
public $user1View; // view on /data/user1/files
|
||||
/** @var \OC\Files\View */
|
||||
public $user2View; // view on /data/user2/files
|
||||
/** @var \OC\Files\View */
|
||||
public $rootView; // view on /data/user
|
||||
public $data;
|
||||
public $filename;
|
||||
public $folder;
|
||||
|
||||
private static $testFiles;
|
||||
|
||||
public static function setUpBeforeClass() {
|
||||
parent::setUpBeforeClass();
|
||||
|
||||
// note: not using a data provider because these
|
||||
// files all need to coexist to make sure the
|
||||
// share keys are found properly (pattern matching)
|
||||
self::$testFiles = array(
|
||||
't est.txt',
|
||||
't est_.txt',
|
||||
't est.doc.txt',
|
||||
't est(.*).txt', // make sure the regexp is escaped
|
||||
'multiple.dots.can.happen.too.txt',
|
||||
't est.' . self::TEST_ENCRYPTION_HOOKS_USER1 . '.txt',
|
||||
't est_.' . self::TEST_ENCRYPTION_HOOKS_USER1 . '.shareKey.txt',
|
||||
'who would upload their.shareKey',
|
||||
'user ones file.txt',
|
||||
'user ones file.txt.backup',
|
||||
'.t est.txt'
|
||||
);
|
||||
|
||||
// reset backend
|
||||
\OC_User::clearBackends();
|
||||
\OC_User::useBackend('database');
|
||||
@@ -73,13 +93,15 @@ class Test_Encryption_Hooks extends \PHPUnit_Framework_TestCase {
|
||||
\OC_FileProxy::register(new OCA\Encryption\Proxy());
|
||||
|
||||
// create test user
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1, true);
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2, true);
|
||||
self::loginHelper(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1, true);
|
||||
self::loginHelper(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2, true);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// set user id
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1);
|
||||
self::loginHelper(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1);
|
||||
\OC_User::setUserId(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1);
|
||||
|
||||
// init filesystem view
|
||||
@@ -89,8 +111,8 @@ class Test_Encryption_Hooks extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
// init short data
|
||||
$this->data = 'hats';
|
||||
$this->filename = 'enc_hooks_tests-' . uniqid() . '.txt';
|
||||
$this->folder = 'enc_hooks_tests_folder-' . uniqid();
|
||||
$this->filename = 'enc_hooks_tests-' . $this->getUniqueID() . '.txt';
|
||||
$this->folder = 'enc_hooks_tests_folder-' . $this->getUniqueID();
|
||||
|
||||
}
|
||||
|
||||
@@ -98,6 +120,16 @@ class Test_Encryption_Hooks extends \PHPUnit_Framework_TestCase {
|
||||
// cleanup test user
|
||||
\OC_User::deleteUser(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1);
|
||||
\OC_User::deleteUser(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2);
|
||||
|
||||
\OC_Hook::clear();
|
||||
\OC_FileProxy::clearProxies();
|
||||
|
||||
// Delete keys in /data/
|
||||
$view = new \OC\Files\View('/');
|
||||
$view->rmdir('public-keys');
|
||||
$view->rmdir('owncloud_private_key');
|
||||
|
||||
parent::tearDownAfterClass();
|
||||
}
|
||||
|
||||
function testDisableHook() {
|
||||
@@ -119,7 +151,7 @@ class Test_Encryption_Hooks extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
// relogin user to initialize the encryption again
|
||||
$user = \OCP\User::getUser();
|
||||
\Test_Encryption_Util::loginHelper($user);
|
||||
self::loginHelper($user);
|
||||
|
||||
}
|
||||
|
||||
@@ -144,8 +176,8 @@ class Test_Encryption_Hooks extends \PHPUnit_Framework_TestCase {
|
||||
self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/keyfiles/' . $this->filename . '.key'));
|
||||
|
||||
|
||||
\Test_Encryption_Util::logoutHelper();
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2);
|
||||
self::logoutHelper();
|
||||
self::loginHelper(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2);
|
||||
\OC_User::setUserId(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2);
|
||||
|
||||
|
||||
@@ -202,8 +234,8 @@ class Test_Encryption_Hooks extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
function testDeleteHooksForSharedFiles() {
|
||||
|
||||
\Test_Encryption_Util::logoutHelper();
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1);
|
||||
self::logoutHelper();
|
||||
self::loginHelper(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1);
|
||||
\OC_User::setUserId(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1);
|
||||
|
||||
// remember files_trashbin state
|
||||
@@ -238,8 +270,8 @@ class Test_Encryption_Hooks extends \PHPUnit_Framework_TestCase {
|
||||
self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/share-keys/'
|
||||
. $this->filename . '.' . \Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2 . '.shareKey'));
|
||||
|
||||
\Test_Encryption_Util::logoutHelper();
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2);
|
||||
self::logoutHelper();
|
||||
self::loginHelper(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2);
|
||||
\OC_User::setUserId(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2);
|
||||
|
||||
// user2 update the shared file
|
||||
@@ -269,8 +301,8 @@ class Test_Encryption_Hooks extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
// cleanup
|
||||
|
||||
\Test_Encryption_Util::logoutHelper();
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1);
|
||||
self::logoutHelper();
|
||||
self::loginHelper(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1);
|
||||
\OC_User::setUserId(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1);
|
||||
|
||||
if ($stateFilesTrashbin) {
|
||||
@@ -281,25 +313,33 @@ class Test_Encryption_Hooks extends \PHPUnit_Framework_TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
function testRenameHook() {
|
||||
// create all files to make sure all keys can coexist properly
|
||||
foreach (self::$testFiles as $file) {
|
||||
// save file with content
|
||||
$cryptedFile = file_put_contents('crypt:///' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $file, $this->data);
|
||||
|
||||
// test that data was successfully written
|
||||
$this->assertTrue(is_int($cryptedFile));
|
||||
}
|
||||
|
||||
foreach (self::$testFiles as $file) {
|
||||
$this->doTestRenameHook($file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* test rename operation
|
||||
*/
|
||||
function testRenameHook() {
|
||||
|
||||
// save file with content
|
||||
$cryptedFile = file_put_contents('crypt:///' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->filename, $this->data);
|
||||
|
||||
// test that data was successfully written
|
||||
$this->assertTrue(is_int($cryptedFile));
|
||||
|
||||
function doTestRenameHook($filename) {
|
||||
// check if keys exists
|
||||
$this->assertTrue($this->rootView->file_exists(
|
||||
'/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/share-keys/'
|
||||
. $this->filename . '.' . self::TEST_ENCRYPTION_HOOKS_USER1 . '.shareKey'));
|
||||
. $filename . '.' . self::TEST_ENCRYPTION_HOOKS_USER1 . '.shareKey'));
|
||||
|
||||
$this->assertTrue($this->rootView->file_exists(
|
||||
'/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/keyfiles/'
|
||||
. $this->filename . '.key'));
|
||||
. $filename . '.key'));
|
||||
|
||||
// make subfolder and sub-subfolder
|
||||
$this->rootView->mkdir('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->folder);
|
||||
@@ -310,31 +350,91 @@ class Test_Encryption_Hooks extends \PHPUnit_Framework_TestCase {
|
||||
// move the file to the sub-subfolder
|
||||
$root = $this->rootView->getRoot();
|
||||
$this->rootView->chroot('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/');
|
||||
$this->rootView->rename($this->filename, '/' . $this->folder . '/' . $this->folder . '/' . $this->filename);
|
||||
$this->rootView->rename($filename, '/' . $this->folder . '/' . $this->folder . '/' . $filename);
|
||||
$this->rootView->chroot($root);
|
||||
|
||||
$this->assertFalse($this->rootView->file_exists('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->filename));
|
||||
$this->assertTrue($this->rootView->file_exists('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->folder . '/' . $this->folder . '/' . $this->filename));
|
||||
$this->assertFalse($this->rootView->file_exists('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $filename));
|
||||
$this->assertTrue($this->rootView->file_exists('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->folder . '/' . $this->folder . '/' . $filename));
|
||||
|
||||
// keys should be renamed too
|
||||
$this->assertFalse($this->rootView->file_exists(
|
||||
'/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/share-keys/'
|
||||
. $this->filename . '.' . self::TEST_ENCRYPTION_HOOKS_USER1 . '.shareKey'));
|
||||
. $filename . '.' . self::TEST_ENCRYPTION_HOOKS_USER1 . '.shareKey'));
|
||||
$this->assertFalse($this->rootView->file_exists(
|
||||
'/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/keyfiles/'
|
||||
. $this->filename . '.key'));
|
||||
. $filename . '.key'));
|
||||
|
||||
$this->assertTrue($this->rootView->file_exists(
|
||||
'/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/share-keys/' . $this->folder . '/' . $this->folder . '/'
|
||||
. $this->filename . '.' . self::TEST_ENCRYPTION_HOOKS_USER1 . '.shareKey'));
|
||||
. $filename . '.' . self::TEST_ENCRYPTION_HOOKS_USER1 . '.shareKey'));
|
||||
$this->assertTrue($this->rootView->file_exists(
|
||||
'/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/keyfiles/' . $this->folder . '/' . $this->folder . '/'
|
||||
. $this->filename . '.key'));
|
||||
. $filename . '.key'));
|
||||
|
||||
// cleanup
|
||||
$this->rootView->unlink('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->folder);
|
||||
}
|
||||
|
||||
function testCopyHook() {
|
||||
// create all files to make sure all keys can coexist properly
|
||||
foreach (self::$testFiles as $file) {
|
||||
// save file with content
|
||||
$cryptedFile = file_put_contents('crypt:///' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $file, $this->data);
|
||||
|
||||
// test that data was successfully written
|
||||
$this->assertTrue(is_int($cryptedFile));
|
||||
}
|
||||
|
||||
foreach (self::$testFiles as $file) {
|
||||
$this->doTestCopyHook($file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* test rename operation
|
||||
*/
|
||||
function doTestCopyHook($filename) {
|
||||
// check if keys exists
|
||||
$this->assertTrue($this->rootView->file_exists(
|
||||
'/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/share-keys/'
|
||||
. $filename . '.' . self::TEST_ENCRYPTION_HOOKS_USER1 . '.shareKey'));
|
||||
|
||||
$this->assertTrue($this->rootView->file_exists(
|
||||
'/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/keyfiles/'
|
||||
. $filename . '.key'));
|
||||
|
||||
// make subfolder and sub-subfolder
|
||||
$this->rootView->mkdir('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->folder);
|
||||
$this->rootView->mkdir('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->folder . '/' . $this->folder);
|
||||
|
||||
$this->assertTrue($this->rootView->is_dir('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->folder . '/' . $this->folder));
|
||||
|
||||
// copy the file to the sub-subfolder
|
||||
\OC\Files\Filesystem::copy($filename, '/' . $this->folder . '/' . $this->folder . '/' . $filename);
|
||||
|
||||
$this->assertTrue($this->rootView->file_exists('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $filename));
|
||||
$this->assertTrue($this->rootView->file_exists('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->folder . '/' . $this->folder . '/' . $filename));
|
||||
|
||||
// keys should be copied too
|
||||
$this->assertTrue($this->rootView->file_exists(
|
||||
'/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/share-keys/'
|
||||
. $filename . '.' . self::TEST_ENCRYPTION_HOOKS_USER1 . '.shareKey'));
|
||||
$this->assertTrue($this->rootView->file_exists(
|
||||
'/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/keyfiles/'
|
||||
. $filename . '.key'));
|
||||
|
||||
$this->assertTrue($this->rootView->file_exists(
|
||||
'/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/share-keys/' . $this->folder . '/' . $this->folder . '/'
|
||||
. $filename . '.' . self::TEST_ENCRYPTION_HOOKS_USER1 . '.shareKey'));
|
||||
$this->assertTrue($this->rootView->file_exists(
|
||||
'/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/keyfiles/' . $this->folder . '/' . $this->folder . '/'
|
||||
. $filename . '.key'));
|
||||
|
||||
// cleanup
|
||||
$this->rootView->unlink('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->folder);
|
||||
$this->rootView->unlink('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief replacing encryption keys during password change should be allowed
|
||||
* until the user logged in for the first time
|
||||
|
||||
@@ -14,16 +14,15 @@ require_once __DIR__ . '/../lib/stream.php';
|
||||
require_once __DIR__ . '/../lib/util.php';
|
||||
require_once __DIR__ . '/../lib/helper.php';
|
||||
require_once __DIR__ . '/../appinfo/app.php';
|
||||
require_once __DIR__ . '/util.php';
|
||||
|
||||
use OCA\Encryption;
|
||||
|
||||
/**
|
||||
* Class Test_Encryption_Keymanager
|
||||
*/
|
||||
class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase {
|
||||
class Test_Encryption_Keymanager extends \OCA\Files_Encryption\Tests\TestCase {
|
||||
|
||||
const TEST_USER = "test-keymanager-user";
|
||||
const TEST_USER = "test-keymanager-user.dot";
|
||||
|
||||
public $userId;
|
||||
public $pass;
|
||||
@@ -36,6 +35,8 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase {
|
||||
public $dataShort;
|
||||
|
||||
public static function setUpBeforeClass() {
|
||||
parent::setUpBeforeClass();
|
||||
|
||||
// reset backend
|
||||
\OC_User::clearBackends();
|
||||
\OC_User::useBackend('database');
|
||||
@@ -58,10 +59,11 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
// create test user
|
||||
\OC_User::deleteUser(\Test_Encryption_Keymanager::TEST_USER);
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Keymanager::TEST_USER, true);
|
||||
parent::loginHelper(\Test_Encryption_Keymanager::TEST_USER, true);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// set content for encrypting / decrypting in tests
|
||||
$this->dataLong = file_get_contents(__DIR__ . '/../lib/crypt.php');
|
||||
$this->dataShort = 'hats';
|
||||
@@ -76,7 +78,7 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
$this->view = new \OC\Files\View('/');
|
||||
|
||||
\Test_Encryption_Util::loginHelper(Test_Encryption_Keymanager::TEST_USER);
|
||||
self::loginHelper(Test_Encryption_Keymanager::TEST_USER);
|
||||
$this->userId = \Test_Encryption_Keymanager::TEST_USER;
|
||||
$this->pass = \Test_Encryption_Keymanager::TEST_USER;
|
||||
|
||||
@@ -87,6 +89,8 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase {
|
||||
function tearDown() {
|
||||
$this->view->deleteAll('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys');
|
||||
$this->view->deleteAll('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/keyfiles');
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass() {
|
||||
@@ -98,6 +102,16 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase {
|
||||
if (self::$stateFilesTrashbin) {
|
||||
OC_App::enable('files_trashbin');
|
||||
}
|
||||
|
||||
\OC_Hook::clear();
|
||||
\OC_FileProxy::clearProxies();
|
||||
|
||||
// Delete keys in /data/
|
||||
$view = new \OC\Files\View('/');
|
||||
$view->rmdir('public-keys');
|
||||
$view->rmdir('owncloud_private_key');
|
||||
|
||||
parent::tearDownAfterClass();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -107,7 +121,7 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
$key = Encryption\Keymanager::getPrivateKey($this->view, $this->userId);
|
||||
|
||||
$privateKey = Encryption\Crypt::symmetricDecryptFileContent($key, $this->pass);
|
||||
$privateKey = Encryption\Crypt::decryptPrivateKey($key, $this->pass);
|
||||
|
||||
$res = openssl_pkey_get_private($privateKey);
|
||||
|
||||
@@ -135,15 +149,25 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase {
|
||||
$this->assertArrayHasKey('key', $sslInfo);
|
||||
}
|
||||
|
||||
function fileNameFromShareKeyProvider() {
|
||||
return array(
|
||||
array('file.user.shareKey', 'user', 'file'),
|
||||
array('file.name.with.dots.user.shareKey', 'user', 'file.name.with.dots'),
|
||||
array('file.name.user.with.dots.shareKey', 'user.with.dots', 'file.name'),
|
||||
array('file.txt', 'user', false),
|
||||
array('user.shareKey', 'user', false),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @small
|
||||
*
|
||||
* @dataProvider fileNameFromShareKeyProvider
|
||||
*/
|
||||
function testGetFilenameFromShareKey() {
|
||||
$this->assertEquals("file",
|
||||
\TestProtectedKeymanagerMethods::testGetFilenameFromShareKey("file.user.shareKey"));
|
||||
$this->assertEquals("file.name.with.dots",
|
||||
\TestProtectedKeymanagerMethods::testGetFilenameFromShareKey("file.name.with.dots.user.shareKey"));
|
||||
$this->assertFalse(\TestProtectedKeymanagerMethods::testGetFilenameFromShareKey("file.txt"));
|
||||
function testGetFilenameFromShareKey($fileName, $user, $expectedFileName) {
|
||||
$this->assertEquals($expectedFileName,
|
||||
\TestProtectedKeymanagerMethods::testGetFilenameFromShareKey($fileName, $user)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -153,7 +177,7 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
$key = $this->randomKey;
|
||||
|
||||
$file = 'unittest-' . uniqid() . '.txt';
|
||||
$file = 'unittest-' . $this->getUniqueID() . '.txt';
|
||||
|
||||
$util = new Encryption\Util($this->view, $this->userId);
|
||||
|
||||
@@ -174,6 +198,38 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase {
|
||||
\OC_FileProxy::$enabled = $proxyStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* @medium
|
||||
*/
|
||||
function testSetPrivateKey() {
|
||||
|
||||
$key = "dummy key";
|
||||
|
||||
Encryption\Keymanager::setPrivateKey($key, 'dummyUser');
|
||||
|
||||
$this->assertTrue($this->view->file_exists('/dummyUser/files_encryption/dummyUser.private.key'));
|
||||
|
||||
//clean up
|
||||
$this->view->deleteAll('/dummyUser');
|
||||
}
|
||||
|
||||
/**
|
||||
* @medium
|
||||
*/
|
||||
function testSetPrivateSystemKey() {
|
||||
|
||||
$key = "dummy key";
|
||||
$keyName = "myDummyKey.private.key";
|
||||
|
||||
Encryption\Keymanager::setPrivateSystemKey($key, $keyName);
|
||||
|
||||
$this->assertTrue($this->view->file_exists('/owncloud_private_key/' . $keyName));
|
||||
|
||||
// clean up
|
||||
$this->view->unlink('/owncloud_private_key/' . $keyName);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @medium
|
||||
*/
|
||||
@@ -189,7 +245,7 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
$this->assertArrayHasKey('key', $sslInfoPublic);
|
||||
|
||||
$privateKey = Encryption\Crypt::symmetricDecryptFileContent($keys['privateKey'], $this->pass);
|
||||
$privateKey = Encryption\Crypt::decryptPrivateKey($keys['privateKey'], $this->pass);
|
||||
|
||||
$resPrivate = openssl_pkey_get_private($privateKey);
|
||||
|
||||
@@ -217,6 +273,12 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase {
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/existingFile.txt.user1.shareKey', 'data');
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/existingFile.txt.' . Test_Encryption_Keymanager::TEST_USER . '.shareKey', 'data');
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/file1.user1.shareKey', 'data');
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/file1.user1.test.shareKey', 'data');
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/file1.test-keymanager-userxdot.shareKey', 'data');
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/file1.userx.' . Test_Encryption_Keymanager::TEST_USER . '.shareKey', 'data');
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/file1.' . Test_Encryption_Keymanager::TEST_USER . '.userx.shareKey', 'data');
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/file1.user1.' . Test_Encryption_Keymanager::TEST_USER . '.shareKey', 'data');
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/file1.' . Test_Encryption_Keymanager::TEST_USER . '.user1.shareKey', 'data');
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/file2.user2.shareKey', 'data');
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/file2.user3.shareKey', 'data');
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/subfolder/file2.user3.shareKey', 'data');
|
||||
@@ -225,7 +287,7 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase {
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/subfolder/subsubfolder/file2.user3.shareKey', 'data');
|
||||
|
||||
// recursive delete share keys from user1 and user2
|
||||
Encryption\Keymanager::delShareKey($this->view, array('user1', 'user2', Test_Encryption_Keymanager::TEST_USER), '/folder1/');
|
||||
Encryption\Keymanager::delShareKey($this->view, array('user1', 'user2', Test_Encryption_Keymanager::TEST_USER), '/folder1/', Test_Encryption_Keymanager::TEST_USER);
|
||||
|
||||
// check if share keys from user1 and user2 are deleted
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
@@ -247,6 +309,23 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase {
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/subfolder/file2.user3.shareKey'));
|
||||
|
||||
// check if share keys for user or file with similar name
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/file1.user1.test.shareKey'));
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/file1.test-keymanager-userxdot.shareKey'));
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/file1.' . Test_Encryption_Keymanager::TEST_USER . '.userx.shareKey'));
|
||||
// FIXME: this case currently cannot be distinguished, needs further fixing
|
||||
/*
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/file1.userx.' . Test_Encryption_Keymanager::TEST_USER . '.shareKey'));
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/file1.user1.' . Test_Encryption_Keymanager::TEST_USER . '.shareKey'));
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/file1.' . Test_Encryption_Keymanager::TEST_USER . '.user1.shareKey'));
|
||||
*/
|
||||
|
||||
// owner key from existing file should still exists because the file is still there
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/existingFile.txt.' . Test_Encryption_Keymanager::TEST_USER . '.shareKey'));
|
||||
@@ -274,7 +353,7 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase {
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/existingFile.txt.' . Test_Encryption_Keymanager::TEST_USER . '.shareKey', 'data');
|
||||
|
||||
// recursive delete share keys from user1 and user2
|
||||
Encryption\Keymanager::delShareKey($this->view, array('user1', 'user2', Test_Encryption_Keymanager::TEST_USER), '/folder1/existingFile.txt');
|
||||
Encryption\Keymanager::delShareKey($this->view, array('user1', 'user2', Test_Encryption_Keymanager::TEST_USER), '/folder1/existingFile.txt', Test_Encryption_Keymanager::TEST_USER);
|
||||
|
||||
// check if share keys from user1 and user2 are deleted
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
@@ -400,18 +479,19 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase {
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/existingFile.txt.user3.shareKey'));
|
||||
|
||||
// try to del all share keys froma file, should fail because the file still exists
|
||||
// try to del all share keys from file, should succeed because the does not exist any more
|
||||
$result2 = Encryption\Keymanager::delAllShareKeys($this->view, Test_Encryption_Keymanager::TEST_USER, 'folder1/nonexistingFile.txt');
|
||||
$this->assertTrue($result2);
|
||||
|
||||
// check if share keys are really gone
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/nonexistingFile.txt.' . Test_Encryption_Keymanager::TEST_USER . '.shareKey'));
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
// check that it only deleted keys or users who had access, others remain
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/nonexistingFile.txt.user1.shareKey'));
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/nonexistingFile.txt.user2.shareKey'));
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/nonexistingFile.txt.user3.shareKey'));
|
||||
|
||||
// cleanup
|
||||
@@ -447,8 +527,8 @@ class TestProtectedKeymanagerMethods extends \OCA\Encryption\Keymanager {
|
||||
/**
|
||||
* @param string $sharekey
|
||||
*/
|
||||
public static function testGetFilenameFromShareKey($sharekey) {
|
||||
return self::getFilenameFromShareKey($sharekey);
|
||||
public static function testGetFilenameFromShareKey($sharekey, $user) {
|
||||
return self::getFilenameFromShareKey($sharekey, $user);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -50,10 +50,44 @@ class Test_Migration extends PHPUnit_Framework_TestCase {
|
||||
|
||||
}
|
||||
|
||||
public function testDataMigration() {
|
||||
public function checkLastIndexId() {
|
||||
$query = \OC_DB::prepare('INSERT INTO `*PREFIX*share` ('
|
||||
.' `item_type`, `item_source`, `item_target`, `share_type`,'
|
||||
.' `share_with`, `uid_owner`, `permissions`, `stime`, `file_source`,'
|
||||
.' `file_target`, `token`, `parent`, `expiration`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)');
|
||||
$query->bindValue(1, 'file');
|
||||
$query->bindValue(2, 949);
|
||||
$query->bindValue(3, '/949');
|
||||
$query->bindValue(4, 0);
|
||||
$query->bindValue(5, 'migrate-test-user');
|
||||
$query->bindValue(6, 'migrate-test-owner');
|
||||
$query->bindValue(7, 23);
|
||||
$query->bindValue(8, 1402493312);
|
||||
$query->bindValue(9, 0);
|
||||
$query->bindValue(10, '/migration.txt');
|
||||
$query->bindValue(11, null);
|
||||
$query->bindValue(12, null);
|
||||
$query->bindValue(13, null);
|
||||
$this->assertEquals(1, $query->execute());
|
||||
|
||||
//FIXME fix this test so that we can enable it again
|
||||
$this->markTestIncomplete('Disabled, because of this tests a lot of other tests fail at the moment');
|
||||
$this->assertNotEquals('0', \OC_DB::insertid('*PREFIX*share'));
|
||||
|
||||
// cleanup
|
||||
$query = \OC_DB::prepare('DELETE FROM `*PREFIX*share` WHERE `file_target` = ?');
|
||||
$query->bindValue(1, '/migration.txt');
|
||||
$this->assertEquals(1, $query->execute());
|
||||
|
||||
}
|
||||
|
||||
public function testBrokenLastIndexId() {
|
||||
|
||||
// create test table
|
||||
$this->checkLastIndexId();
|
||||
OC_DB::createDbFromStructure(__DIR__ . '/encryption_table.xml');
|
||||
$this->checkLastIndexId();
|
||||
}
|
||||
|
||||
public function testDataMigration() {
|
||||
|
||||
$this->assertTableNotExist('encryption_test');
|
||||
|
||||
@@ -80,9 +114,6 @@ class Test_Migration extends PHPUnit_Framework_TestCase {
|
||||
|
||||
public function testDuplicateDataMigration() {
|
||||
|
||||
//FIXME fix this test so that we can enable it again
|
||||
$this->markTestIncomplete('Disabled, because of this tests a lot of other tests fail at the moment');
|
||||
|
||||
// create test table
|
||||
OC_DB::createDbFromStructure(__DIR__ . '/encryption_table.xml');
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ require_once __DIR__ . '/../lib/proxy.php';
|
||||
require_once __DIR__ . '/../lib/stream.php';
|
||||
require_once __DIR__ . '/../lib/util.php';
|
||||
require_once __DIR__ . '/../appinfo/app.php';
|
||||
require_once __DIR__ . '/util.php';
|
||||
|
||||
use OCA\Encryption;
|
||||
|
||||
@@ -35,7 +34,7 @@ use OCA\Encryption;
|
||||
* Class Test_Encryption_Proxy
|
||||
* this class provide basic proxy app tests
|
||||
*/
|
||||
class Test_Encryption_Proxy extends \PHPUnit_Framework_TestCase {
|
||||
class Test_Encryption_Proxy extends \OCA\Files_Encryption\Tests\TestCase {
|
||||
|
||||
const TEST_ENCRYPTION_PROXY_USER1 = "test-proxy-user1";
|
||||
|
||||
@@ -47,9 +46,12 @@ class Test_Encryption_Proxy extends \PHPUnit_Framework_TestCase {
|
||||
public $view; // view in /data/user/files
|
||||
public $rootView; // view on /data/user
|
||||
public $data;
|
||||
public $dataLong;
|
||||
public $filename;
|
||||
|
||||
public static function setUpBeforeClass() {
|
||||
parent::setUpBeforeClass();
|
||||
|
||||
// reset backend
|
||||
\OC_User::clearBackends();
|
||||
\OC_User::useBackend('database');
|
||||
@@ -65,10 +67,12 @@ class Test_Encryption_Proxy extends \PHPUnit_Framework_TestCase {
|
||||
\OC_FileProxy::register(new OCA\Encryption\Proxy());
|
||||
|
||||
// create test user
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Proxy::TEST_ENCRYPTION_PROXY_USER1, true);
|
||||
self::loginHelper(\Test_Encryption_Proxy::TEST_ENCRYPTION_PROXY_USER1, true);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// set user id
|
||||
\OC_User::setUserId(\Test_Encryption_Proxy::TEST_ENCRYPTION_PROXY_USER1);
|
||||
$this->userId = \Test_Encryption_Proxy::TEST_ENCRYPTION_PROXY_USER1;
|
||||
@@ -80,13 +84,24 @@ class Test_Encryption_Proxy extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
// init short data
|
||||
$this->data = 'hats';
|
||||
$this->filename = 'enc_proxy_tests-' . uniqid() . '.txt';
|
||||
$this->dataLong = file_get_contents(__DIR__ . '/../lib/crypt.php');
|
||||
$this->filename = 'enc_proxy_tests-' . $this->getUniqueID() . '.txt';
|
||||
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass() {
|
||||
// cleanup test user
|
||||
\OC_User::deleteUser(\Test_Encryption_Proxy::TEST_ENCRYPTION_PROXY_USER1);
|
||||
|
||||
\OC_Hook::clear();
|
||||
\OC_FileProxy::clearProxies();
|
||||
|
||||
// Delete keys in /data/
|
||||
$view = new \OC\Files\View('/');
|
||||
$view->rmdir('public-keys');
|
||||
$view->rmdir('owncloud_private_key');
|
||||
|
||||
parent::tearDownAfterClass();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,17 +110,19 @@ class Test_Encryption_Proxy extends \PHPUnit_Framework_TestCase {
|
||||
*/
|
||||
function testPostFileSize() {
|
||||
|
||||
$this->view->file_put_contents($this->filename, $this->data);
|
||||
$this->view->file_put_contents($this->filename, $this->dataLong);
|
||||
$size = strlen($this->dataLong);
|
||||
|
||||
\OC_FileProxy::$enabled = false;
|
||||
|
||||
$unencryptedSize = $this->view->filesize($this->filename);
|
||||
$encryptedSize = $this->view->filesize($this->filename);
|
||||
|
||||
\OC_FileProxy::$enabled = true;
|
||||
|
||||
$encryptedSize = $this->view->filesize($this->filename);
|
||||
$unencryptedSize = $this->view->filesize($this->filename);
|
||||
|
||||
$this->assertTrue($encryptedSize !== $unencryptedSize);
|
||||
$this->assertTrue($encryptedSize > $unencryptedSize);
|
||||
$this->assertSame($size, $unencryptedSize);
|
||||
|
||||
// cleanup
|
||||
$this->view->unlink($this->filename);
|
||||
@@ -132,4 +149,42 @@ class Test_Encryption_Proxy extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider isExcludedPathProvider
|
||||
*/
|
||||
function testIsExcludedPath($path, $expected) {
|
||||
$this->view->mkdir(dirname($path));
|
||||
$this->view->file_put_contents($path, "test");
|
||||
|
||||
$testClass = new DummyProxy();
|
||||
|
||||
$result = $testClass->isExcludedPathTesting($path, $this->userId);
|
||||
$this->assertSame($expected, $result);
|
||||
|
||||
$this->view->deleteAll(dirname($path));
|
||||
|
||||
}
|
||||
|
||||
public function isExcludedPathProvider() {
|
||||
return array(
|
||||
array ('/' . \Test_Encryption_Proxy::TEST_ENCRYPTION_PROXY_USER1 . '/files/test.txt', false),
|
||||
array (\Test_Encryption_Proxy::TEST_ENCRYPTION_PROXY_USER1 . '/files/test.txt', false),
|
||||
array ('/files/test.txt', true),
|
||||
array ('/' . \Test_Encryption_Proxy::TEST_ENCRYPTION_PROXY_USER1 . '/files/versions/test.txt', false),
|
||||
array ('/' . \Test_Encryption_Proxy::TEST_ENCRYPTION_PROXY_USER1 . '/files_versions/test.txt', false),
|
||||
array ('/' . \Test_Encryption_Proxy::TEST_ENCRYPTION_PROXY_USER1 . '/files_trashbin/test.txt', true),
|
||||
array ('/' . \Test_Encryption_Proxy::TEST_ENCRYPTION_PROXY_USER1 . '/file/test.txt', true),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Dummy class to make protected methods available for testing
|
||||
*/
|
||||
class DummyProxy extends \OCA\Encryption\Proxy {
|
||||
public function isExcludedPathTesting($path, $uid) {
|
||||
return $this->isExcludedPath($path, $uid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,14 +29,13 @@ require_once __DIR__ . '/../lib/stream.php';
|
||||
require_once __DIR__ . '/../lib/util.php';
|
||||
require_once __DIR__ . '/../lib/helper.php';
|
||||
require_once __DIR__ . '/../appinfo/app.php';
|
||||
require_once __DIR__ . '/util.php';
|
||||
|
||||
use OCA\Encryption;
|
||||
|
||||
/**
|
||||
* Class Test_Encryption_Share
|
||||
*/
|
||||
class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
class Test_Encryption_Share extends \OCA\Files_Encryption\Tests\TestCase {
|
||||
|
||||
const TEST_ENCRYPTION_SHARE_USER1 = "test-share-user1";
|
||||
const TEST_ENCRYPTION_SHARE_USER2 = "test-share-user2";
|
||||
@@ -56,6 +55,8 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
public $subsubfolder;
|
||||
|
||||
public static function setUpBeforeClass() {
|
||||
parent::setUpBeforeClass();
|
||||
|
||||
// reset backend
|
||||
\OC_User::clearBackends();
|
||||
\OC_User::useBackend('database');
|
||||
@@ -65,8 +66,10 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
// clear share hooks
|
||||
\OC_Hook::clear('OCP\\Share');
|
||||
|
||||
// register share hooks
|
||||
\OC::registerShareHooks();
|
||||
\OCP\Util::connectHook('OC_Filesystem', 'setup', '\OC\Files\Storage\Shared', 'setup');
|
||||
\OCA\Files_Sharing\Helper::registerHooks();
|
||||
|
||||
// Sharing related hooks
|
||||
\OCA\Encryption\Helper::registerShareHooks();
|
||||
@@ -76,13 +79,14 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
// clear and register hooks
|
||||
\OC_FileProxy::clearProxies();
|
||||
\OC_FileProxy::register(new OCA\Files\Share\Proxy());
|
||||
\OC_FileProxy::register(new OCA\Encryption\Proxy());
|
||||
|
||||
// create users
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1, true);
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2, true);
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3, true);
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER4, true);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1, true);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2, true);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3, true);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER4, true);
|
||||
|
||||
// create group and assign users
|
||||
\OC_Group::createGroup(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_GROUP1);
|
||||
@@ -90,7 +94,9 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
\OC_Group::addToGroup(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER4, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_GROUP1);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->dataShort = 'hats';
|
||||
$this->view = new \OC\Files\View('/');
|
||||
|
||||
@@ -105,15 +111,20 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
// we don't want to tests with app files_trashbin enabled
|
||||
\OC_App::disable('files_trashbin');
|
||||
|
||||
// login as first user
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
protected function tearDown() {
|
||||
// reset app files_trashbin
|
||||
if ($this->stateFilesTrashbin) {
|
||||
OC_App::enable('files_trashbin');
|
||||
} else {
|
||||
OC_App::disable('files_trashbin');
|
||||
}
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass() {
|
||||
@@ -125,6 +136,16 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
\OC_User::deleteUser(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2);
|
||||
\OC_User::deleteUser(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3);
|
||||
\OC_User::deleteUser(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER4);
|
||||
|
||||
\OC_Hook::clear();
|
||||
\OC_FileProxy::clearProxies();
|
||||
|
||||
// Delete keys in /data/
|
||||
$view = new \OC\Files\View('/');
|
||||
$view->rmdir('public-keys');
|
||||
$view->rmdir('owncloud_private_key');
|
||||
|
||||
parent::tearDownAfterClass();
|
||||
}
|
||||
|
||||
|
||||
@@ -134,7 +155,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
*/
|
||||
function testShareFile($withTeardown = true) {
|
||||
// login as admin
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
|
||||
// save file with content
|
||||
$cryptedFile = file_put_contents('crypt:///' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename, $this->dataShort);
|
||||
@@ -163,7 +184,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
\OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2, OCP\PERMISSION_ALL);
|
||||
|
||||
// login as admin
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
|
||||
// check if share key for user1 exists
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
@@ -171,7 +192,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
. $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '.shareKey'));
|
||||
|
||||
// login as user1
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2);
|
||||
|
||||
// get file contents
|
||||
$retrievedCryptedFile = $this->view->file_get_contents(
|
||||
@@ -184,7 +205,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
if ($withTeardown) {
|
||||
|
||||
// login as admin
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
|
||||
// unshare the file
|
||||
\OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2);
|
||||
@@ -214,7 +235,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
$this->testShareFile(false);
|
||||
|
||||
// login as user2
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2);
|
||||
|
||||
// get the file info
|
||||
$fileInfo = $this->view->getFileInfo(
|
||||
@@ -224,7 +245,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
\OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3, OCP\PERMISSION_ALL);
|
||||
|
||||
// login as admin
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
|
||||
// check if share key for user2 exists
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
@@ -232,7 +253,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
. $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3 . '.shareKey'));
|
||||
|
||||
// login as user2
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3);
|
||||
|
||||
// get file contents
|
||||
$retrievedCryptedFile = $this->view->file_get_contents(
|
||||
@@ -245,13 +266,13 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
if ($withTeardown) {
|
||||
|
||||
// login as user1
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2);
|
||||
|
||||
// unshare the file with user2
|
||||
\OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3);
|
||||
|
||||
// login as admin
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
|
||||
// check if share key not exists
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
@@ -285,7 +306,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
*/
|
||||
function testShareFolder($withTeardown = true) {
|
||||
// login as admin
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
|
||||
// create folder structure
|
||||
$this->view->mkdir('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files' . $this->folder1);
|
||||
@@ -320,7 +341,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
\OCP\Share::shareItem('folder', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2, OCP\PERMISSION_ALL);
|
||||
|
||||
// login as admin
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
|
||||
// check if share key for user1 exists
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
@@ -329,7 +350,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
. $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '.shareKey'));
|
||||
|
||||
// login as user1
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2);
|
||||
|
||||
// get file contents
|
||||
$retrievedCryptedFile = $this->view->file_get_contents(
|
||||
@@ -343,7 +364,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
if ($withTeardown) {
|
||||
|
||||
// login as admin
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
|
||||
// unshare the folder with user1
|
||||
\OCP\Share::unshare('folder', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2);
|
||||
@@ -377,7 +398,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
$fileInfoFolder1 = $this->testShareFolder(false);
|
||||
|
||||
// login as user2
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2);
|
||||
|
||||
// disable encryption proxy to prevent recursive calls
|
||||
$proxyStatus = \OC_FileProxy::$enabled;
|
||||
@@ -398,7 +419,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
\OCP\Share::shareItem('folder', $fileInfoSubFolder['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3, OCP\PERMISSION_ALL);
|
||||
|
||||
// login as admin
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
|
||||
// check if share key for user3 exists
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
@@ -407,7 +428,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
. $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3 . '.shareKey'));
|
||||
|
||||
// login as user3
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3);
|
||||
|
||||
// get file contents
|
||||
$retrievedCryptedFile = $this->view->file_get_contents(
|
||||
@@ -429,7 +450,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
\OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER4, OCP\PERMISSION_ALL);
|
||||
|
||||
// login as admin
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
|
||||
// check if share key for user3 exists
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
@@ -438,7 +459,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
. $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER4 . '.shareKey'));
|
||||
|
||||
// login as user3
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER4);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER4);
|
||||
|
||||
// get file contents
|
||||
$retrievedCryptedFile = $this->view->file_get_contents(
|
||||
@@ -451,7 +472,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
if ($withTeardown) {
|
||||
|
||||
// login as user2
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3);
|
||||
|
||||
// unshare the file with user3
|
||||
\OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER4);
|
||||
@@ -463,7 +484,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
. $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER4 . '.shareKey'));
|
||||
|
||||
// login as user1
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2);
|
||||
|
||||
// unshare the folder with user2
|
||||
\OCP\Share::unshare('folder', $fileInfoSubFolder['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3);
|
||||
@@ -475,7 +496,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
. $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3 . '.shareKey'));
|
||||
|
||||
// login as admin
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
|
||||
// unshare the folder1 with user1
|
||||
\OCP\Share::unshare('folder', $fileInfoFolder1['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2);
|
||||
@@ -502,7 +523,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
function testPublicShareFile() {
|
||||
// login as admin
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
|
||||
// save file with content
|
||||
$cryptedFile = file_put_contents('crypt:///' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename, $this->dataShort);
|
||||
@@ -531,7 +552,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
\OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_LINK, false, OCP\PERMISSION_ALL);
|
||||
|
||||
// login as admin
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
|
||||
$publicShareKeyId = \OC::$server->getAppConfig()->getValue('files_encryption', 'publicShareKeyId');
|
||||
|
||||
@@ -541,9 +562,9 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
. $this->filename . '.' . $publicShareKeyId . '.shareKey'));
|
||||
|
||||
// some hacking to simulate public link
|
||||
$GLOBALS['app'] = 'files_sharing';
|
||||
$GLOBALS['fileOwner'] = \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1;
|
||||
\OC_User::setUserId(false);
|
||||
//$GLOBALS['app'] = 'files_sharing';
|
||||
//$GLOBALS['fileOwner'] = \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1;
|
||||
self::logoutHelper();
|
||||
|
||||
// get file contents
|
||||
$retrievedCryptedFile = file_get_contents('crypt:///' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename);
|
||||
@@ -554,7 +575,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
// tear down
|
||||
|
||||
// login as admin
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
|
||||
// unshare the file
|
||||
\OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_LINK, null);
|
||||
@@ -580,7 +601,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
*/
|
||||
function testShareFileWithGroup() {
|
||||
// login as admin
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
|
||||
// save file with content
|
||||
$cryptedFile = file_put_contents('crypt:///' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename, $this->dataShort);
|
||||
@@ -609,7 +630,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
\OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_GROUP, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_GROUP1, OCP\PERMISSION_ALL);
|
||||
|
||||
// login as admin
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
|
||||
// check if share key for user2 and user3 exists
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
@@ -620,7 +641,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
. $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER4 . '.shareKey'));
|
||||
|
||||
// login as user1
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3);
|
||||
|
||||
// get file contents
|
||||
$retrievedCryptedFile = $this->view->file_get_contents(
|
||||
@@ -630,7 +651,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
$this->assertEquals($this->dataShort, $retrievedCryptedFile);
|
||||
|
||||
// login as admin
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
|
||||
// unshare the file
|
||||
\OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_GROUP, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_GROUP1);
|
||||
@@ -661,13 +682,13 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
function testRecoveryFile() {
|
||||
|
||||
// login as admin
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
|
||||
\OCA\Encryption\Helper::adminEnableRecovery(null, 'test123');
|
||||
$recoveryKeyId = \OC::$server->getAppConfig()->getValue('files_encryption', 'recoveryKeyId');
|
||||
|
||||
// login as admin
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
|
||||
$util = new \OCA\Encryption\Util(new \OC\Files\View('/'), \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
|
||||
@@ -767,7 +788,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
function testRecoveryForUser() {
|
||||
|
||||
// login as admin
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
|
||||
$result = \OCA\Encryption\Helper::adminEnableRecovery(null, 'test123');
|
||||
$this->assertTrue($result);
|
||||
@@ -775,7 +796,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
$recoveryKeyId = \OC::$server->getAppConfig()->getValue('files_encryption', 'recoveryKeyId');
|
||||
|
||||
// login as user2
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2);
|
||||
|
||||
$util = new \OCA\Encryption\Util(new \OC\Files\View('/'), \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2);
|
||||
|
||||
@@ -819,7 +840,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
. $this->filename . '.' . $recoveryKeyId . '.shareKey'));
|
||||
|
||||
// login as admin
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
|
||||
// change password
|
||||
\OC_User::setPassword(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2, 'test', 'test123');
|
||||
@@ -829,7 +850,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
\OCA\Encryption\Hooks::setPassphrase($params);
|
||||
|
||||
// login as user2
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2, false, 'test');
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2, false, 'test');
|
||||
|
||||
// get file contents
|
||||
$retrievedCryptedFile1 = file_get_contents('crypt:///' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/' . $this->filename);
|
||||
@@ -881,7 +902,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
*/
|
||||
function testFailShareFile() {
|
||||
// login as admin
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
|
||||
// save file with content
|
||||
$cryptedFile = file_put_contents('crypt:///' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename, $this->dataShort);
|
||||
@@ -919,7 +940,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
|
||||
// login as admin
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
|
||||
// check if share key for user1 not exists
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
@@ -964,7 +985,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
function testRename() {
|
||||
|
||||
// login as admin
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
|
||||
// save file with content
|
||||
$cryptedFile = file_put_contents('crypt:///' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename, $this->dataShort);
|
||||
@@ -989,7 +1010,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
|
||||
// login as user2
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2);
|
||||
|
||||
$this->assertTrue($this->view->file_exists('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/' . $this->filename));
|
||||
|
||||
@@ -1012,8 +1033,126 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
$this->assertEquals($this->dataShort, $retrievedRenamedFile);
|
||||
|
||||
// cleanup
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
$this->view->unlink('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* test if additional share keys are added if we move a folder to a shared parent
|
||||
* @medium
|
||||
*/
|
||||
function testMoveFolder() {
|
||||
|
||||
$view = new \OC\Files\View('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
|
||||
$filename = '/tmp-' . $this->getUniqueID();
|
||||
$folder = '/folder' . $this->getUniqueID();
|
||||
|
||||
\OC\Files\Filesystem::mkdir($folder);
|
||||
|
||||
// Save long data as encrypted file using stream wrapper
|
||||
$cryptedFile = \OC\Files\Filesystem::file_put_contents($folder . $filename, $this->dataShort);
|
||||
|
||||
// Test that data was successfully written
|
||||
$this->assertTrue(is_int($cryptedFile));
|
||||
|
||||
// Get file decrypted contents
|
||||
$decrypt = \OC\Files\Filesystem::file_get_contents($folder . $filename);
|
||||
|
||||
$this->assertEquals($this->dataShort, $decrypt);
|
||||
|
||||
$newFolder = '/newfolder/subfolder' . $this->getUniqueID();
|
||||
\OC\Files\Filesystem::mkdir('/newfolder');
|
||||
|
||||
// get the file info from previous created file
|
||||
$fileInfo = \OC\Files\Filesystem::getFileInfo('/newfolder');
|
||||
$this->assertTrue($fileInfo instanceof \OC\Files\FileInfo);
|
||||
|
||||
// share the folder
|
||||
\OCP\Share::shareItem('folder', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2, OCP\PERMISSION_ALL);
|
||||
|
||||
\OC\Files\Filesystem::rename($folder, $newFolder);
|
||||
|
||||
// Get file decrypted contents
|
||||
$newDecrypt = \OC\Files\Filesystem::file_get_contents($newFolder . $filename);
|
||||
$this->assertEquals($this->dataShort, $newDecrypt);
|
||||
|
||||
// check if additional share key for user2 exists
|
||||
$this->assertTrue($view->file_exists('files_encryption/share-keys' . $newFolder . '/' . $filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '.shareKey'));
|
||||
|
||||
// check that old keys were removed/moved properly
|
||||
$this->assertFalse($view->file_exists('files_encryption/share-keys' . $folder . '/' . $filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '.shareKey'));
|
||||
|
||||
// tear down
|
||||
\OC\Files\Filesystem::unlink($newFolder);
|
||||
\OC\Files\Filesystem::unlink('/newfolder');
|
||||
}
|
||||
|
||||
function usersProvider() {
|
||||
return array(
|
||||
// test as owner
|
||||
array(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1),
|
||||
// test as share receiver
|
||||
array(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider usersProvider
|
||||
*/
|
||||
function testMoveFileToFolder($userId) {
|
||||
$view = new \OC\Files\View('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
|
||||
$filename = '/tmp-' . $this->getUniqueID();
|
||||
$folder = '/folder' . $this->getUniqueID();
|
||||
|
||||
\OC\Files\Filesystem::mkdir($folder);
|
||||
|
||||
// Save long data as encrypted file using stream wrapper
|
||||
$cryptedFile = \OC\Files\Filesystem::file_put_contents($folder . $filename, $this->dataShort);
|
||||
|
||||
// Test that data was successfully written
|
||||
$this->assertTrue(is_int($cryptedFile));
|
||||
|
||||
// Get file decrypted contents
|
||||
$decrypt = \OC\Files\Filesystem::file_get_contents($folder . $filename);
|
||||
|
||||
$this->assertEquals($this->dataShort, $decrypt);
|
||||
|
||||
$subFolder = $folder . '/subfolder' . $this->getUniqueID();
|
||||
\OC\Files\Filesystem::mkdir($subFolder);
|
||||
|
||||
// get the file info from previous created file
|
||||
$fileInfo = \OC\Files\Filesystem::getFileInfo($folder);
|
||||
$this->assertTrue($fileInfo instanceof \OC\Files\FileInfo);
|
||||
|
||||
// share the folder
|
||||
\OCP\Share::shareItem('folder', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2, OCP\PERMISSION_ALL);
|
||||
|
||||
// check that the share keys exist
|
||||
$this->assertTrue($view->file_exists('files_encryption/share-keys' . $folder . '/' . $filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '.shareKey'));
|
||||
$this->assertTrue($view->file_exists('files_encryption/share-keys' . $folder . '/' . $filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '.shareKey'));
|
||||
|
||||
// move the file into the subfolder as the test user
|
||||
self::loginHelper($userId);
|
||||
\OC\Files\Filesystem::rename($folder . $filename, $subFolder . $filename);
|
||||
self::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
|
||||
// Get file decrypted contents
|
||||
$newDecrypt = \OC\Files\Filesystem::file_get_contents($subFolder . $filename);
|
||||
$this->assertEquals($this->dataShort, $newDecrypt);
|
||||
|
||||
// check if additional share key for user2 exists
|
||||
$this->assertTrue($view->file_exists('files_encryption/share-keys' . $subFolder . '/' . $filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '.shareKey'));
|
||||
$this->assertTrue($view->file_exists('files_encryption/share-keys' . $subFolder . '/' . $filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '.shareKey'));
|
||||
|
||||
// check that old keys were removed/moved properly
|
||||
$this->assertFalse($view->file_exists('files_encryption/share-keys' . $folder . '/' . $filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '.shareKey'));
|
||||
$this->assertFalse($view->file_exists('files_encryption/share-keys' . $folder . '/' . $filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '.shareKey'));
|
||||
|
||||
// tear down
|
||||
\OC\Files\Filesystem::unlink($subFolder);
|
||||
\OC\Files\Filesystem::unlink($folder);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ require_once __DIR__ . '/../lib/proxy.php';
|
||||
require_once __DIR__ . '/../lib/stream.php';
|
||||
require_once __DIR__ . '/../lib/util.php';
|
||||
require_once __DIR__ . '/../appinfo/app.php';
|
||||
require_once __DIR__ . '/util.php';
|
||||
|
||||
use OCA\Encryption;
|
||||
|
||||
@@ -35,7 +34,7 @@ use OCA\Encryption;
|
||||
* Class Test_Encryption_Stream
|
||||
* this class provide basic stream tests
|
||||
*/
|
||||
class Test_Encryption_Stream extends \PHPUnit_Framework_TestCase {
|
||||
class Test_Encryption_Stream extends \OCA\Files_Encryption\Tests\TestCase {
|
||||
|
||||
const TEST_ENCRYPTION_STREAM_USER1 = "test-stream-user1";
|
||||
|
||||
@@ -49,6 +48,8 @@ class Test_Encryption_Stream extends \PHPUnit_Framework_TestCase {
|
||||
public $stateFilesTrashbin;
|
||||
|
||||
public static function setUpBeforeClass() {
|
||||
parent::setUpBeforeClass();
|
||||
|
||||
// reset backend
|
||||
\OC_User::clearBackends();
|
||||
\OC_User::useBackend('database');
|
||||
@@ -61,10 +62,12 @@ class Test_Encryption_Stream extends \PHPUnit_Framework_TestCase {
|
||||
\OC_FileProxy::register(new OCA\Encryption\Proxy());
|
||||
|
||||
// create test user
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Stream::TEST_ENCRYPTION_STREAM_USER1, true);
|
||||
self::loginHelper(\Test_Encryption_Stream::TEST_ENCRYPTION_STREAM_USER1, true);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// set user id
|
||||
\OC_User::setUserId(\Test_Encryption_Stream::TEST_ENCRYPTION_STREAM_USER1);
|
||||
$this->userId = \Test_Encryption_Stream::TEST_ENCRYPTION_STREAM_USER1;
|
||||
@@ -83,7 +86,7 @@ class Test_Encryption_Stream extends \PHPUnit_Framework_TestCase {
|
||||
\OC_App::disable('files_trashbin');
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
protected function tearDown() {
|
||||
// reset app files_trashbin
|
||||
if ($this->stateFilesTrashbin) {
|
||||
OC_App::enable('files_trashbin');
|
||||
@@ -91,15 +94,27 @@ class Test_Encryption_Stream extends \PHPUnit_Framework_TestCase {
|
||||
else {
|
||||
OC_App::disable('files_trashbin');
|
||||
}
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass() {
|
||||
// cleanup test user
|
||||
\OC_User::deleteUser(\Test_Encryption_Stream::TEST_ENCRYPTION_STREAM_USER1);
|
||||
|
||||
\OC_Hook::clear();
|
||||
\OC_FileProxy::clearProxies();
|
||||
|
||||
// Delete keys in /data/
|
||||
$view = new \OC\Files\View('/');
|
||||
$view->rmdir('public-keys');
|
||||
$view->rmdir('owncloud_private_key');
|
||||
|
||||
parent::tearDownAfterClass();
|
||||
}
|
||||
|
||||
function testStreamOptions() {
|
||||
$filename = '/tmp-' . uniqid();
|
||||
$filename = '/tmp-' . $this->getUniqueID();
|
||||
$view = new \OC\Files\View('/' . $this->userId . '/files');
|
||||
|
||||
// Save short data as encrypted file using stream wrapper
|
||||
@@ -117,12 +132,14 @@ class Test_Encryption_Stream extends \PHPUnit_Framework_TestCase {
|
||||
$this->assertTrue(flock($handle, LOCK_SH));
|
||||
$this->assertTrue(flock($handle, LOCK_UN));
|
||||
|
||||
fclose($handle);
|
||||
|
||||
// tear down
|
||||
$view->unlink($filename);
|
||||
}
|
||||
|
||||
function testStreamSetBlocking() {
|
||||
$filename = '/tmp-' . uniqid();
|
||||
$filename = '/tmp-' . $this->getUniqueID();
|
||||
$view = new \OC\Files\View('/' . $this->userId . '/files');
|
||||
|
||||
// Save short data as encrypted file using stream wrapper
|
||||
@@ -133,6 +150,13 @@ class Test_Encryption_Stream extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
$handle = $view->fopen($filename, 'r');
|
||||
|
||||
|
||||
if (\OC_Util::runningOnWindows()) {
|
||||
fclose($handle);
|
||||
$view->unlink($filename);
|
||||
$this->markTestSkipped('[Windows] stream_set_blocking() does not work as expected on Windows.');
|
||||
}
|
||||
|
||||
// set stream options
|
||||
$this->assertTrue(stream_set_blocking($handle, 1));
|
||||
|
||||
@@ -146,7 +170,7 @@ class Test_Encryption_Stream extends \PHPUnit_Framework_TestCase {
|
||||
* @medium
|
||||
*/
|
||||
function testStreamSetTimeout() {
|
||||
$filename = '/tmp-' . uniqid();
|
||||
$filename = '/tmp-' . $this->getUniqueID();
|
||||
$view = new \OC\Files\View('/' . $this->userId . '/files');
|
||||
|
||||
// Save short data as encrypted file using stream wrapper
|
||||
@@ -167,7 +191,7 @@ class Test_Encryption_Stream extends \PHPUnit_Framework_TestCase {
|
||||
}
|
||||
|
||||
function testStreamSetWriteBuffer() {
|
||||
$filename = '/tmp-' . uniqid();
|
||||
$filename = '/tmp-' . $this->getUniqueID();
|
||||
$view = new \OC\Files\View('/' . $this->userId . '/files');
|
||||
|
||||
// Save short data as encrypted file using stream wrapper
|
||||
@@ -193,9 +217,9 @@ class Test_Encryption_Stream extends \PHPUnit_Framework_TestCase {
|
||||
*/
|
||||
function testStreamFromLocalFile() {
|
||||
|
||||
$filename = '/' . $this->userId . '/files/' . 'tmp-' . uniqid().'.txt';
|
||||
$filename = '/' . $this->userId . '/files/' . 'tmp-' . $this->getUniqueID().'.txt';
|
||||
|
||||
$tmpFilename = "/tmp/" . uniqid() . ".txt";
|
||||
$tmpFilename = "/tmp/" . $this->getUniqueID() . ".txt";
|
||||
|
||||
// write an encrypted file
|
||||
$cryptedFile = $this->view->file_put_contents($filename, $this->dataShort);
|
||||
@@ -220,6 +244,8 @@ class Test_Encryption_Stream extends \PHPUnit_Framework_TestCase {
|
||||
// check if it was successful
|
||||
$this->assertEquals($this->dataShort, $contentFromTmpFile);
|
||||
|
||||
fclose($handle);
|
||||
|
||||
// clean up
|
||||
unlink($tmpFilename);
|
||||
$this->view->unlink($filename);
|
||||
|
||||
53
apps/files_encryption/tests/testcase.php
Normal file
53
apps/files_encryption/tests/testcase.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2012 Sam Tuke <samtuke@owncloud.com>
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
|
||||
namespace OCA\Files_Encryption\Tests;
|
||||
|
||||
use OCA\Encryption;
|
||||
|
||||
/**
|
||||
* Class Test_Encryption_TestCase
|
||||
*/
|
||||
abstract class TestCase extends \Test\TestCase {
|
||||
/**
|
||||
* @param string $user
|
||||
* @param bool $create
|
||||
* @param bool $password
|
||||
*/
|
||||
public static function loginHelper($user, $create = false, $password = false, $loadEncryption = true) {
|
||||
if ($create) {
|
||||
try {
|
||||
\OC_User::createUser($user, $user);
|
||||
} catch (\Exception $e) {
|
||||
// catch username is already being used from previous aborted runs
|
||||
}
|
||||
}
|
||||
|
||||
if ($password === false) {
|
||||
$password = $user;
|
||||
}
|
||||
|
||||
\OC_Util::tearDownFS();
|
||||
\OC_User::setUserId('');
|
||||
\OC\Files\Filesystem::tearDown();
|
||||
\OC_User::setUserId($user);
|
||||
\OC_Util::setupFS($user);
|
||||
|
||||
if ($loadEncryption) {
|
||||
$params['uid'] = $user;
|
||||
$params['password'] = $password;
|
||||
\OCA\Encryption\Hooks::login($params);
|
||||
}
|
||||
}
|
||||
|
||||
public static function logoutHelper() {
|
||||
\OC_Util::tearDownFS();
|
||||
\OC_User::setUserId(false);
|
||||
\OC\Files\Filesystem::tearDown();
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,6 @@ require_once __DIR__ . '/../lib/stream.php';
|
||||
require_once __DIR__ . '/../lib/util.php';
|
||||
require_once __DIR__ . '/../appinfo/app.php';
|
||||
require_once __DIR__ . '/../../files_trashbin/appinfo/app.php';
|
||||
require_once __DIR__ . '/util.php';
|
||||
|
||||
use OCA\Encryption;
|
||||
|
||||
@@ -36,7 +35,7 @@ use OCA\Encryption;
|
||||
* Class Test_Encryption_Trashbin
|
||||
* this class provide basic trashbin app tests
|
||||
*/
|
||||
class Test_Encryption_Trashbin extends \PHPUnit_Framework_TestCase {
|
||||
class Test_Encryption_Trashbin extends \OCA\Files_Encryption\Tests\TestCase {
|
||||
|
||||
const TEST_ENCRYPTION_TRASHBIN_USER1 = "test-trashbin-user1";
|
||||
|
||||
@@ -53,6 +52,8 @@ class Test_Encryption_Trashbin extends \PHPUnit_Framework_TestCase {
|
||||
public $subsubfolder;
|
||||
|
||||
public static function setUpBeforeClass() {
|
||||
parent::setUpBeforeClass();
|
||||
|
||||
// reset backend
|
||||
\OC_User::clearBackends();
|
||||
\OC_User::useBackend('database');
|
||||
@@ -71,14 +72,16 @@ class Test_Encryption_Trashbin extends \PHPUnit_Framework_TestCase {
|
||||
\OC_FileProxy::register(new OCA\Encryption\Proxy());
|
||||
|
||||
// create test user
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1, true);
|
||||
self::loginHelper(self::TEST_ENCRYPTION_TRASHBIN_USER1, true);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// set user id
|
||||
\OC_User::setUserId(\Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1);
|
||||
$this->userId = \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1;
|
||||
$this->pass = \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1;
|
||||
\OC_User::setUserId(self::TEST_ENCRYPTION_TRASHBIN_USER1);
|
||||
$this->userId = self::TEST_ENCRYPTION_TRASHBIN_USER1;
|
||||
$this->pass = self::TEST_ENCRYPTION_TRASHBIN_USER1;
|
||||
|
||||
// init filesystem view
|
||||
$this->view = new \OC\Files\View('/');
|
||||
@@ -97,7 +100,7 @@ class Test_Encryption_Trashbin extends \PHPUnit_Framework_TestCase {
|
||||
\OC_App::enable('files_trashbin');
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
protected function tearDown() {
|
||||
// reset app files_trashbin
|
||||
if ($this->stateFilesTrashbin) {
|
||||
OC_App::enable('files_trashbin');
|
||||
@@ -105,11 +108,23 @@ class Test_Encryption_Trashbin extends \PHPUnit_Framework_TestCase {
|
||||
else {
|
||||
OC_App::disable('files_trashbin');
|
||||
}
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass() {
|
||||
// cleanup test user
|
||||
\OC_User::deleteUser(\Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1);
|
||||
\OC_User::deleteUser(self::TEST_ENCRYPTION_TRASHBIN_USER1);
|
||||
|
||||
\OC_Hook::clear();
|
||||
\OC_FileProxy::clearProxies();
|
||||
|
||||
// Delete keys in /data/
|
||||
$view = new \OC\Files\View('/');
|
||||
$view->rmdir('public-keys');
|
||||
$view->rmdir('owncloud_private_key');
|
||||
|
||||
parent::tearDownAfterClass();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,49 +134,72 @@ class Test_Encryption_Trashbin extends \PHPUnit_Framework_TestCase {
|
||||
function testDeleteFile() {
|
||||
|
||||
// generate filename
|
||||
$filename = 'tmp-' . uniqid() . '.txt';
|
||||
$filename = 'tmp-' . $this->getUniqueID() . '.txt';
|
||||
$filename2 = $filename . '.backup'; // a second file with similar name
|
||||
|
||||
// save file with content
|
||||
$cryptedFile = file_put_contents('crypt:///' .\Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1. '/files/'. $filename, $this->dataShort);
|
||||
$cryptedFile = file_put_contents('crypt:///' .self::TEST_ENCRYPTION_TRASHBIN_USER1. '/files/'. $filename, $this->dataShort);
|
||||
$cryptedFile2 = file_put_contents('crypt:///' .self::TEST_ENCRYPTION_TRASHBIN_USER1. '/files/'. $filename2, $this->dataShort);
|
||||
|
||||
// test that data was successfully written
|
||||
$this->assertTrue(is_int($cryptedFile));
|
||||
$this->assertTrue(is_int($cryptedFile2));
|
||||
|
||||
// check if key for admin exists
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/keyfiles/' . $filename
|
||||
'/' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/keyfiles/' . $filename
|
||||
. '.key'));
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/keyfiles/' . $filename2
|
||||
. '.key'));
|
||||
|
||||
// check if share key for admin exists
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/share-keys/'
|
||||
. $filename . '.' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '.shareKey'));
|
||||
'/' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/share-keys/'
|
||||
. $filename . '.' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '.shareKey'));
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/share-keys/'
|
||||
. $filename2 . '.' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '.shareKey'));
|
||||
|
||||
// delete file
|
||||
// delete first file
|
||||
\OC\FIles\Filesystem::unlink($filename);
|
||||
|
||||
// check if file not exists
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
'/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files/' . $filename));
|
||||
'/' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files/' . $filename));
|
||||
|
||||
// check if key for admin not exists
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
'/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/keyfiles/' . $filename
|
||||
'/' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/keyfiles/' . $filename
|
||||
. '.key'));
|
||||
|
||||
// check if share key for admin not exists
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
'/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/share-keys/'
|
||||
. $filename . '.' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '.shareKey'));
|
||||
'/' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/share-keys/'
|
||||
. $filename . '.' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '.shareKey'));
|
||||
|
||||
// check that second file still exists
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files/' . $filename2));
|
||||
|
||||
// check that key for second file still exists
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/keyfiles/' . $filename2
|
||||
. '.key'));
|
||||
|
||||
// check that share key for second file still exists
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/share-keys/'
|
||||
. $filename2 . '.' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '.shareKey'));
|
||||
|
||||
// get files
|
||||
$trashFiles = $this->view->getDirectoryContent(
|
||||
'/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_trashbin/files/');
|
||||
'/' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_trashbin/files/');
|
||||
|
||||
$trashFileSuffix = null;
|
||||
// find created file with timestamp
|
||||
foreach ($trashFiles as $file) {
|
||||
if (strncmp($file['path'], $filename, strlen($filename))) {
|
||||
if (strpos($file['path'], $filename . '.d') !== false) {
|
||||
$path_parts = pathinfo($file['name']);
|
||||
$trashFileSuffix = $path_parts['extension'];
|
||||
}
|
||||
@@ -172,48 +210,77 @@ class Test_Encryption_Trashbin extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
// check if key for admin not exists
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_trashbin/keyfiles/' . $filename
|
||||
'/' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_trashbin/keyfiles/' . $filename
|
||||
. '.key.' . $trashFileSuffix));
|
||||
|
||||
// check if share key for admin not exists
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_trashbin/share-keys/' . $filename
|
||||
. '.' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '.shareKey.' . $trashFileSuffix));
|
||||
|
||||
// return filename for next test
|
||||
return $filename . '.' . $trashFileSuffix;
|
||||
'/' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_trashbin/share-keys/' . $filename
|
||||
. '.' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '.shareKey.' . $trashFileSuffix));
|
||||
}
|
||||
|
||||
/**
|
||||
* @medium
|
||||
* test restore file
|
||||
*
|
||||
* @depends testDeleteFile
|
||||
*/
|
||||
function testRestoreFile($filename) {
|
||||
function testRestoreFile() {
|
||||
// generate filename
|
||||
$filename = 'tmp-' . $this->getUniqueID() . '.txt';
|
||||
$filename2 = $filename . '.backup'; // a second file with similar name
|
||||
|
||||
// save file with content
|
||||
$cryptedFile = file_put_contents('crypt:///' . self::TEST_ENCRYPTION_TRASHBIN_USER1. '/files/'. $filename, $this->dataShort);
|
||||
$cryptedFile2 = file_put_contents('crypt:///' . self::TEST_ENCRYPTION_TRASHBIN_USER1. '/files/'. $filename2, $this->dataShort);
|
||||
|
||||
// delete both files
|
||||
\OC\Files\Filesystem::unlink($filename);
|
||||
\OC\Files\Filesystem::unlink($filename2);
|
||||
|
||||
$trashFiles = $this->view->getDirectoryContent('/' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_trashbin/files/');
|
||||
|
||||
$trashFileSuffix = null;
|
||||
$trashFileSuffix2 = null;
|
||||
// find created file with timestamp
|
||||
foreach ($trashFiles as $file) {
|
||||
if (strpos($file['path'], $filename . '.d') !== false) {
|
||||
$path_parts = pathinfo($file['name']);
|
||||
$trashFileSuffix = $path_parts['extension'];
|
||||
}
|
||||
}
|
||||
|
||||
// prepare file information
|
||||
$path_parts = pathinfo($filename);
|
||||
$trashFileSuffix = $path_parts['extension'];
|
||||
$timestamp = str_replace('d', '', $trashFileSuffix);
|
||||
$fileNameWithoutSuffix = str_replace('.' . $trashFileSuffix, '', $filename);
|
||||
|
||||
// restore file
|
||||
$this->assertTrue(\OCA\Files_Trashbin\Trashbin::restore($filename, $fileNameWithoutSuffix, $timestamp));
|
||||
// restore first file
|
||||
$this->assertTrue(\OCA\Files_Trashbin\Trashbin::restore($filename . '.' . $trashFileSuffix, $filename, $timestamp));
|
||||
|
||||
// check if file exists
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files/' . $fileNameWithoutSuffix));
|
||||
'/' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files/' . $filename));
|
||||
|
||||
// check if key for admin exists
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/keyfiles/'
|
||||
. $fileNameWithoutSuffix . '.key'));
|
||||
'/' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/keyfiles/'
|
||||
. $filename . '.key'));
|
||||
|
||||
// check if share key for admin exists
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/share-keys/'
|
||||
. $fileNameWithoutSuffix . '.' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '.shareKey'));
|
||||
'/' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/share-keys/'
|
||||
. $filename . '.' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '.shareKey'));
|
||||
|
||||
// check that second file was NOT restored
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
'/' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files/' . $filename2));
|
||||
|
||||
// check if key for admin exists
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
'/' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/keyfiles/'
|
||||
. $filename2 . '.key'));
|
||||
|
||||
// check if share key for admin exists
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
'/' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/share-keys/'
|
||||
. $filename2 . '.' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '.shareKey'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -223,7 +290,7 @@ class Test_Encryption_Trashbin extends \PHPUnit_Framework_TestCase {
|
||||
function testPermanentDeleteFile() {
|
||||
|
||||
// generate filename
|
||||
$filename = 'tmp-' . uniqid() . '.txt';
|
||||
$filename = 'tmp-' . $this->getUniqueID() . '.txt';
|
||||
|
||||
// save file with content
|
||||
$cryptedFile = file_put_contents('crypt:///' .$this->userId. '/files/' . $filename, $this->dataShort);
|
||||
@@ -233,30 +300,30 @@ class Test_Encryption_Trashbin extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
// check if key for admin exists
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/keyfiles/' . $filename
|
||||
'/' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/keyfiles/' . $filename
|
||||
. '.key'));
|
||||
|
||||
// check if share key for admin exists
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/share-keys/'
|
||||
. $filename . '.' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '.shareKey'));
|
||||
'/' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/share-keys/'
|
||||
. $filename . '.' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '.shareKey'));
|
||||
|
||||
// delete file
|
||||
\OC\FIles\Filesystem::unlink($filename);
|
||||
\OC\Files\Filesystem::unlink($filename);
|
||||
|
||||
// check if file not exists
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
'/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files/' . $filename));
|
||||
'/' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files/' . $filename));
|
||||
|
||||
// check if key for admin not exists
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
'/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/keyfiles/' . $filename
|
||||
'/' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/keyfiles/' . $filename
|
||||
. '.key'));
|
||||
|
||||
// check if share key for admin not exists
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
'/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/share-keys/'
|
||||
. $filename . '.' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '.shareKey'));
|
||||
'/' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_encryption/share-keys/'
|
||||
. $filename . '.' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '.shareKey'));
|
||||
|
||||
// find created file with timestamp
|
||||
$query = \OC_DB::prepare('SELECT `timestamp`,`type` FROM `*PREFIX*files_trash`'
|
||||
@@ -270,13 +337,13 @@ class Test_Encryption_Trashbin extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
// check if key for admin exists
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_trashbin/keyfiles/' . $filename
|
||||
'/' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_trashbin/keyfiles/' . $filename
|
||||
. '.key.' . $trashFileSuffix));
|
||||
|
||||
// check if share key for admin exists
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_trashbin/share-keys/' . $filename
|
||||
. '.' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '.shareKey.' . $trashFileSuffix));
|
||||
'/' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_trashbin/share-keys/' . $filename
|
||||
. '.' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '.shareKey.' . $trashFileSuffix));
|
||||
|
||||
// get timestamp from file
|
||||
$timestamp = str_replace('d', '', $trashFileSuffix);
|
||||
@@ -286,18 +353,18 @@ class Test_Encryption_Trashbin extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
// check if key for admin not exists
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
'/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_trashbin/files/' . $filename . '.'
|
||||
'/' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_trashbin/files/' . $filename . '.'
|
||||
. $trashFileSuffix));
|
||||
|
||||
// check if key for admin not exists
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
'/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_trashbin/keyfiles/' . $filename
|
||||
'/' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_trashbin/keyfiles/' . $filename
|
||||
. '.key.' . $trashFileSuffix));
|
||||
|
||||
// check if share key for admin not exists
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
'/' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_trashbin/share-keys/' . $filename
|
||||
. '.' . \Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1 . '.shareKey.' . $trashFileSuffix));
|
||||
'/' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '/files_trashbin/share-keys/' . $filename
|
||||
. '.' . self::TEST_ENCRYPTION_TRASHBIN_USER1 . '.shareKey.' . $trashFileSuffix));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,9 +19,12 @@ use OCA\Encryption;
|
||||
/**
|
||||
* Class Test_Encryption_Util
|
||||
*/
|
||||
class Test_Encryption_Util extends \PHPUnit_Framework_TestCase {
|
||||
class Test_Encryption_Util extends \OCA\Files_Encryption\Tests\TestCase {
|
||||
|
||||
const TEST_ENCRYPTION_UTIL_USER1 = "test-util-user1";
|
||||
const TEST_ENCRYPTION_UTIL_USER2 = "test-util-user2";
|
||||
const TEST_ENCRYPTION_UTIL_GROUP1 = "test-util-group1";
|
||||
const TEST_ENCRYPTION_UTIL_GROUP2 = "test-util-group2";
|
||||
const TEST_ENCRYPTION_UTIL_LEGACY_USER = "test-legacy-user";
|
||||
|
||||
public $userId;
|
||||
@@ -46,29 +49,35 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase {
|
||||
public $stateFilesTrashbin;
|
||||
|
||||
public static function setUpBeforeClass() {
|
||||
parent::setUpBeforeClass();
|
||||
|
||||
// reset backend
|
||||
\OC_User::clearBackends();
|
||||
\OC_User::useBackend('database');
|
||||
|
||||
// Filesystem related hooks
|
||||
\OCA\Encryption\Helper::registerFilesystemHooks();
|
||||
|
||||
// clear and register hooks
|
||||
\OC_FileProxy::clearProxies();
|
||||
\OC_FileProxy::register(new OCA\Encryption\Proxy());
|
||||
self::setupHooks();
|
||||
|
||||
// create test user
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Util::TEST_ENCRYPTION_UTIL_USER1, true);
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Util::TEST_ENCRYPTION_UTIL_LEGACY_USER, true);
|
||||
self::loginHelper(self::TEST_ENCRYPTION_UTIL_USER1, true);
|
||||
self::loginHelper(self::TEST_ENCRYPTION_UTIL_USER2, true);
|
||||
self::loginHelper(self::TEST_ENCRYPTION_UTIL_LEGACY_USER, true);
|
||||
|
||||
// create groups
|
||||
\OC_Group::createGroup(self::TEST_ENCRYPTION_UTIL_GROUP1);
|
||||
\OC_Group::createGroup(self::TEST_ENCRYPTION_UTIL_GROUP2);
|
||||
|
||||
// add user 1 to group1
|
||||
\OC_Group::addToGroup(self::TEST_ENCRYPTION_UTIL_USER1, self::TEST_ENCRYPTION_UTIL_GROUP1);
|
||||
}
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
function setUp() {
|
||||
// login user
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Util::TEST_ENCRYPTION_UTIL_USER1);
|
||||
\OC_User::setUserId(\Test_Encryption_Util::TEST_ENCRYPTION_UTIL_USER1);
|
||||
$this->userId = \Test_Encryption_Util::TEST_ENCRYPTION_UTIL_USER1;
|
||||
$this->pass = \Test_Encryption_Util::TEST_ENCRYPTION_UTIL_USER1;
|
||||
self::loginHelper(self::TEST_ENCRYPTION_UTIL_USER1);
|
||||
\OC_User::setUserId(self::TEST_ENCRYPTION_UTIL_USER1);
|
||||
$this->userId = self::TEST_ENCRYPTION_UTIL_USER1;
|
||||
$this->pass = self::TEST_ENCRYPTION_UTIL_USER1;
|
||||
|
||||
// set content for encrypting / decrypting in tests
|
||||
$this->dataUrl = __DIR__ . '/../lib/crypt.php';
|
||||
@@ -103,7 +112,7 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase {
|
||||
\OC_App::disable('files_trashbin');
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
protected function tearDown() {
|
||||
// reset app files_trashbin
|
||||
if ($this->stateFilesTrashbin) {
|
||||
OC_App::enable('files_trashbin');
|
||||
@@ -111,12 +120,38 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase {
|
||||
else {
|
||||
OC_App::disable('files_trashbin');
|
||||
}
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass() {
|
||||
// cleanup test user
|
||||
\OC_User::deleteUser(\Test_Encryption_Util::TEST_ENCRYPTION_UTIL_USER1);
|
||||
\OC_User::deleteUser(\Test_Encryption_Util::TEST_ENCRYPTION_UTIL_LEGACY_USER);
|
||||
\OC_User::deleteUser(self::TEST_ENCRYPTION_UTIL_USER1);
|
||||
\OC_User::deleteUser(self::TEST_ENCRYPTION_UTIL_USER2);
|
||||
\OC_User::deleteUser(self::TEST_ENCRYPTION_UTIL_LEGACY_USER);
|
||||
|
||||
//cleanup groups
|
||||
\OC_Group::deleteGroup(self::TEST_ENCRYPTION_UTIL_GROUP1);
|
||||
\OC_Group::deleteGroup(self::TEST_ENCRYPTION_UTIL_GROUP2);
|
||||
|
||||
\OC_Hook::clear();
|
||||
\OC_FileProxy::clearProxies();
|
||||
|
||||
// Delete keys in /data/
|
||||
$view = new \OC\Files\View('/');
|
||||
$view->rmdir('public-keys');
|
||||
$view->rmdir('owncloud_private_key');
|
||||
|
||||
parent::tearDownAfterClass();
|
||||
}
|
||||
|
||||
public static function setupHooks() {
|
||||
// Filesystem related hooks
|
||||
\OCA\Encryption\Helper::registerFilesystemHooks();
|
||||
|
||||
// clear and register hooks
|
||||
\OC_FileProxy::clearProxies();
|
||||
\OC_FileProxy::register(new OCA\Encryption\Proxy());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -144,8 +179,8 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
self::loginHelper($this->userId);
|
||||
|
||||
$unencryptedFile = '/tmpUnencrypted-' . uniqid() . '.txt';
|
||||
$encryptedFile = '/tmpEncrypted-' . uniqid() . '.txt';
|
||||
$unencryptedFile = '/tmpUnencrypted-' . $this->getUniqueID() . '.txt';
|
||||
$encryptedFile = '/tmpEncrypted-' . $this->getUniqueID() . '.txt';
|
||||
|
||||
// Disable encryption proxy to write a unencrypted file
|
||||
$proxyStatus = \OC_FileProxy::$enabled;
|
||||
@@ -252,9 +287,9 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase {
|
||||
*/
|
||||
function testGetUidAndFilename() {
|
||||
|
||||
\OC_User::setUserId(\Test_Encryption_Util::TEST_ENCRYPTION_UTIL_USER1);
|
||||
\OC_User::setUserId(self::TEST_ENCRYPTION_UTIL_USER1);
|
||||
|
||||
$filename = '/tmp-' . uniqid() . '.test';
|
||||
$filename = '/tmp-' . $this->getUniqueID() . '.test';
|
||||
|
||||
// Disable encryption proxy to prevent recursive calls
|
||||
$proxyStatus = \OC_FileProxy::$enabled;
|
||||
@@ -269,7 +304,7 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
list($fileOwnerUid, $file) = $util->getUidAndFilename($filename);
|
||||
|
||||
$this->assertEquals(\Test_Encryption_Util::TEST_ENCRYPTION_UTIL_USER1, $fileOwnerUid);
|
||||
$this->assertEquals(self::TEST_ENCRYPTION_UTIL_USER1, $fileOwnerUid);
|
||||
|
||||
$this->assertEquals($file, $filename);
|
||||
|
||||
@@ -280,9 +315,9 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase {
|
||||
< * Test that data that is read by the crypto stream wrapper
|
||||
*/
|
||||
function testGetFileSize() {
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Util::TEST_ENCRYPTION_UTIL_USER1);
|
||||
self::loginHelper(self::TEST_ENCRYPTION_UTIL_USER1);
|
||||
|
||||
$filename = 'tmp-' . uniqid();
|
||||
$filename = 'tmp-' . $this->getUniqueID();
|
||||
$externalFilename = '/' . $this->userId . '/files/' . $filename;
|
||||
|
||||
// Test for 0 byte files
|
||||
@@ -306,7 +341,7 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
function testEncryptAll() {
|
||||
|
||||
$filename = "/encryptAll" . uniqid() . ".txt";
|
||||
$filename = "/encryptAll" . $this->getUniqueID() . ".txt";
|
||||
$util = new Encryption\Util($this->view, $this->userId);
|
||||
|
||||
// disable encryption to upload a unencrypted file
|
||||
@@ -337,7 +372,7 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
function testDecryptAll() {
|
||||
|
||||
$filename = "/decryptAll" . uniqid() . ".txt";
|
||||
$filename = "/decryptAll" . $this->getUniqueID() . ".txt";
|
||||
$datadir = \OC_Config::getValue('datadirectory', \OC::$SERVERROOT . '/data/');
|
||||
$userdir = $datadir . '/' . $this->userId . '/files/';
|
||||
|
||||
@@ -411,11 +446,53 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* test if all keys get moved to the backup folder correctly
|
||||
*/
|
||||
function testBackupAllKeys() {
|
||||
self::loginHelper(self::TEST_ENCRYPTION_UTIL_USER1);
|
||||
|
||||
// create some dummy key files
|
||||
$encPath = '/' . self::TEST_ENCRYPTION_UTIL_USER1 . '/files_encryption';
|
||||
$this->view->file_put_contents($encPath . '/keyfiles/foo.key', 'key');
|
||||
$this->view->file_put_contents($encPath . '/share-keys/foo.user1.shareKey', 'share key');
|
||||
|
||||
$util = new \OCA\Encryption\Util($this->view, self::TEST_ENCRYPTION_UTIL_USER1);
|
||||
|
||||
$util->backupAllKeys('testing');
|
||||
|
||||
$encFolderContent = $this->view->getDirectoryContent($encPath);
|
||||
|
||||
$backupPath = '';
|
||||
foreach ($encFolderContent as $c) {
|
||||
$name = $c['name'];
|
||||
if (substr($name, 0, strlen('backup')) === 'backup') {
|
||||
$backupPath = $encPath . '/'. $c['name'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$this->assertTrue($backupPath !== '');
|
||||
|
||||
// check backupDir Content
|
||||
$this->assertTrue($this->view->is_dir($backupPath . '/keyfiles'));
|
||||
$this->assertTrue($this->view->is_dir($backupPath . '/share-keys'));
|
||||
$this->assertTrue($this->view->file_exists($backupPath . '/keyfiles/foo.key'));
|
||||
$this->assertTrue($this->view->file_exists($backupPath . '/share-keys/foo.user1.shareKey'));
|
||||
$this->assertTrue($this->view->file_exists($backupPath . '/' . self::TEST_ENCRYPTION_UTIL_USER1 . '.private.key'));
|
||||
$this->assertTrue($this->view->file_exists($backupPath . '/' . self::TEST_ENCRYPTION_UTIL_USER1 . '.public.key'));
|
||||
|
||||
//cleanup
|
||||
$this->view->deleteAll($backupPath);
|
||||
$this->view->unlink($encPath . '/keyfiles/foo.key', 'key');
|
||||
$this->view->unlink($encPath . '/share-keys/foo.user1.shareKey', 'share key');
|
||||
}
|
||||
|
||||
|
||||
function testDescryptAllWithBrokenFiles() {
|
||||
|
||||
$file1 = "/decryptAll1" . uniqid() . ".txt";
|
||||
$file2 = "/decryptAll2" . uniqid() . ".txt";
|
||||
$file1 = "/decryptAll1" . $this->getUniqueID() . ".txt";
|
||||
$file2 = "/decryptAll2" . $this->getUniqueID() . ".txt";
|
||||
|
||||
$util = new Encryption\Util($this->view, $this->userId);
|
||||
|
||||
@@ -537,6 +614,66 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase {
|
||||
$this->assertTrue($found);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataProviderFortestIsMountPointApplicableToUser
|
||||
*/
|
||||
function testIsMountPointApplicableToUser($mount, $expectedResult) {
|
||||
self::loginHelper(self::TEST_ENCRYPTION_UTIL_USER1);
|
||||
$dummyClass = new DummyUtilClass($this->view, self::TEST_ENCRYPTION_UTIL_USER1);
|
||||
$result = $dummyClass->testIsMountPointApplicableToUser($mount);
|
||||
|
||||
$this->assertSame($expectedResult, $result);
|
||||
}
|
||||
|
||||
function dataProviderFortestIsMountPointApplicableToUser() {
|
||||
return array(
|
||||
array(array('applicable' => array('groups' => array(), 'users' => array(self::TEST_ENCRYPTION_UTIL_USER1))), true),
|
||||
array(array('applicable' => array('groups' => array(), 'users' => array(self::TEST_ENCRYPTION_UTIL_USER2))), false),
|
||||
array(array('applicable' => array('groups' => array(self::TEST_ENCRYPTION_UTIL_GROUP1), 'users' => array())), true),
|
||||
array(array('applicable' => array('groups' => array(self::TEST_ENCRYPTION_UTIL_GROUP1), 'users' => array(self::TEST_ENCRYPTION_UTIL_USER2))), true),
|
||||
array(array('applicable' => array('groups' => array(self::TEST_ENCRYPTION_UTIL_GROUP2), 'users' => array(self::TEST_ENCRYPTION_UTIL_USER2))), false),
|
||||
array(array('applicable' => array('groups' => array(self::TEST_ENCRYPTION_UTIL_GROUP2), 'users' => array(self::TEST_ENCRYPTION_UTIL_USER2, 'all'))), true),
|
||||
array(array('applicable' => array('groups' => array(self::TEST_ENCRYPTION_UTIL_GROUP2), 'users' => array('all'))), true),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that filterShareReadyUsers() returns the correct list of
|
||||
* users that are ready or not ready for encryption
|
||||
*/
|
||||
public function testFilterShareReadyUsers() {
|
||||
$appConfig = \OC::$server->getAppConfig();
|
||||
|
||||
$publicShareKeyId = $appConfig->getValue('files_encryption', 'publicShareKeyId');
|
||||
$recoveryKeyId = $appConfig->getValue('files_encryption', 'recoveryKeyId');
|
||||
|
||||
$usersToTest = array(
|
||||
'readyUser',
|
||||
'notReadyUser',
|
||||
'nonExistingUser',
|
||||
$publicShareKeyId,
|
||||
$recoveryKeyId,
|
||||
);
|
||||
\Test_Encryption_Util::loginHelper('readyUser', true);
|
||||
\Test_Encryption_Util::loginHelper('notReadyUser', true);
|
||||
// delete encryption dir to make it not ready
|
||||
$this->view->unlink('notReadyUser/files_encryption/');
|
||||
|
||||
// login as user1
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Util::TEST_ENCRYPTION_UTIL_USER1);
|
||||
|
||||
$result = $this->util->filterShareReadyUsers($usersToTest);
|
||||
$this->assertEquals(
|
||||
array('readyUser', $publicShareKeyId, $recoveryKeyId),
|
||||
$result['ready']
|
||||
);
|
||||
$this->assertEquals(
|
||||
array('notReadyUser', 'nonExistingUser'),
|
||||
$result['unready']
|
||||
);
|
||||
\OC_User::deleteUser('readyUser');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $user
|
||||
* @param bool $create
|
||||
@@ -570,7 +707,7 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
public static function logoutHelper() {
|
||||
\OC_Util::tearDownFS();
|
||||
\OC_User::setUserId('');
|
||||
\OC_User::setUserId(false);
|
||||
\OC\Files\Filesystem::tearDown();
|
||||
}
|
||||
|
||||
@@ -587,3 +724,12 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* dummy class extends \OCA\Encryption\Util to access protected methods for testing
|
||||
*/
|
||||
class DummyUtilClass extends \OCA\Encryption\Util {
|
||||
public function testIsMountPointApplicableToUser($mount) {
|
||||
return $this->isMountPointApplicableToUser($mount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ require_once __DIR__ . '/../lib/proxy.php';
|
||||
require_once __DIR__ . '/../lib/stream.php';
|
||||
require_once __DIR__ . '/../lib/util.php';
|
||||
require_once __DIR__ . '/../appinfo/app.php';
|
||||
require_once __DIR__ . '/util.php';
|
||||
|
||||
use OCA\Encryption;
|
||||
|
||||
@@ -36,7 +35,7 @@ use OCA\Encryption;
|
||||
*
|
||||
* this class provide basic webdav tests for PUT,GET and DELETE
|
||||
*/
|
||||
class Test_Encryption_Webdav extends \PHPUnit_Framework_TestCase {
|
||||
class Test_Encryption_Webdav extends \OCA\Files_Encryption\Tests\TestCase {
|
||||
|
||||
const TEST_ENCRYPTION_WEBDAV_USER1 = "test-webdav-user1";
|
||||
|
||||
@@ -52,6 +51,8 @@ class Test_Encryption_Webdav extends \PHPUnit_Framework_TestCase {
|
||||
private $storage;
|
||||
|
||||
public static function setUpBeforeClass() {
|
||||
parent::setUpBeforeClass();
|
||||
|
||||
// reset backend
|
||||
\OC_User::clearBackends();
|
||||
\OC_User::useBackend('database');
|
||||
@@ -67,11 +68,13 @@ class Test_Encryption_Webdav extends \PHPUnit_Framework_TestCase {
|
||||
\OC_FileProxy::register(new OCA\Encryption\Proxy());
|
||||
|
||||
// create test user
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Webdav::TEST_ENCRYPTION_WEBDAV_USER1, true);
|
||||
self::loginHelper(\Test_Encryption_Webdav::TEST_ENCRYPTION_WEBDAV_USER1, true);
|
||||
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// reset backend
|
||||
\OC_User::useBackend('database');
|
||||
|
||||
@@ -93,21 +96,33 @@ class Test_Encryption_Webdav extends \PHPUnit_Framework_TestCase {
|
||||
\OC_App::disable('files_trashbin');
|
||||
|
||||
// create test user
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Webdav::TEST_ENCRYPTION_WEBDAV_USER1);
|
||||
self::loginHelper(\Test_Encryption_Webdav::TEST_ENCRYPTION_WEBDAV_USER1);
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
protected function tearDown() {
|
||||
// reset app files_trashbin
|
||||
if ($this->stateFilesTrashbin) {
|
||||
OC_App::enable('files_trashbin');
|
||||
} else {
|
||||
OC_App::disable('files_trashbin');
|
||||
}
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass() {
|
||||
// cleanup test user
|
||||
\OC_User::deleteUser(\Test_Encryption_Webdav::TEST_ENCRYPTION_WEBDAV_USER1);
|
||||
|
||||
\OC_Hook::clear();
|
||||
\OC_FileProxy::clearProxies();
|
||||
|
||||
// Delete keys in /data/
|
||||
$view = new \OC\Files\View('/');
|
||||
$view->rmdir('public-keys');
|
||||
$view->rmdir('owncloud_private_key');
|
||||
|
||||
parent::tearDownAfterClass();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,7 +131,7 @@ class Test_Encryption_Webdav extends \PHPUnit_Framework_TestCase {
|
||||
function testWebdavPUT() {
|
||||
|
||||
// generate filename
|
||||
$filename = '/tmp-' . uniqid() . '.txt';
|
||||
$filename = '/tmp-' . $this->getUniqueID() . '.txt';
|
||||
|
||||
// set server vars
|
||||
$_SERVER['REQUEST_METHOD'] = 'OPTIONS';
|
||||
|
||||
@@ -10,10 +10,17 @@ if ($_POST['isPersonal'] == 'true') {
|
||||
OCP\JSON::checkAdminUser();
|
||||
$isPersonal = false;
|
||||
}
|
||||
$status = OC_Mount_Config::addMountPoint($_POST['mountPoint'],
|
||||
$_POST['class'],
|
||||
$_POST['classOptions'],
|
||||
$_POST['mountType'],
|
||||
$_POST['applicable'],
|
||||
$isPersonal);
|
||||
|
||||
$mountPoint = $_POST['mountPoint'];
|
||||
$oldMountPoint = $_POST['oldMountPoint'];
|
||||
$class = $_POST['class'];
|
||||
$options = $_POST['classOptions'];
|
||||
$type = $_POST['mountType'];
|
||||
$applicable = $_POST['applicable'];
|
||||
|
||||
if ($oldMountPoint and $oldMountPoint !== $mountPoint) {
|
||||
OC_Mount_Config::removeMountPoint($oldMountPoint, $type, $applicable, $isPersonal);
|
||||
}
|
||||
|
||||
$status = OC_Mount_Config::addMountPoint($mountPoint, $class, $options, $type, $applicable, $isPersonal);
|
||||
OCP\JSON::success(array('data' => array('message' => $status)));
|
||||
|
||||
26
apps/files_external/ajax/applicable.php
Normal file
26
apps/files_external/ajax/applicable.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
OCP\JSON::checkAppEnabled('files_external');
|
||||
OCP\JSON::callCheck();
|
||||
|
||||
OCP\JSON::checkAdminUser();
|
||||
|
||||
$pattern = '';
|
||||
$limit = null;
|
||||
$offset = null;
|
||||
if (isset($_GET['pattern'])) {
|
||||
$pattern = $_GET['pattern'];
|
||||
}
|
||||
if (isset($_GET['limit'])) {
|
||||
$limit = $_GET['limit'];
|
||||
}
|
||||
if (isset($_GET['offset'])) {
|
||||
$offset = $_GET['offset'];
|
||||
}
|
||||
|
||||
$groups = \OC_Group::getGroups($pattern, $limit, $offset);
|
||||
$users = \OCP\User::getDisplayNames($pattern, $limit, $offset);
|
||||
|
||||
$results = array('groups' => $groups, 'users' => $users);
|
||||
|
||||
\OCP\JSON::success($results);
|
||||
@@ -2,12 +2,20 @@
|
||||
<info>
|
||||
<id>files_external</id>
|
||||
<name>External storage support</name>
|
||||
<description>Mount external storage sources</description>
|
||||
<description>
|
||||
This application enables administrators to configure connections to external storage providers, such as FTP servers, S3 or SWIFT object stores, Google Drive, Dropbox, other ownCloud servers, WebDAV servers and more. Administrators can choose in the GUI which type of storage to enable, and can mount these storage locations for a user, a group, or the entire system. Users will see a new folder appear in their root ownCloud directory, and then can then access and use it like any other ownCloud folder. External Storage also allows users to share files stored in these external location. In these cases, the credentials for the owner of the file are used then the recipient requests the file from external storage, thereby ensuring that the recipient can get at the file that was shared.
|
||||
In addition to the GUI, it is possible to configure external storage manually at the command line. This option provides the advanced user with more flexibility for configuring bulk external storage mounts, as well as setting mount priorities. More information is available in the External Storage GUI documentation and the External Storage Configuration File documentation.
|
||||
|
||||
</description>
|
||||
<licence>AGPL</licence>
|
||||
<author>Robin Appelman, Michael Gapczynski, Vincent Petry</author>
|
||||
<requiremin>4.93</requiremin>
|
||||
<shipped>true</shipped>
|
||||
<documentation>
|
||||
<admin>admin-external-storage</admin>
|
||||
</documentation>
|
||||
<types>
|
||||
<filesystem/>
|
||||
</types>
|
||||
<ocsid>166048</ocsid>
|
||||
</info>
|
||||
|
||||
@@ -20,8 +20,10 @@
|
||||
*
|
||||
*/
|
||||
|
||||
OC_API::register('get',
|
||||
'/apps/files_external/api/v1/mounts',
|
||||
array('\OCA\Files\External\Api', 'getUserMounts'),
|
||||
'files_external');
|
||||
$this->create('files_external_list_applicable', '/applicable')->actionInclude('files_external/ajax/applicable.php');
|
||||
|
||||
OC_API::register('get',
|
||||
'/apps/files_external/api/v1/mounts',
|
||||
array('\OCA\Files\External\Api', 'getUserMounts'),
|
||||
'files_external');
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.2
|
||||
0.2.2
|
||||
@@ -10,7 +10,20 @@ td.remove>img { visibility:hidden; padding-top:7px; }
|
||||
tr:hover>td.remove>img { visibility:visible; cursor:pointer; }
|
||||
#addMountPoint>td { border:none; }
|
||||
#addMountPoint>td.applicable { visibility:hidden; }
|
||||
#selectBackend { margin-left:-10px; }
|
||||
|
||||
#selectBackend {
|
||||
margin-left: -10px;
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
#externalStorage td.configuration,
|
||||
#externalStorage td.backend {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
#externalStorage td.configuration input.added {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
#externalStorage label > input[type="checkbox"] {
|
||||
margin-right: 3px;
|
||||
@@ -29,3 +42,19 @@ tr:hover>td.remove>img { visibility:visible; cursor:pointer; }
|
||||
#userMountingBackends {
|
||||
padding-left: 25px;
|
||||
}
|
||||
|
||||
#body-settings .select2-results .select2-result-label {
|
||||
height: 32px;
|
||||
padding: 3px;
|
||||
}
|
||||
.select2-results .select2-result-label .avatardiv {
|
||||
display:inline-block;
|
||||
}
|
||||
.select2-results .select2-result-label .avatardiv + span {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.select2-results .select2-result-label .avatardiv[data-type="group"] + span {
|
||||
vertical-align: top;
|
||||
top: 6px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@@ -65,7 +65,6 @@
|
||||
},
|
||||
|
||||
reload: function() {
|
||||
var self = this;
|
||||
this.showMask();
|
||||
if (this._reloadCall) {
|
||||
this._reloadCall.abort();
|
||||
@@ -78,14 +77,10 @@
|
||||
type: 'GET',
|
||||
beforeSend: function(xhr) {
|
||||
xhr.setRequestHeader('OCS-APIREQUEST', 'true');
|
||||
},
|
||||
error: function(result) {
|
||||
self.reloadCallback(result);
|
||||
},
|
||||
success: function(result) {
|
||||
self.reloadCallback(result);
|
||||
}
|
||||
});
|
||||
var callBack = this.reloadCallback.bind(this);
|
||||
return this._reloadCall.then(callBack, callBack);
|
||||
},
|
||||
|
||||
reloadCallback: function(result) {
|
||||
@@ -109,6 +104,7 @@
|
||||
_makeFiles: function(data) {
|
||||
var files = _.map(data, function(fileData) {
|
||||
fileData.icon = OC.imagePath('core', 'filetypes/folder-external');
|
||||
fileData.mountType = 'external';
|
||||
return fileData;
|
||||
});
|
||||
|
||||
|
||||
@@ -11,9 +11,18 @@ function updateStatus(statusEl, result){
|
||||
}
|
||||
}
|
||||
|
||||
function getSelection($row) {
|
||||
var values = $row.find('.applicableUsers').select2('val');
|
||||
if (!values || values.length === 0) {
|
||||
values = ['all'];
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
OC.MountConfig={
|
||||
saveStorage:function(tr, callback) {
|
||||
var mountPoint = $(tr).find('.mountPoint input').val();
|
||||
var oldMountPoint = $(tr).find('.mountPoint input').data('mountpoint');
|
||||
if (mountPoint == '') {
|
||||
return false;
|
||||
}
|
||||
@@ -41,10 +50,7 @@ OC.MountConfig={
|
||||
}
|
||||
});
|
||||
if ($('#externalStorage').data('admin') === true) {
|
||||
var multiselect = $(tr).find('.chzn-select').val();
|
||||
if (multiselect == null) {
|
||||
return false;
|
||||
}
|
||||
var multiselect = getSelection($(tr));
|
||||
}
|
||||
if (addMountPoint) {
|
||||
var status = false;
|
||||
@@ -80,9 +86,11 @@ OC.MountConfig={
|
||||
classOptions: classOptions,
|
||||
mountType: mountType,
|
||||
applicable: applicable,
|
||||
isPersonal: isPersonal
|
||||
isPersonal: isPersonal,
|
||||
oldMountPoint: oldMountPoint
|
||||
},
|
||||
success: function(result) {
|
||||
$(tr).find('.mountPoint input').data('mountpoint', mountPoint);
|
||||
status = updateStatus(statusSpan, result);
|
||||
if (callback) {
|
||||
callback(status);
|
||||
@@ -139,9 +147,11 @@ OC.MountConfig={
|
||||
classOptions: classOptions,
|
||||
mountType: mountType,
|
||||
applicable: applicable,
|
||||
isPersonal: isPersonal
|
||||
isPersonal: isPersonal,
|
||||
oldMountPoint: oldMountPoint
|
||||
},
|
||||
success: function(result) {
|
||||
$(tr).find('.mountPoint input').data('mountpoint', mountPoint);
|
||||
status = updateStatus(statusSpan, result);
|
||||
if (callback) {
|
||||
callback(status);
|
||||
@@ -161,8 +171,139 @@ OC.MountConfig={
|
||||
};
|
||||
|
||||
$(document).ready(function() {
|
||||
$('.chzn-select').chosen();
|
||||
//initialize hidden input field with list of users and groups
|
||||
$('#externalStorage').find('tr:not(#addMountPoint)').each(function(i,tr) {
|
||||
var applicable = $(tr).find('.applicable');
|
||||
if (applicable.length > 0) {
|
||||
var groups = applicable.data('applicable-groups');
|
||||
var groupsId = [];
|
||||
$.each(groups, function () {
|
||||
groupsId.push(this+"(group)");
|
||||
});
|
||||
var users = applicable.data('applicable-users');
|
||||
if (users.indexOf('all') > -1) {
|
||||
$(tr).find('.applicableUsers').val('');
|
||||
} else {
|
||||
$(tr).find('.applicableUsers').val(groupsId.concat(users).join(','));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var userListLimit = 30;
|
||||
function addSelect2 ($elements) {
|
||||
if ($elements.length > 0) {
|
||||
$elements.select2({
|
||||
placeholder: t('files_external', 'All users. Type to select user or group.'),
|
||||
allowClear: true,
|
||||
multiple: true,
|
||||
//minimumInputLength: 1,
|
||||
ajax: {
|
||||
url: OC.generateUrl('apps/files_external/applicable'),
|
||||
dataType: 'json',
|
||||
quietMillis: 100,
|
||||
data: function (term, page) { // page is the one-based page number tracked by Select2
|
||||
return {
|
||||
pattern: term, //search term
|
||||
limit: userListLimit, // page size
|
||||
offset: userListLimit*(page-1) // page number starts with 0
|
||||
};
|
||||
},
|
||||
results: function (data, page) {
|
||||
if (data.status === "success") {
|
||||
|
||||
var results = [];
|
||||
var userCount = 0; // users is an object
|
||||
|
||||
// add groups
|
||||
$.each(data.groups, function(i, group) {
|
||||
results.push({name:group+'(group)', displayname:group, type:'group' });
|
||||
});
|
||||
// add users
|
||||
$.each(data.users, function(id, user) {
|
||||
userCount++;
|
||||
results.push({name:id, displayname:user, type:'user' });
|
||||
});
|
||||
|
||||
|
||||
var more = (userCount >= userListLimit) || (data.groups.length >= userListLimit);
|
||||
return {results: results, more: more};
|
||||
} else {
|
||||
//FIXME add error handling
|
||||
}
|
||||
}
|
||||
},
|
||||
initSelection: function(element, callback) {
|
||||
|
||||
var promises = [];
|
||||
|
||||
var results = [];
|
||||
|
||||
$(element.val().split(",")).each(function (i,userId) {
|
||||
var def = new $.Deferred();
|
||||
promises.push(def.promise());
|
||||
|
||||
var pos = userId.indexOf('(group)');
|
||||
if (pos !== -1) {
|
||||
//add as group
|
||||
results.push({name:userId, displayname:userId.substr(0, pos), type:'group'});
|
||||
def.resolve();
|
||||
} else {
|
||||
$.ajax(OC.generateUrl('apps/files_external/applicable'), {
|
||||
data: {
|
||||
pattern: userId
|
||||
},
|
||||
dataType: "json"
|
||||
}).done(function(data) {
|
||||
if (data.status === "success") {
|
||||
if (data.users[userId]) {
|
||||
results.push({name:userId, displayname:data.users[userId], type:'user'});
|
||||
}
|
||||
def.resolve();
|
||||
} else {
|
||||
//FIXME add error handling
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
$.when.apply(undefined, promises).then(function(){
|
||||
callback(results);
|
||||
});
|
||||
},
|
||||
id: function(element) {
|
||||
return element.name;
|
||||
},
|
||||
formatResult: function (element) {
|
||||
var $result = $('<span><div class="avatardiv"/><span>'+escapeHTML(element.displayname)+'</span></span>');
|
||||
var $div = $result.find('.avatardiv')
|
||||
.attr('data-type', element.type)
|
||||
.attr('data-name', element.name)
|
||||
.attr('data-displayname', element.displayname);
|
||||
if (element.type === 'group') {
|
||||
var url = OC.imagePath('core','places/contacts-dark'); // TODO better group icon
|
||||
$div.html('<img width="32" height="32" src="'+url+'">');
|
||||
}
|
||||
return $result.get(0).outerHTML;
|
||||
},
|
||||
formatSelection: function (element) {
|
||||
if (element.type === 'group') {
|
||||
return '<span title="'+escapeHTML(element.name)+'" class="group">'+escapeHTML(element.displayname+' '+t('files_external', '(group)'))+'</span>';
|
||||
} else {
|
||||
return '<span title="'+escapeHTML(element.name)+'" class="user">'+escapeHTML(element.displayname)+'</span>';
|
||||
}
|
||||
},
|
||||
escapeMarkup: function (m) { return m; } // we escape the markup in formatResult and formatSelection
|
||||
}).on("select2-loaded", function() {
|
||||
$.each($(".avatardiv"), function(i, div) {
|
||||
$div = $(div);
|
||||
if ($div.data('type') === 'user') {
|
||||
$div.avatar($div.data('name'),32);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
addSelect2($('tr:not(#addMountPoint) .applicableUsers'));
|
||||
|
||||
$('#externalStorage').on('change', '#selectBackend', function() {
|
||||
var tr = $(this).parent().parent();
|
||||
$('#externalStorage tbody').append($(tr).clone());
|
||||
@@ -187,15 +328,15 @@ $(document).ready(function() {
|
||||
placeholder = placeholder.substring(1);
|
||||
}
|
||||
if (placeholder.indexOf('*') === 0) {
|
||||
var class_string = is_optional ? ' class="optional"' : '';
|
||||
td.append('<input type="password"' + class_string + ' data-parameter="'+parameter+'" placeholder="'+placeholder.substring(1)+'" />');
|
||||
var class_string = is_optional ? ' optional' : '';
|
||||
td.append('<input type="password" class="added' + class_string + '" data-parameter="'+parameter+'" placeholder="'+placeholder.substring(1)+'" />');
|
||||
} else if (placeholder.indexOf('!') === 0) {
|
||||
td.append('<label><input type="checkbox" data-parameter="'+parameter+'" />'+placeholder.substring(1)+'</label>');
|
||||
td.append('<label><input type="checkbox" class="added" data-parameter="'+parameter+'" />'+placeholder.substring(1)+'</label>');
|
||||
} else if (placeholder.indexOf('#') === 0) {
|
||||
td.append('<input type="hidden" data-parameter="'+parameter+'" />');
|
||||
td.append('<input type="hidden" class="added" data-parameter="'+parameter+'" />');
|
||||
} else {
|
||||
var class_string = is_optional ? ' class="optional"' : '';
|
||||
td.append('<input type="text"' + class_string + ' data-parameter="'+parameter+'" placeholder="'+placeholder+'" />');
|
||||
var class_string = is_optional ? ' optional' : '';
|
||||
td.append('<input type="text" class="added' + class_string + '" data-parameter="'+parameter+'" placeholder="'+placeholder+'" />');
|
||||
}
|
||||
});
|
||||
if (parameters['custom'] && $('#externalStorage tbody tr.'+backendClass.replace(/\\/g, '\\\\')).length == 1) {
|
||||
@@ -204,15 +345,11 @@ $(document).ready(function() {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
// Reset chosen
|
||||
var chosen = $(tr).find('.applicable select');
|
||||
chosen.parent().find('div').remove();
|
||||
chosen.removeAttr('id').removeClass('chzn-done').css({display:'inline-block'});
|
||||
chosen.chosen();
|
||||
$(tr).find('td').last().attr('class', 'remove');
|
||||
$(tr).find('td').last().removeAttr('style');
|
||||
$(tr).removeAttr('id');
|
||||
$(this).remove();
|
||||
addSelect2(tr.find('.applicableUsers'));
|
||||
});
|
||||
|
||||
function suggestMountPoint(defaultMountPoint) {
|
||||
@@ -265,8 +402,8 @@ $(document).ready(function() {
|
||||
OC.MountConfig.saveStorage($(this).parent().parent().parent());
|
||||
});
|
||||
|
||||
$('#externalStorage').on('change', '.applicable .chzn-select', function() {
|
||||
OC.MountConfig.saveStorage($(this).parent().parent());
|
||||
$('#externalStorage').on('change', '.applicable', function() {
|
||||
OC.MountConfig.saveStorage($(this).parent());
|
||||
});
|
||||
|
||||
$('#sslCertificate').on('click', 'td.remove>img', function() {
|
||||
@@ -283,20 +420,18 @@ $(document).ready(function() {
|
||||
|
||||
if ($('#externalStorage').data('admin') === true) {
|
||||
var isPersonal = false;
|
||||
var multiselect = $(tr).find('.chzn-select').val();
|
||||
if (multiselect != null) {
|
||||
$.each(multiselect, function(index, value) {
|
||||
var pos = value.indexOf('(group)');
|
||||
if (pos != -1) {
|
||||
var mountType = 'group';
|
||||
var applicable = value.substr(0, pos);
|
||||
} else {
|
||||
var mountType = 'user';
|
||||
var applicable = value;
|
||||
}
|
||||
$.post(OC.filePath('files_external', 'ajax', 'removeMountPoint.php'), { mountPoint: mountPoint, mountType: mountType, applicable: applicable, isPersonal: isPersonal });
|
||||
});
|
||||
}
|
||||
var multiselect = getSelection($(tr));
|
||||
$.each(multiselect, function(index, value) {
|
||||
var pos = value.indexOf('(group)');
|
||||
if (pos != -1) {
|
||||
var mountType = 'group';
|
||||
var applicable = value.substr(0, pos);
|
||||
} else {
|
||||
var mountType = 'user';
|
||||
var applicable = value;
|
||||
}
|
||||
$.post(OC.filePath('files_external', 'ajax', 'removeMountPoint.php'), { mountPoint: mountPoint, mountType: mountType, applicable: applicable, isPersonal: isPersonal });
|
||||
});
|
||||
} else {
|
||||
var mountType = 'user';
|
||||
var applicable = OC.currentUser;
|
||||
@@ -317,14 +452,14 @@ $(document).ready(function() {
|
||||
OC.AppConfig.setValue('files_external', 'allow_user_mounting', 'no');
|
||||
$('#userMountingBackends').addClass('hidden');
|
||||
}
|
||||
OC.msg.finishedSaving('#userMountingMsg', {status: 'success', data: {message: t('settings', 'Saved')}});
|
||||
OC.msg.finishedSaving('#userMountingMsg', {status: 'success', data: {message: t('files_external', 'Saved')}});
|
||||
});
|
||||
|
||||
$('input[name="allowUserMountingBackends\\[\\]"]').bind('change', function() {
|
||||
OC.msg.startSaving('#userMountingMsg');
|
||||
var userMountingBackends = $('input[name="allowUserMountingBackends\\[\\]"]:checked').map(function(){return $(this).val();}).get();
|
||||
OC.AppConfig.setValue('files_external', 'user_mounting_backends', userMountingBackends.join());
|
||||
OC.msg.finishedSaving('#userMountingMsg', {status: 'success', data: {message: t('settings', 'Saved')}});
|
||||
OC.msg.finishedSaving('#userMountingMsg', {status: 'success', data: {message: t('files_external', 'Saved')}});
|
||||
|
||||
// disable allowUserMounting
|
||||
if(userMountingBackends.length === 0) {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<?php
|
||||
$TRANSLATIONS = array(
|
||||
"Fetching request tokens failed. Verify that your Dropbox app key and secret are correct." => "La requête de récupération des jetons d’authentification a échoué. Veuillez vérifier votre App-Key Dropbox ainsi que le mot de passe.",
|
||||
"Fetching request tokens failed. Verify that your Dropbox app key and secret are correct." => "La récupération des jetons d’authentification a échoué. Veuillez vérifier votre clé d'application Dropbox ainsi que le mot de passe.",
|
||||
"Fetching access tokens failed. Verify that your Dropbox app key and secret are correct." => "La requête d’accès aux jetons d’authentification a échoué. Veuillez vérifier votre App-Key Dropbox ainsi que le mot de passe.",
|
||||
"Please provide a valid Dropbox app key and secret." => "Veuillez fournir une clé d'application (app key) ainsi qu'un mot de passe valides.",
|
||||
"Step 1 failed. Exception: %s" => "L’étape 1 a échoué. Erreur: %s",
|
||||
"Step 2 failed. Exception: %s" => "L’étape 2 a échoué. Erreur: %s",
|
||||
"Step 1 failed. Exception: %s" => "L’étape 1 a échoué. Erreur : %s",
|
||||
"Step 2 failed. Exception: %s" => "L’étape 2 a échoué. Erreur : %s",
|
||||
"External storage" => "Stockage externe",
|
||||
"Local" => "Local",
|
||||
"Location" => "Emplacement",
|
||||
@@ -26,7 +26,7 @@ $TRANSLATIONS = array(
|
||||
"Username" => "Nom d'utilisateur",
|
||||
"Password" => "Mot de passe",
|
||||
"Root" => "Root",
|
||||
"Secure ftps://" => "Sécuriser via ftps://",
|
||||
"Secure ftps://" => "Sécurisation ftps://",
|
||||
"Client ID" => "ID Client",
|
||||
"Client secret" => "Secret client",
|
||||
"OpenStack Object Storage" => "Object de Stockage OpenStack",
|
||||
@@ -40,7 +40,7 @@ $TRANSLATIONS = array(
|
||||
"URL of identity endpoint (required for OpenStack Object Storage)" => "URL du point d'accès d'identité (requis pour le stockage OpenStack)",
|
||||
"Timeout of HTTP requests in seconds (optional)" => "Temps maximal de requête HTTP en seconde (facultatif)",
|
||||
"Share" => "Partager",
|
||||
"SMB / CIFS using OC login" => "SMB / CIFS utilise le nom d'utilisateur OC",
|
||||
"SMB / CIFS using OC login" => "SMB / CIFS en utilisant les identifiants OC",
|
||||
"Username as share" => "Nom d'utilisateur du partage",
|
||||
"URL" => "URL",
|
||||
"Secure https://" => "Sécurisation https://",
|
||||
@@ -54,8 +54,8 @@ $TRANSLATIONS = array(
|
||||
"Saved" => "Sauvegarder",
|
||||
"<b>Note:</b> " => "<b>Attention :</b>",
|
||||
" and " => "et",
|
||||
"<b>Note:</b> The cURL support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "<b>Attention :</b> Le support de cURL de PHP n'est pas activé ou installé. Le montage de %s n'est pas possible. Contactez votre administrateur système pour l'installer.",
|
||||
"<b>Note:</b> The FTP support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "<b>Attention : </b> Le support FTP de PHP n'est pas activé ou installé. Le montage de %s n'est pas possible. Contactez votre administrateur système pour l'installer.",
|
||||
"<b>Note:</b> The cURL support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "<b>Attention :</b> La prise en charge de cURL par PHP n'est pas activée ou installée. Le montage de %s n'est pas possible. Contactez votre administrateur système pour l'installer.",
|
||||
"<b>Note:</b> The FTP support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "<b>Attention : </b> La prise en charge du FTP par PHP n'est pas activée ou installée. Le montage de %s n'est pas possible. Contactez votre administrateur système pour l'installer.",
|
||||
"<b>Note:</b> \"%s\" is not installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "<b>Attention : </b> \"%s\" n'est pas installé. Le montage de %s n'est pas possible. Contactez votre administrateur système pour l'installer.",
|
||||
"You don't have any external storages" => "Vous n'avez pas de support de stockage externe",
|
||||
"Name" => "Nom",
|
||||
@@ -72,7 +72,7 @@ $TRANSLATIONS = array(
|
||||
"Groups" => "Groupes",
|
||||
"Users" => "Utilisateurs",
|
||||
"Delete" => "Supprimer",
|
||||
"Enable User External Storage" => "Activer le stockage externe pour les utilisateurs",
|
||||
"Enable User External Storage" => "Autoriser les utilisateurs à ajouter des stockages externes",
|
||||
"Allow users to mount the following external storage" => "Autorise les utilisateurs à monter les stockage externes suivants",
|
||||
"SSL root certificates" => "Certificats racine SSL",
|
||||
"Import Root Certificate" => "Importer un certificat racine"
|
||||
|
||||
@@ -45,6 +45,10 @@ class AmazonS3 extends \OC\Files\Storage\Common {
|
||||
* @var array
|
||||
*/
|
||||
private static $tmpFiles = array();
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $params;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
@@ -53,9 +57,14 @@ class AmazonS3 extends \OC\Files\Storage\Common {
|
||||
* @var int
|
||||
*/
|
||||
private $timeout = 15;
|
||||
/**
|
||||
* @var int in seconds
|
||||
*/
|
||||
private $rescanDelay = 10;
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @return string correctly encoded path
|
||||
*/
|
||||
private function normalizePath($path) {
|
||||
$path = trim($path, '/');
|
||||
@@ -67,67 +76,92 @@ class AmazonS3 extends \OC\Files\Storage\Common {
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* when running the tests wait to let the buckets catch up
|
||||
*/
|
||||
private function testTimeout() {
|
||||
if ($this->test) {
|
||||
sleep($this->timeout);
|
||||
}
|
||||
}
|
||||
|
||||
private function isRoot($path) {
|
||||
return $path === '.';
|
||||
}
|
||||
|
||||
private function cleanKey($path) {
|
||||
if ($this->isRoot($path)) {
|
||||
return '/';
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
public function __construct($params) {
|
||||
if (!isset($params['key']) || !isset($params['secret']) || !isset($params['bucket'])) {
|
||||
if (empty($params['key']) || empty($params['secret']) || empty($params['bucket'])) {
|
||||
throw new \Exception("Access Key, Secret and Bucket have to be configured.");
|
||||
}
|
||||
|
||||
$this->id = 'amazon::' . $params['key'] . md5($params['secret']);
|
||||
$this->id = 'amazon::' . $params['bucket'];
|
||||
$this->updateLegacyId($params);
|
||||
|
||||
$this->bucket = $params['bucket'];
|
||||
$scheme = ($params['use_ssl'] === 'false') ? 'http' : 'https';
|
||||
$this->test = isset($params['test']);
|
||||
$this->timeout = ( ! isset($params['timeout'])) ? 15 : $params['timeout'];
|
||||
$params['region'] = ( ! isset($params['region']) || $params['region'] === '' ) ? 'eu-west-1' : $params['region'];
|
||||
$params['hostname'] = ( !isset($params['hostname']) || $params['hostname'] === '' ) ? 's3.amazonaws.com' : $params['hostname'];
|
||||
$this->timeout = (!isset($params['timeout'])) ? 15 : $params['timeout'];
|
||||
$this->rescanDelay = (!isset($params['rescanDelay'])) ? 10 : $params['rescanDelay'];
|
||||
$params['region'] = empty($params['region']) ? 'eu-west-1' : $params['region'];
|
||||
$params['hostname'] = empty($params['hostname']) ? 's3.amazonaws.com' : $params['hostname'];
|
||||
if (!isset($params['port']) || $params['port'] === '') {
|
||||
$params['port'] = ($params['use_ssl'] === 'false') ? 80 : 443;
|
||||
}
|
||||
$base_url = $scheme.'://'.$params['hostname'].':'.$params['port'].'/';
|
||||
$this->params = $params;
|
||||
}
|
||||
|
||||
$this->connection = S3Client::factory(array(
|
||||
'key' => $params['key'],
|
||||
'secret' => $params['secret'],
|
||||
'base_url' => $base_url,
|
||||
'region' => $params['region']
|
||||
));
|
||||
/**
|
||||
* Updates old storage ids (v0.2.1 and older) that are based on key and secret to new ones based on the bucket name.
|
||||
* TODO Do this in an update.php. requires iterating over all users and loading the mount.json from their home
|
||||
*
|
||||
* @param array $params
|
||||
*/
|
||||
public function updateLegacyId (array $params) {
|
||||
$oldId = 'amazon::' . $params['key'] . md5($params['secret']);
|
||||
|
||||
if (!$this->connection->isValidBucketName($this->bucket)) {
|
||||
throw new \Exception("The configured bucket name is invalid.");
|
||||
// find by old id or bucket
|
||||
$stmt = \OC::$server->getDatabaseConnection()->prepare(
|
||||
'SELECT `numeric_id`, `id` FROM `*PREFIX*storages` WHERE `id` IN (?, ?)'
|
||||
);
|
||||
$stmt->execute(array($oldId, $this->id));
|
||||
while ($row = $stmt->fetch()) {
|
||||
$storages[$row['id']] = $row['numeric_id'];
|
||||
}
|
||||
|
||||
if (!$this->connection->doesBucketExist($this->bucket)) {
|
||||
try {
|
||||
$result = $this->connection->createBucket(array(
|
||||
'Bucket' => $this->bucket
|
||||
));
|
||||
$this->connection->waitUntilBucketExists(array(
|
||||
'Bucket' => $this->bucket,
|
||||
'waiter.interval' => 1,
|
||||
'waiter.max_attempts' => 15
|
||||
));
|
||||
$this->testTimeout();
|
||||
} catch (S3Exception $e) {
|
||||
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
|
||||
throw new \Exception("Creation of bucket failed.");
|
||||
}
|
||||
if (isset($storages[$this->id]) && isset($storages[$oldId])) {
|
||||
// if both ids exist, delete the old storage and corresponding filecache entries
|
||||
\OC\Files\Cache\Storage::remove($oldId);
|
||||
} else if (isset($storages[$oldId])) {
|
||||
// if only the old id exists do an update
|
||||
$stmt = \OC::$server->getDatabaseConnection()->prepare(
|
||||
'UPDATE `*PREFIX*storages` SET `id` = ? WHERE `id` = ?'
|
||||
);
|
||||
$stmt->execute(array($this->id, $oldId));
|
||||
}
|
||||
// only the bucket based id may exist, do nothing
|
||||
}
|
||||
|
||||
if (!$this->file_exists('.')) {
|
||||
$result = $this->connection->putObject(array(
|
||||
'Bucket' => $this->bucket,
|
||||
'Key' => '.',
|
||||
'Body' => '',
|
||||
'ContentType' => 'httpd/unix-directory',
|
||||
'ContentLength' => 0
|
||||
));
|
||||
$this->testTimeout();
|
||||
/**
|
||||
* Remove a file or folder
|
||||
*
|
||||
* @param string $path
|
||||
* @return bool
|
||||
*/
|
||||
protected function remove($path) {
|
||||
// remember fileType to reduce http calls
|
||||
$fileType = $this->filetype($path);
|
||||
if ($fileType === 'dir') {
|
||||
return $this->rmdir($path);
|
||||
} else if ($fileType === 'file') {
|
||||
return $this->unlink($path);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,16 +173,14 @@ class AmazonS3 extends \OC\Files\Storage\Common {
|
||||
}
|
||||
|
||||
try {
|
||||
$result = $this->connection->putObject(array(
|
||||
$this->getConnection()->putObject(array(
|
||||
'Bucket' => $this->bucket,
|
||||
'Key' => $path . '/',
|
||||
'Body' => '',
|
||||
'ContentType' => 'httpd/unix-directory',
|
||||
'ContentLength' => 0
|
||||
'Key' => $path . '/',
|
||||
'ContentType' => 'httpd/unix-directory'
|
||||
));
|
||||
$this->testTimeout();
|
||||
} catch (S3Exception $e) {
|
||||
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
|
||||
\OCP\Util::logException('files_external', $e);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -156,77 +188,75 @@ class AmazonS3 extends \OC\Files\Storage\Common {
|
||||
}
|
||||
|
||||
public function file_exists($path) {
|
||||
$path = $this->normalizePath($path);
|
||||
|
||||
if (!$path) {
|
||||
$path = '.';
|
||||
} else if ($path != '.' && $this->is_dir($path)) {
|
||||
$path .= '/';
|
||||
}
|
||||
|
||||
try {
|
||||
$result = $this->connection->doesObjectExist(
|
||||
$this->bucket,
|
||||
$path
|
||||
);
|
||||
} catch (S3Exception $e) {
|
||||
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
return $result;
|
||||
return $this->filetype($path) !== false;
|
||||
}
|
||||
|
||||
|
||||
public function rmdir($path) {
|
||||
$path = $this->normalizePath($path);
|
||||
|
||||
if ($this->isRoot($path)) {
|
||||
return $this->clearBucket();
|
||||
}
|
||||
|
||||
if (!$this->file_exists($path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$dh = $this->opendir($path);
|
||||
|
||||
if(is_resource($dh)) {
|
||||
while (($file = readdir($dh)) !== false) {
|
||||
if ($file === '.' || $file === '..') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->is_dir($path . '/' . $file)) {
|
||||
$this->rmdir($path . '/' . $file);
|
||||
} else {
|
||||
$this->unlink($path . '/' . $file);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this->batchDelete($path);
|
||||
}
|
||||
|
||||
protected function clearBucket() {
|
||||
try {
|
||||
$result = $this->connection->deleteObject(array(
|
||||
'Bucket' => $this->bucket,
|
||||
'Key' => $path . '/'
|
||||
));
|
||||
$this->testTimeout();
|
||||
$this->getConnection()->clearBucket($this->bucket);
|
||||
return true;
|
||||
// clearBucket() is not working with Ceph, so if it fails we try the slower approach
|
||||
} catch (\Exception $e) {
|
||||
return $this->batchDelete();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function batchDelete ($path = null) {
|
||||
$params = array(
|
||||
'Bucket' => $this->bucket
|
||||
);
|
||||
if ($path !== null) {
|
||||
$params['Prefix'] = $path . '/';
|
||||
}
|
||||
try {
|
||||
// Since there are no real directories on S3, we need
|
||||
// to delete all objects prefixed with the path.
|
||||
do {
|
||||
// instead of the iterator, manually loop over the list ...
|
||||
$objects = $this->getConnection()->listObjects($params);
|
||||
// ... so we can delete the files in batches
|
||||
$this->getConnection()->deleteObjects(array(
|
||||
'Bucket' => $this->bucket,
|
||||
'Objects' => $objects['Contents']
|
||||
));
|
||||
$this->testTimeout();
|
||||
// we reached the end when the list is no longer truncated
|
||||
} while ($objects['IsTruncated']);
|
||||
} catch (S3Exception $e) {
|
||||
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
|
||||
\OCP\Util::logException('files_external', $e);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function opendir($path) {
|
||||
$path = $this->normalizePath($path);
|
||||
|
||||
if ($path === '.') {
|
||||
if ($this->isRoot($path)) {
|
||||
$path = '';
|
||||
} else if ($path) {
|
||||
} else {
|
||||
$path .= '/';
|
||||
}
|
||||
|
||||
try {
|
||||
$files = array();
|
||||
$result = $this->connection->getIterator('ListObjects', array(
|
||||
$result = $this->getConnection()->getIterator('ListObjects', array(
|
||||
'Bucket' => $this->bucket,
|
||||
'Delimiter' => '/',
|
||||
'Prefix' => $path
|
||||
@@ -236,17 +266,14 @@ class AmazonS3 extends \OC\Files\Storage\Common {
|
||||
$file = basename(
|
||||
isset($object['Key']) ? $object['Key'] : $object['Prefix']
|
||||
);
|
||||
|
||||
if ($file != basename($path)) {
|
||||
$files[] = $file;
|
||||
}
|
||||
$files[] = $file;
|
||||
}
|
||||
|
||||
\OC\Files\Stream\Dir::register('amazons3' . $path, $files);
|
||||
|
||||
return opendir('fakedir://amazons3' . $path);
|
||||
} catch (S3Exception $e) {
|
||||
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
|
||||
\OCP\Util::logException('files_external', $e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -255,27 +282,29 @@ class AmazonS3 extends \OC\Files\Storage\Common {
|
||||
$path = $this->normalizePath($path);
|
||||
|
||||
try {
|
||||
if ($this->is_dir($path) && $path != '.') {
|
||||
$path .= '/';
|
||||
}
|
||||
|
||||
$result = $this->connection->headObject(array(
|
||||
'Bucket' => $this->bucket,
|
||||
'Key' => $path
|
||||
));
|
||||
|
||||
$stat = array();
|
||||
$stat['size'] = $result['ContentLength'] ? $result['ContentLength'] : 0;
|
||||
if ($result['Metadata']['lastmodified']) {
|
||||
$stat['mtime'] = strtotime($result['Metadata']['lastmodified']);
|
||||
if ($this->is_dir($path)) {
|
||||
//folders don't really exist
|
||||
$stat['size'] = -1; //unknown
|
||||
$stat['mtime'] = time() - $this->rescanDelay * 1000;
|
||||
} else {
|
||||
$stat['mtime'] = strtotime($result['LastModified']);
|
||||
$result = $this->getConnection()->headObject(array(
|
||||
'Bucket' => $this->bucket,
|
||||
'Key' => $path
|
||||
));
|
||||
|
||||
$stat['size'] = $result['ContentLength'] ? $result['ContentLength'] : 0;
|
||||
if ($result['Metadata']['lastmodified']) {
|
||||
$stat['mtime'] = strtotime($result['Metadata']['lastmodified']);
|
||||
} else {
|
||||
$stat['mtime'] = strtotime($result['LastModified']);
|
||||
}
|
||||
}
|
||||
$stat['atime'] = time();
|
||||
|
||||
return $stat;
|
||||
} catch(S3Exception $e) {
|
||||
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
|
||||
\OCP\Util::logException('files_external', $e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -283,20 +312,19 @@ class AmazonS3 extends \OC\Files\Storage\Common {
|
||||
public function filetype($path) {
|
||||
$path = $this->normalizePath($path);
|
||||
|
||||
if ($this->isRoot($path)) {
|
||||
return 'dir';
|
||||
}
|
||||
|
||||
try {
|
||||
if ($path != '.' && $this->connection->doesObjectExist($this->bucket, $path)) {
|
||||
if ($this->getConnection()->doesObjectExist($this->bucket, $path)) {
|
||||
return 'file';
|
||||
}
|
||||
|
||||
if ($path != '.') {
|
||||
$path .= '/';
|
||||
}
|
||||
|
||||
if ($this->connection->doesObjectExist($this->bucket, $path)) {
|
||||
if ($this->getConnection()->doesObjectExist($this->bucket, $path.'/')) {
|
||||
return 'dir';
|
||||
}
|
||||
} catch (S3Exception $e) {
|
||||
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
|
||||
\OCP\Util::logException('files_external', $e);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -306,14 +334,18 @@ class AmazonS3 extends \OC\Files\Storage\Common {
|
||||
public function unlink($path) {
|
||||
$path = $this->normalizePath($path);
|
||||
|
||||
if ($this->is_dir($path)) {
|
||||
return $this->rmdir($path);
|
||||
}
|
||||
|
||||
try {
|
||||
$result = $this->connection->deleteObject(array(
|
||||
$this->getConnection()->deleteObject(array(
|
||||
'Bucket' => $this->bucket,
|
||||
'Key' => $path
|
||||
));
|
||||
$this->testTimeout();
|
||||
} catch (S3Exception $e) {
|
||||
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
|
||||
\OCP\Util::logException('files_external', $e);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -330,13 +362,13 @@ class AmazonS3 extends \OC\Files\Storage\Common {
|
||||
self::$tmpFiles[$tmpFile] = $path;
|
||||
|
||||
try {
|
||||
$result = $this->connection->getObject(array(
|
||||
$this->getConnection()->getObject(array(
|
||||
'Bucket' => $this->bucket,
|
||||
'Key' => $path,
|
||||
'SaveAs' => $tmpFile
|
||||
));
|
||||
} catch (S3Exception $e) {
|
||||
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
|
||||
\OCP\Util::logException('files_external', $e);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -378,12 +410,12 @@ class AmazonS3 extends \OC\Files\Storage\Common {
|
||||
return 'httpd/unix-directory';
|
||||
} else if ($this->file_exists($path)) {
|
||||
try {
|
||||
$result = $this->connection->headObject(array(
|
||||
$result = $this->getConnection()->headObject(array(
|
||||
'Bucket' => $this->bucket,
|
||||
'Key' => $path
|
||||
));
|
||||
} catch (S3Exception $e) {
|
||||
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
|
||||
\OCP\Util::logException('files_external', $e);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -400,28 +432,31 @@ class AmazonS3 extends \OC\Files\Storage\Common {
|
||||
$metadata = array('lastmodified' => $mtime);
|
||||
}
|
||||
|
||||
$fileType = $this->filetype($path);
|
||||
try {
|
||||
if ($this->file_exists($path)) {
|
||||
if ($this->is_dir($path) && $path != '.') {
|
||||
if ($fileType !== false) {
|
||||
if ($fileType === 'dir' && ! $this->isRoot($path)) {
|
||||
$path .= '/';
|
||||
}
|
||||
$result = $this->connection->copyObject(array(
|
||||
$this->getConnection()->copyObject(array(
|
||||
'Bucket' => $this->bucket,
|
||||
'Key' => $path,
|
||||
'Key' => $this->cleanKey($path),
|
||||
'Metadata' => $metadata,
|
||||
'CopySource' => $this->bucket . '/' . $path
|
||||
));
|
||||
$this->testTimeout();
|
||||
} else {
|
||||
$result = $this->connection->putObject(array(
|
||||
$mimeType = \OC_Helper::getMimetypeDetector()->detectPath($path);
|
||||
$this->getConnection()->putObject(array(
|
||||
'Bucket' => $this->bucket,
|
||||
'Key' => $path,
|
||||
'Metadata' => $metadata
|
||||
'Key' => $this->cleanKey($path),
|
||||
'Metadata' => $metadata,
|
||||
'ContentType' => $mimeType
|
||||
));
|
||||
$this->testTimeout();
|
||||
}
|
||||
} catch (S3Exception $e) {
|
||||
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
|
||||
\OCP\Util::logException('files_external', $e);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -434,35 +469,33 @@ class AmazonS3 extends \OC\Files\Storage\Common {
|
||||
|
||||
if ($this->is_file($path1)) {
|
||||
try {
|
||||
$result = $this->connection->copyObject(array(
|
||||
$this->getConnection()->copyObject(array(
|
||||
'Bucket' => $this->bucket,
|
||||
'Key' => $path2,
|
||||
'CopySource' => $this->bucket . '/' . $path1
|
||||
'Key' => $this->cleanKey($path2),
|
||||
'CopySource' => S3Client::encodeKey($this->bucket . '/' . $path1)
|
||||
));
|
||||
$this->testTimeout();
|
||||
} catch (S3Exception $e) {
|
||||
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
|
||||
\OCP\Util::logException('files_external', $e);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if ($this->file_exists($path2)) {
|
||||
return false;
|
||||
}
|
||||
$this->remove($path2);
|
||||
|
||||
try {
|
||||
$result = $this->connection->copyObject(array(
|
||||
$this->getConnection()->copyObject(array(
|
||||
'Bucket' => $this->bucket,
|
||||
'Key' => $path2 . '/',
|
||||
'CopySource' => $this->bucket . '/' . $path1 . '/'
|
||||
'CopySource' => S3Client::encodeKey($this->bucket . '/' . $path1 . '/')
|
||||
));
|
||||
$this->testTimeout();
|
||||
} catch (S3Exception $e) {
|
||||
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
|
||||
\OCP\Util::logException('files_external', $e);
|
||||
return false;
|
||||
}
|
||||
|
||||
$dh = $this->opendir($path1);
|
||||
if(is_resource($dh)) {
|
||||
if (is_resource($dh)) {
|
||||
while (($file = readdir($dh)) !== false) {
|
||||
if ($file === '.' || $file === '..') {
|
||||
continue;
|
||||
@@ -483,6 +516,7 @@ class AmazonS3 extends \OC\Files\Storage\Common {
|
||||
$path2 = $this->normalizePath($path2);
|
||||
|
||||
if ($this->is_file($path1)) {
|
||||
|
||||
if ($this->copy($path1, $path2) === false) {
|
||||
return false;
|
||||
}
|
||||
@@ -492,9 +526,6 @@ class AmazonS3 extends \OC\Files\Storage\Common {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if ($this->file_exists($path2)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->copy($path1, $path2) === false) {
|
||||
return false;
|
||||
@@ -510,7 +541,7 @@ class AmazonS3 extends \OC\Files\Storage\Common {
|
||||
}
|
||||
|
||||
public function test() {
|
||||
$test = $this->connection->getBucketAcl(array(
|
||||
$test = $this->getConnection()->getBucketAcl(array(
|
||||
'Bucket' => $this->bucket,
|
||||
));
|
||||
if (isset($test) && !is_null($test->getPath('Owner/ID'))) {
|
||||
@@ -523,7 +554,48 @@ class AmazonS3 extends \OC\Files\Storage\Common {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the connection
|
||||
*
|
||||
* @return S3Client connected client
|
||||
* @throws \Exception if connection could not be made
|
||||
*/
|
||||
public function getConnection() {
|
||||
if (!is_null($this->connection)) {
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
$scheme = ($this->params['use_ssl'] === 'false') ? 'http' : 'https';
|
||||
$base_url = $scheme . '://' . $this->params['hostname'] . ':' . $this->params['port'] . '/';
|
||||
|
||||
$this->connection = S3Client::factory(array(
|
||||
'key' => $this->params['key'],
|
||||
'secret' => $this->params['secret'],
|
||||
'base_url' => $base_url,
|
||||
'region' => $this->params['region']
|
||||
));
|
||||
|
||||
if (!$this->connection->isValidBucketName($this->bucket)) {
|
||||
throw new \Exception("The configured bucket name is invalid.");
|
||||
}
|
||||
|
||||
if (!$this->connection->doesBucketExist($this->bucket)) {
|
||||
try {
|
||||
$this->connection->createBucket(array(
|
||||
'Bucket' => $this->bucket
|
||||
));
|
||||
$this->connection->waitUntilBucketExists(array(
|
||||
'Bucket' => $this->bucket,
|
||||
'waiter.interval' => 1,
|
||||
'waiter.max_attempts' => 15
|
||||
));
|
||||
$this->testTimeout();
|
||||
} catch (S3Exception $e) {
|
||||
\OCP\Util::logException('files_external', $e);
|
||||
throw new \Exception('Creation of bucket failed. '.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
@@ -533,9 +605,9 @@ class AmazonS3 extends \OC\Files\Storage\Common {
|
||||
}
|
||||
|
||||
try {
|
||||
$result= $this->connection->putObject(array(
|
||||
$this->getConnection()->putObject(array(
|
||||
'Bucket' => $this->bucket,
|
||||
'Key' => self::$tmpFiles[$tmpFile],
|
||||
'Key' => $this->cleanKey(self::$tmpFiles[$tmpFile]),
|
||||
'SourceFile' => $tmpFile,
|
||||
'ContentType' => \OC_Helper::getMimeType($tmpFile),
|
||||
'ContentLength' => filesize($tmpFile)
|
||||
@@ -544,7 +616,7 @@ class AmazonS3 extends \OC\Files\Storage\Common {
|
||||
|
||||
unlink($tmpFile);
|
||||
} catch (S3Exception $e) {
|
||||
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
|
||||
\OCP\Util::logException('files_external', $e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
54
apps/files_external/lib/config.php
Executable file → Normal file
54
apps/files_external/lib/config.php
Executable file → Normal file
@@ -118,6 +118,30 @@ class OC_Mount_Config {
|
||||
}
|
||||
$manager->addMount($mount);
|
||||
}
|
||||
|
||||
if ($data['user']) {
|
||||
$user = \OC::$server->getUserManager()->get($data['user']);
|
||||
if (!$user) {
|
||||
\OC_Log::write(
|
||||
'files_external',
|
||||
'Cannot init external mount points for non-existant user "' . $data['user'] . '".',
|
||||
\OC_Log::WARN
|
||||
);
|
||||
return;
|
||||
}
|
||||
$userView = new \OC\Files\View('/' . $user->getUID() . '/files');
|
||||
$changePropagator = new \OC\Files\Cache\ChangePropagator($userView);
|
||||
$etagPropagator = new \OCA\Files_External\EtagPropagator($user, $changePropagator, \OC::$server->getConfig());
|
||||
$etagPropagator->propagateDirtyMountPoints();
|
||||
\OCP\Util::connectHook(
|
||||
\OC\Files\Filesystem::CLASSNAME,
|
||||
\OC\Files\Filesystem::signal_create_mount,
|
||||
$etagPropagator, 'updateHook');
|
||||
\OCP\Util::connectHook(
|
||||
\OC\Files\Filesystem::CLASSNAME,
|
||||
\OC\Files\Filesystem::signal_delete_mount,
|
||||
$etagPropagator, 'updateHook');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -169,6 +193,7 @@ class OC_Mount_Config {
|
||||
foreach ($options as &$option) {
|
||||
$option = self::setUserVars($user, $option);
|
||||
}
|
||||
$options['personal'] = false;
|
||||
$options['options'] = self::decryptPasswords($options['options']);
|
||||
if (!isset($options['priority'])) {
|
||||
$options['priority'] = $backends[$options['class']]['priority'];
|
||||
@@ -458,6 +483,7 @@ class OC_Mount_Config {
|
||||
$priority = null) {
|
||||
$backends = self::getBackends();
|
||||
$mountPoint = OC\Files\Filesystem::normalizePath($mountPoint);
|
||||
$relMountPoint = $mountPoint;
|
||||
if ($mountPoint === '' || $mountPoint === '/') {
|
||||
// can't mount at root folder
|
||||
return false;
|
||||
@@ -490,6 +516,10 @@ class OC_Mount_Config {
|
||||
}
|
||||
|
||||
$mountPoints = self::readData($isPersonal ? OCP\User::getUser() : NULL);
|
||||
// who else loves multi-dimensional array ?
|
||||
$isNew = !isset($mountPoints[$mountType]) ||
|
||||
!isset($mountPoints[$mountType][$applicable]) ||
|
||||
!isset($mountPoints[$mountType][$applicable][$mountPoint]);
|
||||
$mountPoints = self::mergeMountPoints($mountPoints, $mount, $mountType);
|
||||
|
||||
// Set default priority if none set
|
||||
@@ -505,7 +535,19 @@ class OC_Mount_Config {
|
||||
|
||||
self::writeData($isPersonal ? OCP\User::getUser() : NULL, $mountPoints);
|
||||
|
||||
return self::getBackendStatus($class, $classOptions, $isPersonal);
|
||||
$result = self::getBackendStatus($class, $classOptions, $isPersonal);
|
||||
if ($result && $isNew) {
|
||||
\OC_Hook::emit(
|
||||
\OC\Files\Filesystem::CLASSNAME,
|
||||
\OC\Files\Filesystem::signal_create_mount,
|
||||
array(
|
||||
\OC\Files\Filesystem::signal_param_path => $relMountPoint,
|
||||
\OC\Files\Filesystem::signal_param_mount_type => $mountType,
|
||||
\OC\Files\Filesystem::signal_param_users => $applicable,
|
||||
)
|
||||
);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -518,6 +560,7 @@ class OC_Mount_Config {
|
||||
*/
|
||||
public static function removeMountPoint($mountPoint, $mountType, $applicable, $isPersonal = false) {
|
||||
// Verify that the mount point applies for the current user
|
||||
$relMountPoints = $mountPoint;
|
||||
if ($isPersonal) {
|
||||
if ($applicable != OCP\User::getUser()) {
|
||||
return false;
|
||||
@@ -538,6 +581,15 @@ class OC_Mount_Config {
|
||||
}
|
||||
}
|
||||
self::writeData($isPersonal ? OCP\User::getUser() : NULL, $mountPoints);
|
||||
\OC_Hook::emit(
|
||||
\OC\Files\Filesystem::CLASSNAME,
|
||||
\OC\Files\Filesystem::signal_delete_mount,
|
||||
array(
|
||||
\OC\Files\Filesystem::signal_param_path => $relMountPoints,
|
||||
\OC\Files\Filesystem::signal_param_mount_type => $mountType,
|
||||
\OC\Files\Filesystem::signal_param_users => $applicable,
|
||||
)
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
1
apps/files_external/lib/dropbox.php
Executable file → Normal file
1
apps/files_external/lib/dropbox.php
Executable file → Normal file
@@ -44,6 +44,7 @@ class Dropbox extends \OC\Files\Storage\Common {
|
||||
$this->id = 'dropbox::'.$params['app_key'] . $params['token']. '/' . $this->root;
|
||||
$oauth = new \Dropbox_OAuth_Curl($params['app_key'], $params['app_secret']);
|
||||
$oauth->setToken($params['token'], $params['token_secret']);
|
||||
// note: Dropbox_API connection is lazy
|
||||
$this->dropbox = new \Dropbox_API($oauth, 'auto');
|
||||
} else {
|
||||
throw new \Exception('Creating \OC\Files\Storage\Dropbox storage failed');
|
||||
|
||||
126
apps/files_external/lib/etagpropagator.php
Normal file
126
apps/files_external/lib/etagpropagator.php
Normal file
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
|
||||
namespace OCA\Files_External;
|
||||
|
||||
use OC\Files\Filesystem;
|
||||
|
||||
/**
|
||||
* Updates the etag of parent folders whenever a new external storage mount
|
||||
* point has been created or deleted. Updates need to be triggered using
|
||||
* the updateHook() method.
|
||||
*
|
||||
* There are two modes of operation:
|
||||
* - for personal mount points, the etag is propagated directly
|
||||
* - for system mount points, a dirty flag is saved in the configuration and
|
||||
* the etag will be updated the next time propagateDirtyMountPoints() is called
|
||||
*/
|
||||
class EtagPropagator {
|
||||
/**
|
||||
* @var \OCP\IUser
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* @var \OC\Files\Cache\ChangePropagator
|
||||
*/
|
||||
protected $changePropagator;
|
||||
|
||||
/**
|
||||
* @var \OCP\IConfig
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* @param \OCP\IUser $user current user, must match the propagator's
|
||||
* user
|
||||
* @param \OC\Files\Cache\ChangePropagator $changePropagator change propagator
|
||||
* initialized with a view for $user
|
||||
* @param \OCP\IConfig $config
|
||||
*/
|
||||
public function __construct($user, $changePropagator, $config) {
|
||||
$this->user = $user;
|
||||
$this->changePropagator = $changePropagator;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Propagate the etag changes for all mountpoints marked as dirty and mark the mountpoints as clean
|
||||
*
|
||||
* @param int $time
|
||||
*/
|
||||
public function propagateDirtyMountPoints($time = null) {
|
||||
if ($time === null) {
|
||||
$time = time();
|
||||
}
|
||||
$mountPoints = $this->getDirtyMountPoints();
|
||||
foreach ($mountPoints as $mountPoint) {
|
||||
$this->changePropagator->addChange($mountPoint);
|
||||
$this->config->setUserValue($this->user->getUID(), 'files_external', $mountPoint, $time);
|
||||
}
|
||||
if (count($mountPoints)) {
|
||||
$this->changePropagator->propagateChanges($time);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all mountpoints we need to update the etag for
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected function getDirtyMountPoints() {
|
||||
$dirty = array();
|
||||
$mountPoints = $this->config->getAppKeys('files_external');
|
||||
foreach ($mountPoints as $mountPoint) {
|
||||
if (substr($mountPoint, 0, 1) === '/') {
|
||||
$updateTime = $this->config->getAppValue('files_external', $mountPoint);
|
||||
$userTime = $this->config->getUserValue($this->user->getUID(), 'files_external', $mountPoint);
|
||||
if ($updateTime > $userTime) {
|
||||
$dirty[] = $mountPoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $dirty;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $mountPoint
|
||||
* @param int $time
|
||||
*/
|
||||
protected function markDirty($mountPoint, $time = null) {
|
||||
if ($time === null) {
|
||||
$time = time();
|
||||
}
|
||||
$this->config->setAppValue('files_external', $mountPoint, $time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update etags for mount points for known user
|
||||
* For global or group mount points, updating the etag for every user is not feasible
|
||||
* instead we mark the mount point as dirty and update the etag when the filesystem is loaded for the user
|
||||
* For personal mount points, the change is propagated directly
|
||||
*
|
||||
* @param array $params hook parameters
|
||||
* @param int $time update time to use when marking a mount point as dirty
|
||||
*/
|
||||
public function updateHook($params, $time = null) {
|
||||
if ($time === null) {
|
||||
$time = time();
|
||||
}
|
||||
$users = $params[Filesystem::signal_param_users];
|
||||
$type = $params[Filesystem::signal_param_mount_type];
|
||||
$mountPoint = $params[Filesystem::signal_param_path];
|
||||
$mountPoint = Filesystem::normalizePath($mountPoint);
|
||||
if ($type === \OC_Mount_Config::MOUNT_TYPE_GROUP or $users === 'all') {
|
||||
$this->markDirty($mountPoint, $time);
|
||||
} else {
|
||||
$this->changePropagator->addChange($mountPoint);
|
||||
$this->changePropagator->propagateChanges($time);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,7 @@ class FTP extends \OC\Files\Storage\StreamWrapper{
|
||||
$this->root .= '/';
|
||||
}
|
||||
} else {
|
||||
throw new \Exception();
|
||||
throw new \Exception('Creating \OC\Files\Storage\FTP storage failed');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ class Google extends \OC\Files\Storage\Common {
|
||||
$client->setScopes(array('https://www.googleapis.com/auth/drive'));
|
||||
$client->setUseObjects(true);
|
||||
$client->setAccessToken($params['token']);
|
||||
// note: API connection is lazy
|
||||
$this->service = new \Google_DriveService($client);
|
||||
$token = json_decode($params['token'], true);
|
||||
$this->id = 'google::'.substr($params['client_id'], 0, 30).$token['created'];
|
||||
|
||||
@@ -22,6 +22,14 @@ class OwnCloud extends \OC\Files\Storage\DAV{
|
||||
// extract context path from host if specified
|
||||
// (owncloud install path on host)
|
||||
$host = $params['host'];
|
||||
// strip protocol
|
||||
if (substr($host, 0, 8) == "https://") {
|
||||
$host = substr($host, 8);
|
||||
$params['secure'] = true;
|
||||
} else if (substr($host, 0, 7) == "http://") {
|
||||
$host = substr($host, 7);
|
||||
$params['secure'] = false;
|
||||
}
|
||||
$contextPath = '';
|
||||
$hostSlashPos = strpos($host, '/');
|
||||
if ($hostSlashPos !== false){
|
||||
|
||||
@@ -7,17 +7,35 @@
|
||||
*/
|
||||
namespace OC\Files\Storage;
|
||||
|
||||
/**
|
||||
* Uses phpseclib's Net_SFTP class and the Net_SFTP_Stream stream wrapper to
|
||||
* provide access to SFTP servers.
|
||||
*/
|
||||
class SFTP extends \OC\Files\Storage\Common {
|
||||
private $host;
|
||||
private $user;
|
||||
private $password;
|
||||
private $root;
|
||||
|
||||
/**
|
||||
* @var \Net_SFTP
|
||||
*/
|
||||
private $client;
|
||||
|
||||
private static $tempFiles = array();
|
||||
|
||||
public function __construct($params) {
|
||||
// The sftp:// scheme has to be manually registered via inclusion of
|
||||
// the 'Net/SFTP/Stream.php' file which registers the Net_SFTP_Stream
|
||||
// stream wrapper as a side effect.
|
||||
// A slightly better way to register the stream wrapper is available
|
||||
// since phpseclib 0.3.7 in the form of a static call to
|
||||
// Net_SFTP_Stream::register() which will trigger autoloading if
|
||||
// necessary.
|
||||
// TODO: Call Net_SFTP_Stream::register() instead when phpseclib is
|
||||
// updated to 0.3.7 or higher.
|
||||
require_once 'Net/SFTP/Stream.php';
|
||||
|
||||
$this->host = $params['host'];
|
||||
$proto = strpos($this->host, '://');
|
||||
if ($proto != false) {
|
||||
@@ -35,16 +53,24 @@ class SFTP extends \OC\Files\Storage\Common {
|
||||
if (substr($this->root, -1, 1) != '/') {
|
||||
$this->root .= '/';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the connection.
|
||||
*
|
||||
* @return \Net_SFTP connected client instance
|
||||
* @throws \Exception when the connection failed
|
||||
*/
|
||||
public function getConnection() {
|
||||
if (!is_null($this->client)) {
|
||||
return $this->client;
|
||||
}
|
||||
|
||||
$hostKeys = $this->readHostKeys();
|
||||
$this->client = new \Net_SFTP($this->host);
|
||||
|
||||
if (!$this->client->login($this->user, $this->password)) {
|
||||
throw new \Exception('Login failed');
|
||||
}
|
||||
|
||||
// The SSH Host Key MUST be verified before login().
|
||||
$currentHostKey = $this->client->getServerPublicHostKey();
|
||||
|
||||
if (array_key_exists($this->host, $hostKeys)) {
|
||||
if ($hostKeys[$this->host] != $currentHostKey) {
|
||||
throw new \Exception('Host public key does not match known key');
|
||||
@@ -53,6 +79,11 @@ class SFTP extends \OC\Files\Storage\Common {
|
||||
$hostKeys[$this->host] = $currentHostKey;
|
||||
$this->writeHostKeys($hostKeys);
|
||||
}
|
||||
|
||||
if (!$this->client->login($this->user, $this->password)) {
|
||||
throw new \Exception('Login failed');
|
||||
}
|
||||
return $this->client;
|
||||
}
|
||||
|
||||
public function test() {
|
||||
@@ -63,7 +94,7 @@ class SFTP extends \OC\Files\Storage\Common {
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return $this->client->nlist() !== false;
|
||||
return $this->getConnection()->nlist() !== false;
|
||||
}
|
||||
|
||||
public function getId(){
|
||||
@@ -131,7 +162,7 @@ class SFTP extends \OC\Files\Storage\Common {
|
||||
|
||||
public function mkdir($path) {
|
||||
try {
|
||||
return $this->client->mkdir($this->absPath($path));
|
||||
return $this->getConnection()->mkdir($this->absPath($path));
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
@@ -139,7 +170,7 @@ class SFTP extends \OC\Files\Storage\Common {
|
||||
|
||||
public function rmdir($path) {
|
||||
try {
|
||||
return $this->client->delete($this->absPath($path), true);
|
||||
return $this->getConnection()->delete($this->absPath($path), true);
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
@@ -147,7 +178,7 @@ class SFTP extends \OC\Files\Storage\Common {
|
||||
|
||||
public function opendir($path) {
|
||||
try {
|
||||
$list = $this->client->nlist($this->absPath($path));
|
||||
$list = $this->getConnection()->nlist($this->absPath($path));
|
||||
|
||||
$id = md5('sftp:' . $path);
|
||||
$dirStream = array();
|
||||
@@ -165,7 +196,7 @@ class SFTP extends \OC\Files\Storage\Common {
|
||||
|
||||
public function filetype($path) {
|
||||
try {
|
||||
$stat = $this->client->stat($this->absPath($path));
|
||||
$stat = $this->getConnection()->stat($this->absPath($path));
|
||||
if ($stat['type'] == NET_SFTP_TYPE_REGULAR) {
|
||||
return 'file';
|
||||
}
|
||||
@@ -181,7 +212,7 @@ class SFTP extends \OC\Files\Storage\Common {
|
||||
|
||||
public function file_exists($path) {
|
||||
try {
|
||||
return $this->client->stat($this->absPath($path)) !== false;
|
||||
return $this->getConnection()->stat($this->absPath($path)) !== false;
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
@@ -189,7 +220,7 @@ class SFTP extends \OC\Files\Storage\Common {
|
||||
|
||||
public function unlink($path) {
|
||||
try {
|
||||
return $this->client->delete($this->absPath($path), true);
|
||||
return $this->getConnection()->delete($this->absPath($path), true);
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
@@ -216,8 +247,8 @@ class SFTP extends \OC\Files\Storage\Common {
|
||||
case 'x+':
|
||||
case 'c':
|
||||
case 'c+':
|
||||
// FIXME: make client login lazy to prevent it when using fopen()
|
||||
return fopen($this->constructUrl($path), $mode);
|
||||
$context = stream_context_create(array('sftp' => array('session' => $this->getConnection())));
|
||||
return fopen($this->constructUrl($path), $mode, false, $context);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
@@ -230,7 +261,7 @@ class SFTP extends \OC\Files\Storage\Common {
|
||||
return false;
|
||||
}
|
||||
if (!$this->file_exists($path)) {
|
||||
$this->client->put($this->absPath($path), '');
|
||||
$this->getConnection()->put($this->absPath($path), '');
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -241,11 +272,11 @@ class SFTP extends \OC\Files\Storage\Common {
|
||||
}
|
||||
|
||||
public function getFile($path, $target) {
|
||||
$this->client->get($path, $target);
|
||||
$this->getConnection()->get($path, $target);
|
||||
}
|
||||
|
||||
public function uploadFile($path, $target) {
|
||||
$this->client->put($target, $path, NET_SFTP_LOCAL_FILE);
|
||||
$this->getConnection()->put($target, $path, NET_SFTP_LOCAL_FILE);
|
||||
}
|
||||
|
||||
public function rename($source, $target) {
|
||||
@@ -253,7 +284,7 @@ class SFTP extends \OC\Files\Storage\Common {
|
||||
if (!$this->is_dir($target) && $this->file_exists($target)) {
|
||||
$this->unlink($target);
|
||||
}
|
||||
return $this->client->rename(
|
||||
return $this->getConnection()->rename(
|
||||
$this->absPath($source),
|
||||
$this->absPath($target)
|
||||
);
|
||||
@@ -264,7 +295,7 @@ class SFTP extends \OC\Files\Storage\Common {
|
||||
|
||||
public function stat($path) {
|
||||
try {
|
||||
$stat = $this->client->stat($this->absPath($path));
|
||||
$stat = $this->getConnection()->stat($this->absPath($path));
|
||||
|
||||
$mtime = $stat ? $stat['mtime'] : -1;
|
||||
$size = $stat ? $stat['size'] : 0;
|
||||
@@ -279,7 +310,10 @@ class SFTP extends \OC\Files\Storage\Common {
|
||||
* @param string $path
|
||||
*/
|
||||
public function constructUrl($path) {
|
||||
$url = 'sftp://'.$this->user.':'.$this->password.'@'.$this->host.$this->root.$path;
|
||||
// Do not pass the password here. We want to use the Net_SFTP object
|
||||
// supplied via stream context or fail. We only supply username and
|
||||
// hostname because this might show up in logs (they are not used).
|
||||
$url = 'sftp://'.$this->user.'@'.$this->host.$this->root.$path;
|
||||
return $url;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,12 +13,16 @@ require_once __DIR__ . '/../3rdparty/smb4php/smb.php';
|
||||
class SMB_OC extends \OC\Files\Storage\SMB {
|
||||
private $username_as_share;
|
||||
|
||||
/**
|
||||
* @param array $params
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct($params) {
|
||||
if (isset($params['host']) && \OC::$session->exists('smb-credentials')) {
|
||||
$host=$params['host'];
|
||||
$this->username_as_share = ($params['username_as_share'] === 'true');
|
||||
|
||||
$params_auth = \OC::$session->get('smb-credentials');
|
||||
$params_auth = json_decode(\OC::$server->getCrypto()->decrypt(\OC::$session->get('smb-credentials')), true);
|
||||
$user = \OC::$session->get('loginname');
|
||||
$password = $params_auth['password'];
|
||||
|
||||
@@ -44,14 +48,34 @@ class SMB_OC extends \OC\Files\Storage\SMB {
|
||||
}
|
||||
}
|
||||
|
||||
public static function login( $params ) {
|
||||
\OC::$session->set('smb-credentials', $params);
|
||||
/**
|
||||
* Intercepts the user credentials on login and stores them
|
||||
* encrypted inside the session if SMB_OC storage is enabled.
|
||||
* @param array $params
|
||||
*/
|
||||
public static function login($params) {
|
||||
$mountpoints = \OC_Mount_Config::getAbsoluteMountPoints($params['uid']);
|
||||
$mountpointClasses = array();
|
||||
foreach($mountpoints as $mountpoint) {
|
||||
$mountpointClasses[$mountpoint['class']] = true;
|
||||
}
|
||||
if(isset($mountpointClasses['\OC\Files\Storage\SMB_OC'])) {
|
||||
\OC::$session->set('smb-credentials', \OC::$server->getCrypto()->encrypt(json_encode($params)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @return boolean
|
||||
*/
|
||||
public function isSharable($path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $isPersonal
|
||||
* @return bool
|
||||
*/
|
||||
public function test($isPersonal = true) {
|
||||
if ($isPersonal) {
|
||||
if ($this->stat('')) {
|
||||
|
||||
@@ -47,6 +47,12 @@ class Swift extends \OC\Files\Storage\Common {
|
||||
* @var string
|
||||
*/
|
||||
private $bucket;
|
||||
/**
|
||||
* Connection parameters
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $params;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
@@ -84,7 +90,7 @@ class Swift extends \OC\Files\Storage\Common {
|
||||
*/
|
||||
private function doesObjectExist($path) {
|
||||
try {
|
||||
$this->container->getPartialObject($path);
|
||||
$this->getContainer()->getPartialObject($path);
|
||||
return true;
|
||||
} catch (ClientErrorResponseException $e) {
|
||||
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
|
||||
@@ -110,37 +116,7 @@ class Swift extends \OC\Files\Storage\Common {
|
||||
$params['service_name'] = 'cloudFiles';
|
||||
}
|
||||
|
||||
$settings = array(
|
||||
'username' => $params['user'],
|
||||
);
|
||||
|
||||
if (isset($params['password'])) {
|
||||
$settings['password'] = $params['password'];
|
||||
} else if (isset($params['key'])) {
|
||||
$settings['apiKey'] = $params['key'];
|
||||
}
|
||||
|
||||
if (isset($params['tenant'])) {
|
||||
$settings['tenantName'] = $params['tenant'];
|
||||
}
|
||||
|
||||
if (isset($params['timeout'])) {
|
||||
$settings['timeout'] = $params['timeout'];
|
||||
}
|
||||
|
||||
$this->anchor = new OpenStack($params['url'], $settings);
|
||||
|
||||
$this->connection = $this->anchor->objectStoreService($params['service_name'], $params['region']);
|
||||
|
||||
try {
|
||||
$this->container = $this->connection->getContainer($this->bucket);
|
||||
} catch (ClientErrorResponseException $e) {
|
||||
$this->container = $this->connection->createContainer($this->bucket);
|
||||
}
|
||||
|
||||
if (!$this->file_exists('.')) {
|
||||
$this->mkdir('.');
|
||||
}
|
||||
$this->params = $params;
|
||||
}
|
||||
|
||||
public function mkdir($path) {
|
||||
@@ -158,7 +134,7 @@ class Swift extends \OC\Files\Storage\Common {
|
||||
$customHeaders = array('content-type' => 'httpd/unix-directory');
|
||||
$metadataHeaders = DataObject::stockHeaders(array());
|
||||
$allHeaders = $customHeaders + $metadataHeaders;
|
||||
$this->container->uploadObject($path, '', $allHeaders);
|
||||
$this->getContainer()->uploadObject($path, '', $allHeaders);
|
||||
} catch (Exceptions\CreateUpdateError $e) {
|
||||
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
|
||||
return false;
|
||||
@@ -198,7 +174,7 @@ class Swift extends \OC\Files\Storage\Common {
|
||||
}
|
||||
|
||||
try {
|
||||
$this->container->dataObject()->setName($path . '/')->delete();
|
||||
$this->getContainer()->dataObject()->setName($path . '/')->delete();
|
||||
} catch (Exceptions\DeleteError $e) {
|
||||
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
|
||||
return false;
|
||||
@@ -221,7 +197,7 @@ class Swift extends \OC\Files\Storage\Common {
|
||||
try {
|
||||
$files = array();
|
||||
/** @var OpenCloud\Common\Collection $objects */
|
||||
$objects = $this->container->objectList(array(
|
||||
$objects = $this->getContainer()->objectList(array(
|
||||
'prefix' => $path,
|
||||
'delimiter' => '/'
|
||||
));
|
||||
@@ -251,7 +227,7 @@ class Swift extends \OC\Files\Storage\Common {
|
||||
}
|
||||
|
||||
try {
|
||||
$object = $this->container->getPartialObject($path);
|
||||
$object = $this->getContainer()->getPartialObject($path);
|
||||
} catch (ClientErrorResponseException $e) {
|
||||
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
|
||||
return false;
|
||||
@@ -304,7 +280,7 @@ class Swift extends \OC\Files\Storage\Common {
|
||||
}
|
||||
|
||||
try {
|
||||
$this->container->dataObject()->setName($path)->delete();
|
||||
$this->getContainer()->dataObject()->setName($path)->delete();
|
||||
} catch (ClientErrorResponseException $e) {
|
||||
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
|
||||
return false;
|
||||
@@ -322,7 +298,7 @@ class Swift extends \OC\Files\Storage\Common {
|
||||
$tmpFile = \OC_Helper::tmpFile();
|
||||
self::$tmpFiles[$tmpFile] = $path;
|
||||
try {
|
||||
$object = $this->container->getObject($path);
|
||||
$object = $this->getContainer()->getObject($path);
|
||||
} catch (ClientErrorResponseException $e) {
|
||||
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
|
||||
return false;
|
||||
@@ -375,7 +351,7 @@ class Swift extends \OC\Files\Storage\Common {
|
||||
if ($this->is_dir($path)) {
|
||||
return 'httpd/unix-directory';
|
||||
} else if ($this->file_exists($path)) {
|
||||
$object = $this->container->getPartialObject($path);
|
||||
$object = $this->getContainer()->getPartialObject($path);
|
||||
return $object->getContentType();
|
||||
}
|
||||
return false;
|
||||
@@ -392,14 +368,15 @@ class Swift extends \OC\Files\Storage\Common {
|
||||
$path .= '/';
|
||||
}
|
||||
|
||||
$object = $this->container->getPartialObject($path);
|
||||
$object = $this->getContainer()->getPartialObject($path);
|
||||
$object->saveMetadata($metadata);
|
||||
return true;
|
||||
} else {
|
||||
$customHeaders = array('content-type' => 'text/plain');
|
||||
$mimeType = \OC_Helper::getMimetypeDetector()->detectPath($path);
|
||||
$customHeaders = array('content-type' => $mimeType);
|
||||
$metadataHeaders = DataObject::stockHeaders($metadata);
|
||||
$allHeaders = $customHeaders + $metadataHeaders;
|
||||
$this->container->uploadObject($path, '', $allHeaders);
|
||||
$this->getContainer()->uploadObject($path, '', $allHeaders);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -415,7 +392,7 @@ class Swift extends \OC\Files\Storage\Common {
|
||||
$this->unlink($path2);
|
||||
|
||||
try {
|
||||
$source = $this->container->getPartialObject($path1);
|
||||
$source = $this->getContainer()->getPartialObject($path1);
|
||||
$source->copy($this->bucket.'/'.$path2);
|
||||
} catch (ClientErrorResponseException $e) {
|
||||
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
|
||||
@@ -428,7 +405,7 @@ class Swift extends \OC\Files\Storage\Common {
|
||||
$this->unlink($path2);
|
||||
|
||||
try {
|
||||
$source = $this->container->getPartialObject($path1 . '/');
|
||||
$source = $this->getContainer()->getPartialObject($path1 . '/');
|
||||
$source->copy($this->bucket.'/'.$path2 . '/');
|
||||
} catch (ClientErrorResponseException $e) {
|
||||
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
|
||||
@@ -486,16 +463,71 @@ class Swift extends \OC\Files\Storage\Common {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the connection
|
||||
*
|
||||
* @return OpenCloud\ObjectStore\Service connected client
|
||||
* @throws \Exception if connection could not be made
|
||||
*/
|
||||
public function getConnection() {
|
||||
if (!is_null($this->connection)) {
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
$settings = array(
|
||||
'username' => $this->params['user'],
|
||||
);
|
||||
|
||||
if (!empty($this->params['password'])) {
|
||||
$settings['password'] = $this->params['password'];
|
||||
} else if (!empty($this->params['key'])) {
|
||||
$settings['apiKey'] = $this->params['key'];
|
||||
}
|
||||
|
||||
if (!empty($this->params['tenant'])) {
|
||||
$settings['tenantName'] = $this->params['tenant'];
|
||||
}
|
||||
|
||||
if (!empty($this->params['timeout'])) {
|
||||
$settings['timeout'] = $this->params['timeout'];
|
||||
}
|
||||
|
||||
$this->anchor = new OpenStack($this->params['url'], $settings);
|
||||
|
||||
$this->connection = $this->anchor->objectStoreService($this->params['service_name'], $this->params['region']);
|
||||
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the initialized object store container.
|
||||
*
|
||||
* @return OpenCloud\ObjectStore\Resource\Container
|
||||
*/
|
||||
public function getContainer() {
|
||||
if (!is_null($this->container)) {
|
||||
return $this->container;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->container = $this->getConnection()->getContainer($this->bucket);
|
||||
} catch (ClientErrorResponseException $e) {
|
||||
$this->container = $this->getConnection()->createContainer($this->bucket);
|
||||
}
|
||||
|
||||
if (!$this->file_exists('.')) {
|
||||
$this->mkdir('.');
|
||||
}
|
||||
|
||||
return $this->container;
|
||||
}
|
||||
|
||||
public function writeBack($tmpFile) {
|
||||
if (!isset(self::$tmpFiles[$tmpFile])) {
|
||||
return false;
|
||||
}
|
||||
$fileData = fopen($tmpFile, 'r');
|
||||
$this->container->uploadObject(self::$tmpFiles[$tmpFile], $fileData);
|
||||
$this->getContainer()->uploadObject(self::$tmpFiles[$tmpFile], $fileData);
|
||||
unlink($tmpFile);
|
||||
}
|
||||
|
||||
|
||||
0
apps/files_external/personal.php
Executable file → Normal file
0
apps/files_external/personal.php
Executable file → Normal file
@@ -23,9 +23,10 @@
|
||||
OC_Util::checkAdminUser();
|
||||
|
||||
OCP\Util::addScript('files_external', 'settings');
|
||||
OCP\Util::addscript('3rdparty', 'chosen/chosen.jquery.min');
|
||||
OCP\Util::addStyle('files_external', 'settings');
|
||||
OCP\Util::addStyle('3rdparty', 'chosen/chosen');
|
||||
|
||||
OCP\Util::addScript('core', 'select2/select2');
|
||||
OCP\Util::addStyle('core', 'select2/select2');
|
||||
|
||||
$backends = OC_Mount_Config::getBackends();
|
||||
$personal_backends = array();
|
||||
@@ -46,9 +47,6 @@ $tmpl->assign('isAdminPage', true);
|
||||
$tmpl->assign('mounts', OC_Mount_Config::getSystemMountPoints());
|
||||
$tmpl->assign('backends', $backends);
|
||||
$tmpl->assign('personal_backends', $personal_backends);
|
||||
$tmpl->assign('groups', OC_Group::getGroups());
|
||||
$tmpl->assign('users', OCP\User::getUsers());
|
||||
$tmpl->assign('userDisplayNames', OC_User::getDisplayNames());
|
||||
$tmpl->assign('dependencies', OC_Mount_Config::checkDependencies());
|
||||
$tmpl->assign('allowUserMounting', OCP\Config::getAppValue('files_external', 'allow_user_mounting', 'yes'));
|
||||
return $tmpl->fetchPage();
|
||||
|
||||
@@ -8,12 +8,11 @@
|
||||
<th><?php p($l->t('Folder name')); ?></th>
|
||||
<th><?php p($l->t('External storage')); ?></th>
|
||||
<th><?php p($l->t('Configuration')); ?></th>
|
||||
<!--<th><?php p($l->t('Options')); ?></th> -->
|
||||
<?php if ($_['isAdminPage']) print_unescaped('<th>'.$l->t('Available for').'</th>'); ?>
|
||||
<th> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody width="100%">
|
||||
<tbody>
|
||||
<?php $_['mounts'] = array_merge($_['mounts'], array('' => array())); ?>
|
||||
<?php foreach ($_['mounts'] as $mount): ?>
|
||||
<tr <?php print_unescaped(isset($mount['mountpoint']) ? 'class="'.OC_Util::sanitizeHTML($mount['class']).'"' : 'id="addMountPoint"'); ?>>
|
||||
@@ -24,7 +23,9 @@
|
||||
</td>
|
||||
<td class="mountPoint"><input type="text" name="mountPoint"
|
||||
value="<?php p(isset($mount['mountpoint']) ? $mount['mountpoint'] : ''); ?>"
|
||||
placeholder="<?php p($l->t('Folder name')); ?>" /></td>
|
||||
data-mountpoint="<?php p(isset($mount['mountpoint']) ? $mount['mountpoint'] : ''); ?>"
|
||||
placeholder="<?php p($l->t('Folder name')); ?>" />
|
||||
</td>
|
||||
<?php if (!isset($mount['mountpoint'])): ?>
|
||||
<td class="backend">
|
||||
<select id="selectBackend" data-configurations='<?php p(json_encode($_['backends'])); ?>'>
|
||||
@@ -36,10 +37,10 @@
|
||||
</select>
|
||||
</td>
|
||||
<?php else: ?>
|
||||
<td class="backend"
|
||||
data-class="<?php p($mount['class']); ?>"><?php p($mount['backend']); ?></td>
|
||||
<td class="backend" data-class="<?php p($mount['class']); ?>"><?php p($mount['backend']); ?>
|
||||
</td>
|
||||
<?php endif; ?>
|
||||
<td class ="configuration" width="100%">
|
||||
<td class ="configuration">
|
||||
<?php if (isset($mount['options'])): ?>
|
||||
<?php foreach ($mount['options'] as $parameter => $value): ?>
|
||||
<?php if (isset($_['backends'][$mount['class']]['configuration'][$parameter])): ?>
|
||||
@@ -87,31 +88,8 @@
|
||||
print_unescaped(json_encode($mount['applicable']['groups'])); ?>'
|
||||
data-applicable-users='<?php if (isset($mount['applicable']['users']))
|
||||
print_unescaped(json_encode($mount['applicable']['users'])); ?>'>
|
||||
<select class="chzn-select"
|
||||
multiple style="width:20em;"
|
||||
data-placeholder="<?php p($l->t('No user or group')); ?>">
|
||||
<option value="all"
|
||||
<?php if (empty($mount['class']) || (isset($mount['applicable']['users']) && in_array('all', $mount['applicable']['users']))) print_unescaped('selected="selected"');?> >
|
||||
<?php p($l->t('All Users')); ?>
|
||||
</option>
|
||||
<optgroup label="<?php p($l->t('Groups')); ?>">
|
||||
<?php foreach ($_['groups'] as $group): ?>
|
||||
<option value="<?php p($group); ?>(group)"
|
||||
<?php if (isset($mount['applicable']['groups']) && in_array($group, $mount['applicable']['groups'])): ?>
|
||||
selected="selected"
|
||||
<?php endif; ?>><?php p($group); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</optgroup>
|
||||
<optgroup label="<?php p($l->t('Users')); ?>">
|
||||
<?php foreach ($_['users'] as $user): ?>
|
||||
<option value="<?php p($user); ?>"
|
||||
<?php if (isset($mount['applicable']['users']) && in_array($user, $mount['applicable']['users'])): ?>
|
||||
selected="selected"
|
||||
<?php endif; ?>><?php p($_['userDisplayNames'][$user]); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</optgroup>
|
||||
</select>
|
||||
</td>
|
||||
<input type="hidden" class="applicableUsers" style="width:20em;" value=""/>
|
||||
</td>
|
||||
<?php endif; ?>
|
||||
<td <?php if (isset($mount['mountpoint'])): ?>class="remove"
|
||||
<?php else: ?>style="visibility:hidden;"
|
||||
@@ -149,7 +127,7 @@
|
||||
action="<?php p(OCP\Util::linkTo('files_external', 'ajax/addRootCertificate.php')); ?>">
|
||||
<h2><?php p($l->t('SSL root certificates'));?></h2>
|
||||
<table id="sslCertificate" data-admin='<?php print_unescaped(json_encode($_['isAdminPage'])); ?>'>
|
||||
<tbody width="100%">
|
||||
<tbody>
|
||||
<?php foreach ($_['certs'] as $rootCert): ?>
|
||||
<tr id="<?php p($rootCert) ?>">
|
||||
<td class="rootCert"><?php p($rootCert) ?></td>
|
||||
|
||||
@@ -38,29 +38,11 @@ class AmazonS3 extends Storage {
|
||||
|
||||
public function tearDown() {
|
||||
if ($this->instance) {
|
||||
$connection = $this->instance->getConnection();
|
||||
|
||||
try {
|
||||
// NOTE(berendt): clearBucket() is not working with Ceph
|
||||
$iterator = $connection->getIterator('ListObjects', array(
|
||||
'Bucket' => $this->config['amazons3']['bucket']
|
||||
));
|
||||
|
||||
foreach ($iterator as $object) {
|
||||
$connection->deleteObject(array(
|
||||
'Bucket' => $this->config['amazons3']['bucket'],
|
||||
'Key' => $object['Key']
|
||||
));
|
||||
}
|
||||
} catch (S3Exception $e) {
|
||||
}
|
||||
|
||||
$connection->deleteBucket(array(
|
||||
'Bucket' => $this->config['amazons3']['bucket']
|
||||
));
|
||||
|
||||
//wait some seconds for completing the replication
|
||||
sleep(30);
|
||||
$this->instance->rmdir('');
|
||||
}
|
||||
}
|
||||
|
||||
public function testStat() {
|
||||
$this->markTestSkipped('S3 doesn\'t update the parents folder mtime');
|
||||
}
|
||||
}
|
||||
|
||||
117
apps/files_external/tests/amazons3migration.php
Normal file
117
apps/files_external/tests/amazons3migration.php
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* ownCloud
|
||||
*
|
||||
* @author Jörn Friedrich Dreyer
|
||||
* @copyright 2012 Jörn Friedrich Dreyer jfd@owncloud.com
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library 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 along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
namespace Test\Files\Storage;
|
||||
|
||||
class AmazonS3Migration extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
/**
|
||||
* @var \OC\Files\Storage\Storage instance
|
||||
*/
|
||||
protected $instance;
|
||||
|
||||
public function setUp () {
|
||||
$uuid = uniqid();
|
||||
|
||||
$this->params['key'] = 'key'.$uuid;
|
||||
$this->params['secret'] = 'secret'.$uuid;
|
||||
$this->params['bucket'] = 'bucket'.$uuid;
|
||||
|
||||
$this->oldId = 'amazon::' . $this->params['key'] . md5($this->params['secret']);
|
||||
$this->newId = 'amazon::' . $this->params['bucket'];
|
||||
}
|
||||
|
||||
public function tearDown () {
|
||||
$this->deleteStorage($this->oldId);
|
||||
$this->deleteStorage($this->newId);
|
||||
}
|
||||
|
||||
public function testUpdateLegacyOnlyId () {
|
||||
// add storage ids
|
||||
$oldCache = new \OC\Files\Cache\Cache($this->oldId);
|
||||
|
||||
// add file to old cache
|
||||
$fileId = $oldCache->put('/', array('size' => 0, 'mtime' => time(), 'mimetype' => 'httpd/directory'));
|
||||
|
||||
try {
|
||||
$this->instance = new \OC\Files\Storage\AmazonS3($this->params);
|
||||
} catch (\Exception $e) {
|
||||
//ignore
|
||||
}
|
||||
$storages = $this->getStorages();
|
||||
|
||||
$this->assertTrue(isset($storages[$this->newId]));
|
||||
$this->assertFalse(isset($storages[$this->oldId]));
|
||||
$this->assertSame((int)$oldCache->getNumericStorageId(), (int)$storages[$this->newId]);
|
||||
|
||||
list($storageId, $path) = \OC\Files\Cache\Cache::getById($fileId);
|
||||
|
||||
$this->assertSame($this->newId, $storageId);
|
||||
$this->assertSame('/', $path);
|
||||
}
|
||||
|
||||
public function testUpdateLegacyAndNewId () {
|
||||
// add storage ids
|
||||
|
||||
$oldCache = new \OC\Files\Cache\Cache($this->oldId);
|
||||
new \OC\Files\Cache\Cache($this->newId);
|
||||
|
||||
// add file to old cache
|
||||
$fileId = $oldCache->put('/', array('size' => 0, 'mtime' => time(), 'mimetype' => 'httpd/directory'));
|
||||
|
||||
try {
|
||||
$this->instance = new \OC\Files\Storage\AmazonS3($this->params);
|
||||
} catch (\Exception $e) {
|
||||
//ignore
|
||||
}
|
||||
$storages = $this->getStorages();
|
||||
|
||||
$this->assertTrue(isset($storages[$this->newId]));
|
||||
$this->assertFalse(isset($storages[$this->oldId]));
|
||||
|
||||
$this->assertNull(\OC\Files\Cache\Cache::getById($fileId), 'old filecache has not been cleared');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $storages
|
||||
* @return array
|
||||
*/
|
||||
public function getStorages() {
|
||||
$storages = array();
|
||||
$stmt = \OC::$server->getDatabaseConnection()->prepare(
|
||||
'SELECT `numeric_id`, `id` FROM `*PREFIX*storages` WHERE `id` IN (?, ?)'
|
||||
);
|
||||
$stmt->execute(array($this->oldId, $this->newId));
|
||||
while ($row = $stmt->fetch()) {
|
||||
$storages[$row['id']] = $row['numeric_id'];
|
||||
}
|
||||
return $storages;
|
||||
}
|
||||
|
||||
public function deleteStorage($id) {
|
||||
$stmt = \OC::$server->getDatabaseConnection()->prepare(
|
||||
'DELETE FROM `*PREFIX*storages` WHERE `id` = ?'
|
||||
);
|
||||
$stmt->execute(array($id));
|
||||
}
|
||||
}
|
||||
328
apps/files_external/tests/etagpropagator.php
Normal file
328
apps/files_external/tests/etagpropagator.php
Normal file
@@ -0,0 +1,328 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
|
||||
namespace Tests\Files_External;
|
||||
|
||||
use OC\Files\Filesystem;
|
||||
use OC\User\User;
|
||||
|
||||
class EtagPropagator extends \PHPUnit_Framework_TestCase {
|
||||
protected function getUser() {
|
||||
return new User(uniqid(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \PHPUnit_Framework_MockObject_MockObject | \OC\Files\Cache\ChangePropagator
|
||||
*/
|
||||
protected function getChangePropagator() {
|
||||
return $this->getMockBuilder('\OC\Files\Cache\ChangePropagator')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \PHPUnit_Framework_MockObject_MockObject | \OCP\IConfig
|
||||
*/
|
||||
protected function getConfig() {
|
||||
$appConfig = array();
|
||||
$userConfig = array();
|
||||
$mock = $this->getMockBuilder('\OCP\IConfig')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$mock->expects($this->any())
|
||||
->method('getAppValue')
|
||||
->will($this->returnCallback(function ($appId, $key, $default = null) use (&$appConfig) {
|
||||
if (isset($appConfig[$appId]) and isset($appConfig[$appId][$key])) {
|
||||
return $appConfig[$appId][$key];
|
||||
} else {
|
||||
return $default;
|
||||
}
|
||||
}));
|
||||
$mock->expects($this->any())
|
||||
->method('setAppValue')
|
||||
->will($this->returnCallback(function ($appId, $key, $value) use (&$appConfig) {
|
||||
if (!isset($appConfig[$appId])) {
|
||||
$appConfig[$appId] = array();
|
||||
}
|
||||
$appConfig[$appId][$key] = $value;
|
||||
}));
|
||||
$mock->expects($this->any())
|
||||
->method('getAppKeys')
|
||||
->will($this->returnCallback(function ($appId) use (&$appConfig) {
|
||||
if (!isset($appConfig[$appId])) {
|
||||
$appConfig[$appId] = array();
|
||||
}
|
||||
return array_keys($appConfig[$appId]);
|
||||
}));
|
||||
|
||||
$mock->expects($this->any())
|
||||
->method('getUserValue')
|
||||
->will($this->returnCallback(function ($userId, $appId, $key, $default = null) use (&$userConfig) {
|
||||
if (isset($userConfig[$userId]) and isset($userConfig[$userId][$appId]) and isset($userConfig[$userId][$appId][$key])) {
|
||||
return $userConfig[$userId][$appId][$key];
|
||||
} else {
|
||||
return $default;
|
||||
}
|
||||
}));
|
||||
$mock->expects($this->any())
|
||||
->method('setUserValue')
|
||||
->will($this->returnCallback(function ($userId, $appId, $key, $value) use (&$userConfig) {
|
||||
if (!isset($userConfig[$userId])) {
|
||||
$userConfig[$userId] = array();
|
||||
}
|
||||
if (!isset($userConfig[$userId][$appId])) {
|
||||
$userConfig[$userId][$appId] = array();
|
||||
}
|
||||
$userConfig[$userId][$appId][$key] = $value;
|
||||
}));
|
||||
|
||||
return $mock;
|
||||
}
|
||||
|
||||
public function testSingleUserMount() {
|
||||
$time = time();
|
||||
$user = $this->getUser();
|
||||
$config = $this->getConfig();
|
||||
$changePropagator = $this->getChangePropagator();
|
||||
$propagator = new \OCA\Files_External\EtagPropagator($user, $changePropagator, $config);
|
||||
|
||||
$changePropagator->expects($this->once())
|
||||
->method('addChange')
|
||||
->with('/test');
|
||||
$changePropagator->expects($this->once())
|
||||
->method('propagateChanges')
|
||||
->with($time);
|
||||
|
||||
$propagator->updateHook(array(
|
||||
Filesystem::signal_param_path => '/test',
|
||||
Filesystem::signal_param_mount_type => \OC_Mount_Config::MOUNT_TYPE_USER,
|
||||
Filesystem::signal_param_users => $user->getUID(),
|
||||
), $time);
|
||||
}
|
||||
|
||||
public function testGlobalMountNoDirectUpdate() {
|
||||
$time = time();
|
||||
$user = $this->getUser();
|
||||
$config = $this->getConfig();
|
||||
$changePropagator = $this->getChangePropagator();
|
||||
$propagator = new \OCA\Files_External\EtagPropagator($user, $changePropagator, $config);
|
||||
|
||||
// not updated directly
|
||||
$changePropagator->expects($this->never())
|
||||
->method('addChange');
|
||||
$changePropagator->expects($this->never())
|
||||
->method('propagateChanges');
|
||||
|
||||
$propagator->updateHook(array(
|
||||
Filesystem::signal_param_path => '/test',
|
||||
Filesystem::signal_param_mount_type => \OC_Mount_Config::MOUNT_TYPE_USER,
|
||||
Filesystem::signal_param_users => 'all',
|
||||
), $time);
|
||||
|
||||
// mount point marked as dirty
|
||||
$this->assertEquals(array('/test'), $config->getAppKeys('files_external'));
|
||||
$this->assertEquals($time, $config->getAppValue('files_external', '/test'));
|
||||
}
|
||||
|
||||
public function testGroupMountNoDirectUpdate() {
|
||||
$time = time();
|
||||
$user = $this->getUser();
|
||||
$config = $this->getConfig();
|
||||
$changePropagator = $this->getChangePropagator();
|
||||
$propagator = new \OCA\Files_External\EtagPropagator($user, $changePropagator, $config);
|
||||
|
||||
// not updated directly
|
||||
$changePropagator->expects($this->never())
|
||||
->method('addChange');
|
||||
$changePropagator->expects($this->never())
|
||||
->method('propagateChanges');
|
||||
|
||||
$propagator->updateHook(array(
|
||||
Filesystem::signal_param_path => '/test',
|
||||
Filesystem::signal_param_mount_type => \OC_Mount_Config::MOUNT_TYPE_GROUP,
|
||||
Filesystem::signal_param_users => 'test',
|
||||
), $time);
|
||||
|
||||
// mount point marked as dirty
|
||||
$this->assertEquals(array('/test'), $config->getAppKeys('files_external'));
|
||||
$this->assertEquals($time, $config->getAppValue('files_external', '/test'));
|
||||
}
|
||||
|
||||
public function testGlobalMountNoDirtyMountPoint() {
|
||||
$time = time();
|
||||
$user = $this->getUser();
|
||||
$config = $this->getConfig();
|
||||
$changePropagator = $this->getChangePropagator();
|
||||
$propagator = new \OCA\Files_External\EtagPropagator($user, $changePropagator, $config);
|
||||
|
||||
$changePropagator->expects($this->never())
|
||||
->method('addChange');
|
||||
$changePropagator->expects($this->never())
|
||||
->method('propagateChanges');
|
||||
|
||||
$propagator->propagateDirtyMountPoints($time);
|
||||
|
||||
$this->assertEquals(0, $config->getUserValue($user->getUID(), 'files_external', '/test', 0));
|
||||
}
|
||||
|
||||
public function testGlobalMountDirtyMountPointFirstTime() {
|
||||
$time = time();
|
||||
$user = $this->getUser();
|
||||
$config = $this->getConfig();
|
||||
$changePropagator = $this->getChangePropagator();
|
||||
$propagator = new \OCA\Files_External\EtagPropagator($user, $changePropagator, $config);
|
||||
|
||||
$config->setAppValue('files_external', '/test', $time - 10);
|
||||
|
||||
$changePropagator->expects($this->once())
|
||||
->method('addChange')
|
||||
->with('/test');
|
||||
$changePropagator->expects($this->once())
|
||||
->method('propagateChanges')
|
||||
->with($time);
|
||||
|
||||
$propagator->propagateDirtyMountPoints($time);
|
||||
|
||||
$this->assertEquals($time, $config->getUserValue($user->getUID(), 'files_external', '/test'));
|
||||
}
|
||||
|
||||
public function testGlobalMountNonDirtyMountPoint() {
|
||||
$time = time();
|
||||
$user = $this->getUser();
|
||||
$config = $this->getConfig();
|
||||
$changePropagator = $this->getChangePropagator();
|
||||
$propagator = new \OCA\Files_External\EtagPropagator($user, $changePropagator, $config);
|
||||
|
||||
$config->setAppValue('files_external', '/test', $time - 10);
|
||||
$config->setUserValue($user->getUID(), 'files_external', '/test', $time - 10);
|
||||
|
||||
$changePropagator->expects($this->never())
|
||||
->method('addChange');
|
||||
$changePropagator->expects($this->never())
|
||||
->method('propagateChanges');
|
||||
|
||||
$propagator->propagateDirtyMountPoints($time);
|
||||
|
||||
$this->assertEquals($time - 10, $config->getUserValue($user->getUID(), 'files_external', '/test'));
|
||||
}
|
||||
|
||||
public function testGlobalMountNonDirtyMountPointOtherUser() {
|
||||
$time = time();
|
||||
$user = $this->getUser();
|
||||
$user2 = $this->getUser();
|
||||
$config = $this->getConfig();
|
||||
$changePropagator = $this->getChangePropagator();
|
||||
$propagator = new \OCA\Files_External\EtagPropagator($user, $changePropagator, $config);
|
||||
|
||||
$config->setAppValue('files_external', '/test', $time - 10);
|
||||
$config->setUserValue($user2->getUID(), 'files_external', '/test', $time - 10);
|
||||
|
||||
$changePropagator->expects($this->once())
|
||||
->method('addChange')
|
||||
->with('/test');
|
||||
$changePropagator->expects($this->once())
|
||||
->method('propagateChanges')
|
||||
->with($time);
|
||||
|
||||
$propagator->propagateDirtyMountPoints($time);
|
||||
|
||||
$this->assertEquals($time, $config->getUserValue($user->getUID(), 'files_external', '/test'));
|
||||
}
|
||||
|
||||
public function testGlobalMountDirtyMountPointSecondTime() {
|
||||
$time = time();
|
||||
$user = $this->getUser();
|
||||
$config = $this->getConfig();
|
||||
$changePropagator = $this->getChangePropagator();
|
||||
$propagator = new \OCA\Files_External\EtagPropagator($user, $changePropagator, $config);
|
||||
|
||||
$config->setAppValue('files_external', '/test', $time - 10);
|
||||
$config->setUserValue($user->getUID(), 'files_external', '/test', $time - 20);
|
||||
|
||||
$changePropagator->expects($this->once())
|
||||
->method('addChange')
|
||||
->with('/test');
|
||||
$changePropagator->expects($this->once())
|
||||
->method('propagateChanges')
|
||||
->with($time);
|
||||
|
||||
$propagator->propagateDirtyMountPoints($time);
|
||||
|
||||
$this->assertEquals($time, $config->getUserValue($user->getUID(), 'files_external', '/test'));
|
||||
}
|
||||
|
||||
public function testGlobalMountMultipleUsers() {
|
||||
$time = time();
|
||||
$config = $this->getConfig();
|
||||
$user1 = $this->getUser();
|
||||
$user2 = $this->getUser();
|
||||
$user3 = $this->getUser();
|
||||
$changePropagator1 = $this->getChangePropagator();
|
||||
$changePropagator2 = $this->getChangePropagator();
|
||||
$changePropagator3 = $this->getChangePropagator();
|
||||
$propagator1 = new \OCA\Files_External\EtagPropagator($user1, $changePropagator1, $config);
|
||||
$propagator2 = new \OCA\Files_External\EtagPropagator($user2, $changePropagator2, $config);
|
||||
$propagator3 = new \OCA\Files_External\EtagPropagator($user3, $changePropagator3, $config);
|
||||
|
||||
$config->setAppValue('files_external', '/test', $time - 10);
|
||||
|
||||
$changePropagator1->expects($this->once())
|
||||
->method('addChange')
|
||||
->with('/test');
|
||||
$changePropagator1->expects($this->once())
|
||||
->method('propagateChanges')
|
||||
->with($time);
|
||||
|
||||
$propagator1->propagateDirtyMountPoints($time);
|
||||
|
||||
$this->assertEquals($time, $config->getUserValue($user1->getUID(), 'files_external', '/test'));
|
||||
$this->assertEquals(0, $config->getUserValue($user2->getUID(), 'files_external', '/test', 0));
|
||||
$this->assertEquals(0, $config->getUserValue($user3->getUID(), 'files_external', '/test', 0));
|
||||
|
||||
$changePropagator2->expects($this->once())
|
||||
->method('addChange')
|
||||
->with('/test');
|
||||
$changePropagator2->expects($this->once())
|
||||
->method('propagateChanges')
|
||||
->with($time);
|
||||
|
||||
$propagator2->propagateDirtyMountPoints($time);
|
||||
|
||||
$this->assertEquals($time, $config->getUserValue($user1->getUID(), 'files_external', '/test'));
|
||||
$this->assertEquals($time, $config->getUserValue($user2->getUID(), 'files_external', '/test', 0));
|
||||
$this->assertEquals(0, $config->getUserValue($user3->getUID(), 'files_external', '/test', 0));
|
||||
}
|
||||
|
||||
public function testGlobalMountMultipleDirtyMountPoints() {
|
||||
$time = time();
|
||||
$user = $this->getUser();
|
||||
$config = $this->getConfig();
|
||||
$changePropagator = $this->getChangePropagator();
|
||||
$propagator = new \OCA\Files_External\EtagPropagator($user, $changePropagator, $config);
|
||||
|
||||
$config->setAppValue('files_external', '/test', $time - 10);
|
||||
$config->setAppValue('files_external', '/foo', $time - 50);
|
||||
$config->setAppValue('files_external', '/bar', $time - 70);
|
||||
|
||||
$config->setUserValue($user->getUID(), 'files_external', '/foo', $time - 70);
|
||||
$config->setUserValue($user->getUID(), 'files_external', '/bar', $time - 70);
|
||||
|
||||
$changePropagator->expects($this->exactly(2))
|
||||
->method('addChange');
|
||||
$changePropagator->expects($this->once())
|
||||
->method('propagateChanges')
|
||||
->with($time);
|
||||
|
||||
$propagator->propagateDirtyMountPoints($time);
|
||||
|
||||
$this->assertEquals($time, $config->getUserValue($user->getUID(), 'files_external', '/test'));
|
||||
$this->assertEquals($time, $config->getUserValue($user->getUID(), 'files_external', '/foo'));
|
||||
$this->assertEquals($time - 70, $config->getUserValue($user->getUID(), 'files_external', '/bar'));
|
||||
}
|
||||
}
|
||||
@@ -20,14 +20,48 @@
|
||||
*
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../lib/base.php';
|
||||
|
||||
class Test_Mount_Config_Dummy_Storage {
|
||||
public function test() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class Test_Mount_Config_Hook_Test {
|
||||
static $signal;
|
||||
static $params;
|
||||
|
||||
public static function setUpHooks() {
|
||||
self::clear();
|
||||
\OCP\Util::connectHook(
|
||||
\OC\Files\Filesystem::CLASSNAME,
|
||||
\OC\Files\Filesystem::signal_create_mount,
|
||||
'\Test_Mount_Config_Hook_Test', 'createHookCallback');
|
||||
\OCP\Util::connectHook(
|
||||
\OC\Files\Filesystem::CLASSNAME,
|
||||
\OC\Files\Filesystem::signal_delete_mount,
|
||||
'\Test_Mount_Config_Hook_Test', 'deleteHookCallback');
|
||||
}
|
||||
|
||||
public static function clear() {
|
||||
self::$signal = null;
|
||||
self::$params = null;
|
||||
}
|
||||
|
||||
public static function createHookCallback($params) {
|
||||
self::$signal = \OC\Files\Filesystem::signal_create_mount;
|
||||
self::$params = $params;
|
||||
}
|
||||
|
||||
public static function deleteHookCallback($params) {
|
||||
self::$signal = \OC\Files\Filesystem::signal_delete_mount;
|
||||
self::$params = $params;
|
||||
}
|
||||
|
||||
public static function getLastCall() {
|
||||
return array(self::$signal, self::$params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class Test_Mount_Config
|
||||
*/
|
||||
@@ -79,9 +113,11 @@ class Test_Mount_Config extends \PHPUnit_Framework_TestCase {
|
||||
);
|
||||
|
||||
OC_Mount_Config::$skipTest = true;
|
||||
Test_Mount_Config_Hook_Test::setupHooks();
|
||||
}
|
||||
|
||||
public function tearDown() {
|
||||
Test_Mount_Config_Hook_Test::clear();
|
||||
OC_Mount_Config::$skipTest = false;
|
||||
|
||||
\OC_User::deleteUser(self::TEST_USER2);
|
||||
@@ -329,6 +365,102 @@ class Test_Mount_Config extends \PHPUnit_Framework_TestCase {
|
||||
$this->assertEquals(array_keys($options), array_keys($savedOptions));
|
||||
}
|
||||
|
||||
public function testHooks() {
|
||||
$mountPoint = '/test';
|
||||
$mountType = 'user';
|
||||
$applicable = 'all';
|
||||
$isPersonal = false;
|
||||
|
||||
$mountConfig = array(
|
||||
'host' => 'smbhost',
|
||||
'user' => 'smbuser',
|
||||
'password' => 'smbpassword',
|
||||
'share' => 'smbshare',
|
||||
'root' => 'smbroot'
|
||||
);
|
||||
|
||||
// write config
|
||||
$this->assertTrue(
|
||||
OC_Mount_Config::addMountPoint(
|
||||
$mountPoint,
|
||||
'\OC\Files\Storage\SMB',
|
||||
$mountConfig,
|
||||
$mountType,
|
||||
$applicable,
|
||||
$isPersonal
|
||||
)
|
||||
);
|
||||
|
||||
list($hookName, $params) = Test_Mount_Config_Hook_Test::getLastCall();
|
||||
$this->assertEquals(
|
||||
\OC\Files\Filesystem::signal_create_mount,
|
||||
$hookName
|
||||
);
|
||||
$this->assertEquals(
|
||||
$mountPoint,
|
||||
$params[\OC\Files\Filesystem::signal_param_path]
|
||||
);
|
||||
$this->assertEquals(
|
||||
$mountType,
|
||||
$params[\OC\Files\Filesystem::signal_param_mount_type]
|
||||
);
|
||||
$this->assertEquals(
|
||||
$applicable,
|
||||
$params[\OC\Files\Filesystem::signal_param_users]
|
||||
);
|
||||
|
||||
Test_Mount_Config_Hook_Test::clear();
|
||||
|
||||
// edit
|
||||
$mountConfig['host'] = 'anothersmbhost';
|
||||
$this->assertTrue(
|
||||
OC_Mount_Config::addMountPoint(
|
||||
$mountPoint,
|
||||
'\OC\Files\Storage\SMB',
|
||||
$mountConfig,
|
||||
$mountType,
|
||||
$applicable,
|
||||
$isPersonal
|
||||
)
|
||||
);
|
||||
|
||||
// hook must not be called on edit
|
||||
list($hookName, $params) = Test_Mount_Config_Hook_Test::getLastCall();
|
||||
$this->assertEquals(
|
||||
null,
|
||||
$hookName
|
||||
);
|
||||
|
||||
Test_Mount_Config_Hook_Test::clear();
|
||||
|
||||
$this->assertTrue(
|
||||
OC_Mount_Config::removeMountPoint(
|
||||
$mountPoint,
|
||||
$mountType,
|
||||
$applicable,
|
||||
$isPersonal
|
||||
)
|
||||
);
|
||||
|
||||
list($hookName, $params) = Test_Mount_Config_Hook_Test::getLastCall();
|
||||
$this->assertEquals(
|
||||
\OC\Files\Filesystem::signal_delete_mount,
|
||||
$hookName
|
||||
);
|
||||
$this->assertEquals(
|
||||
$mountPoint,
|
||||
$params[\OC\Files\Filesystem::signal_param_path]
|
||||
);
|
||||
$this->assertEquals(
|
||||
$mountType,
|
||||
$params[\OC\Files\Filesystem::signal_param_mount_type]
|
||||
);
|
||||
$this->assertEquals(
|
||||
$applicable,
|
||||
$params[\OC\Files\Filesystem::signal_param_users]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test password obfuscation
|
||||
*/
|
||||
@@ -800,4 +932,41 @@ class Test_Mount_Config extends \PHPUnit_Framework_TestCase {
|
||||
$this->assertEquals($priority,
|
||||
$mountPoints['/'.self::TEST_USER1.'/files/ext']['priority']);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test for correct personal configuration loading in file sharing scenarios
|
||||
*/
|
||||
public function testMultiUserPersonalConfigLoading() {
|
||||
$mountConfig = array(
|
||||
'host' => 'somehost',
|
||||
'user' => 'someuser',
|
||||
'password' => 'somepassword',
|
||||
'root' => 'someroot'
|
||||
);
|
||||
|
||||
// Create personal mount point
|
||||
$this->assertTrue(
|
||||
OC_Mount_Config::addMountPoint(
|
||||
'/ext',
|
||||
'\OC\Files\Storage\SMB',
|
||||
$mountConfig,
|
||||
OC_Mount_Config::MOUNT_TYPE_USER,
|
||||
self::TEST_USER1,
|
||||
true
|
||||
)
|
||||
);
|
||||
|
||||
// Ensure other user can read mount points
|
||||
\OC_User::setUserId(self::TEST_USER2);
|
||||
$mountPointsMe = OC_Mount_Config::getAbsoluteMountPoints(self::TEST_USER2);
|
||||
$mountPointsOther = OC_Mount_Config::getAbsoluteMountPoints(self::TEST_USER1);
|
||||
|
||||
$this->assertEquals(0, count($mountPointsMe));
|
||||
$this->assertEquals(1, count($mountPointsOther));
|
||||
$this->assertTrue(isset($mountPointsOther['/'.self::TEST_USER1.'/files/ext']));
|
||||
$this->assertEquals('\OC\Files\Storage\SMB',
|
||||
$mountPointsOther['/'.self::TEST_USER1.'/files/ext']['class']);
|
||||
$this->assertEquals($mountConfig,
|
||||
$mountPointsOther['/'.self::TEST_USER1.'/files/ext']['options']);
|
||||
}
|
||||
}
|
||||
|
||||
83
apps/files_external/tests/owncloudfunctions.php
Normal file
83
apps/files_external/tests/owncloudfunctions.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Vincent Petry <pvince81@owncloud.com>
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
|
||||
namespace Test\Files\Storage;
|
||||
|
||||
class OwnCloudFunctions extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
function configUrlProvider() {
|
||||
return array(
|
||||
array(
|
||||
array(
|
||||
'host' => 'testhost',
|
||||
'root' => 'testroot',
|
||||
'secure' => false
|
||||
),
|
||||
'http://testhost/remote.php/webdav/testroot/',
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'host' => 'testhost',
|
||||
'root' => 'testroot',
|
||||
'secure' => true
|
||||
),
|
||||
'https://testhost/remote.php/webdav/testroot/',
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'host' => 'http://testhost',
|
||||
'root' => 'testroot',
|
||||
'secure' => false
|
||||
),
|
||||
'http://testhost/remote.php/webdav/testroot/',
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'host' => 'https://testhost',
|
||||
'root' => 'testroot',
|
||||
'secure' => false
|
||||
),
|
||||
'https://testhost/remote.php/webdav/testroot/',
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'host' => 'https://testhost/testroot',
|
||||
'root' => '',
|
||||
'secure' => false
|
||||
),
|
||||
'https://testhost/testroot/remote.php/webdav/',
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'host' => 'https://testhost/testroot',
|
||||
'root' => 'subdir',
|
||||
'secure' => false
|
||||
),
|
||||
'https://testhost/testroot/remote.php/webdav/subdir/',
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'host' => 'http://testhost/testroot',
|
||||
'root' => 'subdir',
|
||||
'secure' => true
|
||||
),
|
||||
'http://testhost/testroot/remote.php/webdav/subdir/',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider configUrlProvider
|
||||
*/
|
||||
public function testConfig($config, $expectedUri) {
|
||||
$config['user'] = 'someuser';
|
||||
$config['password'] = 'somepassword';
|
||||
$instance = new \OC\Files\Storage\OwnCloud($config);
|
||||
$this->assertEquals($expectedUri, $instance->createBaseUri());
|
||||
}
|
||||
}
|
||||
@@ -24,25 +24,39 @@ $owner = $_POST['owner'];
|
||||
$name = $_POST['name'];
|
||||
$password = $_POST['password'];
|
||||
|
||||
// Check for invalid name
|
||||
if(!\OCP\Util::isValidFileName($name)) {
|
||||
\OCP\JSON::error(array('data' => array('message' => $l->t('The mountpoint name contains invalid characters.'))));
|
||||
exit();
|
||||
}
|
||||
|
||||
$user = \OC::$server->getUserSession()->getUser();
|
||||
$uid = ($user) ? $user->getUID() : null;
|
||||
$externalManager = new \OCA\Files_Sharing\External\Manager(
|
||||
\OC::$server->getDatabaseConnection(),
|
||||
\OC\Files\Filesystem::getMountManager(),
|
||||
\OC\Files\Filesystem::getLoader(),
|
||||
\OC::$server->getUserSession()
|
||||
$uid
|
||||
);
|
||||
|
||||
$name = OCP\Files::buildNotExistingFileName('/', $name);
|
||||
|
||||
$mount = $externalManager->addShare($remote, $token, $password, $name, $owner);
|
||||
/**
|
||||
* @var \OCA\Files_Sharing\External\Storage $storage
|
||||
*/
|
||||
$storage = $mount->getStorage();
|
||||
$result = $storage->file_exists('');
|
||||
if($result){
|
||||
$storage->getScanner()->scanAll();
|
||||
\OCP\JSON::success();
|
||||
// check for ssl cert
|
||||
if (substr($remote, 0, 5) === 'https' and !OC_Util::getUrlContent($remote)) {
|
||||
\OCP\JSON::error(array('data' => array('message' => $l->t("Invalid or untrusted ssl certificate"))));
|
||||
exit;
|
||||
} else {
|
||||
$externalManager->removeShare($mount->getMountPoint());
|
||||
\OCP\JSON::error(array('data' => array('message' => $l->t("Couldn't add remote share"))));
|
||||
$mount = $externalManager->addShare($remote, $token, $password, $name, $owner);
|
||||
/**
|
||||
* @var \OCA\Files_Sharing\External\Storage $storage
|
||||
*/
|
||||
$storage = $mount->getStorage();
|
||||
$result = $storage->file_exists('');
|
||||
if ($result) {
|
||||
$storage->getScanner()->scanAll();
|
||||
\OCP\JSON::success();
|
||||
} else {
|
||||
$externalManager->removeShare($mount->getMountPoint());
|
||||
\OCP\JSON::error(array('data' => array('message' => $l->t("Couldn't add remote share"))));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,10 +70,6 @@ if(substr($path, 0, 1) === '/') {
|
||||
$path = substr($path, 1);
|
||||
}
|
||||
|
||||
if ($keepAspect === true) {
|
||||
$maxY = $maxX;
|
||||
}
|
||||
|
||||
if($maxX === 0 || $maxY === 0) {
|
||||
\OC_Response::setStatus(\OC_Response::STATUS_BAD_REQUEST);
|
||||
\OC_Log::write('core-preview', 'x and/or y set to 0', \OC_Log::DEBUG);
|
||||
|
||||
@@ -14,7 +14,8 @@ function testUrl($url) {
|
||||
try {
|
||||
$result = file_get_contents($url);
|
||||
$data = json_decode($result);
|
||||
return is_object($data) and !empty($data->version);
|
||||
// public link mount is only supported in ownCloud 7+
|
||||
return is_object($data) and !empty($data->version) and version_compare($data->version, '7.0.0', '>=');
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ $l = OC_L10N::get('files_sharing');
|
||||
OC::$CLASSPATH['OC_Share_Backend_File'] = 'files_sharing/lib/share/file.php';
|
||||
OC::$CLASSPATH['OC_Share_Backend_Folder'] = 'files_sharing/lib/share/folder.php';
|
||||
OC::$CLASSPATH['OC\Files\Storage\Shared'] = 'files_sharing/lib/sharedstorage.php';
|
||||
OC::$CLASSPATH['OC\Files\Cache\SharedScanner'] = 'files_sharing/lib/scanner.php';
|
||||
OC::$CLASSPATH['OC\Files\Cache\Shared_Cache'] = 'files_sharing/lib/cache.php';
|
||||
OC::$CLASSPATH['OC\Files\Cache\Shared_Permissions'] = 'files_sharing/lib/permissions.php';
|
||||
OC::$CLASSPATH['OC\Files\Cache\Shared_Updater'] = 'files_sharing/lib/updater.php';
|
||||
@@ -12,6 +13,9 @@ OC::$CLASSPATH['OCA\Files\Share\Api'] = 'files_sharing/lib/api.php';
|
||||
OC::$CLASSPATH['OCA\Files\Share\Maintainer'] = 'files_sharing/lib/maintainer.php';
|
||||
OC::$CLASSPATH['OCA\Files\Share\Proxy'] = 'files_sharing/lib/proxy.php';
|
||||
|
||||
// Exceptions
|
||||
OC::$CLASSPATH['OCA\Files_Sharing\Exceptions\BrokenPath'] = 'files_sharing/lib/exceptions.php';
|
||||
|
||||
\OCP\App::registerAdmin('files_sharing', 'settings-admin');
|
||||
|
||||
\OCA\Files_Sharing\Helper::registerHooks();
|
||||
@@ -24,30 +28,38 @@ OCP\Util::addScript('files_sharing', 'external');
|
||||
|
||||
OC_FileProxy::register(new OCA\Files\Share\Proxy());
|
||||
|
||||
\OCA\Files\App::getNavigationManager()->add(
|
||||
array(
|
||||
"id" => 'sharingin',
|
||||
"appname" => 'files_sharing',
|
||||
"script" => 'list.php',
|
||||
"order" => 10,
|
||||
"name" => $l->t('Shared with you')
|
||||
)
|
||||
);
|
||||
\OCA\Files\App::getNavigationManager()->add(
|
||||
array(
|
||||
"id" => 'sharingout',
|
||||
"appname" => 'files_sharing',
|
||||
"script" => 'list.php',
|
||||
"order" => 15,
|
||||
"name" => $l->t('Shared with others')
|
||||
)
|
||||
);
|
||||
\OCA\Files\App::getNavigationManager()->add(
|
||||
array(
|
||||
"id" => 'sharinglinks',
|
||||
"appname" => 'files_sharing',
|
||||
"script" => 'list.php',
|
||||
"order" => 20,
|
||||
"name" => $l->t('Shared by link')
|
||||
)
|
||||
);
|
||||
$config = \OC::$server->getConfig();
|
||||
if ($config->getAppValue('core', 'shareapi_enabled', 'yes') === 'yes') {
|
||||
|
||||
\OCA\Files\App::getNavigationManager()->add(
|
||||
array(
|
||||
"id" => 'sharingin',
|
||||
"appname" => 'files_sharing',
|
||||
"script" => 'list.php',
|
||||
"order" => 10,
|
||||
"name" => $l->t('Shared with you')
|
||||
)
|
||||
);
|
||||
|
||||
if (\OCP\Util::isSharingDisabledForUser() === false) {
|
||||
|
||||
\OCA\Files\App::getNavigationManager()->add(
|
||||
array(
|
||||
"id" => 'sharingout',
|
||||
"appname" => 'files_sharing',
|
||||
"script" => 'list.php',
|
||||
"order" => 15,
|
||||
"name" => $l->t('Shared with others')
|
||||
)
|
||||
);
|
||||
\OCA\Files\App::getNavigationManager()->add(
|
||||
array(
|
||||
"id" => 'sharinglinks',
|
||||
"appname" => 'files_sharing',
|
||||
"script" => 'list.php',
|
||||
"order" => 20,
|
||||
"name" => $l->t('Shared by link')
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
<field>
|
||||
<name>password</name>
|
||||
<type>text</type>
|
||||
<notnull>true</notnull>
|
||||
<notnull>false</notnull>
|
||||
<length>64</length>
|
||||
<comments>Optional password for the public share</comments>
|
||||
</field>
|
||||
|
||||
@@ -2,7 +2,11 @@
|
||||
<info>
|
||||
<id>files_sharing</id>
|
||||
<name>Share Files</name>
|
||||
<description>File sharing between users</description>
|
||||
<description>
|
||||
This application enables users to share files within ownCloud. If enabled, the admin can choose which groups can share files. The applicable users can then share files and folders with other users and groups within ownCloud. In addition, if the admin enables the share link feature, an external link can be used to share files with other users outside of ownCloud. Admins can also enforce passwords, expirations dates, and enable server to server sharing via share links, as well as sharing from mobile devices.
|
||||
Turning the feature off removes shared files and folders on the server for all share recipients, and also on the sync clients and mobile apps. More information is available in the ownCloud Documentation.
|
||||
|
||||
</description>
|
||||
<licence>AGPL</licence>
|
||||
<author>Michael Gapczynski, Bjoern Schiessle</author>
|
||||
<requiremin>4.93</requiremin>
|
||||
@@ -15,4 +19,5 @@
|
||||
<files>public.php</files>
|
||||
<webdav>publicwebdav.php</webdav>
|
||||
</public>
|
||||
<ocsid>166050</ocsid>
|
||||
</info>
|
||||
|
||||
@@ -32,6 +32,7 @@ function updateFilePermissions($chunkSize = 99) {
|
||||
}
|
||||
}
|
||||
|
||||
$connection = \OC_DB::getConnection();
|
||||
$chunkedPermissionList = array_chunk($updatedRows, $chunkSize, true);
|
||||
|
||||
foreach ($chunkedPermissionList as $subList) {
|
||||
@@ -39,7 +40,7 @@ function updateFilePermissions($chunkSize = 99) {
|
||||
//update share table
|
||||
$ids = implode(',', array_keys($subList));
|
||||
foreach ($subList as $id => $permission) {
|
||||
$statement .= "WHEN " . $id . " THEN " . $permission . " ";
|
||||
$statement .= "WHEN " . $connection->quote($id, \PDO::PARAM_INT) . " THEN " . $permission . " ";
|
||||
}
|
||||
$statement .= ' END WHERE `id` IN (' . $ids . ')';
|
||||
|
||||
@@ -95,6 +96,7 @@ function removeSharedFolder($mkdirs = true, $chunkSize = 99) {
|
||||
}
|
||||
|
||||
$chunkedShareList = array_chunk($shares, $chunkSize, true);
|
||||
$connection = \OC_DB::getConnection();
|
||||
|
||||
foreach ($chunkedShareList as $subList) {
|
||||
|
||||
@@ -102,7 +104,7 @@ function removeSharedFolder($mkdirs = true, $chunkSize = 99) {
|
||||
//update share table
|
||||
$ids = implode(',', array_keys($subList));
|
||||
foreach ($subList as $id => $target) {
|
||||
$statement .= "WHEN " . $id . " THEN '/Shared" . $target . "' ";
|
||||
$statement .= "WHEN " . $connection->quote($id, \PDO::PARAM_INT) . " THEN " . $connection->quote('/Shared' . $target, \PDO::PARAM_STR);
|
||||
}
|
||||
$statement .= ' END WHERE `id` IN (' . $ids . ')';
|
||||
|
||||
@@ -111,5 +113,8 @@ function removeSharedFolder($mkdirs = true, $chunkSize = 99) {
|
||||
$query->execute(array());
|
||||
}
|
||||
|
||||
// set config to keep the Shared folder as the default location for new shares
|
||||
\OCA\Files_Sharing\Helper::setShareFolder('/Shared');
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.5.2
|
||||
0.5.3
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
background: #fff;
|
||||
text-align: center;
|
||||
margin: 45px auto 0;
|
||||
min-height: 150px;
|
||||
min-height: 600px;
|
||||
}
|
||||
|
||||
#preview .notCreatable {
|
||||
@@ -44,8 +44,9 @@ p.info a {
|
||||
max-width:100%;
|
||||
}
|
||||
|
||||
/* fix multiselect bar offset on shared page */
|
||||
thead {
|
||||
padding-left: 0 !important; /* fixes multiselect bar offset on shared page */
|
||||
left: 0 !important;
|
||||
}
|
||||
|
||||
#data-upload-form {
|
||||
@@ -89,21 +90,48 @@ thead {
|
||||
}
|
||||
|
||||
/* within #save */
|
||||
#remote_address {
|
||||
margin: 0;
|
||||
height: 14px;
|
||||
padding: 6px;
|
||||
#save .save-form {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#save button {
|
||||
#remote_address {
|
||||
margin: 0;
|
||||
width: 130px;
|
||||
height: 14px;
|
||||
padding: 6px;
|
||||
padding-right: 24px;
|
||||
}
|
||||
|
||||
.ie8 #remote_address {
|
||||
padding-right: 30px;
|
||||
}
|
||||
|
||||
#save #save-button,
|
||||
#save #save-button-confirm {
|
||||
margin: 0 5px;
|
||||
height: 28px;
|
||||
padding-bottom: 4px;
|
||||
line-height: 14px;
|
||||
}
|
||||
|
||||
#save .save-form [type="submit"] {
|
||||
margin: 0 5px;
|
||||
height: 28px;
|
||||
padding-bottom: 4px;
|
||||
#save-button-confirm {
|
||||
position: absolute;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
margin: 2px 4px !important;
|
||||
right: 0;
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";
|
||||
filter: alpha(opacity=50);
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
.ie8 #save-button-confirm {
|
||||
margin: 2px 0 !important;
|
||||
}
|
||||
|
||||
#save-button-confirm:hover,
|
||||
#save-button-confirm:focus {
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";
|
||||
filter: alpha(opacity=100);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@@ -92,6 +92,21 @@ OCA.Sharing.App = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Destroy the app
|
||||
*/
|
||||
destroy: function() {
|
||||
OCA.Files.fileActions.off('setDefault.app-sharing', this._onActionsUpdated);
|
||||
OCA.Files.fileActions.off('registerAction.app-sharing', this._onActionsUpdated);
|
||||
this.removeSharingIn();
|
||||
this.removeSharingOut();
|
||||
this.removeSharingLinks();
|
||||
this._inFileList = null;
|
||||
this._outFileList = null;
|
||||
this._linkFileList = null;
|
||||
delete this._globalActionsInitialized;
|
||||
},
|
||||
|
||||
_createFileActions: function() {
|
||||
// inherit file actions from the files app
|
||||
var fileActions = new OCA.Files.FileActions();
|
||||
@@ -100,6 +115,14 @@ OCA.Sharing.App = {
|
||||
fileActions.registerDefaultActions();
|
||||
fileActions.merge(OCA.Files.fileActions);
|
||||
|
||||
if (!this._globalActionsInitialized) {
|
||||
// in case actions are registered later
|
||||
this._onActionsUpdated = _.bind(this._onActionsUpdated, this);
|
||||
OCA.Files.fileActions.on('setDefault.app-sharing', this._onActionsUpdated);
|
||||
OCA.Files.fileActions.on('registerAction.app-sharing', this._onActionsUpdated);
|
||||
this._globalActionsInitialized = true;
|
||||
}
|
||||
|
||||
// when the user clicks on a folder, redirect to the corresponding
|
||||
// folder in the files app instead of opening it directly
|
||||
fileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename, context) {
|
||||
@@ -110,6 +133,23 @@ OCA.Sharing.App = {
|
||||
return fileActions;
|
||||
},
|
||||
|
||||
_onActionsUpdated: function(ev) {
|
||||
_.each([this._inFileList, this._outFileList, this._linkFileList], function(list) {
|
||||
if (!list) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ev.action) {
|
||||
list.fileActions.registerAction(ev.action);
|
||||
} else if (ev.defaultAction) {
|
||||
list.fileActions.setDefault(
|
||||
ev.defaultAction.mime,
|
||||
ev.defaultAction.name
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_extendFileList: function(fileList) {
|
||||
// remove size column from summary
|
||||
fileList.fileSummary.$el.find('.filesize').remove();
|
||||
|
||||
@@ -42,13 +42,40 @@
|
||||
}
|
||||
};
|
||||
if (!passwordProtected) {
|
||||
OC.dialogs.confirm(t('files_sharing', 'Add {name} from {owner}@{remote}', {name: name, owner: owner, remote: remoteClean})
|
||||
, t('files_sharing','Add Share'), callback, true);
|
||||
OC.dialogs.confirm(
|
||||
t(
|
||||
'files_sharing',
|
||||
'Do you want to add the remote share {name} from {owner}@{remote}?',
|
||||
{name: name, owner: owner, remote: remoteClean}
|
||||
),
|
||||
t('files_sharing','Remote share'),
|
||||
callback,
|
||||
true
|
||||
).then(this._adjustDialog);
|
||||
} else {
|
||||
OC.dialogs.prompt(t('files_sharing', 'Add {name} from {owner}@{remote}', {name: name, owner: owner, remote: remoteClean})
|
||||
, t('files_sharing','Add Share'), callback, true, t('files_sharing','Password'), true);
|
||||
OC.dialogs.prompt(
|
||||
t(
|
||||
'files_sharing',
|
||||
'Do you want to add the remote share {name} from {owner}@{remote}?',
|
||||
{name: name, owner: owner, remote: remoteClean}
|
||||
),
|
||||
t('files_sharing','Remote share'),
|
||||
callback,
|
||||
true,
|
||||
t('files_sharing','Remote share password'),
|
||||
true
|
||||
).then(this._adjustDialog);
|
||||
}
|
||||
};
|
||||
|
||||
OCA.Sharing._adjustDialog = function() {
|
||||
var $dialog = $('.oc-dialog:visible');
|
||||
var $buttons = $dialog.find('button');
|
||||
// hack the buttons
|
||||
$dialog.find('.ui-icon').remove();
|
||||
$buttons.eq(0).text(t('core', 'Cancel'));
|
||||
$buttons.eq(1).text(t('files_sharing', 'Add remote share'));
|
||||
};
|
||||
})();
|
||||
|
||||
$(document).ready(function () {
|
||||
|
||||
@@ -56,6 +56,11 @@ OCA.Sharing.PublicApp = {
|
||||
}
|
||||
|
||||
var mimetype = $('#mimetype').val();
|
||||
var mimetypeIcon = $('#mimetypeIcon').val();
|
||||
mimetypeIcon = mimetypeIcon.substring(0, mimetypeIcon.length - 3);
|
||||
mimetypeIcon = mimetypeIcon + 'svg';
|
||||
|
||||
var previewSupported = $('#previewSupported').val();
|
||||
|
||||
if (typeof FileActions !== 'undefined') {
|
||||
// Show file preview if previewer is available, images are already handled by the template
|
||||
@@ -68,20 +73,25 @@ OCA.Sharing.PublicApp = {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// dynamically load image previews
|
||||
if (mimetype.substr(0, mimetype.indexOf('/')) === 'image') {
|
||||
var params = {
|
||||
x: $(document).width() * window.devicePixelRatio,
|
||||
y: $(document).height() * window.devicePixelRatio,
|
||||
a: 'true',
|
||||
file: encodeURIComponent(this.initialDir + $('#filename').val()),
|
||||
t: $('#sharingToken').val(),
|
||||
scalingup: 0
|
||||
};
|
||||
|
||||
var params = {
|
||||
x: $(document).width() * window.devicePixelRatio,
|
||||
a: 'true',
|
||||
file: encodeURIComponent(this.initialDir + $('#filename').val()),
|
||||
t: $('#sharingToken').val(),
|
||||
scalingup: 0
|
||||
};
|
||||
|
||||
var img = $('<img class="publicpreview">');
|
||||
var img = $('<img class="publicpreview">');
|
||||
if (previewSupported === 'true' || mimetype.substr(0, mimetype.indexOf('/')) === 'image' && mimetype !== 'image/svg+xml') {
|
||||
img.attr('src', OC.filePath('files_sharing', 'ajax', 'publicpreview.php') + '?' + OC.buildQueryString(params));
|
||||
img.appendTo('#imgframe');
|
||||
} else if (mimetype.substr(0, mimetype.indexOf('/')) !== 'video') {
|
||||
img.attr('src', OC.Util.replaceSVGIcon(mimetypeIcon));
|
||||
img.attr('width', 128);
|
||||
img.appendTo('#imgframe');
|
||||
}
|
||||
|
||||
if (this.fileList) {
|
||||
@@ -163,7 +173,7 @@ OCA.Sharing.PublicApp = {
|
||||
OCA.Sharing.PublicApp._saveToOwnCloud(remote, token, owner, name, isProtected);
|
||||
});
|
||||
|
||||
$('#save > button').click(function () {
|
||||
$('#save #save-button').click(function () {
|
||||
$(this).hide();
|
||||
$('.save-form').css('display', 'inline');
|
||||
$('#remote_address').focus();
|
||||
@@ -200,7 +210,7 @@ OCA.Sharing.PublicApp = {
|
||||
// this check needs to happen on the server due to the Content Security Policy directive
|
||||
$.get(OC.generateUrl('apps/files_sharing/testremote'), {remote: remote}).then(function (protocol) {
|
||||
if (protocol !== 'http' && protocol !== 'https') {
|
||||
OC.dialogs.alert(t('files_sharing', 'No ownCloud installation found at {remote}', {remote: remote}),
|
||||
OC.dialogs.alert(t('files_sharing', 'No ownCloud installation (7 or higher) found at {remote}', {remote: remote}),
|
||||
t('files_sharing', 'Invalid ownCloud url'));
|
||||
} else {
|
||||
OC.redirect(protocol + '://' + url);
|
||||
|
||||
@@ -143,7 +143,7 @@
|
||||
' data-action="Share-Notification" href="#" original-title="">' +
|
||||
' <img class="svg" src="' + OC.imagePath('core', 'actions/share') + '"></img>';
|
||||
$tr.find('.fileactions').append(function() {
|
||||
var shareBy = t('files_sharing', 'Shared by {owner}', {owner: escapeHTML($tr.attr('data-share-owner'))});
|
||||
var shareBy = escapeHTML($tr.attr('data-share-owner'));
|
||||
var $result = $(shareNotification + '<span> ' + shareBy + '</span></span>');
|
||||
$result.on('click', function() {
|
||||
return false;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user