packages feed

hails 0.9.2.2 → 0.11.0.0

raw patch · 18 files changed

+436/−360 lines, 18 filesdep ~HUnitdep ~QuickCheckdep ~SHAPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: HUnit, QuickCheck, SHA, authenticate, base, base64-bytestring, binary, blaze-builder, bson, bytestring, conduit, containers, cookie, directory, failure, filepath, ghc-paths, http-conduit, http-types, lio, mongoDB, mtl, network, parsec, quickcheck-instances, quickcheck-lio-instances, resourcet, test-framework, test-framework-hunit, test-framework-quickcheck2, text, time, transformers, unix, wai, wai-app-static, wai-extra, wai-test, warp

API changes (from Hackage documentation)

- Hails.Web.Controller: instance MonadLIO DCLabel Controller
- Hails.Web.Controller: instance Routeable (Controller Response)
- Hails.Web.Router: instance Monad RouteM
- Hails.Web.Router: instance Monoid Route
- Hails.Web.Router: instance Routeable (RouteM a)
- Hails.Web.Router: instance Routeable Application
- Hails.Web.Router: instance Routeable Response
+ Hails.HttpClient: InvalidChunkHeaders :: HttpException
+ Hails.HttpClient: ResponseBodyTooShort :: Word64 -> Word64 -> HttpException
+ Hails.HttpServer.Auth: externalAuth :: ByteString -> String -> Middleware
+ Hails.PolicyModule.DSL: unrestricted :: CNF
+ Hails.Web.Controller: csPathParams :: ControllerState -> Query
+ Hails.Web.Controller: instance [safe] MonadLIO DCLabel Controller
+ Hails.Web.Controller: instance [safe] Routeable (Controller Response)
+ Hails.Web.Router: instance [safe] Monad RouteM
+ Hails.Web.Router: instance [safe] Monoid Route
+ Hails.Web.Router: instance [safe] Routeable (RouteM a)
+ Hails.Web.Router: instance [safe] Routeable Application
+ Hails.Web.Router: instance [safe] Routeable Response
- Hails.Data.Hson: genObjectId :: MonadDC m => m ObjectId
+ Hails.Data.Hson: genObjectId :: MonadLIO DCLabel m => m ObjectId
- Hails.Data.Hson: labeledRequestToHson :: MonadDC m => DCLabeled Request -> m (DCLabeled HsonDocument)
+ Hails.Data.Hson: labeledRequestToHson :: MonadLIO DCLabel m => DCLabeled Request -> m (DCLabeled HsonDocument)
- Hails.Database: class InsertLike doc where insert = insertP noPriv insert_ c d = void $ insert c d insertP_ p c d = void $ insertP p c d save = saveP noPriv
+ Hails.Database: class InsertLike doc where insert = insertP mempty insert_ c d = void $ insert c d insertP_ p c d = void $ insertP p c d save = saveP mempty
- Hails.Database.Query: applyCollectionPolicyP :: MonadDC m => DCPriv -> Collection -> HsonDocument -> m (LabeledHsonDocument)
+ Hails.Database.Query: applyCollectionPolicyP :: MonadLIO DCLabel m => DCPriv -> Collection -> HsonDocument -> m (LabeledHsonDocument)
- Hails.Database.Query: class InsertLike doc where insert = insertP noPriv insert_ c d = void $ insert c d insertP_ p c d = void $ insertP p c d save = saveP noPriv
+ Hails.Database.Query: class InsertLike doc where insert = insertP mempty insert_ c d = void $ insert c d insertP_ p c d = void $ insertP p c d save = saveP mempty
- Hails.Database.Structured: class (PolicyModule pm, DCRecord a) => DCLabeledRecord pm a | a -> pm where insertLabeledRecord lrec = insertLabeledRecordP noPriv lrec saveLabeledRecord lrec = saveLabeledRecordP noPriv lrec insertLabeledRecordP p lrec = liftDB $ do { let cName = recordCollection (forceType lrec); ldoc <- toLabeledDocumentP p lrec; insertP p cName ldoc } saveLabeledRecordP p lrec = liftDB $ do { let cName = recordCollection (forceType lrec); ldoc <- toLabeledDocumentP p lrec; saveP p cName ldoc }
+ Hails.Database.Structured: class (PolicyModule pm, DCRecord a) => DCLabeledRecord pm a | a -> pm where insertLabeledRecord lrec = insertLabeledRecordP mempty lrec saveLabeledRecord lrec = saveLabeledRecordP mempty lrec insertLabeledRecordP p lrec = liftDB $ do { let cName = recordCollection (forceType lrec); ldoc <- toLabeledDocumentP p lrec; insertP p cName ldoc } saveLabeledRecordP p lrec = liftDB $ do { let cName = recordCollection (forceType lrec); ldoc <- toLabeledDocumentP p lrec; saveP p cName ldoc }
- Hails.Database.Structured: class DCRecord a where findBy = findByP noPriv findWhere = findWhereP noPriv insertRecord = insertRecordP noPriv saveRecord = saveRecordP noPriv insertRecordP p r = liftDB $ do { insertP p (recordCollection r) $ toDocument r } saveRecordP p r = liftDB $ do { saveP p (recordCollection r) $ toDocument r } findByP p cName k v = findWhereP p (select [k -: v] cName) findWhereP p query = liftDB $ do { mldoc <- findOneP p query; c <- getClearance; case mldoc of { Just ldoc | canFlowToP p (labelOf ldoc) c -> fromDocument `liftM` (liftLIO $ unlabelP p ldoc) _ -> return Nothing } }
+ Hails.Database.Structured: class DCRecord a where findBy = findByP mempty findWhere = findWhereP mempty insertRecord = insertRecordP mempty saveRecord = saveRecordP mempty insertRecordP p r = liftDB $ do { insertP p (recordCollection r) $ toDocument r } saveRecordP p r = liftDB $ do { saveP p (recordCollection r) $ toDocument r } findByP p cName k v = findWhereP p (select [k -: v] cName) findWhereP p query = liftDB $ do { mldoc <- findOneP p query; c <- liftLIO $ getClearance; case mldoc of { Just ldoc | canFlowToP p (labelOf ldoc) c -> fromDocument `liftM` (liftLIO $ unlabelP p ldoc) _ -> return Nothing } }
- Hails.HttpClient: simpleHttpP :: DCPriv -> Request -> DC Response
+ Hails.HttpClient: simpleHttpP :: PrivDesc DCLabel p => Priv p -> Request -> DC Response
- Hails.PolicyModule.DSL: (<==) :: (Role r s m, ToComponent c) => r -> c -> m ()
+ Hails.PolicyModule.DSL: (<==) :: (Role r s m, ToCNF c) => r -> c -> m ()
- Hails.PolicyModule.DSL: (==>) :: (Role r s m, ToComponent c) => r -> c -> m ()
+ Hails.PolicyModule.DSL: (==>) :: (Role r s m, ToCNF c) => r -> c -> m ()
- Hails.Web.Controller: ControllerState :: DCLabeled Request -> ControllerState
+ Hails.Web.Controller: ControllerState :: DCLabeled Request -> Query -> ControllerState

Files

