From d8e33715fe8495acaa5e94cf129b104dcd485904 Mon Sep 17 00:00:00 2001 From: Alex Kuang Date: Fri, 24 May 2013 16:25:01 -0400 Subject: [PATCH 1/5] Change over to HTML5 and designer friendly template style. --- .../main/scala/bootstrap/liftweb/Boot.scala | 5 +++- example/src/main/webapp/index.html | 22 ++++++++++------ example/src/main/webapp/login.html | 26 ++++++++++++------- example/src/main/webapp/restricted.html | 13 +++++++--- .../main/webapp/templates-hidden/default.html | 14 +++++----- 5 files changed, 52 insertions(+), 28 deletions(-) diff --git a/example/src/main/scala/bootstrap/liftweb/Boot.scala b/example/src/main/scala/bootstrap/liftweb/Boot.scala index b4cd4bf..b81d1e2 100644 --- a/example/src/main/scala/bootstrap/liftweb/Boot.scala +++ b/example/src/main/scala/bootstrap/liftweb/Boot.scala @@ -1,6 +1,6 @@ package bootstrap.liftweb -import net.liftweb.http.LiftRules +import net.liftweb.http.{Html5Properties, LiftRules, Req} import net.liftweb.sitemap._ import shiro.Shiro import shiro.sitemap.Locs._ @@ -17,5 +17,8 @@ class Boot { Menu("Login") / "login" >> DefaultLogin >> RequireNoAuthentication ) ::: Shiro.menus: _* )) + + LiftRules.htmlProperties.default.set((r: Req) => + new Html5Properties(r.userAgent)) } } diff --git a/example/src/main/webapp/index.html b/example/src/main/webapp/index.html index bb63339..28dbcd3 100644 --- a/example/src/main/webapp/index.html +++ b/example/src/main/webapp/index.html @@ -1,8 +1,14 @@ - -

Home

- - -

This content is only available for admins

-
- -
+ + + Lift Shiro + + +
+

Home

+ +
+

This content is only available for admins

+
+
+ + diff --git a/example/src/main/webapp/login.html b/example/src/main/webapp/login.html index 8061566..d7c62f4 100644 --- a/example/src/main/webapp/login.html +++ b/example/src/main/webapp/login.html @@ -1,10 +1,16 @@ - -

Login

- -
-

Username:

-

Password:

-

-
- -
+ + + Lift Shiro + + +
+

Login

+ +
+

Username:

+

Password:

+

+
+
+ + diff --git a/example/src/main/webapp/restricted.html b/example/src/main/webapp/restricted.html index f7e7bcc..693d0f7 100644 --- a/example/src/main/webapp/restricted.html +++ b/example/src/main/webapp/restricted.html @@ -1,3 +1,10 @@ - -

Restricted to admin

-
+ + + Lift Shiro + + +
+

Restricted to admin

