{-# LANGUAGE MultiParamTypeClasses, DeriveDataTypeable,
             FlexibleInstances, UndecidableInstances #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  Security.InfoFlow.Policy.Paralocks
-- Copyright   :  (c) Niklas Broberg 2013
-- License     :  BSD-style (see the file LICENSE.txt)
--
-- Maintainer  :  Niklas Broberg, niklas.broberg@chalmers.se
-- Stability   :  transient
-- Portability :  portable
--
-- This module provides a concrete instantiation of the 
-- Flow Locks Framework, in the form of the Paralocks
-- language, as presented in Broberg & Sands, 
-- "Paralocks - Role-Based Information Flow Control and Beyond",
-- POPL'10.
--
-----------------------------------------------------------------------------
module Security.InfoFlow.Policy.Paralocks where

import Security.InfoFlow.Policy.FlowLocks

import Data.Generics (Data, Typeable)

import Control.Applicative

-- $story
--   In order to provide a complete language,
--   Paralocks needs to provide concrete 
--   representations for:
--   * Actors, here being a concrete data type
--   * Actor sets, here being either singleton 
--     actors or the set of all actors.

{----------------------------------------}
{-    Actor identity representation     -}
{----------------------------------------}

-- | 'ActorIdentity' are approximations of actual
--   identities of actors, to the best of our
--   knowledge.
data ActorIdentity
    = Fresh Int   
    -- ^ We know the identity.
    | Unknown Int [Int]
    -- ^ We don't know the identity, but know it
    --   to be one of the listed ones.
  deriving (Show, Data, Typeable)

instance Eq ActorIdentity where
  Fresh i == Fresh j = i == j
  Unknown i _ == Unknown j _ = i == j
  _ == _ = False

instance ActorId ActorIdentity where
  Unknown i is `mayEq` Unknown j js =
      or [ x == y | x <- (i:is), y <- (j:js) ]

  Unknown _ is `mayEq` Fresh j = j `elem` is
  Fresh i `mayEq` Unknown _ js = i `elem` js
  Fresh i `mayEq` Fresh j      = i == j


{----------------------------------------}
{-       Actor set representation       -}
{----------------------------------------}

-- | In Paralocks, an actor representation
--   can either mention a concrete actor,
--   or be a quantified variable representing
--   any actor. We also include a distinguished
--   'top' element, which is one of the
--   requirements (must form a join semi-lattice).
data ActorSetRep aid
    = SingletonActor aid
    | AnyActor
    | NoActor
  deriving (Eq, Show, Data, Typeable)

instance (Functor m, Monad m, Applicative m, ActorId aid) => 
          PartialOrder m (ActorSetRep aid) where
    AnyActor `leq` _ = return True
    SingletonActor aid1 `leq` SingletonActor aid2 = return $
        aid1 == aid2
    _ `leq` NoActor = return True
    _ `leq` _ = return False

instance (Functor m, Monad m, Applicative m, ActorId aid) => 
          JoinSemiLattice m (ActorSetRep aid) where
    topM = return NoActor

    NoActor `lub` _ = return NoActor
    _ `lub` NoActor = return NoActor

    AnyActor `lub` a = return a
    a `lub` AnyActor = return a

    SingletonActor aid1 `lub` SingletonActor aid2
                       | aid1 == aid2 = return $ SingletonActor aid1
                       | otherwise    = return NoActor

instance (Functor m, Monad m, Applicative m, ActorId aid) => 
          Lattice m (ActorSetRep aid) where
    bottomM = return AnyActor

    NoActor `glb` a = return a
    a `glb` NoActor = return a

    AnyActor `glb` _a = return AnyActor
    _a `glb` AnyActor = return AnyActor

    SingletonActor aid1 `glb` SingletonActor aid2
                       | aid1 == aid2 = return $ SingletonActor aid1
                       | otherwise    = return AnyActor


instance (Functor m, Monad m, Applicative m, ActorId aid) => 
          ActorSet m (ActorSetRep aid) aid where

  inSet aid1 (SingletonActor aid2) = return $ aid1 == aid2
  inSet _aid AnyActor = return True
  inSet _aid NoActor  = return False

  enumSetMembers (SingletonActor aid) = return $ Just [aid]
  enumSetMembers AnyActor = return Nothing
  enumSetMembers NoActor  = return $ Just []

-- This gives us, for free, the following instances:
--
--   instance (Ord name, Functor m, Monad m) => 
--      Containment m (XPolicy name ActorSetRep) 
--                  name ActorSetRep ActorId
--
-- where XPolicy \in { Policy, VarPolicy, MetaPolicy }