Hails/Data/Hson.hs view
@@ -109,8 +109,7 @@  import           LIO import           LIO.DCLabel-import           LIO.Labeled.TCB-import           LIO.TCB (ioTCB, ShowTCB(..))+import           LIO.TCB  import           Network.Wai.Parse ( FileInfo(..)                                    , sinkRequestBody@@ -321,17 +320,16 @@ -- | Convert a labeled request to a labeled document. Values of fields that -- have a name that ends with @[]@ are converted to arrays and the -- suffix @[]@ is stripped from the name.-labeledRequestToHson :: MonadDC m+labeledRequestToHson :: MonadLIO DCLabel m                      => DCLabeled Request -> m (DCLabeled HsonDocument)-labeledRequestToHson lreq = do-  let origLabel = labelOf lreq-      req       = unlabelTCB lreq+labeledRequestToHson lreq = liftLIO $ do+  let (LabeledTCB origLabel req) = lreq       btype     = fromMaybe UrlEncoded $ getRequestBodyType req   (ps, fs) <- liftLIO . ioTCB $ runResourceT $                 sourceLbs (requestBody req) $$ sinkRequestBody lbsBackEnd btype   let psDoc     = map convertPS ps       fsDoc     = map convertFS fs-  return $ labelTCB origLabel $ arrayify $ psDoc ++ fsDoc+  return $ LabeledTCB origLabel $ arrayify $ psDoc ++ fsDoc   where convertPS (k,v) = HsonField                            (T.pack . S8.unpack $ k)                            (toHsonValue . S8.unpack $ v)@@ -643,7 +641,7 @@ --  -- | Create a fresh ObjectId.-genObjectId :: MonadDC m => m ObjectId+genObjectId :: MonadLIO DCLabel m => m ObjectId genObjectId = liftLIO $ ioTCB Bson.genObjectId  --
Hails/Data/Hson/TCB.hs view
@@ -60,8 +60,8 @@ import qualified Data.Binary.Put as Binary import qualified Data.Binary.Get as Binary -import           LIO.Labeled.TCB (unlabelTCB) import           LIO.DCLabel+import           LIO.TCB  -- | Strict ByeString type S8 = S8.ByteString@@ -171,9 +171,10 @@ -- applied to label the field. hsonToDataBsonTCB :: HsonValue -> Bson.Value hsonToDataBsonTCB (HsonValue b) = bsonToDataBsonTCB b-hsonToDataBsonTCB (HsonLabeled (HasPolicyTCB lv)) =+hsonToDataBsonTCB (HsonLabeled (HasPolicyTCB (LabeledTCB _ lv))) =   toUserDef . hsonDocToDataBsonDocTCB $ -     [ HsonField __hails_HsonLabeled_value $ HsonValue (unlabelTCB lv) ]+     [ HsonField __hails_HsonLabeled_value $+            HsonValue lv ]     where toUserDef = Bson.UserDef                     . Bson.UserDefined                     . strictify
Hails/Database/Core.hs view
@@ -29,6 +29,7 @@   , Pipe, AccessMode(..), master, slaveOk   ) where +import           Data.Monoid import           Control.Monad import           Control.Monad.Trans.State @@ -66,14 +67,14 @@ -- label on collections which can be projected given a 'Database' -- value. getDatabase :: DBAction Database-getDatabase = getDatabaseP noPriv+getDatabase = getDatabaseP mempty  -- | Same as 'getDatabase', but uses privileges when raising the -- current label. getDatabaseP :: DCPriv -> DBAction Database getDatabaseP p = do   db <- dbActionDB `liftM` getActionStateTCB-  taintP p (databaseLabel db)+  liftLIO $ taintP p (databaseLabel db)   return db  -- | Arbitrary monad that can perform database actions.
Hails/Database/Query.hs view
@@ -54,6 +54,7 @@  import           Prelude hiding (lookup) import           Data.Maybe+import           Data.Monoid import           Data.List (sortBy) import qualified Data.List as List import           Data.Map (Map)@@ -65,7 +66,6 @@ import qualified Data.Traversable as T  import           Control.Monad-import           Control.Exception (Exception)  import qualified Data.Bson        as Bson import qualified Database.MongoDB as Mongo@@ -74,8 +74,9 @@                                         , BatchSize)  import           LIO+import           LIO.Error import           LIO.DCLabel-import           LIO.Labeled.TCB (unlabelTCB, labelTCB)+import           LIO.TCB  import           Hails.Data.Hson import           Hails.Data.Hson.TCB@@ -202,7 +203,7 @@   insert :: CollectionName          -> doc          -> DBAction ObjectId-  insert = insertP noPriv+  insert = insertP mempty    -- | Same as 'insert' except it does not return @_id@   insert_ :: CollectionName@@ -233,7 +234,7 @@   save :: CollectionName        -> doc        -> DBAction ()-  save = saveP noPriv+  save = saveP mempty    -- | Same as 'save', but uses privileges when applying the   -- policies and performing label comparisons.@@ -252,9 +253,9 @@     withCollection priv True cName $ \col -> do       -- Already checked that we can write to DB and collection,       -- apply policies:-      ldoc <- applyCollectionPolicyP priv col doc+      (LabeledTCB _ ndoc) <- applyCollectionPolicyP priv col doc       -- No IFC violation, perform insert:-      let bsonDoc = hsonDocToDataBsonDocTCB . unlabelTCB $ ldoc+      let bsonDoc = hsonDocToDataBsonDocTCB ndoc       _id `liftM` (execMongoActionTCB $ Mongo.insert cName bsonDoc)     where _id i = let HsonValue (BsonObjId i') = dataBsonValueToHsonValueTCB i                   in i'@@ -269,11 +270,11 @@         Just (_id :: ObjectId) -> do           mdoc <- findOneP priv $ select [_id_n -: _id] cName           -- If document exists, check that we can overwrite it:-          maybe (return ()) (guardWriteP priv . labelOf) mdoc+          maybe (return ()) (liftLIO . guardWriteP priv . labelOf) mdoc           -- Okay, save document:           saveIt ldoc-      where saveIt ldoc =-              let bsonDoc = hsonDocToDataBsonDocTCB . unlabelTCB $ ldoc+      where saveIt (LabeledTCB _ nd) =+              let bsonDoc = hsonDocToDataBsonDocTCB nd               in execMongoActionTCB $ Mongo.save cName bsonDoc  instance InsertLike LabeledHsonDocument where@@ -284,9 +285,10 @@   -- current computation may insert a document it could otherwise not   -- have created.   insertP priv cName ldoc' = do-    guardInsertOrSaveLabeledHsonDocument priv cName ldoc' $ \ldoc ->+    guardInsertOrSaveLabeledHsonDocument priv cName ldoc' $+      \(LabeledTCB _ doc) ->       -- No IFC violation, perform insert:-      let bsonDoc = hsonDocToDataBsonDocTCB . unlabelTCB $ ldoc+      let bsonDoc = hsonDocToDataBsonDocTCB doc       in _id `liftM` (execMongoActionTCB $ Mongo.insert cName bsonDoc)     where _id i = let HsonValue (BsonObjId i') = dataBsonValueToHsonValueTCB i                   in i'@@ -299,23 +301,21 @@   -- document it could otherwise not have created.   saveP priv cName ldoc' = do     guardInsertOrSaveLabeledHsonDocument priv cName ldoc' $ \ldoc ->-      let doc   = unlabelTCB ldoc+      let (LabeledTCB ld doc)   = ldoc           _id_n = Text.pack "_id"       in case lookup _id_n doc of         Nothing -> saveIt ldoc         Just (_id :: ObjectId) -> do           mdoc <- findOneP priv $ select [_id_n -: _id] cName           -- If document exists, check that we can overwrite it:-          maybe (return ()) (guardWriteP' (labelOf ldoc) . labelOf) mdoc+          maybe (return ()) (liftLIO . guardWriteP' ld . labelOf) mdoc           -- Okay, save document:           saveIt ldoc      where guardWriteP' lnew lold = -             unless (canFlowToP priv lnew lold) $ throwLIO $ -               VMonitorFailure {-                 monitorFailure = CanFlowToViolation-               , monitorMessage = "New document label doesn't flow to the old" }-           saveIt ldoc =-             let bsonDoc = hsonDocToDataBsonDocTCB . unlabelTCB $ ldoc+             unless (canFlowToP priv lnew lold) $ labelErrorP+              "New document label doesn't flow to the old" priv [lnew, lold]+           saveIt (LabeledTCB _ doc) =+             let bsonDoc = hsonDocToDataBsonDocTCB doc              in execMongoActionTCB $ Mongo.save cName bsonDoc  --@@ -346,22 +346,23 @@     withCollection priv True cName $ \col -> do       -- Already checked that we can write to DB and collection       -- Document is labeled, remove label:-      let doc = unlabelTCB ldoc+      let (LabeledTCB ld doc) = ldoc       -- Check that labels are same as if we had applied them       -- Apply policies to the unlabeled document,       -- asserts that labeled values are below collection clearance:       dbPriv <- dbActionPriv `liftM` getActionStateTCB-      ldocTCB <- applyCollectionPolicyP dbPriv col doc+      (LabeledTCB ltcb docTCB) <- applyCollectionPolicyP dbPriv col doc       -- Check that all the fields are the same (i.e., if there was a       -- unlabeled PolicyLabeled value an this will fail):-      let same = compareDoc doc  (unlabelTCB ldocTCB)-      unless same $ throwLIO PolicyViolation-      -- Check that label of the passed in document `canFlowToP`-      -- the label of document created by the policy:-      unless (canFlowToP priv (labelOf ldoc) (labelOf ldocTCB)) $-        throwLIO PolicyViolation+      let same = compareDoc doc docTCB+      liftLIO $ do+        unless same $ throwLIO PolicyViolation+        -- Check that label of the passed in document `canFlowToP`+        -- the label of document created by the policy:+        unless (canFlowToP priv ld ltcb) $+          throwLIO PolicyViolation       -- Perform action on policy-labeled document:-      act ldocTCB+      act $ LabeledTCB ltcb docTCB   where compareDoc d1' d2' =            let d1 = sortDoc d1'               d2 = sortDoc d2'@@ -395,7 +396,7 @@ -- 'order', or 'hint' are /ignored/ (as opposed to throwing an -- exception). find :: Query -> DBAction Cursor-find = findP noPriv+find = findP mempty  -- | Same as 'find', but uses privileges when reading from the -- collection and database.@@ -436,13 +437,13 @@ -- The returned document is labeled according to the underlying -- 'Collection' policy. next :: Cursor -> DBAction (Maybe LabeledHsonDocument)-next = nextP noPriv+next = nextP mempty  -- | Same as 'next', but usess privileges when raising the current label. nextP :: DCPriv -> Cursor -> DBAction (Maybe LabeledHsonDocument) nextP p cur = do   -- Raise current label, can read from DB+collection:-  taintP p $ curLabel cur+  liftLIO $ taintP p $ curLabel cur   -- Read the document:   mMongoDoc <- execMongoActionTCB $ Mongo.next $ curInternal cur   case mMongoDoc of@@ -451,17 +452,16 @@       let doc0 = dataBsonDocToHsonDocTCB mongoDoc       dbPriv <- dbActionPriv `liftM` getActionStateTCB       ldoc <- applyCollectionPolicyP dbPriv (curCollection cur) doc0-      let doc = unlabelTCB ldoc-          l   = labelOf ldoc+      let (LabeledTCB l doc) = ldoc           proj = case curProject cur of                   [] -> id                   xs -> include xs-      return . Just . labelTCB l . proj $ doc+      return . Just . LabeledTCB l . proj $ doc  -- | Fetch the first document satisfying query, or 'Nothing' if not -- documents matched the query. findOne :: Query -> DBAction (Maybe LabeledHsonDocument)-findOne = findOneP noPriv+findOne = findOneP mempty  -- | Same as 'findOne', but uses privileges when performing label -- comparisons.@@ -477,18 +477,18 @@ -- existing documents. That is, the current label must flow -- to the label of each document that matches the selection. delete :: Selection ->  DBAction ()-delete = deleteP noPriv+delete = deleteP mempty  -- | Same as 'delete', but uses privileges. deleteP :: DCPriv -> Selection ->  DBAction () deleteP p sel = do   let qry = select (selectionSelector sel) (selectionCollection sel)   cur <- findP p qry-  forAll cur $ \ld -> do+  forAll cur $ \(LabeledTCB l ld) -> do     -- Can write to the document?-    guardWriteP p (labelOf ld)+    liftLIO $ guardWriteP p l     -- Delete only _this_ document, avoid TOCTTOU-    let doc' = hsonDocToDataBsonDocTCB $ ["_id"] `include` (unlabelTCB ld)+    let doc' = hsonDocToDataBsonDocTCB $ ["_id"] `include` ld     -- Remove this document     execMongoActionTCB $ Mongo.deleteOne $                           Mongo.select doc' (selectionCollection sel)@@ -540,14 +540,16 @@                -> DBAction a withCollection priv isWrite cName act = do   db <- getDatabaseP priv-  -- If this is a write: check that we can write to database:-  when isWrite $ guardWriteP priv (databaseLabel db)-  -- Check that we can read collection names associated with DB:-  cs <- unlabelP priv $ databaseCollections db-  -- Lookup collection name in the collection set associated with DB:-  col <- maybe (throwLIO UnknownCollection) return $ getCol cs-  -- If this is a write: check that we can write to collection:-  when isWrite $ guardWriteP priv (colLabel col)+  col <- liftLIO $ do+    -- If this is a write: check that we can write to database:+    when isWrite $ guardWriteP priv (databaseLabel db)+    -- Check that we can read collection names associated with DB:+    cs <- unlabelP priv $ databaseCollections db+    -- Lookup collection name in the collection set associated with DB:+    col0 <- maybe (throwLIO UnknownCollection) return $ getCol cs+    -- If this is a write: check that we can write to collection:+    when isWrite $ guardWriteP priv (colLabel col0)+    return col0   -- Execute action on collection:   act col     where getCol = listToMaybe . Set.toList . Set.filter ((==cName) . colName)@@ -585,7 +587,7 @@ -- Additionally, these labels must flow to the label of the collection -- clearance. (Of course, in both cases privileges are used to allow for -- more permissive flows.)-applyCollectionPolicyP :: MonadDC m+applyCollectionPolicyP :: MonadLIO DCLabel m                        => DCPriv        -- ^ Privileges                        -> Collection    -- ^ Collection and policies                        -> HsonDocument  -- ^ Document to apply policies to@@ -594,7 +596,7 @@   let doc1 = List.nubBy (\f1 f2 -> fieldName f1 == fieldName f2) doc0   typeCheckDocument fieldPolicies doc1   c <- getClearance-  withClearanceP p ((colClearance col) `lowerBound` c) $ do+  withClearanceP p ((colClearance col) `glb` c) $ do     -- Apply fied policies:     doc2 <- T.for doc1 $ \f@(HsonField n v) ->       case v of
Hails/Database/Structured.hs view
@@ -21,10 +21,9 @@                                  , toLabeledDocumentP, fromLabeledDocumentP                                  ) where -import           Data.Monoid (mappend)+import           Data.Monoid (mappend, mempty) import           Control.Monad (liftM)-import           Control.Exception (SomeException)-                 + import           LIO import           LIO.DCLabel                  @@ -71,13 +70,13 @@   --    ---  findBy = findByP noPriv+  findBy = findByP mempty   ---  findWhere = findWhereP noPriv+  findWhere = findWhereP mempty   ---  insertRecord = insertRecordP noPriv+  insertRecord = insertRecordP mempty   ---  saveRecord = saveRecordP noPriv+  saveRecord = saveRecordP mempty   --   insertRecordP p r = liftDB $ do     insertP p (recordCollection r) $ toDocument r@@ -90,7 +89,7 @@   --   findWhereP p query  = liftDB $ do     mldoc <- findOneP p query-    c <- getClearance+    c <- liftLIO $ getClearance     case mldoc of       Just ldoc | canFlowToP p (labelOf ldoc) c ->                     fromDocument `liftM` (liftLIO $ unlabelP p ldoc)@@ -114,7 +113,7 @@ -- | Find all records that satisfy the query and can be read, subject -- to the current clearance. findAll :: (DCRecord a, MonadDB m) => Query -> m [a]-findAll = findAllP noPriv+findAll = findAllP mempty  -- | Same as 'findAll', but uses privileges. findAllP :: (DCRecord a, MonadDB m)@@ -126,7 +125,7 @@           mldoc <- nextP p cur           case mldoc of             Just ldoc -> do-              c <- getClearance+              c <- liftLIO getClearance               if canFlowTo (labelOf ldoc) c                 then do md <- fromDocument `liftM` (liftLIO $ unlabelP p ldoc)                         cursorToRecords cur $ maybe docs (:docs) md@@ -165,9 +164,9 @@   --    ---  insertLabeledRecord lrec = insertLabeledRecordP noPriv lrec+  insertLabeledRecord lrec = insertLabeledRecordP mempty lrec   ---  saveLabeledRecord lrec = saveLabeledRecordP noPriv lrec+  saveLabeledRecord lrec = saveLabeledRecordP mempty lrec   --   insertLabeledRecordP p lrec = liftDB $ do     let cName = recordCollection (forceType lrec)@@ -184,7 +183,7 @@ toLabeledDocument :: (MonadDB m, DCLabeledRecord pm a)                   => DCLabeled a                   -> m (DCLabeled Document)-toLabeledDocument = toLabeledDocumentP noPriv+toLabeledDocument = toLabeledDocumentP mempty  -- | Uses the policy modules\'s privileges to convert a labeled record -- to a labeled document, if the policy module created an instance of@@ -195,20 +194,26 @@                    -> m (DCLabeled Document) toLabeledDocumentP p' lr = liftDB $ do   pmPriv' <- dbActionPriv `liftM` getActionStateTCB-  -- Fail if not endorsed:-  pmPriv <- liftLIO $ (evaluate . endorseInstance $ lr) >> return pmPriv'-                      `catchLIO` (\(_ :: SomeException) -> return noPriv)-  let p = p' `mappend` pmPriv-  r <- unlabelP p lr-  lcur <- getLabel-  let lres = partDowngradeP p lcur (labelOf lr)-  labelP p lres $ toDocument r+  liftLIO $ do+    -- Fail if not endorsed:+    pmPriv <- (evaluate . endorseInstance $ lr) >> return pmPriv'+                      `catch` (\(_ :: SomeException) -> return mempty)+    let p = p' `mappend` pmPriv+    scopeClearance $ do+      -- raise clearance:+      clr <- getClearance+      setClearanceP p $ clr `lub` (p %% True)+      --+      r <- unlabelP p lr+      lcur <- getLabel+      let lres = downgradeP p lcur `lub` (labelOf lr)+      labelP p lres $ toDocument r  -- | Convert labeled document to labeled record fromLabeledDocument :: forall m pm a. (MonadDB m, DCLabeledRecord pm a)                     => DCLabeled Document                     -> m (DCLabeled a)-fromLabeledDocument = fromLabeledDocumentP noPriv+fromLabeledDocument = fromLabeledDocumentP mempty  -- | Uses the policy modules\'s privileges to convert a labeled document -- to a labeled record, if the policy module created an instance of@@ -221,13 +226,18 @@   pmPriv' <- dbActionPriv `liftM` getActionStateTCB   -- Fail if not endorsed:   pmPriv <- liftLIO $ (evaluate . endorseInstance $ fake) >> return pmPriv'-                      `catchLIO` (\(_ :: SomeException) -> return noPriv)+                      `catch` (\(_ :: SomeException) -> return mempty)   let p = p' `mappend` pmPriv-  doc <- unlabelP p ldoc-  lcur <- getLabel-  let lres = partDowngradeP p lcur (labelOf ldoc)-  rec <- fromDocument doc-  labelP p lres rec+  liftLIO $ scopeClearance $ do+    -- raise clearance:+    clr <- getClearance+    setClearanceP p $ clr `lub` (p %% True)+    -- get at the document+    doc <- liftLIO $ unlabelP p ldoc+    lcur <- liftLIO $ getLabel+    let lres = downgradeP p lcur `lub` (labelOf ldoc)+    rec <- fromDocument doc+    labelP p lres rec     where fake :: DCLabeled a           fake = undefined 
Hails/Database/TCB.hs view
@@ -61,8 +61,7 @@                                         )  import           LIO-import           LIO.TCB (rethrowIoTCB)-import           LIO.Labeled.TCB (labelTCB, unlabelTCB)+import           LIO.TCB import           LIO.DCLabel  import           Hails.Data.Hson@@ -237,8 +236,8 @@                    , dbActionPriv = priv }     where db = DatabaseTCB { databaseName  = dbName                             , databaseLabel = l-                           , databaseCollections = labelTCB l Set.empty }-          l = dcLabel prin prin+                           , databaseCollections = LabeledTCB l Set.empty }+          l = prin %% prin           prin = privDesc priv  -- | Set the label of the underlying database to the supplied label,@@ -253,8 +252,8 @@ setCollectionSetLabelTCB :: DCLabel -> DBAction () setCollectionSetLabelTCB l = updateActionStateTCB $ \s ->   let db = dbActionDB s-     cs = databaseCollections db-     cs' = labelTCB l $! unlabelTCB cs+     (LabeledTCB _ cs) = databaseCollections db+     cs' = LabeledTCB l $! cs  in s { dbActionDB = db { databaseCollections = cs' } }  -- | Associate a collection with underlying database, ignoring IFC.@@ -264,9 +263,9 @@  let db = dbActionDB s  in s { dbActionDB = doUpdate db }   where doUpdate db = -          let cs = databaseCollections db-          in  db { databaseCollections = labelTCB (labelOf cs) $-                                         Set.insert col $ unlabelTCB cs }+          let (LabeledTCB l cs) = databaseCollections db+          in  db { databaseCollections = LabeledTCB l $+                                         Set.insert col cs }  -- | Lift a mongoDB action into the 'DBAction' monad. This function -- always executes the action with "Database.MongoDB"\'s @access@. If@@ -277,7 +276,7 @@   let pipe = dbActionPipe s       mode = dbActionMode s       db   = databaseName . dbActionDB $ s-  liftLIO $ rethrowIoTCB $ do+  liftLIO $ ioTCB $ do     res <- Mongo.access pipe mode db act     case res of       Left err -> throwIO $ ExecFailure err
Hails/HttpClient.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE Trustworthy #-} {-# LANGUAGE FlexibleInstances,-             MultiParamTypeClasses #-}+             MultiParamTypeClasses,+             FlexibleContexts #-} {- |  Exports basic HTTP client functions inside the 'DC' Monad.@@ -95,6 +96,7 @@  import qualified Data.ByteString.Char8 as S8 import qualified Data.Conduit as C+import           Data.Monoid                                import           Control.Failure import           Control.Exception@@ -128,16 +130,17 @@ -- | Perform a simple HTTP(S) request. simpleHttp :: Request  -- ^ Request            -> DC Response-simpleHttp = simpleHttpP noPriv+simpleHttp = simpleHttpP noPrivs  -- | Same as 'simpleHttp', but uses privileges.-simpleHttpP :: DCPriv      -- ^ Privilege+simpleHttpP :: PrivDesc DCLabel p+            => Priv p      -- ^ Privilege             -> Request     -- ^ Request             -> DC Response simpleHttpP p req' = do   let req = req' { proxy = Nothing, socksProxy = Nothing }   guardWriteURLP p req-  resp <- rethrowIoTCB $ C.withManager $ C.httpLbs req+  resp <- ioTCB $ C.withManager $ C.httpLbs req   return $ Response { respStatus  = C.responseStatus resp                     , respHeaders = C.responseHeaders resp                     , respBody    = C.responseBody resp@@ -157,7 +160,7 @@  -- | Simple HTTP GET request. simpleGetHttp :: String -> DC Response-simpleGetHttp = simpleGetHttpP noPriv+simpleGetHttp = simpleGetHttpP mempty  -- | Simple HTTP HEAD request. simpleHeadHttpP :: DCPriv     -- ^ Privilege@@ -169,7 +172,7 @@  -- | Simple HTTP HEAD request. simpleHeadHttp :: String -> DC Response-simpleHeadHttp = simpleHeadHttpP noPriv+simpleHeadHttp = simpleHeadHttpP mempty   --@@ -177,7 +180,7 @@ --  -- | Check that current label can flow to label of request.-guardWriteURLP :: DCPriv -> Request -> DC ()+guardWriteURLP :: PrivDesc DCLabel p => Priv p -> Request -> DC () guardWriteURLP p req = do   let (lr, lw) = labelOfReq req   guardAllocP p lr@@ -203,9 +206,9 @@ -- absolute URL makes senes. labelOfReq :: Request -> (DCLabel, DCLabel) labelOfReq req =-  let scheme = if secure req then "https://" else "http://"-      prin = concat [scheme, S8.unpack (host req), ':' : show (port req)]-  in (dcLabel (toComponent prin) dcTrue, dcLabel dcTrue (toComponent prin))+  let scheme = if secure req then (S8.pack "https://") else (S8.pack "http://")+      prin = principalBS $ S8.concat [scheme, host req, S8.pack ":", S8.pack $ show (port req)]+  in (prin %% True, True %% prin)  -- | Convert a URL into a 'Request'. --
Hails/HttpServer.hs view
@@ -31,17 +31,20 @@   ) where  import qualified Data.List as List+import qualified Data.Set as Set import qualified Data.ByteString.Char8 as S8 import qualified Data.ByteString.Lazy as L import           Data.Conduit-import           Data.Conduit.List+import           Data.Conduit.List hiding (head)+import           Data.Monoid +import           Control.Monad (liftM) import           Control.Monad.IO.Class (liftIO) import           Control.Monad.Error.Class-import           Control.Exception (fromException)   import           Network.HTTP.Types+import           Network.URI (isURI) import qualified Network.Wai as W import qualified Network.Wai.Application.Static as W import           Network.Wai.Middleware.MethodOverridePost@@ -49,8 +52,6 @@ import           LIO import           LIO.TCB import           LIO.DCLabel-import           LIO.DCLabel.Privs.TCB-import           LIO.Labeled.TCB  import           Hails.HttpServer.Types @@ -101,35 +102,35 @@              then response              else Response status403 [] "" --- | Adds the header @X-Hails-Label@ to the response. If the+-- | Adds the header @Content-Security-Policy@ to the response, if the -- label of the computation does not flow to the public label,--- 'dcPub', the JSON field @isPublic@ is set to @true@, otherwise--- it is set to @true@ and the JSON @label@ is set to the secrecy+-- 'dcPublic'.  The @default-src@ directive is set to the secrecy -- component of the response label (if it is a disjunction--- of principals is added). An example may be:------ > X-Hails-Label = { isPublic: true }--- --- or+-- of principals). Currently, @'self'@ is always added to the+-- whitelist. An example may be: ----- > X-Hails-Label = { isPublic: false, label : ["http://google.com:80", "alice"] }+-- > Content-Security-Policy: default-src 'self' http://google.com:80 https://a.lvh.me:3000; -- guardSensitiveResp :: Middleware-guardSensitiveResp happ p req = do-  response <- happ p req+guardSensitiveResp app config req = do+  response <- (flip removeResponseHeader) csp `liftM` app config req   resultLabel <- getLabel-  return $ addResponseHeader response $ -    ("X-Hails-Label", S8.pack $-      if resultLabel `canFlowTo` dcPub-        then "{\"isPublic\": true}"-        else "{\"isPublic\": false, \"label\": [" ++ mkClientLabel resultLabel ++ "]}")-      where mkClientLabel l = let s  = dcSecrecy l-                                  cs = toList s-                              in if s == dcFalse || length cs /= 1-                                   then ""-                                   else List.intercalate ", " $ -                                        List.map (show . S8.unpack . principalName) $-                                        List.head cs+  return $ if resultLabel `canFlowTo` dcPublic+    then response+    else addResponseHeader response $+          ( csp+          , "default-src " <> headerVal resultLabel <> ";")+      where csp = "Content-Security-Policy"+            headerVal l =+              let secrecy     = dcSecrecy l+                  secrecySet  = cToSet secrecy+                  uriList     = Set.filter (isURI . S8.unpack) $ +                                Set.map principalName $ +                                dToSet $ head $ Set.elems secrecySet+              in if secrecy == cFalse || Set.size secrecySet > 1+                   then "\'none\'" -- false/conjunction+                   else S8.unwords $+                          "\'self\'":"\'unsafe-inline\'":(Set.toList uriList)  -- | Remove anything from the response that could cause inadvertant -- declasification. Currently this only removes the @Set-Cookie@@@ -138,7 +139,7 @@ sanitizeResp hailsApp conf req = do   response <- hailsApp conf req   return $ foldr (\h r -> removeResponseHeader r h) response unsafeHeaders-   where unsafeHeaders = ["Set-Cookie", "X-Hails-Label"]+   where unsafeHeaders = ["Set-Cookie"]     @@ -147,12 +148,12 @@ -- straight forward from other middleware: -- -- > secureApplication = 'browserLabelGuard'  -- Return 403, if user should not read--- >                   . 'guardSensitiveResp' -- Add X-Hails-Sensitive if not public--- >                   . 'sanitizeResp'       -- Remove Cookies+-- >                   . 'sanitizeResp'       -- Remove Cookies/CSP+-- >                   . 'guardSensitiveResp' -- Add CSP if not public secureApplication :: Middleware secureApplication = browserLabelGuard  -- Return 403, if user should not read                   . sanitizeResp       -- Remove Cookies and X-Hails-Sensitive-                  . guardSensitiveResp -- Add X-Hails-Sensitive if not public+                  . guardSensitiveResp -- Add CSP if not public  -- | Catch all exceptions thrown by middleware and return 500. catchAllExceptions :: W.Middleware@@ -195,28 +196,27 @@   hailsRequest <- waiToHailsReq req0   -- Extract browser/request configuration   let conf = getRequestConf hailsRequest-  result <- liftIO $ paranoidDC' conf $ do-    let lreq = labelTCB (requestLabel conf) hailsRequest+  (result, dcState) <- liftIO $ tryDCDef conf $ do+    let lreq = LabeledTCB (requestLabel conf) hailsRequest     app conf lreq   case result of-    Right (response,_) -> return $ hailsToWaiResponse response+    Right response -> return $ hailsToWaiResponse response     Left err -> do       liftIO $ hPutStrLn stderr $ "App threw exception: " ++ show err-      return $ case fromException err of-        Just (LabeledExceptionTCB l _) -> -          -- as in browserLabelGuard :-          if l `canFlowTo` (browserLabel conf)-            then resp500 else resp403 -        _ -> resp500+      return $+        if lioLabel dcState `canFlowTo` (browserLabel conf) then+          resp500+          else resp403     where app = secureApplication app0           isStatic req = case W.pathInfo req of                            ("static":_) -> True                            _            -> False           resp403 = W.responseLBS status403 [] ""            resp500 = W.responseLBS status500 [] ""-          paranoidDC' conf act =-            paranoidLIO act $ LIOState { lioLabel = dcPub-                                       , lioClearance = browserLabel conf}+          tryDCDef conf act = tryDC $ do+            putLIOStateTCB $ LIOState { lioLabel = dcPublic+                                     , lioClearance = browserLabel conf}+            act   --@@ -228,12 +228,12 @@ getRequestConf :: Request -> RequestConfig getRequestConf req =   let headers = requestHeaders req-      userName  = toComponent `fmap` lookup "x-hails-user" headers-      appName  = '@' : (S8.unpack . S8.takeWhile (/= '.') $ serverName req)-      appPriv = DCPrivTCB $ toComponent appName+      muserName = principalBS `fmap` lookup "x-hails-user" headers+      appName  = "@" `S8.append` (S8.takeWhile (/= '.') $ serverName req)+      appPriv = PrivTCB $ toCNF $ principalBS appName   in RequestConfig-      { browserLabel = maybe dcPub (\un -> dcLabel un anybody) userName-      , requestLabel = maybe dcPub (\un -> dcLabel anybody un) userName+      { browserLabel = maybe dcPublic (\userName -> userName %% True) muserName+      , requestLabel = maybe dcPublic (\userName -> True %% userName) muserName       , appPrivilege = appPriv }  
Hails/HttpServer/Auth.hs view
@@ -23,6 +23,8 @@   , personaAuth   -- ** OpenID   , openIdAuth+  -- ** Authenticate with external app+  , externalAuth   -- * Development: basic authentication   , devBasicAuth   ) where@@ -227,3 +229,37 @@   , if serverPort req `notElem` [80, 443] then portBS else ""   , path ]   where portBS = S8.pack $ ":" ++ show (serverPort req)+++-- Cookie authentication+--++-- | Use an external authentication service that sets cookies.+-- The cookie names are @_hails_user@, whose contents contains the+-- @user-name@, and @_hails_user_hmac@, whose contents contains+-- @HMAC-SHA1(user-name)@. This function simply checks that the cookie+-- exists and the MAC'd user name is correct. If this is the case, it+-- returns a request with the cookie removed and @x-hails-user@ header+-- set. Otherwies the original request is returned.+-- The login service retuns a redirect (to the provided url).+-- Additionally, cookie @_hails_refer$ is set to the current+-- URL (@scheme://domain:port/path@).+externalAuth :: L8.ByteString -> String -> Middleware+externalAuth key url app req = do+  let mreqAuth = do+        cookieHeaders <- lookup hCookie $ requestHeaders req+        let cookies = parseCookies cookieHeaders+        mac0 <- fmap (S8.takeWhile (/= '"') . S8.dropWhile (== '"')) $ lookup "_hails_user_hmac" cookies+        user <- fmap (S8.takeWhile (/= '"') . S8.dropWhile (== '"')) $ lookup "_hails_user" cookies+        let mac1 = showDigest $ hmacSha1 key (lazyfy user)+        if S8.unpack mac0 == mac1+          then Just $ req { requestHeaders = ("X-Hails-User", user)+                                               : requestHeaders req }+          else Nothing+      req0 = maybe req id mreqAuth+  requireLoginMiddleware redirectResp app req0+  where redirectResp = return $ responseLBS status302+                          [(hLocation, S8.pack url)] ""+        --+        lazyfy = L8.fromChunks . (:[])+
Hails/HttpServer/Types.hs view
@@ -7,6 +7,7 @@   , addRequestHeader, removeRequestHeader   -- * Responses   , Response(..)+  , module Network.HTTP.Types   , addResponseHeader, removeResponseHeader   -- * Applications and middleware   , Application, RequestConfig(..)@@ -20,8 +21,7 @@ import qualified Data.ByteString.Lazy as L  import           Network.Socket (SockAddr)-import qualified Network.HTTP.Types as H-import qualified Network.HTTP.Types.Header as H+import           Network.HTTP.Types import           Network.Wai.Parse (RequestBodyType(..))  import           Data.Time (UTCTime)@@ -35,9 +35,9 @@ -- | A request sent by the end-user. data Request = Request {     -- | HTTP Request (e.g., @GET@, @POST@, etc.).-  requestMethod  :: H.Method+  requestMethod  :: Method   -- | HTTP version (e.g., 1.1 or 1.0).-  ,  httpVersion    :: H.HttpVersion+  ,  httpVersion    :: HttpVersion   -- | Extra path information sent by the client.   ,  rawPathInfo    :: S.ByteString   -- | If no query string was specified, this should be empty. This value@@ -54,7 +54,7 @@   -- this value should not be used in URL construction.   ,  serverPort     :: Int   -- | The request headers.-  ,  requestHeaders :: H.RequestHeaders+  ,  requestHeaders :: RequestHeaders   -- | Was this request made over an SSL connection?   ,  isSecure       :: Bool   -- | The client\'s host information.@@ -63,7 +63,7 @@   -- and without a query string, split on forward slashes,   ,  pathInfo       :: [Text]   -- | Parsed query string information-  ,  queryString    :: H.Query+  ,  queryString    :: Query   -- | Lazy ByteString containing the request body.   ,  requestBody    :: L.ByteString   -- | Time request was received.@@ -92,13 +92,13 @@                         else Nothing             else Nothing --- | Add/replace a 'H.Header' to the 'Request'-addRequestHeader :: Request -> H.Header -> Request+-- | Add/replace a 'Header' to the 'Request'+addRequestHeader :: Request -> Header -> Request addRequestHeader req hdr@(hname, _) = req { requestHeaders = hdr:headers }    where headers = List.filter ((/= hname) . fst) $ requestHeaders req -- | Remove a header (if it exists) from the 'Request'-removeRequestHeader :: Request -> H.HeaderName -> Request+removeRequestHeader :: Request -> HeaderName -> Request removeRequestHeader req hname = req { requestHeaders = headers }   where headers = List.filter ((/= hname) . fst) $ requestHeaders req @@ -110,20 +110,20 @@ -- | A response sent by the app. data Response = Response {   -- | Response status-    respStatus :: H.Status+    respStatus :: Status   -- | Response headers-  , respHeaders :: H.ResponseHeaders +  , respHeaders :: ResponseHeaders    -- | Response body   , respBody :: L.ByteString   } deriving Show --- | Add/replace a 'H.Header' to the 'Response'-addResponseHeader :: Response -> H.Header -> Response+-- | Add/replace a 'Header' to the 'Response'+addResponseHeader :: Response -> Header -> Response addResponseHeader resp hdr@(hname, _) = resp { respHeaders = hdr:headers }   where headers = List.filter ((/= hname) . fst) $ respHeaders resp  -- | Remove a header (if it exists) from the 'Response'-removeResponseHeader :: Response -> H.HeaderName -> Response+removeResponseHeader :: Response -> HeaderName -> Response removeResponseHeader resp hname = resp { respHeaders = headers }   where headers = List.filter ((/= hname) . fst) $ respHeaders resp 
Hails/PolicyModule.hs view
@@ -55,11 +55,11 @@   import           Data.Maybe+import           Data.Monoid import qualified Data.List as List import           Data.Map (Map) import qualified Data.Map as Map import           Data.Typeable-import qualified Data.ByteString.Char8 as S8 import qualified Data.Text as T import qualified Data.Bson as Bson @@ -69,8 +69,7 @@ import           Database.MongoDB (GetLastError)  import           LIO-import           LIO.Privs.TCB (mintTCB)-import           LIO.TCB (ioTCB, rethrowIoTCB)+import           LIO.TCB import           LIO.DCLabel import           Hails.Data.Hson import           Hails.Database.Core@@ -207,7 +206,7 @@ -- database label. The latter requirement  suggests that every policy -- module use 'setDatabaseLabelP' when first changing the label. setDatabaseLabel :: DCLabel -> PMAction ()-setDatabaseLabel = setDatabaseLabelP noPriv+setDatabaseLabel = setDatabaseLabelP mempty  -- | Same as 'setDatabaseLabel', but uses privileges when performing -- label comparisons. If a policy module wishes to allow other policy@@ -219,9 +218,9 @@                   -> DCLabel   -- ^ New database label                   -> PMAction () setDatabaseLabelP p l = liftDB $ do-  guardAllocP p l+  liftLIO $ guardAllocP p l   db  <-  dbActionDB `liftM` getActionStateTCB-  guardWriteP p (databaseLabel db)+  liftLIO $ guardWriteP p (databaseLabel db)   setDatabaseLabelTCB l  -- | The collections label protects the collection-set of the database.@@ -236,7 +235,7 @@ -- the label of the database which protects the label of the -- colleciton set. In most cases code should use 'setCollectionSetLabelP'. setCollectionSetLabel :: DCLabel -> PMAction ()-setCollectionSetLabel = setCollectionSetLabelP noPriv+setCollectionSetLabel = setCollectionSetLabelP mempty  -- | Same as 'setCollectionSetLabel', but uses the supplied privileges -- when performing label comparisons.@@ -244,9 +243,9 @@                        -> DCLabel     -- ^ New collections label                        -> PMAction () setCollectionSetLabelP p l = liftDB $ do-  guardAllocP p l+  liftLIO $ guardAllocP p l   db  <-  dbActionDB `liftM` getActionStateTCB-  guardWriteP p (databaseLabel db)+  liftLIO $ guardWriteP p (databaseLabel db)   setCollectionSetLabelTCB l  -- | This is the first action that any policy module should execute.  It@@ -325,7 +324,7 @@                  -> DCLabel         -- ^ Collection clearance                  -> CollectionPolicy-- ^ Collection policy                  -> PMAction ()-createCollection = createCollectionP noPriv+createCollection = createCollectionP mempty  -- | Same as 'createCollection', but uses privileges when performing -- IFC checks.@@ -337,10 +336,11 @@                   -> PMAction () createCollectionP p n l c pol = liftDB $ do   db <- dbActionDB `liftM` getActionStateTCB-  taintP p $ databaseLabel db-  guardWriteP p $ labelOf (databaseCollections db)-  guardAllocP p l-  guardAllocP p c+  liftLIO $ do+    taintP p $ databaseLabel db+    guardWriteP p $ labelOf (databaseCollections db)+    guardAllocP p l+    guardAllocP p c   associateCollectionTCB $ collectionTCB n l c newPol     where newPol = let ps  = fieldLabelPolicies pol                        ps' = Map.insert (T.pack "_id") SearchableField ps@@ -406,7 +406,7 @@   ls   <- lines `liftM` readFile conf   Map.fromList `liftM` mapM xfmLine ls     where xfmLine l = do (tn, dn) <- readIO l-                         return (tn,(principal (S8.pack $ '_':tn), dn))+                         return (tn,(principal ('_':tn), dn))  -- | This function is the used to execute database queries on policy -- module databases. The function firstly invokes the policy module,@@ -431,26 +431,27 @@                                List.lookup "HAILS_MONGODB_SERVER" env           mode     = maybe master parseMode $                                   List.lookup "HAILS_MONGODB_MODE" env-      pipe <- rethrowIoTCB $ Mongo.runIOE $ Mongo.connect (Mongo.host hostName)-      let priv = mintTCB (toComponent pmOwner)+      pipe <- ioTCB $ Mongo.runIOE $ Mongo.connect (Mongo.host hostName)+      let priv = PrivTCB (toCNF pmOwner)           s0 = makeDBActionStateTCB priv dbName pipe mode       -- Execute policy module entry function with raised clearance:       (policy, s1) <- withClearanceP' priv $ runDBAction (pmAct priv) s0       let s2 = s1 { dbActionDB = dbActionDB s1 }       res <- evalDBAction (act policy) s2-      rethrowIoTCB $ Mongo.close pipe+      ioTCB $ Mongo.close pipe       return res   where tn = policyModuleTypeName (undefined :: pm)         pmAct priv = unPMActionTCB $ initPolicyModule priv :: DBAction pm         withClearanceP' priv io = do           c <- getClearance-          let lpriv = dcLabel (privDesc priv) (privDesc priv) `lub` c-          bracketP priv+          let lpriv = (priv %%  priv) `lub` c+          -- XXX: does this actually work? Used to be bracketP+          bracket                    -- Raise clearance:                    (setClearanceP priv lpriv)                    -- Lower clearance:                    (const $ do c' <- getClearance -                               setClearanceP priv (partDowngradeP priv c' c))+                               setClearanceP priv (downgradeP priv c' `lub` c))                    -- Execute policy module entry point, in between:                    (const io) 
Hails/PolicyModule/DSL.hs view
@@ -29,19 +29,19 @@   'initPolicyModule' priv = do     'setPolicy' priv $ do       'database' $ do-        'readers' '==>' 'anybody'-        'writers' '==>' 'anybody'+        'readers' '==>' 'unrestricted'+        'writers' '==>' 'unrestricted'         'admins'  '==>' this       'collection' \"users\" $ do         'access' $ do-          'readers' '==>' 'anybody'-          'writers' '==>' 'anybody'+          'readers' '==>' 'unrestricted'+          'writers' '==>' 'unrestricted'         'clearance' $ do           'secrecy'   '==>' this-          'integrity' '==>' 'anybody'+          'integrity' '==>' 'unrestricted'         'document' $ \doc -> do-          'readers' '==>' 'anybody'-          'writers' '==>' 'anybody'+          'readers' '==>' 'unrestricted'+          'writers' '==>' 'unrestricted'         'field' \"name\"     $ 'searchable'         'field' \"password\" $ 'labeled' $ \doc -> do           let user = \"name\" ``at`` doc :: String@@ -77,6 +77,7 @@   -- * Label components (or roles)   , readers, secrecy   , writers, integrity+  , unrestricted   , admins   , (==>), (<==)   -- * Creating databases label policies@@ -146,10 +147,10 @@ class MonadState s m => Role r s m where   -- | @r ==> c@ effectively states that role @r@ (i.e., 'readers',   -- 'writers', 'admins' must imply label component @c@).-  (==>) :: (ToComponent c) => r -> c -> m ()+  (==>) :: (ToCNF c) => r -> c -> m ()   -- | Inverse implication. Purely provided for readability. The   -- direction is not relevant to the internal representation.-  (<==) :: (ToComponent c) => r -> c -> m ()+  (<==) :: (ToCNF c) => r -> c -> m ()   (<==) = (==>)  @@ -164,11 +165,11 @@ -- >   writers ==> "Alice" -- >   admins  ==> "Alice" ---data DBExp = DBExp Component Component Component+data DBExp = DBExp CNF CNF CNF   deriving Show  -- | Database expression solely contains a list of components.-type DBExpS = Map String Component+type DBExpS = Map String CNF  -- | Database expression composition monad newtype DBExpM a = DBExpM (ErrorT String (State DBExpS) a)@@ -179,21 +180,21 @@     s <- get      case Map.lookup (show readers) s of       Just _ -> fail "Database readers already specified."-      Nothing -> put $ Map.insert (show readers) (toComponent c) s+      Nothing -> put $ Map.insert (show readers) (toCNF c) s  instance Role Writers DBExpS DBExpM where    _ ==> c = DBExpM $ do     s <- get      case Map.lookup (show writers) s of       Just _ -> fail "Database writers already specified."-      Nothing -> put $ Map.insert (show writers) (toComponent c) s+      Nothing -> put $ Map.insert (show writers) (toCNF c) s  instance Role Admins DBExpS DBExpM where    _ ==> c = DBExpM $ do     s <- get      case Map.lookup (show admins) s of       Just _ -> fail "Database admins already specified."-      Nothing -> put $ Map.insert (show admins) (toComponent c) s+      Nothing -> put $ Map.insert (show admins) (toCNF c) s   -- | Create a database lebeling policy The policy must set the label@@ -239,11 +240,11 @@ -- >   readers ==> "Alice" \/ "Bob" -- >   writers ==> "Alice" ---data ColAccExp = ColAccExp Component Component+data ColAccExp = ColAccExp CNF CNF   deriving Show  -- | Access expression solely contains a list of components.-type ColAccExpS = Map String Component+type ColAccExpS = Map String CNF  -- | Access expression composition monad newtype ColAccExpM a =@@ -257,7 +258,7 @@     case Map.lookup (show readers) s of       Just _ -> fail $ "Collection " ++ show cName                           ++ " access readers already specified."-      Nothing -> put $ Map.insert (show readers) (toComponent c) s+      Nothing -> put $ Map.insert (show readers) (toCNF c) s  instance Role Writers ColAccExpS ColAccExpM where    _ ==> c = ColAccExpM $ do@@ -266,7 +267,7 @@     case Map.lookup (show writers) s of       Just _ -> fail $ "Collection " ++ show cName                           ++ " access writers already specified."-      Nothing -> put $ Map.insert (show writers) (toComponent c) s+      Nothing -> put $ Map.insert (show writers) (toCNF c) s   --------------------------------------------------------------@@ -277,11 +278,11 @@ -- >   readers ==> "Alice" \/ "Bob" -- >   writers ==> "Alice" ---data ColClrExp = ColClrExp Component Component+data ColClrExp = ColClrExp CNF CNF   deriving Show  -- | Clress expression solely contains a list of components.-type ColClrExpS = Map String Component+type ColClrExpS = Map String CNF  -- | Database expression composition monad newtype ColClrExpM a =@@ -295,7 +296,7 @@     case Map.lookup (show readers) s of       Just _ -> fail $ "Collection " ++ show cName                           ++ " clearance readers already specified."-      Nothing -> lift . put $ Map.insert (show readers) (toComponent c) s+      Nothing -> lift . put $ Map.insert (show readers) (toCNF c) s  instance Role Writers ColClrExpS ColClrExpM where    _ ==> c = ColClrExpM $ do@@ -304,7 +305,7 @@     case Map.lookup (show writers) s of       Just _ -> fail $ "Collection " ++ show cName                           ++ " clearance writers already specified."-      Nothing -> put $ Map.insert (show writers) (toComponent c) s+      Nothing -> put $ Map.insert (show writers) (toCNF c) s   @@ -320,10 +321,10 @@ instance Show ColDocExp where show _ = "ColDocExp {- function -}"  -- | A Label expression has two components.-data LabelExp = LabelExp Component Component+data LabelExp = LabelExp CNF CNF  -- | Document expression solely contains a list of components.-type ColDocExpS = Map String Component+type ColDocExpS = Map String CNF  -- | Document expression composition monad newtype ColDocExpM a =@@ -337,7 +338,7 @@     case Map.lookup (show readers) s of       Just _ -> fail $ "Collection " ++ show cName                           ++ " document readers already specified."-      Nothing -> lift . put $ Map.insert (show readers) (toComponent c) s+      Nothing -> lift . put $ Map.insert (show readers) (toCNF c) s  instance Role Writers ColDocExpS ColDocExpM where    _ ==> c = ColDocExpM $ do@@ -346,7 +347,7 @@     case Map.lookup (show writers) s of       Just _ -> fail $ "Collection " ++ show cName                           ++ " document writers already specified."-      Nothing -> put $ Map.insert (show writers) (toComponent c) s+      Nothing -> put $ Map.insert (show writers) (toCNF c) s   @@ -367,7 +368,7 @@   show (ColLabFieldExp _) = "ColLabFieldExp {- function -}"  -- | Labeled field expression solely contains a list of components.-type ColLabFieldExpS = Map String Component+type ColLabFieldExpS = Map String CNF  -- | Labeled field expression composition monad. newtype ColLabFieldExpM a =@@ -381,7 +382,7 @@     case Map.lookup (show readers) s of       Just _ -> fail $ "Collection " ++ show cName ++ " field " ++ show fName                           ++ " readers already specified."-      Nothing -> lift . put $ Map.insert (show readers) (toComponent c) s+      Nothing -> lift . put $ Map.insert (show readers) (toCNF c) s  instance Role Writers ColLabFieldExpS ColLabFieldExpM where    _ ==> c = ColLabFieldExpM $ do@@ -390,7 +391,7 @@     case Map.lookup (show writers) s of       Just _ -> fail $ "Collection " ++ show cName ++ " field " ++ show fName                           ++ " writers already specified."-      Nothing -> put $ Map.insert (show writers) (toComponent c) s+      Nothing -> put $ Map.insert (show writers) (toCNF c) s  -- | Field expression composition monad. newtype ColFieldExpM a =@@ -451,7 +452,7 @@ -- >     secrecy   ==> "Users" -- >     integrity ==> "Alice"           -- >   document $ \doc ->  do--- >     readers ==> anybody+-- >     readers ==> unrestricted -- >     writers ==> "Alice" \/ (("name" `at`doc) :: String) -- >   field "name" searchable -- >   field "password" $ labeled $ \doc -> do@@ -571,7 +572,7 @@ -- > collection "w00t" $ do -- >   ... -- >   document $ \doc ->  do--- >     readers ==> anybody+-- >     readers ==> 'unrestricted' -- >     writers ==> "Alice" \/ (("name" `at`doc) :: String) -- -- states that every document in the collection is readable by anybody,@@ -651,7 +652,7 @@ -- >     secrecy   ==> "Users" -- >     integrity ==> "Alice"           -- >   document $ \doc ->  do--- >     readers ==> anybody+-- >     readers ==> 'unrestricted' -- >     writers ==> "Alice" \/ (("name" `at`doc) :: String) -- >   field "name" searchable -- >   field "password" $ labeled $ \doc -> do@@ -703,19 +704,19 @@ setPolicy :: DCPriv -> PolicyExpM () -> PMAction () setPolicy priv pol =    case runPolicy pol of-    Left err -> throwLIO $ PolicyCompileError err+    Left err -> liftLIO $ throwLIO $ PolicyCompileError err     Right policy -> execPolicy policy   where execPolicy (PolicyExp db cs) = do           execPolicyDB db            void $ forM cs execPolicyCol         --         execPolicyDB (DBExp r w a) = do-          setDatabaseLabelP priv (dcLabel r w)-          setCollectionSetLabelP priv (dcLabel r a)+          setDatabaseLabelP priv (r %% w)+          setCollectionSetLabelP priv (r %% a)         --         execPolicyCol (ColExp n (ColAccExp lr lw) (ColClrExp cr cw) doc fs) =           let cps = mkColPol doc fs-          in createCollectionP priv n (dcLabel lr lw) (dcLabel cr cw) cps+          in createCollectionP priv n (lr %% lw) (cr %% cw) cps         --         mkColPol (ColDocExp fdocE) cs =            let fdoc = unDataPolicy fdocE@@ -724,7 +725,7 @@         --         unDataPolicy fpolE = \doc ->            let (LabelExp s i) = fpolE doc-          in dcLabel s i+          in s %% i         --         unFieldExp ColFieldSearchable = SearchableField         unFieldExp (ColLabFieldExp f) = FieldPolicy (unDataPolicy f)@@ -744,3 +745,6 @@ fromRight :: Either String b -> b fromRight (Right x) = x fromRight (Left e)  = throw . PolicyRuntimeError $ e++unrestricted :: CNF+unrestricted = cTrue
Hails/PolicyModule/Groups.hs view
@@ -11,8 +11,8 @@ module Hails.PolicyModule.Groups ( Groups(..)                                  , labelRewrite ) where -import           Data.Maybe-import qualified Data.List as List+import           Data.Monoid+import qualified Data.Set as Set import qualified Data.Map as Map  import           Control.Monad@@ -53,36 +53,43 @@ labelRewrite pm lx = do   -- Make sure that 'groupsInstanceEndorse' is not bottom   _ <- liftLIO $ evaluate (groupsInstanceEndorse :: unused_pm)-  -- Get underlying privileges if they corresponds to the named (by-  -- the first argument) policy module   pmPriv <- getPMPriv+   -- Build map from principals to list of princpals-  pmap <- forM principals $ \p -> groups pm pmPriv p >>= \ps -> return (p, ps)+  pMap <- Set.fold (\p act -> act >>= \m -> do+            ps <- groups pm pmPriv p+            return (Map.insert p ps m)) (return Map.empty) principals   -- Apply map to all principals in the label-  let lnew = dcLabel (mk pmap s) (mk pmap i)+  let lnew = (expandPrincipals pMap s) %% (expandPrincipals pMap i)   -- Relabel labeled value-  relabelLabeledP pmPriv lnew lx+  liftLIO $ relabelLabeledP pmPriv lnew lx     where getPMPriv = do             pmPriv <- dbActionPriv `liftM` getActionStateTCB             -- Make sure that the underlying policy module             -- and one named in the first parameter are the same             case Map.lookup (policyModuleTypeName pm) availablePolicyModules of-              Nothing -> return noPriv-              Just (p,_) -> return $ if toComponent p == privDesc pmPriv+              Nothing -> return mempty+              Just (p,_) -> return $ if toCNF p == privDesc pmPriv                                        then pmPriv-                                       else noPriv+                                       else mempty           -- Modify label by expanding principals according to the map-          mk pmap lc =-            let f = map (concatMap (\x -> fromJust $ List.lookup x pmap))-            in if lc == dcFalse-                 then lc-                 else fromList . f . toList $ lc+          expandPrincipals pMap origPrincipals =+                -- Function to fold over disjunctions in a CNF, expanding each+                -- principal with the groups map+            let cFoldF :: Disjunction -> CNF -> CNF+                cFoldF disj accm =+                  (Set.foldr expandOne cFalse $ dToSet disj) /\ accm+                -- Inner fold function, expands a single principal and adds+                -- to a CNF (that represents a Disjunction+                expandOne :: Principal -> CNF -> CNF+                expandOne princ accm =+                  (dFromList $ pMap Map.! princ) \/ accm+            in Set.foldr cFoldF cTrue $ cToSet origPrincipals           -- Label components           s = dcSecrecy $ labelOf lx           i = dcIntegrity $ labelOf lx           -- All unique principals in the labe-          principals = List.nub $ getPrincipals s ++ getPrincipals i+          principals = getPrincipals s <> getPrincipals i           -- Get principals form component-          getPrincipals lc = if lc == dcFalse-                              then []-                              else List.nub . concat . toList $ lc+          getPrincipals = mconcat . (map dToSet) . Set.elems . cToSet+
Hails/Web/Controller.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE Safe #-} {-# LANGUAGE OverloadedStrings            , TypeSynonymInstances            , FlexibleInstances@@ -31,12 +31,13 @@ import qualified Data.ByteString.Char8 as S8 import qualified Data.ByteString.Lazy.Char8 as L8 -import           Network.HTTP.Types.Header import           Hails.HttpServer import           Hails.Web.Router import           Hails.Web.Responses -data ControllerState = ControllerState { csRequest :: DCLabeled Request }+data ControllerState = ControllerState+                        { csRequest :: DCLabeled Request+                        , csPathParams :: Query }  -- | A controller is simply a reader monad atop 'DC' with the 'Labeled' -- 'Request' as the environment.@@ -46,19 +47,24 @@   liftLIO = lift  instance Routeable (Controller Response) where-  runRoute controller _ _ req = fmap Just $-    runReaderT controller $ ControllerState req+  runRoute controller _ eq _ req = fmap Just $+    runReaderT controller $ ControllerState req eq  -- | Get the underlying request. request :: Controller (DCLabeled Request) request = fmap csRequest ask +-- | Get the underlying request.+pathParams :: Controller [(S8.ByteString, Maybe S8.ByteString)]+pathParams = fmap csPathParams ask+ -- | Get the query parameter mathing the supplied variable name. queryParam :: S8.ByteString -> Controller (Maybe S8.ByteString) queryParam varName = do   req <- request >>= liftLIO . unlabel+  params <- pathParams   let qr = queryString req-  case lookup varName qr of+  case lookup varName (params ++ qr) of     Just n -> return n     _ -> return Nothing 
Hails/Web/REST.hs view
@@ -109,9 +109,9 @@  instance Routeable (RESTControllerM a) where   runRoute controller = rt-    where rt pi conf req = do+    where rt pi eq conf req = do             (_, st) <- runStateT controller defaultRESTControllerState-            runRoute st pi conf req+            runRoute st pi eq conf req   -- |GET \/
Hails/Web/Router.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE Safe #-} {-# LANGUAGE FlexibleInstances #-} {- | @@ -37,13 +37,13 @@ import           Data.Monoid import           Data.Text (Text) import qualified Data.Text as T-import           Network.HTTP.Types import           Hails.HttpServer import           Hails.Web.Responses  -- | Route handler is a fucntion from the path info, request -- configuration, and labeled request to a response.-type RouteHandler = [Text]              -- ^ Path info+type RouteHandler =  [Text]             -- ^ Path info+                  -> [(S8.ByteString, Maybe S8.ByteString)]        -- ^ Extra query params                   -> RequestConfig      -- ^ Request configuration                   -> DCLabeled Request  -- ^ Labeled request                   -> DC (Maybe Response)@@ -72,17 +72,17 @@ mkRouter route conf lreq = do   req <- liftLIO $ unlabel lreq   let pi = pathInfo req-  mapp <- runRoute route pi conf lreq+  mapp <- runRoute route pi [] conf lreq   case mapp of     Just resp -> return resp     Nothing -> return notFound   instance Routeable Application where-  runRoute app _ conf req = fmap Just $ app conf req+  runRoute app _ _ conf req = fmap Just $ app conf req  instance Routeable Response where-  runRoute resp _ _ _ = return . Just $ resp+  runRoute resp _ _ _  _ = return . Just $ resp  {- | The 'RouteM' type is a basic instance of 'Routeable' that simply holds@@ -136,25 +136,25 @@ mroute handler = Route handler ()  instance Monad RouteM where-  return a = Route (const . const . const $ return Nothing) a+  return a = Route (const . const . const . const $ return Nothing) a   (Route rtA valA) >>= fn =     let (Route rtB valB) = fn valA-    in Route (\pi conf req -> do-      resA <- rtA pi conf req+    in Route (\pi eq conf req -> do+      resA <- rtA pi eq conf req       case resA of-        Nothing -> rtB pi conf req+        Nothing -> rtB pi eq conf req         Just _ -> return resA) valB  instance Monoid Route where-  mempty = mroute $ const . const . const $ return Nothing-  mappend (Route a _) (Route b _) = mroute $ \pi conf req -> do-    c <- a pi conf req+  mempty = mroute $ const . const . const . const $ return Nothing+  mappend (Route a _) (Route b _) = mroute $ \pi eq conf req -> do+    c <- a pi eq conf req     case c of-      Nothing -> b pi conf req+      Nothing -> b pi eq conf req       Just _ -> return c  instance Routeable (RouteM a) where-  runRoute (Route rtr _) pi conf req = rtr pi conf req+  runRoute (Route rtr _) pi eq conf req = rtr pi eq conf req  -- | A route that always matches (useful for converting a 'Routeable' into a -- 'Route').@@ -164,27 +164,27 @@ -- | Matches on the hostname from the 'Request'. The route only successeds on -- exact matches. routeHost :: Routeable r => S.ByteString -> r -> Route-routeHost host route = mroute $ \pi conf lreq -> do+routeHost host route = mroute $ \pi eq conf lreq -> do   req <- unlabel lreq   if host == serverName req-    then runRoute route pi conf lreq+    then runRoute route pi eq conf lreq     else return Nothing  -- | Matches if the path is empty. Note that this route checks that 'pathInfo' -- is empty, so it works as expected when nested under namespaces or other -- routes that pop the 'pathInfo' list. routeTop :: Routeable r => r -> Route-routeTop route = mroute $ \pi conf lreq -> do+routeTop route = mroute $ \pi eq conf lreq -> do   if null pi || (T.null . head $ pi)-    then runRoute route pi conf lreq+    then runRoute route pi eq conf lreq     else return Nothing  -- | Matches on the HTTP request method (e.g. 'GET', 'POST', 'PUT') routeMethod :: Routeable r => StdMethod -> r -> Route-routeMethod method route = mroute $ \pi conf lreq -> do+routeMethod method route = mroute $ \pi eq conf lreq -> do   req <- unlabel lreq   if renderStdMethod method == requestMethod req then-    runRoute route pi conf lreq+    runRoute route pi eq conf lreq     else return Nothing  -- | Routes the given URL pattern. Patterns can include@@ -206,22 +206,21 @@  -- | Matches if the first directory in the path matches the given 'ByteString' routeName :: Routeable r => S.ByteString -> r -> Route-routeName name route = mroute $ \pi conf lreq -> do+routeName name route = mroute $ \pi eq conf lreq -> do   if (not . null $ pi) && S8.unpack name == (T.unpack . head $ pi)-    then runRoute route (tail pi) conf lreq+    then runRoute route (tail pi) eq conf lreq     else return Nothing  -- | Always matches if there is at least one directory in 'pathInfo' but and -- adds a parameter to 'queryString' where the key is the supplied -- variable name and the value is the directory consumed from the path. routeVar :: Routeable r => S.ByteString -> r -> Route-routeVar varName route = mroute $ \pi conf lreq -> do+routeVar varName route = mroute $ \pi eq conf lreq ->   if null pi     then return Nothing-    else do lreqNext  <- liftLIO $ lFmap lreq $ \req ->-                let varVal = S8.pack . T.unpack . head $ pi-                in req {queryString = (varName, Just varVal):(queryString req)}-            runRoute route (tail pi) conf lreqNext+    else let varVal = S8.pack . T.unpack . head $ pi+             neqp = (varName, Just varVal):eq+         in runRoute route (tail pi) neqp conf lreq  {- $Example  #example#
hails.cabal view
@@ -1,5 +1,5 @@ Name:           hails-Version:        0.9.2.2+Version:        0.11.0.0 build-type:     Simple License:        GPL-2 License-File:   LICENSE@@ -83,37 +83,37 @@  Source-repository head   Type:     git-  Location: ssh://anonymous@gitstar.com/scs/hails.git+  Location: ssh://git@github.com.com/scslab/hails.git   Library   Build-Depends:     base              >= 4.5     && < 5.0-   ,transformers      >= 0.2.2-   ,mtl               >= 2.0-   ,containers        >= 0.4.2-   ,bytestring        >= 0.10-   ,text              >= 0.11-   ,parsec            >= 3.1.2-   ,binary            >= 0.5-   ,time              >= 1.2.0.5-   ,lio               >= 0.9.1.1-   ,base64-bytestring >= 0.1-   ,bson              >= 0.2-   ,mongoDB           >= 1.3.0-   ,network           >= 2.3-   ,conduit           >= 0.5-   ,resourcet         >= 0.3.3.1-   ,http-conduit      >= 1.5-   ,wai               >= 1.3-   ,wai-app-static    >= 1.3.0.1-   ,wai-extra         >= 1.3.0.1-   ,http-types        >= 0.7-   ,authenticate      >= 1.3-   ,cookie            >= 0.4-   ,blaze-builder     >= 0.3.1-   ,failure           >= 0.2.0.1-   ,SHA               >= 1.5.0.0+   ,transformers+   ,mtl+   ,containers+   ,bytestring+   ,text >= 0.11.3.0+   ,parsec+   ,binary+   ,time+   ,lio+   ,base64-bytestring+   ,bson+   ,mongoDB+   ,network+   ,conduit+   ,resourcet+   ,http-conduit+   ,wai+   ,wai-app-static+   ,wai-extra+   ,http-types+   ,authenticate+   ,cookie+   ,blaze-builder+   ,failure+   ,SHA    GHC-options: -Wall -fno-warn-orphans @@ -150,35 +150,35 @@   ghc-options: -package ghc -Wall -fno-warn-orphans   Build-Depends:     base              >= 4.5     && < 5.0-   ,transformers      >= 0.2.2-   ,mtl               >= 2.0-   ,containers        >= 0.4.2-   ,bytestring        >= 0.9-   ,text              >= 0.11-   ,parsec            >= 3.1.2-   ,binary            >= 0.5-   ,time              >= 1.2.0.5-   ,lio               >= 0.9.1-   ,base64-bytestring >= 0.1-   ,bson              >= 0.2-   ,mongoDB           >= 1.3.0-   ,network           >= 2.3-   ,conduit           >= 0.5-   ,resourcet         >= 0.3.3.1-   ,http-conduit      >= 1.5-   ,wai               >= 1.3-   ,wai-extra         >= 1.3-   ,wai-app-static    >= 1.3-   ,warp              >= 1.3-   ,http-types        >= 0.7-   ,authenticate      >= 1.3-   ,cookie            >= 0.4-   ,blaze-builder     >= 0.3.1-   ,directory         >= 1.1-   ,filepath          >= 1.3-   ,unix              >= 2.5.1-   ,ghc-paths         >= 0.1.0.8-   ,SHA               >= 1.5.0.0+   ,transformers+   ,mtl+   ,containers+   ,bytestring+   ,text+   ,parsec+   ,binary+   ,time+   ,lio+   ,base64-bytestring+   ,bson+   ,mongoDB+   ,network+   ,conduit+   ,resourcet+   ,http-conduit+   ,wai+   ,wai-extra+   ,wai-app-static+   ,warp+   ,http-types+   ,authenticate+   ,cookie+   ,blaze-builder+   ,directory+   ,filepath+   ,unix+   ,ghc-paths+   ,SHA    ,hails  test-suite tests@@ -190,21 +190,21 @@    build-depends:     hails-   ,base                       >= 4.5-   ,containers                 >= 0.4.2-   ,unix                       >= 2.5-   ,time                       >= 1.2.0.5-   ,text                       >= 0.11-   ,QuickCheck                 >= 2.3-   ,HUnit                      >= 1.2.5-   ,quickcheck-instances       >= 0.3.0-   ,test-framework             >= 0.6-   ,test-framework-quickcheck2 >= 0.2.11-   ,test-framework-hunit       >= 0.2.7-   ,lio                        >= 0.9.0.0-   ,quickcheck-lio-instances   >= 0.9.0.0-   ,bson                       >= 0.2-   ,mongoDB                    >= 1.3.0-   ,wai                        >= 1.3-   ,wai-test                   >= 1.3-   ,http-types                 >= 0.7+   ,base+   ,containers+   ,unix+   ,time+   ,text+   ,QuickCheck+   ,HUnit+   ,quickcheck-instances+   ,test-framework+   ,test-framework-quickcheck2+   ,test-framework-hunit+   ,lio+   ,quickcheck-lio-instances+   ,bson+   ,mongoDB+   ,wai+   ,wai-test+   ,http-types
hails.hs view
@@ -82,12 +82,15 @@       hmac_key = L8.pack . fromJust $ optHmacKey opts       persona = personaAuth hmac_key $ T.pack . fromJust . optPersonaAud $ opts       openid  = openIdAuth  $ T.pack . fromJust . optOpenID $ opts+      external  = externalAuth  hmac_key+                                (fromJust $ optExternal $ opts)       logMiddleware  = if optDev opts then logStdoutDev  else logStdout       authMiddleware = case () of          -- dev/production mode with persona:          _ | isJust (optPersonaAud opts) -> persona          -- dev/productoin mode with openid:          _ | isJust (optOpenID opts)     -> openid+         _ | isJust (optExternal opts)     -> external          -- dev mode:          _                               -> devBasicAuth   app <- loadApp (optSafe opts) (optPkgConf opts) (fromJust $ optName opts)@@ -142,6 +145,7 @@    , optSafe        :: Bool          -- ^ Use @-XSafe@    , optForce       :: Bool          -- ^ Force unsafe in production    , optDev         :: Bool          -- ^ Development/Production+   , optExternal    :: Maybe String  -- ^ External Auth URL    , optOpenID      :: Maybe String  -- ^ OpenID provider    , optHmacKey     :: Maybe String  -- ^ HMAC cookie key    , optPersonaAud  :: Maybe String  -- ^ Persona audience@@ -161,6 +165,7 @@                       , optSafe        = True                       , optForce       = False                       , optDev         = True+                      , optExternal    = Nothing                       , optOpenID      = Nothing                       , optHmacKey     = Nothing                       , optPersonaAud  = Nothing@@ -180,6 +185,7 @@                          , optSafe        = True                          , optForce       = False                          , optDev         = True+                         , optExternal    = Nothing                          , optOpenID      = Nothing                          , optHmacKey     = Just "hails-d34adb33f-key"                          , optPersonaAud  = Nothing@@ -267,6 +273,7 @@                             _ -> optPort opts        , optOpenID      = mFromEnvOrOpt "OPENID_PROVIDER" optOpenID         , optPersonaAud  = mFromEnvOrOpt "PERSONA_AUDIENCE" optPersonaAud+       , optExternal    = mFromEnvOrOpt "AUTH_URL" optPersonaAud        , optHmacKey     = mFromEnvOrOpt "HMAC_KEY" optHmacKey        , optDBConf      = mFromEnvOrOpt "DATABASE_CONFIG_FILE" optDBConf        , optPkgConf     = mFromEnvOrOpt "PACKAGE_CONF" optPkgConf@@ -296,6 +303,7 @@                     , optPort        = mergeMaybe optPort                     , optOpenID      = mergeMaybe optOpenID                     , optPersonaAud  = mergeMaybe optPersonaAud+                    , optExternal    = mergeMaybe optExternal                     , optHmacKey     = mergeMaybe optHmacKey                     , optDBConf      = mergeMaybe optDBConf                     , optMongoServer = mergeMaybe optMongoServer }@@ -318,7 +326,8 @@   checkIsJust [(optName        ,"APP_NAME"            )]   checkIsJust [(optPort        ,"PORT"                )]   checkIsJust [(optOpenID      ,"OPENID_PROVIDER"     ) {- or -}-              ,(optPersonaAud  ,"PERSONA_AUDIENCE"    )]+              ,(optPersonaAud  ,"PERSONA_AUDIENCE"    ) {- or -}+              ,(optExternal    ,"AUTH_URL")]   when (isJust $ optPersonaAud opts0) $ checkIsJust [(optHmacKey ,"HMAC_KEY")]   checkIsJust [(optDBConf      ,"DATABASE_CONFIG_FILE")]   checkIsJust [(optMongoServer ,"HAILS_MONGODB_SERVER")]