+
+ + diff --git a/example/src/main/webapp/templates-hidden/default.html b/example/src/main/webapp/templates-hidden/default.html index c8f70b1..fb4a901 100644 --- a/example/src/main/webapp/templates-hidden/default.html +++ b/example/src/main/webapp/templates-hidden/default.html @@ -2,13 +2,15 @@ Lift Shiro - - -
- -
- +
+
+
+
+
+
+
From b5184dc00ba64f00ec842ee4463dcc1be0d07c77 Mon Sep 17 00:00:00 2001 From: Alex Kuang Date: Fri, 24 May 2013 20:25:53 -0400 Subject: [PATCH 2/5] A custom realm file instead of using pure static .ini configuration. --- example/src/main/resources/shiro.ini | 68 ++++++++-------- .../src/main/scala/example/ExampleRealm.scala | 81 +++++++++++++++++++ 2 files changed, 117 insertions(+), 32 deletions(-) create mode 100644 example/src/main/scala/example/ExampleRealm.scala diff --git a/example/src/main/resources/shiro.ini b/example/src/main/resources/shiro.ini index b77a78d..427a11a 100644 --- a/example/src/main/resources/shiro.ini +++ b/example/src/main/resources/shiro.ini @@ -4,37 +4,41 @@ # For those that might not understand the references in this file, the # definitions are all based on the classic Mel Brooks' film "Spaceballs". ;) # ============================================================================= +[main] +myRealm = example.ExampleRealm -# ----------------------------------------------------------------------------- -# Users and their assigned roles -# -# Each line conforms to the format defined in the -# org.apache.shiro.realm.text.TextConfigurationRealm#setUserDefinitions JavaDoc -# ----------------------------------------------------------------------------- -[users] -# user 'root' with password 'secret' and the 'admin' role -root = secret, admin -# user 'guest' with the password 'guest' and the 'guest' role -guest = guest, guest -# user 'presidentskroob' with password '12345' ("That's the same combination on -# my luggage!!!" ;)), and role 'president' -presidentskroob = 12345, president -# user 'darkhelmet' with password 'ludicrousspeed' and roles 'darklord' and 'schwartz' -darkhelmet = ludicrousspeed, darklord, schwartz -# user 'lonestarr' with password 'vespa' and roles 'goodguy' and 'schwartz' -lonestarr = vespa, goodguy, schwartz +# Uncomment the below to use simple static configuration instead of a custom realm. -# ----------------------------------------------------------------------------- -# Roles with assigned permissions -# -# Each line conforms to the format defined in the -# org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions JavaDoc -# ----------------------------------------------------------------------------- -[roles] -# 'admin' role has all permissions, indicated by the wildcard '*' -admin = * -# The 'schwartz' role can do anything (*) with any lightsaber: -schwartz = lightsaber:* -# The 'goodguy' role is allowed to 'drive' (action) the winnebago (type) with -# license plate 'eagle5' (instance specific id) -goodguy = winnebago:drive:eagle5 +## ----------------------------------------------------------------------------- +## Users and their assigned roles +## +## Each line conforms to the format defined in the +## org.apache.shiro.realm.text.TextConfigurationRealm#setUserDefinitions JavaDoc +## ----------------------------------------------------------------------------- +#[users] +## user 'root' with password 'secret' and the 'admin' role +#root = secret, admin +## user 'guest' with the password 'guest' and the 'guest' role +#guest = guest, guest +## user 'presidentskroob' with password '12345' ("That's the same combination on +## my luggage!!!" ;)), and role 'president' +#presidentskroob = 12345, president +## user 'darkhelmet' with password 'ludicrousspeed' and roles 'darklord' and 'schwartz' +#darkhelmet = ludicrousspeed, darklord, schwartz +## user 'lonestarr' with password 'vespa' and roles 'goodguy' and 'schwartz' +#lonestarr = vespa, goodguy, schwartz +# +## ----------------------------------------------------------------------------- +## Roles with assigned permissions +## +## Each line conforms to the format defined in the +## org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions JavaDoc +## ----------------------------------------------------------------------------- +#[roles] +## 'admin' role has all permissions, indicated by the wildcard '*' +#admin = * +## The 'schwartz' role can do anything (*) with any lightsaber: +#schwartz = lightsaber:* +## The 'goodguy' role is allowed to 'drive' (action) the winnebago (type) with +## license plate 'eagle5' (instance specific id) +#goodguy = winnebago:drive:eagle5 diff --git a/example/src/main/scala/example/ExampleRealm.scala b/example/src/main/scala/example/ExampleRealm.scala new file mode 100644 index 0000000..bc4eee5 --- /dev/null +++ b/example/src/main/scala/example/ExampleRealm.scala @@ -0,0 +1,81 @@ +package example + +import org.apache.shiro.realm.AuthorizingRealm +import org.apache.shiro.authc._ +import org.apache.shiro.authz._ +import org.apache.shiro.authz.permission.WildcardPermission +import org.apache.shiro.subject.PrincipalCollection + +import scala.collection.immutable.{Set, HashMap} +import scala.collection.JavaConversions._ + +class ExampleRealm extends AuthorizingRealm { + class User(val username: String, val password: String) + + /** + * A fake DAO for storing user credentials, roles, permissions, etc. + * In practice this will probably be a db/persistence layer of some sort. + */ + object UserDAO { + // Passwords are stored plain here but in real life please at least BCrypt them like a decent human being. + private[this] val userCredentials = HashMap( + "root" -> "secret", + "guest" -> "guest", + "presidentskroob" -> "12345", + "darkhelmet" -> "ludicrousspeed", + "lonestarr" -> "vespa" + ) + + private[this] val userRoles = HashMap( + "root" -> Set("admin"), + "guest" -> Set("guest"), + "presidentskroob" -> Set("president"), + "darkhelmet" -> Set("darklord", "schwartz"), + "lonestarr" -> Set("goodguy", "schwartz") + ) + + private[this] val rolePermissions = HashMap( + "admin" -> Set(new WildcardPermission("*")), + "schwartz" -> Set(new WildcardPermission("lightsaber:*")), + "goodguy" -> Set(new WildcardPermission("winnebago:drive:eagle5")) + ) + + def getUser(username: String, password: String): Option[User] = { + if ((userCredentials contains username) && userCredentials(username) == password) + Some(new User(username, password)) + else + None + } + + def getRoles(user: User): Set[String] = { + userRoles.getOrElse(user.username, Set()) + } + + def getRolePermissions(role: String): Set[WildcardPermission] = { + rolePermissions.getOrElse(role, Set()) + } + } + + // The methods from AuthorizingRealm that actually have to be implemented. + + def doGetAuthenticationInfo(token: AuthenticationToken): AuthenticationInfo = { + val userpassToken = token.asInstanceOf[UsernamePasswordToken] + val username = userpassToken.getUsername() + val password = userpassToken.getPassword() + + UserDAO.getUser(username, password.mkString("")) match { + case Some(user: User) => new SimpleAuthenticationInfo(user, user.password, "ExampleRealm") + case None => throw new AuthenticationException("Invalid credentials provided!") + } + } + + def doGetAuthorizationInfo(principals: PrincipalCollection): AuthorizationInfo = { + val roles: Set[String] = principals.flatMap(p => UserDAO.getRoles(p.asInstanceOf[User])).toSet + val permissions: Set[Permission] = roles.flatMap(r => UserDAO.getRolePermissions(r)).toSet + + val authInfo = new SimpleAuthorizationInfo(roles) + authInfo.setObjectPermissions(permissions) + + return authInfo + } +} From e0cb57631a6e8dbc3727b71512a195826791ab60 Mon Sep 17 00:00:00 2001 From: Alex Kuang Date: Fri, 24 May 2013 20:55:26 -0400 Subject: [PATCH 3/5] A little bit more exercising wildcard permissions. --- example/src/main/scala/example/ExampleRealm.scala | 9 +++++++-- example/src/main/webapp/index.html | 7 +++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/example/src/main/scala/example/ExampleRealm.scala b/example/src/main/scala/example/ExampleRealm.scala index bc4eee5..c3fab10 100644 --- a/example/src/main/scala/example/ExampleRealm.scala +++ b/example/src/main/scala/example/ExampleRealm.scala @@ -9,12 +9,15 @@ import org.apache.shiro.subject.PrincipalCollection import scala.collection.immutable.{Set, HashMap} import scala.collection.JavaConversions._ +/** + * An example class to demonstrate setting up a custom realm in shiro. + */ class ExampleRealm extends AuthorizingRealm { class User(val username: String, val password: String) /** * A fake DAO for storing user credentials, roles, permissions, etc. - * In practice this will probably be a db/persistence layer of some sort. + * In practice this will probably be a db/persistence obj of some sort. */ object UserDAO { // Passwords are stored plain here but in real life please at least BCrypt them like a decent human being. @@ -37,7 +40,9 @@ class ExampleRealm extends AuthorizingRealm { private[this] val rolePermissions = HashMap( "admin" -> Set(new WildcardPermission("*")), "schwartz" -> Set(new WildcardPermission("lightsaber:*")), - "goodguy" -> Set(new WildcardPermission("winnebago:drive:eagle5")) + "darklord" -> Set(new WildcardPermission("winnebago:steal:eagle5")), + // Good guys can do whatever they want with the eagle5. + "goodguy" -> Set(new WildcardPermission("winnebago:*:eagle5")) ) def getUser(username: String, password: String): Option[User] = { diff --git a/example/src/main/webapp/index.html b/example/src/main/webapp/index.html index 28dbcd3..b39df32 100644 --- a/example/src/main/webapp/index.html +++ b/example/src/main/webapp/index.html @@ -9,6 +9,13 @@

