-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathFileSharing2.als
More file actions
206 lines (176 loc) · 4.69 KB
/
FileSharing2.als
File metadata and controls
206 lines (176 loc) · 4.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
module Apps/FileSharing2
open Action
open Reaction
// Composed concepts
open Concepts/Trash[File]
open Concepts/Permalink[File,Token]
one sig T extends Trash {}
one sig P extends Permalink {}
// Types
sig File {}
sig Token {}
// App specific views of the state of the concepts to simplify the specification and visualization
fun uploaded : set File { T.accessible + T.trashed }
fun trashed : set File { T.trashed }
fun shared : File -> Token { P.urls :> (Token - P.revoked) }
fun downloaded : set Token { P.accessed }
// App specific action names
pred upload[f : File] { T.create[f] }
pred delete[f : File] { T.delete[f] }
pred restore[f : File] { T.restore[f] }
pred empty { T.empty }
pred share[f : File, t : Token] { P.share[f,t] }
pred download[t : Token] { P.access[t] }
// The design goal
// The shared tokes are those that have been shared while the respective file was accessible
// and not deleted nor downloaded afterwards
// The downloaded tokens are those that have been accessed when the respective file was accessible
check Design {
always {
no reactions iff {
shared = { f : File, t : Token | before (not (f in trashed and empty[] or download[t]) since (share[f,t] and f in uploaded)) }
downloaded = { t : Token | before once (download[t] and shared.t not in trashed) }
}
}
} for 2 but 10 Action, 10 Reaction expect 0
// Additional properties
check Invariant {
always {
no reactions implies {
shared.Token in uploaded
accessed in revoked
}
}
} for 2 but 10 Action, 10 Reaction expect 0
// Expected revoked value
check Revoked {
always {
no reactions implies {
P.revoked = { t : Token | before once (download[t] or some f : File | t in f.shared and f in trashed and empty[]) }
}
}
} for 2 but 10 Action, 10 Reaction expect 0
// Tokens can only be accessed once
check SingleAccess {
all t : Token | always {
download[t] implies before historically not download[t]
}
} for 2 but 10 Action, 10 Reaction expect 0
// Scenarios
// A file is uploaded, shared, deleted, and the trash is emptied.
// Then a reaction will revoke the token
run Scenario1 {
some f : File, t : Token {
upload[f]; share[f,t]; delete[f]; empty[]
}
eventually always no reactions
} for exactly 1 File, exactly 1 Token, 10 Action, 10 Reaction expect 1
// A file is uploaded, shared, deleted, restored, and downloaded.
run Scenario2 {
some f : File, t : Token {
upload[f]; share[f,t]; delete[f]; restore[f]; download[t]
}
eventually always no reactions
} for exactly 1 File, exactly 1 Token, 10 Action, 10 Reaction expect 1
// A file is uploaded and shared.
// Then one tries to revoke the token, which should not be possible.
run Scenario3 {
some f : File, t : Token {
upload[f]; share[f,t]; P.revoke[t]
}
eventually always no reactions
} for exactly 1 File, exactly 1 Token, 10 Action, 10 Reaction expect 0
// Reactions
/*
reaction empty_revoke
when
T.empty[]
where
f in T.trashed and t in f.shared
then
P.revoke[t]
*/
sig Empty_Revoke extends Reaction { token : Token }
fact {
all x,y : Empty_Revoke | x.token = y.token implies x = y
}
fact {
all t : Token | always {
(some r : Empty_Revoke & reactions_to_add | r.token = t) iff (some f : File | T.empty[] and f in T.trashed and t in f.shared)
(some r : Empty_Revoke & reactions_to_remove | r.token = t) iff P.revoke[t]
}
}
/*
reaction access_revoke
when
P.access[t]
then
P.revoke[t]
*/
sig Access_Revoke extends Reaction { token : Token }
fact {
all x,y : Access_Revoke | x.token = y.token implies x = y
}
fact {
all t : Token | always {
(some r : Access_Revoke & reactions_to_add | r.token = t) iff P.access[t]
(some r : Access_Revoke & reactions_to_remove | r.token = t) iff P.revoke[t]
}
}
/*
reaction share_error
when
P.share[f,t]
where
f not in uploaded
then
error
*/
sig Share_Error extends Reaction { }
fact {
all x,y : Share_Error | x = y
}
fact {
always {
some Share_Error & reactions_to_add iff (some f : File, t : Token | P.share[f,t] and f not in uploaded)
some Share_Error & reactions_to_remove iff error
}
}
/*
reaction revoke_error
when
P.revoke[t]
where
t not in P.accessed and shared.t in uploaded
then
error
*/
sig Revoke_Error extends Reaction { }
fact {
all x,y : Revoke_Error | x = y
}
fact {
always {
some Revoke_Error & reactions_to_add iff (some t : Token | P.revoke[t] and t not in P.accessed and shared.t in uploaded)
some Revoke_Error & reactions_to_remove iff error
}
}
/*
reaction access_error
when
P.access[t]
where
shared.t in T.trashed
then
error
*/
sig Access_Error extends Reaction { }
fact {
all x,y : Access_Error | x = y
}
fact {
always {
some Access_Error & reactions_to_add iff (some t : Token | P.access[t] and shared.t in T.trashed)
some Access_Error & reactions_to_remove iff error
}
}