Skip to content

Instantly share code, notes, and snippets.

@gbluma
Last active August 29, 2015 14:07
Show Gist options
  • Save gbluma/e061ee1f058b737ecf1c to your computer and use it in GitHub Desktop.
Save gbluma/e061ee1f058b737ecf1c to your computer and use it in GitHub Desktop.
A minimal security monad that allows low-security and high-security elements to be mixed, but in all places where the values are mixed, the output adopts the highest-security tag. This is useful in situations where sensitive data pervades many computational contexts and where the security rules are bipolar (strictly public/private, etc.)
/**
* A minimal security monad that allows low-security and high-security elements
* to be mixed, but in all places where the values are mixed, the output
* adopts the highest-security tag. This is useful in situations where
* sensitive data pervades many computational contexts and where the security
* rules are bipolar (strictly public/private, etc.)
*
* @author Garrett Bluma
* @date Apr 9, 2012 (Monad design and implementation)
* @revised Oct 2, 2014 (Cleaned up. Added examples)
* @copyright Garrett Bluma. (c) 2014 - All rights reserved.
*/
// SecurityMonad is a normal Scala monad, therefore it must exhibit a certain
// structure. These abstract methods are unnecessary, but helpful for
// type-checking and for reference.
sealed trait SecurityMonad[+A] {
def map[B] (f: A => B) : SecurityMonad[B]
def flatMap[B](f: A => SecurityMonad[B]): SecurityMonad[B]
}
// The LowSM class effectively tags an object as being of a low security (i.e.
// public)
case class LowSM[+A](a: A) extends SecurityMonad[A]
{ // When introducing a new block, wrap it in a low security container
def map[B] (f: A => B) : SecurityMonad[B] = LowSM(f(a))
// When binding two computations together, the relationship should allow a
// low security object. Further binds will root it out if it is not
// sufficient.
def flatMap[B] (f: A => SecurityMonad[B]) = f(a)
// The decrypt method yields a high security object of the original contents
// that were encrpyted.
def decrypt[B] = HighSM("[[not implemented]]")
}
// The HighSM class tags an object as being of a high security (i.e. private)
case class HighSM[+A](a: A) extends SecurityMonad[A]
{
// When introducing a new block, wrap it in a high security container
def map[B] (f: A => B) : SecurityMonad[B] = HighSM(f(a))
// When binding two computations together, the relationship should always
// favor higher security
def flatMap[B] (f: A => SecurityMonad[B]) = {
f(a) match {
case LowSM(b) => HighSM(b)
case HighSM(b) => HighSM(b)
}
}
// The encrypt method yields a low security object once the contents have
// been encrypted.
def encrypt[B] = LowSM("[[not implemented]]")
}
// ------------------------
// test program
object main {
def main(args:Array[String]) {
// Some inline testing to skip a test framework
// A low security element and a high security element should always
// combine to be the higher security level.
// ----------------------------------------------------------------------
// Basic sanity test
print("Low * High * High = High --> ")
val a:SecurityMonad[Int] = for ( x <- LowSM(9);
y <- HighSM(3);
z <- HighSM(7)
) yield { x * y * z }
a match {
case LowSM(x) => println("Failed")
case HighSM(x) => println("Success")
}
// ----------------------------------------------------------------------
// Positive test for low-security non-elevation
print("Low * Low * Low = Low --> ")
val b:SecurityMonad[Int] = for ( x <- LowSM(9);
y <- LowSM(3);
z <- LowSM(5)
) yield { x * y * z }
b match {
case LowSM(x) => println("Success")
case HighSM(x) => println("Failed")
}
// ----------------------------------------------------------------------
// Test for a single (inner) High security propagates
print("Low * High * Low = High --> ")
val c:SecurityMonad[Int] = for ( x <- LowSM(9);
y <- HighSM(3);
z <- LowSM(3)
) yield { x * y * z }
c match {
case LowSM(x) => println("Failed")
case HighSM(x) => println("Success")
}
// ----------------------------------------------------------------------
// try the same as the last test with for-comprehension expanded
print("Low * High * Low = High --> ")
val d = LowSM(9).flatMap { a =>
HighSM(3).flatMap { b =>
LowSM(3).map { c =>
a * b * c } } }
d match {
case LowSM(x) => println("Failed")
case HighSM(x) => println("Success")
}
// ----------------------------------------------------------------------
// An extra sanity test using strings instead of Ints
print("Low * High * High = High --> ")
val e:SecurityMonad[String] = for ( x <- LowSM("Hello");
y <- HighSM("World");
z <- HighSM("!")
) yield { x + y + z }
e match {
case LowSM(x) => println("Failed")
case HighSM(x) => println("Success")
}
println(e); // => HighSM("HelloWorld!")
// Output: $ sbt run
//
// [info] Running main
// Low * High * High = High --> Success
// Low * Low * Low = Low --> Success
// Low * High * Low = High --> Success
// Low * High * Low = High --> Success
// Low * High * High = High --> Success
// HighSM(HelloWorld!)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment