Forcing Authentication not working

10 views
Skip to first unread message

aw

unread,
Feb 6, 2010, 3:19:03 PM2/6/10
to Lift
According to:

http://scala-tools.org/mvnsites-snapshots/liftweb/lift-base/lift-webkit/scaladocs/net/liftweb/http/LiftRules$object.html#httpAuthProtectedResource

It says that if I have a rule that responds with Empty, it means that
authentications is performed, but not an authorization check. This is
what I need. So, I have a rule something like:

LiftRules.httpAuthProtectedResource.append {
case req : Req => req.path match {
case ParsePath("restricted" :: _, _, _, _) => restrictedRole
case _ => Empty
}
}

Alas, I can clearly see that my authentication is NOT being performed
for just any page.

Am I missing something, or is the documentation incorrect, or is this
a bug? Note that I am using 2.0-M1.

Timothy Perrett

unread,
Feb 7, 2010, 6:11:54 AM2/7/10
to lif...@googlegroups.com
How odd - why dont you just match on the req unapply rather that this awkward sub match?

You need to do something like:

LiftRules.httpAuthProtectedResource.prepend {
case Req("restricted" :: _,_,_) => Full(AuthRole("admin"))
}

LiftRules.authentication = HttpBasicAuthentication("myrealm"){
case ("admin", "password", req) => {
userRoles(AuthRole("admin"))
true
}
case (user, pass,_) => Log.warn("Attempted report login with: " + user + ":" + pass); false
}

Cheers, Tim

aw

unread,
Feb 7, 2010, 2:59:04 PM2/7/10
to Lift
Thank you for responding, and I can use your advice to make my code a
little less verbose and concise. Alas, it did not address my core
issue of forcing authentication. The behavior is exactly the same.

If I go to a protected resource with a Role requirement, then
authentication and an authorization check is performed. But
otherwise, nothing, and I need the authentication done because that is
what defines the roles, and I have some pages that are accessible, but
adjust due to a user's restrictions (fine-grained access control)...

Perhaps it would help to elaborate on my solution...

My authentication mechanism is really done by a special SSO Filter.
The Filter populates the HTTPRequest's remote user, so then Lift can
simply ask that for who was authenticated. While the Servlet layer
has a valid username, I still need to build some kind of bridge to
Lift land. As a result, I have a specialized HttpAuthentication like
so:

case class FilterAuthentication (authorizationFunc:
PartialFunction[(String), Boolean]) extends HttpAuthentication {

/** Extract the remote username from the HTTP Request. */
private def authenticatedUsername (r : Req) =
r.request.asInstanceOf[HTTPRequestServlet].req.getRemoteUser().toLowerCase()

/** Extract the Authenticated User and call the Authorization
function. */
def verified_? = {
case req => {
authenticatedUsername(req) match {
case u if null != u && u.length > 1 &&
authorizationFunc.isDefinedAt(u) => authorizationFunc(u)
case _ => false
}
}
}
}

Then, I need to hook this into LiftRules like so:

LiftRules.authentication = FilterAuthentication( {
case username => Log.info("Authenticating: " + username)
val isAuthorized = Auth.isAuthorized(username)
if (isAuthorized) {
val authorizedRoles = Auth.authorizedRoles(username)
userRoles(authorizedRoles)
}
isAuthorized
})

Note that the Filter will acquire an authenticated user -- but that
user may still not have access to this particular application. As a
result, I need to do the "isAuthorized" check that basically does a
lookup to see if jdoe, for example, is authorized to access my
application. If he is, then roles are populated and true is returned;
if not, false is returned.

I need to restrict some pages, so I do something like:

LiftRules.httpAuthProtectedResource.prepend {
case Req("restricted" :: _,_,_) => restrictedRole
case _ => Empty
}

If the user goes to /restricted/, then I see the authentication and
authorization kick in. But I am also interested in authentication
(i.e. validating that the user is allowed to access the application)
even if index.xhtml is hit. I thought I am telling Lift to do that
with the "case _ => Empty".

I am not using the siteMap to generate my menus because I need to
control the styling. As a result, when I create my menus, I do
something like:

<lift:Auth.secure role="restricted"><a href="/restricted/">Restricted
Access</a></lift:Auth.secure>

In my case, I actually have the privilege, so I should see this link,
however because Lift isn't calling the FilterAuthentication logic, it
doesn't know my roles and hence hides the link.
If I go to /restricted/, then all is well...

