diff --git a/src/main/scala/bootstrap/liftweb/Boot.scala b/src/main/scala/bootstrap/liftweb/Boot.scala
index cbe1955e9..398c648d1 100755
--- a/src/main/scala/bootstrap/liftweb/Boot.scala
+++ b/src/main/scala/bootstrap/liftweb/Boot.scala
@@ -166,6 +166,8 @@ class Boot extends Loggable{
Props.get("connector").getOrElse("") == "kafka" )
schemifyAll()
+ val superuser = Role.findOrCreateAndSave("superuser", "a category", code.model.dataAccess.MappedPermission.fromAPermission(APermission.all))
+
// This sets up MongoDB config (for the mongodb connector)
if(Props.get("connector").getOrElse("") == "mongodb")
MongoConfig.init
@@ -382,6 +384,8 @@ class Boot extends Loggable{
object ToSchemify {
val models = List(OBPUser,
+ Role,
+ MappedPermission,
Admin,
Nonce,
Token,
diff --git a/src/main/scala/code/model/dataAccess/APermission.scala b/src/main/scala/code/model/dataAccess/APermission.scala
new file mode 100644
index 000000000..5c52b9dc3
--- /dev/null
+++ b/src/main/scala/code/model/dataAccess/APermission.scala
@@ -0,0 +1,72 @@
+package code.model.dataAccess
+
+
+
+
+/* APermission.scala */
+
+/*
+ * A permission that has three parts; domain, actions, and entities
+ */
+case class APermission(
+ val domain: String,
+ val actions: Set[String] = Set(APermission.wildcardToken),
+ val entities: Set[String] = Set(APermission.wildcardToken))
+{
+ def implies(p: APermission) = p match {
+ case APermission(d, a, e) =>
+ if (d == APermission.wildcardToken)
+ true
+ else if (d == this.domain) {
+ if (a.contains(APermission.wildcardToken))
+ true
+ else if (this.actions.headOption.map(it => a.contains(it)).getOrElse(false))
+ if (e.contains(APermission.wildcardToken))
+ true
+ else
+ this.entities.headOption.map(it => e.contains(it)).getOrElse(false)
+ else
+ false
+ }
+ else
+ false
+ case _ => false
+ }
+
+ def implies(ps: Set[APermission]): Boolean = ps.exists(this.implies)
+
+ override def toString = {
+ domain+APermission.partDivider+actions.mkString(APermission.subpartDivider)+APermission.partDivider+entities.mkString(APermission.subpartDivider)
+ }
+
+}
+
+object APermission {
+ // Permission
+ val permissionWilcardToken = "*"
+ val permissionPartDivider = ":"
+ val permissionSubpartDivider = ","
+ val permissionCaseSensitive = true
+
+ lazy val wildcardToken = permissionWilcardToken
+ lazy val partDivider = permissionPartDivider
+ lazy val subpartDivider = permissionSubpartDivider
+
+ def apply(domain: String, actions: String): APermission =
+ apply(domain, actions.split(APermission.subpartDivider).toSet)
+
+ def apply(domain: String, actions: String, entities: String): APermission =
+ apply(domain, actions.split(APermission.subpartDivider).toSet, entities.split(APermission.subpartDivider).toSet)
+
+ def fromString(s: String): APermission = s.split(partDivider).toList match {
+ case s :: Nil if (s == wildcardToken) => all
+ case s :: Nil if (s.length == 0) => none
+ case dom :: Nil => APermission(dom)
+ case dom :: acts :: Nil => APermission(dom, acts)
+ case dom :: acts :: ents :: Nil => APermission(dom, acts, ents)
+ case _ => none
+ }
+
+ lazy val all = APermission(wildcardToken)
+ lazy val none = APermission("")
+}
\ No newline at end of file
diff --git a/src/main/scala/code/model/dataAccess/MappedPermission.scala b/src/main/scala/code/model/dataAccess/MappedPermission.scala
new file mode 100644
index 000000000..731c4f692
--- /dev/null
+++ b/src/main/scala/code/model/dataAccess/MappedPermission.scala
@@ -0,0 +1,45 @@
+package code.model.dataAccess
+
+import net.liftweb.mapper._
+
+
+/*
+ * Simple record for storing permissions.
+ */
+object MappedPermission extends MappedPermission with LongKeyedMetaMapper[MappedPermission] {
+
+ def createUserPermission(uid: Long, aPerm: APermission) = {
+ create.userId(uid).permission(aPerm.toString)
+ }
+
+ def removeAllUserPermissions(uid: Long) = {
+ MappedPermission.findAll(By(userId, uid)).map(_.delete_!)
+ }
+
+ def toAPermission(perm: MappedPermission) = APermission.fromString(perm.permission.get)
+ def fromAPermission(aPerm: APermission): MappedPermission = MappedPermission.create.permission(aPerm.toString)
+
+ def userPermissions(uid: Long): List[APermission] = MappedPermission.findAll(By(userId, uid)).map(toAPermission)
+
+}
+
+class MappedPermission extends LongKeyedMapper[MappedPermission] with IdPK {
+ def getSingleton = MappedPermission
+
+ /**
+ * This field is empty for permissions attached directly to the user
+ */
+ object roleId extends MappedStringForeignKey(this, Role, 32) {
+ def foreignMeta = Role
+ }
+
+ /**
+ * This field is empty for permissions attached to a role
+ */
+ object userId extends MappedLong(this) {
+ override def dbIndexed_? = true
+ }
+
+ object permission extends MappedString(this, 1024)
+}
+
diff --git a/src/main/scala/code/model/dataAccess/MappedRole.scala b/src/main/scala/code/model/dataAccess/MappedRole.scala
new file mode 100644
index 000000000..9d7e1f5a4
--- /dev/null
+++ b/src/main/scala/code/model/dataAccess/MappedRole.scala
@@ -0,0 +1,53 @@
+package code.model.dataAccess
+
+import net.liftweb.common.{Box, Empty, Full}
+import net.liftweb.mapper.{MappedString, Mapper}
+
+
+abstract class MappedRole[T<:Mapper[T]](_fieldOwner: T) extends MappedString[T](_fieldOwner, 1024) {
+
+ import scala.xml._
+ import net.liftweb.http.SHtml._
+
+ def buildDisplayList: List[(String, String)] = {
+ Role.allRoles(Role.CAT_TEAM) map (r => (r.id.get, r.displayName))
+ }
+
+ def chosenElement = names match {
+ case name :: _ => Full(name)
+ case _ => Empty
+ }
+
+ override def _toForm: Box[Elem] =
+ Full(selectObj[String](buildDisplayList, chosenElement, v => this.setRole(v)))
+
+ override def asHtml = firstRole.map(_.asHtml).openOr(Text(""))
+
+ def permissions: List[APermission] = names.flatMap(n => Role.find(n).map(_.permissions.allPerms).openOr(Nil))
+
+ def names: List[String] = {
+ val current = if (get == null) "" else get
+ current.split(",").toList
+ }
+
+ def addRole(role: String*) = {
+ val current = if (get == null) "" else get
+ val add = (if (current.length() > 0) "," else "") + role.mkString(",")
+ set(current + add)
+ fieldOwner
+ }
+
+ def setRole(role: String): T = {
+ removeAll
+ addRole(role)
+ fieldOwner
+ }
+
+ def setRole(role: Role): T = setRole(role.id.get)
+
+ def removeAll = {
+ set(""); this
+ }
+
+ def firstRole: Box[Role] = names.headOption.flatMap(name => Role.find(name))
+}
\ No newline at end of file
diff --git a/src/main/scala/code/model/dataAccess/OBPUser.scala b/src/main/scala/code/model/dataAccess/OBPUser.scala
index 3724c1a02..3748b75d1 100755
--- a/src/main/scala/code/model/dataAccess/OBPUser.scala
+++ b/src/main/scala/code/model/dataAccess/OBPUser.scala
@@ -36,7 +36,7 @@ import net.liftweb.util.Mailer.{BCC, To, Subject, From}
import net.liftweb.util._
import net.liftweb.common._
import scala.xml.{Text, NodeSeq}
-import net.liftweb.http.{SHtml, SessionVar, Templates, S}
+import net.liftweb.http._
import net.liftweb.http.js.JsCmds.FocusOnLoad
import java.util.UUID
@@ -49,6 +49,45 @@ import code.sandbox.SandboxUserImport
class OBPUser extends MegaProtoUser[OBPUser] with Logger {
def getSingleton = OBPUser // what's the "meta" server
+ /* Roles START */
+
+ /*
+ * String representing the User ID
+ */
+ override def userIdAsString: String = id.toString
+
+ /*
+ * A list of this user's permissions
+ */
+ object userRoles extends MappedRole(this)
+ lazy val authPermissions: Set[APermission] = (MappedPermission.userPermissions(id.get) ::: userRoles.permissions).toSet
+
+ /*
+ * A list of this user's roles
+ */
+ lazy val authRoles: Set[String] = userRoles.names.toSet
+
+ // Request var that holds the User instance
+ //private object curUserId extends SessionVar[Box[String]](Empty)
+ //def currentUserId: Box[String] = curUserId.is
+ //private object curUser extends RequestVar[Box[OBPUser]](currentUserId.flatMap(findByStringId))
+ //with CleanRequestVarOnSessionTransition {
+ // override lazy val __nameSalt = Helpers.nextFuncName
+ //}
+ //def currentUser: Box[OBPUser] = curUser.is
+
+ import net.liftweb.mapper._
+
+ def findByStringId(strId: String): Box[OBPUser] =
+ try {
+ OBPUser.find(By(id, strId.toLong))
+ } catch {
+ case e: Exception => Empty
+ }
+
+ /* Roles END */
+
+
object user extends MappedLongForeignKey(this, APIUser)
/**
@@ -126,8 +165,8 @@ import net.liftweb.util.Helpers._
override def screenWrap = Full()
// define the order fields will appear in forms and output
- override def fieldOrder = List(id, firstName, lastName, email, password, provider)
- override def signupFields = List(firstName, lastName, email, password)
+ //override def fieldOrder = List(id, firstName, lastName, email, password, provider)
+ //override def signupFields = List(firstName, lastName, email, password)
// comment this line out to require email validations
override def skipEmailValidation = true
@@ -260,26 +299,40 @@ import net.liftweb.util.Helpers._
val producer: KafkaProducer = new KafkaProducer()
// Send request to Kafka, marked with reqId
// so we can fetch the corresponding response
- val argList = Map( "email" -> username,
- "password" -> password )
- producer.send(reqId, "getUser", argList, "1")
+ val isTemenos = username.endsWith("@TEMENOS")
+ val argList = if (isTemenos) {
+ Map("username" -> username.replaceAll("@TEMENOS", ""),
+ "password" -> password)
+ } else {
+ Map("email" -> username,
+ "password" -> password)
+ }
+
+ producer.send(reqId, "getUser", argList, "1")
// Request sent, now we wait for response with the same reqId
val consumer = new KafkaConsumer()
// Create entry only for the first item on returned list
val r = consumer.getResponse(reqId).head
+ println("-------------------> " + r)
// For testing without Kafka
//val r = Map("email"->"test@email.me","password"->"secret","display_name"->"DN")
- var recDisplayName = r.getOrElse("display_name", "Not Found")
- var recEmail = r.getOrElse("email", "Not Found")
+ val recDisplayName = if (isTemenos) r.getOrElse("Name", "Not Found")
+ else r.getOrElse("display_name", "Not Found")
+
+ val recEmail = if (isTemenos) r.getOrElse("Id", "Not Found") + "@TEMENOS"
+ else r.getOrElse("email", "Not Found")
+
+ val recRoles = if (isTemenos) List(r.getOrElse("UserType", "Not Found"))
+ else r.getOrElse("roles", List()).asInstanceOf[List[String]]
if (recEmail == username && recEmail != "Not Found") {
if (recDisplayName == "")
- Full(new SandboxUserImport( username, password, recEmail))
+ Full(new SandboxUserImport( username, password, recRoles, recEmail))
else
- Full(new SandboxUserImport( username, password, recDisplayName))
+ Full(new SandboxUserImport( username, password, recRoles, recDisplayName))
} else {
// If empty result from Kafka return empty data
Empty
@@ -308,10 +361,13 @@ import net.liftweb.util.Helpers._
case _ => 0
}
}
+ def hasRole(role: String): Boolean = currentUser.map(u => hasRole(u, role)).openOr(false)
+
+ def hasRole(user: OBPUser, role: String): Boolean = user.authRoles.exists(_ == role)
def getExternalUser(username: String, password: String):Box[OBPUser] = {
getUserViaKafka(username, password) match {
- case Full(SandboxUserImport(extEmail, extPassword, extDisplayName)) => {
+ case Full(SandboxUserImport(extEmail, extPassword, extRoles, extDisplayName)) => {
val preLoginState = capturePreLoginState()
info("external user authenticated. login redir: " + loginRedirect.get)
val redir = loginRedirect.get match {
@@ -346,6 +402,16 @@ import net.liftweb.util.Helpers._
.password(dummyPassword)
.provider(extProvider)
.validated(true)
+
+ for ( r <- extRoles ) {
+ info("adding role (" + r + ") to new user")
+ if (Role.find(r).openOr("") != "") {
+ newUser.userRoles.addRole(r).saveMe
+ info("role (" + r + ") added")
+ }
+ else
+ info("role (" + r + ") not defined")
+ }
// Save the user in order to be able to log in
newUser.save()
// Return created user
@@ -398,6 +464,8 @@ import net.liftweb.util.Helpers._
val user = getExternalUser(S.param("username").get, S.param("password").get)
if (!user.isEmpty) {
+ println ("-----------------> admin: " + hasRole(user.get, "admin") )
+ println ("-----------------> test: " + hasRole(user.get, "test") )
logUserIn(user.get, () => {
S.notice(S.?("logged.in"))
@@ -460,3 +528,6 @@ import net.liftweb.util.Helpers._
innerSignup
}
}
+
+
+
diff --git a/src/main/scala/code/model/dataAccess/Role.scala b/src/main/scala/code/model/dataAccess/Role.scala
new file mode 100644
index 000000000..6c326ec3a
--- /dev/null
+++ b/src/main/scala/code/model/dataAccess/Role.scala
@@ -0,0 +1,74 @@
+package code.model.dataAccess
+
+import net.liftweb.common.Loggable
+import net.liftweb.http.S
+import net.liftweb.mapper._
+
+/*
+ * Simple record for storing roles. Role name is the PK.
+ */
+object Role extends Role with KeyedMetaMapper[String, Role] with Loggable {
+
+ val R_SUPERUSER = "superuser"
+ val R_USER = "user"
+ val R_TEAM_OWNER = "owner"
+ val R_TEAM_MEMBER = "member"
+ val R_TEAM_WATCHER = "watcher"
+
+ val CAT_SYSTEM = "system"
+ val CAT_TEAM = "team"
+
+ override def dbTableName = "roles"
+
+ def findOrCreate(roleId: String): Role = find(roleId).openOr(create.id(roleId))
+ def findOrCreateAndSave(roleId: String, category: String, perms: MappedPermission*): Role = {
+ find(roleId).openOr {
+ logger.info("Create Role %s for category %s".format(roleId, category))
+ val r = create.id(roleId).category(category)
+ r.permissions.appendAll(perms)
+ r.saveMe
+ }
+ }
+
+ def allRoles(cat: String) = Role.findAll(By(category, cat))
+
+ lazy val TeamOwner = Role.find(R_TEAM_OWNER).openOrThrowException("No Owner Role found")
+ lazy val TeamMember = Role.find(R_TEAM_MEMBER).openOrThrowException("No Member Role found")
+ lazy val TeamWatcher = Role.find(R_TEAM_WATCHER).openOrThrowException("No Watcher Role found")
+
+}
+
+class Role extends KeyedMapper[String, Role] with OneToMany[String,Role] {
+ def getSingleton = Role
+ def primaryKeyField = id
+ object id extends MappedStringIndex(this, 32) {
+ override def writePermission_? = true
+ override def dbAutogenerated_? = false
+ override def dbNotNull_? = true
+ override def dbIndexed_? = true
+ override def displayName = "Name"
+ }
+
+ object category extends MappedString(this, 50)
+
+ object permissions extends MappedOneToMany(MappedPermission, MappedPermission.roleId) {
+ def allPerms: List[APermission] = all.map(MappedPermission.toAPermission)
+ }
+
+ override def equals(other: Any): Boolean = other match {
+ case r: Role => r.id.get == this.id.get
+ case _ => false
+ }
+
+ def displayName() = S ? ("userClientConnection.role."+id.get)
+
+ override def asHtml = {
+ val cls = "label" + (id.get match {
+ case Role.R_TEAM_OWNER => " label-important"
+ case Role.R_TEAM_MEMBER => " label-info"
+ case _ => ""
+ })
+ {displayName}
+ }
+
+}
diff --git a/src/main/scala/code/sandbox/OBPDataImport.scala b/src/main/scala/code/sandbox/OBPDataImport.scala
index 03030c65a..62f62cddf 100644
--- a/src/main/scala/code/sandbox/OBPDataImport.scala
+++ b/src/main/scala/code/sandbox/OBPDataImport.scala
@@ -597,6 +597,7 @@ case class SandboxLocationImport(
case class SandboxUserImport(
email : String,
password : String,
+ roles: List[String],
display_name : String)
case class SandboxAccountImport(
diff --git a/src/test/scala/code/sandbox/SandboxDataLoadingTest.scala b/src/test/scala/code/sandbox/SandboxDataLoadingTest.scala
index 4fd795b2a..f1c202270 100644
--- a/src/test/scala/code/sandbox/SandboxDataLoadingTest.scala
+++ b/src/test/scala/code/sandbox/SandboxDataLoadingTest.scala
@@ -457,8 +457,8 @@ class SandboxDataLoadingTest extends FlatSpec with SendServerRequests with Shoul
val standardProducts = product1AtBank1 :: product2AtBank1 :: Nil
- val user1 = SandboxUserImport(email = "user1@example.com", password = "qwerty", display_name = "User 1")
- val user2 = SandboxUserImport(email = "user2@example.com", password = "qwerty", display_name = "User 2")
+ val user1 = SandboxUserImport(email = "user1@example.com", password = "qwerty", display_name = "User 1", roles = List())
+ val user2 = SandboxUserImport(email = "user2@example.com", password = "qwerty", display_name = "User 2", roles = List())
val standardUsers = user1 :: user2 :: Nil