• No results found

Data Store Correctness

We use our approach to verify correctness of abstract data stores in two respects: data integrity and access control.

2.3.1

Data Integrity

Given an abstract data store DS = hC, L, A, I, R, P i, we call DS consistent if and only if all reachable states of DS satisfy all the invariants of DS, i.e., DS is consistent if and only if for all hO, T, U i ∈ DSR, for all i ∈ I, i(hO, T, U i) = true. The verification problem for data integrity is to determine if a given abstract data store is consistent. Since we do not bound the sizes of the classes and relations in a data model, and since we allow arbitrary quantification in invariant properties, determining if a data store specified in the ADS language is consistent or not is an undecidable verification problem.

As we discussed earlier, in RESTful applications, each action is required to preserve the invariants of the data model independently of the previous execution history. This is a stronger requirement that implies the consistency condition defined above, and can be formulated as inductive invariant verification. An inductive invariant is a property where given a state that satisfies the property, all the next states of that state also satisfy the property. In other words, an inductive invariant is a property that is preserved by all transitions (i.e., all actions) of a given system. An abstract data store DS = hC, L, A, I, R, P i is consistent if the conjunction of all the invariants i ∈ I is an inductive invariant. In other words, an abstract data store DS = hC, L, A, I, R, P i is consistent if and only if every execution of every action preserves all invariants:

Fcons ≡ ∀a ∈ A : ∀hs, s0, αi ∈ a : (∀i ∈ I : i(s)) ⇒ (∀i ∈ I : i(s0))

2.3.2

Access Control

Authorization is fundamentally about ensuring that users can view and modify only data that they have been permitted to view and modify, using a set of methods that are permitted to them. The goal of approach is to check whether all operations that could

Abstract Data Stores Chapter 2

be executed by any action and for any user are permissible with respect to the access control policy.

Given a data store DS = hC, L, A, I, R, P i, a permit p = hg, ops, ei ∈ P accepts an operation op on an object o in state s, denoted as p[op, s, o], if and only if the current

user oU has at least one role from g, the operation op is in ops, and o is inside the set

that e evaluates to in data store state s. Formally:

p[op, s, o] ⇔ (∃r ∈ g : hoU, ri ∈ U ) ∧ op ∈ ops ∧ o ∈ e(s)

Now that we defined how to check permissions for a given operation in a given state for a given set of objects, we need to extend this check to cover all possible behaviors.

One question we need to answer before we can do that is: In which state of an action’s execution should we check for permissions? If we choose the pre-state, it becomes impossible to check permissions for object creation because the created objects do not yet exist in the pre-state, as well as tuples that might be necessary to check the access control policy correctly. Similarly, it is impossible to evaluate permissions for the delete operation in the post-state of an execution. In order to handle all possible scenarios, we chose to evaluate creation permissions in the post-state (once all the objects and tuples have been created), deletion permissions in the pre-state (before any objects or tuples have been deleted), and read permissions in the post-state (as this is the state shown to the user of the application).

We define whether an action a ∈ A of a data store DS = hC, L, A, I, R, P i correctly enforces the access control policy as follows. An action correctly enforces the access control policy if and only if, for every execution in a:

• There exists a permit that accepts the creation of every object created by this execution,

• There exists a permit that accepts the deletion of every object deleted by this execution, and

• There exists a permit that accepts the read operation on every object read by this execution.

Formally, an action a correctly enforces the access control policy if and only if:

∀hs = hO, T, U i, s0 = hO0, T0, U0i, αi ∈ a :

∀o ∈ created(hs, s0, αi) ∃p ∈ P : p[{create}, s0, o] ∧ ∀o ∈ deleted(hs, s0, αi) ∃p ∈ P : p[{delete}, s, o] ∧ ∀o ∈ read(hs, s0, αi) ∃p ∈ P : p[{read}, s0, o]

For example, let us take a look at the TodosController#create action in Figure 2.1.

This action will, in its every execution hs, s0, αi, create a single Todo object as well as a pair of tuples. No matter the role of the current user, this creation is covered by the access control policy: if the current user is an admin, by a permit that corresponds to line 24. Otherwise, if the current user is a nonadmin, this creation is covered by the

permit that corresponds to line 27 because, in state s0, the newly created object will be

associated to the current user.

Let us examine the ProjectsController#destroy action in Figure 2.1. Logically, if any

object is deleted, then the action has not been aborted in line 48. Therefore, the current

user is either an admin (in which case the permit in line 24 accepts the object and the

operation), or the current user is a nonadmin but is also the user of the project (which is

covered by the permit in line 27). Since there exists no execution in which the project is deleted without proper authorization, this action correctly enforces the access control policy with regards to deleting objects of the Project class.

Abstract Data Stores Chapter 2