In summary, according to the documentation, returning an Empty should
force an authentication, but that doesn't seem to be working. Am I
doing something wrong? Or is the documentation incorrect? Or is
there a bug in 2.0-M1?

(This is a big show stopper for me, so I certainly appreciate the
assistance.)

aw

unread,
Feb 7, 2010, 3:44:34 PM2/7/10
to Lift
OK, I think I found the source code that is the culprit. From
LiftServlet.scala:

http://github.com/dpp/liftweb/blob/master/framework/lift-base/lift-webkit/src/main/scala/net/liftweb/http/LiftServlet.scala

private def authPassed_?(req: Req): Boolean = {

val checkRoles: (Role, List[Role]) => Boolean = {
case (resRole, roles) => (false /: roles)((l, r) => l ||
resRole.isChildOf(r.name))
}

val role = NamedPF.applyBox(req,
LiftRules.httpAuthProtectedResource.toList)
role.map(_ match {
case Full(r) =>
LiftRules.authentication.verified_?(req) match {
case true => checkRoles(r, userRoles.get)
case _ => false
}
case _ => true
}) openOr true
}

This logic seems to be inconsistent with the documentation found in
LiftRules.scala:

http://github.com/dpp/liftweb/blob/master/framework/lift-base/lift-webkit/src/main/scala/net/liftweb/http/LiftRules.scala

/**
* Defines the resources that are protected by authentication and
authorization. If this function
* is notdefined for the input data, the resource is considered
unprotected ergo no authentication
* is performed. If this function is defined and returns a Full can,
it means that this resource
* is protected by authentication,and authenticated subjed must be
assigned to the role returned by
* this function or to a role that is child-of this role. If this
function returns Empty it means that
* this resource is protected by authentication but no authorization
is performed meaning that roles are
* not verified.
*/
val httpAuthProtectedResource =
RulesSeq[HttpAuthProtectedResourcePF]

Shouldn't there be something like:

case Empty =>
LiftRules.authentication.verified_?(req)

?

Marius

unread,
Feb 8, 2010, 2:31:59 AM2/8/10
to Lift
Please open a defect here http://www.assembla.com/spaces/liftweb/tickets

Br's,
Marius

On Feb 7, 10:44 pm, aw <anth...@whitford.com> wrote:
> OK, I think I found the source code that is the culprit.  From
> LiftServlet.scala:
>

> http://github.com/dpp/liftweb/blob/master/framework/lift-base/lift-we...


>
>   private def authPassed_?(req: Req): Boolean = {
>
>     val checkRoles: (Role, List[Role]) => Boolean = {
>       case (resRole, roles) => (false /: roles)((l, r) => l ||
> resRole.isChildOf(r.name))
>     }
>
>     val role = NamedPF.applyBox(req,
> LiftRules.httpAuthProtectedResource.toList)
>     role.map(_ match {
>       case Full(r) =>
>         LiftRules.authentication.verified_?(req) match {
>           case true => checkRoles(r, userRoles.get)
>           case _ => false
>         }
>       case _ => true
>     }) openOr true
>   }
>
> This logic seems to be inconsistent with the documentation found in
> LiftRules.scala:
>

> http://github.com/dpp/liftweb/blob/master/framework/lift-base/lift-we...

aw

unread,
Feb 8, 2010, 4:18:41 AM2/8/10
to Lift
On Feb 7, 11:31 pm, Marius <marius.dan...@gmail.com> wrote:
> Please open a defect herehttp://www.assembla.com/spaces/liftweb/tickets

Would love to, but the "New Ticket" button does not seem to exist...
Is this project configured correctly to accept non-teammate
submissions?

Indrajit Raychaudhuri

unread,
Feb 8, 2010, 4:41:22 AM2/8/10
to lif...@googlegroups.com
You have to register yourself as "Watcher" of the space liftweb to see
the "New Ticket" button :) We have this rule to avoid (Anonymous) spams
in the ticket.

Cheers, Indrajit

Timothy Perrett

unread,
Feb 8, 2010, 4:42:54 AM2/8/10
to lif...@googlegroups.com
You need to be registered on assembla and watching the Lift space - we do that to avoid spam

Cheers, Tim

> --
> You received this message because you are subscribed to the Google Groups "Lift" group.
> To post to this group, send email to lif...@googlegroups.com.
> To unsubscribe from this group, send email to liftweb+u...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.
>
>

Reply all
Reply to author
Forward
0 new messages