Home

This content is only available for admins

+ +
+

Whoooooosh, you can drive the eagle5!

+
+
+

Sneaky sneaky, you can steal the eagle5!

+
From c0d2e605e8c8582795db06818339b46097bd08a4 Mon Sep 17 00:00:00 2001 From: Alex Kuang Date: Sun, 2 Jun 2013 12:42:41 -0400 Subject: [PATCH 4/5] Use Map instead of HashMap, get rid of extraneous imports, change JavaConversions to explicit calls with JavaConverters. --- example/src/main/scala/example/ExampleRealm.scala | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/example/src/main/scala/example/ExampleRealm.scala b/example/src/main/scala/example/ExampleRealm.scala index c3fab10..1b52aa4 100644 --- a/example/src/main/scala/example/ExampleRealm.scala +++ b/example/src/main/scala/example/ExampleRealm.scala @@ -6,8 +6,7 @@ import org.apache.shiro.authz._ import org.apache.shiro.authz.permission.WildcardPermission import org.apache.shiro.subject.PrincipalCollection -import scala.collection.immutable.{Set, HashMap} -import scala.collection.JavaConversions._ +import collection.JavaConverters._ /** * An example class to demonstrate setting up a custom realm in shiro. @@ -21,7 +20,7 @@ class ExampleRealm extends AuthorizingRealm { */ object UserDAO { // Passwords are stored plain here but in real life please at least BCrypt them like a decent human being. - private[this] val userCredentials = HashMap( + private[this] val userCredentials = Map( "root" -> "secret", "guest" -> "guest", "presidentskroob" -> "12345", @@ -29,7 +28,7 @@ class ExampleRealm extends AuthorizingRealm { "lonestarr" -> "vespa" ) - private[this] val userRoles = HashMap( + private[this] val userRoles = Map( "root" -> Set("admin"), "guest" -> Set("guest"), "presidentskroob" -> Set("president"), @@ -37,7 +36,7 @@ class ExampleRealm extends AuthorizingRealm { "lonestarr" -> Set("goodguy", "schwartz") ) - private[this] val rolePermissions = HashMap( + private[this] val rolePermissions = Map( "admin" -> Set(new WildcardPermission("*")), "schwartz" -> Set(new WildcardPermission("lightsaber:*")), "darklord" -> Set(new WildcardPermission("winnebago:steal:eagle5")), @@ -75,11 +74,11 @@ class ExampleRealm extends AuthorizingRealm { } def doGetAuthorizationInfo(principals: PrincipalCollection): AuthorizationInfo = { - val roles: Set[String] = principals.flatMap(p => UserDAO.getRoles(p.asInstanceOf[User])).toSet + val roles: Set[String] = principals.asScala.flatMap(p => UserDAO.getRoles(p.asInstanceOf[User])).toSet val permissions: Set[Permission] = roles.flatMap(r => UserDAO.getRolePermissions(r)).toSet - val authInfo = new SimpleAuthorizationInfo(roles) - authInfo.setObjectPermissions(permissions) + val authInfo = new SimpleAuthorizationInfo(roles.asJava) + authInfo.setObjectPermissions(permissions.asJava) return authInfo } From bf2474cc8daf0946719bfd58563ebeb4899a5db2 Mon Sep 17 00:00:00 2001 From: Alex Kuang Date: Tue, 13 Aug 2013 19:02:06 -0400 Subject: [PATCH 5/5] Make getUser more idiomatic --- example/src/main/scala/example/ExampleRealm.scala | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/example/src/main/scala/example/ExampleRealm.scala b/example/src/main/scala/example/ExampleRealm.scala index 1b52aa4..b43ece3 100644 --- a/example/src/main/scala/example/ExampleRealm.scala +++ b/example/src/main/scala/example/ExampleRealm.scala @@ -44,12 +44,10 @@ class ExampleRealm extends AuthorizingRealm { "goodguy" -> Set(new WildcardPermission("winnebago:*:eagle5")) ) - def getUser(username: String, password: String): Option[User] = { - if ((userCredentials contains username) && userCredentials(username) == password) - Some(new User(username, password)) - else - None - } + def getUser(username: String, password: String): Option[User] = for { + pass <- userCredentials.get(username) + if (pass == password) + } yield new User(username, password) def getRoles(user: User): Set[String] = { userRoles.getOrElse(user.username, Set())