summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Moger <james.moger@gitblit.com>2012-08-10 17:37:36 -0400
committerJames Moger <james.moger@gitblit.com>2012-08-10 17:37:36 -0400
commit88fb67f6cfdef7a3d44691aca623d3486fec3655 (patch)
treeff6b573fe7ba067e00c17f9945744c320744af19
parent086c0447227a4075b66b976088542fee113b0d4f (diff)
downloadgitblit-88fb67f6cfdef7a3d44691aca623d3486fec3655.tar.gz
gitblit-88fb67f6cfdef7a3d44691aca623d3486fec3655.zip
Improve submodule checking in LuceneExecutor (issue-119)
-rw-r--r--src/com/gitblit/LuceneExecutor.java3
-rw-r--r--src/com/gitblit/models/PathModel.java11
2 files changed, 14 insertions, 0 deletions
diff --git a/src/com/gitblit/LuceneExecutor.java b/src/com/gitblit/LuceneExecutor.java
index d1c27f4d..9cae3d52 100644
--- a/src/com/gitblit/LuceneExecutor.java
+++ b/src/com/gitblit/LuceneExecutor.java
@@ -652,6 +652,9 @@ public class LuceneExecutor implements Runnable {
Resolution.MINUTE);
IndexWriter writer = getIndexWriter(repositoryName);
for (PathChangeModel path : changedPaths) {
+ if (path.isSubmodule()) {
+ continue;
+ }
// delete the indexed blob
deleteBlob(repositoryName, branch, path.name);
diff --git a/src/com/gitblit/models/PathModel.java b/src/com/gitblit/models/PathModel.java
index c78b300c..9bb7eb73 100644
--- a/src/com/gitblit/models/PathModel.java
+++ b/src/com/gitblit/models/PathModel.java
@@ -46,6 +46,10 @@ public class PathModel implements Serializable, Comparable<PathModel> {
this.commitId = commitId;
}
+ public boolean isSubmodule() {
+ return FileMode.GITLINK.equals(mode);
+ }
+
public boolean isTree() {
return FileMode.TREE.equals(mode);
}
@@ -71,6 +75,13 @@ public class PathModel implements Serializable, Comparable<PathModel> {
if (isTree && otherTree) {
return path.compareTo(o.path);
} else if (!isTree && !otherTree) {
+ if (isSubmodule() && o.isSubmodule()) {
+ return path.compareTo(o.path);
+ } else if (isSubmodule()) {
+ return -1;
+ } else if (o.isSubmodule()) {
+ return 1;
+ }
return path.compareTo(o.path);
} else if (isTree && !otherTree) {
return -1;
47'>47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
---
title: Using Vaadin CDI With JAAS Authentication
order: 50
layout: page
---

[[using-vaadin-cdi-with-jaas-authentication]]
Using Vaadin CDI with JAAS authentication
-----------------------------------------

Servlet 3.0 is awesome, so is CDI. They work well and are a joy to set
up. Even adding the Vaadin Navigator to the mix isn't an issue, since
you can use the CDIViewProvider to maintain the injection chains.
Everything works nicely with annotations, and you don't need to mess
around with nasty XML. But what if you want to use JAAS? Recently I
stumbled upon a small dilemma in a customer project where the customer
wanted to use JAAS in combination with CDI. How in the world was I going
to combine the two?

For this example I am using JBoss 7.1 for out-of-the-box support for CDI
and JAAS. I also tried this on Wildfly (the newest JBoss), but ran into
a bug that Matti has made
https://github.com/mstahv/vaadin-cdi-jaas-jbossas-example/tree/workaround[a
bit hacky workaround here]. I use a Postgres database, because a
database-based mechanism is more in the real world then the simplest
cases. But you can use any authentication mechanism you want, like LDAP,
AD and so on; you’ll find tutorials for integrating these into the JAAS
modules of almost any application server.

There is a little bit of configuration involved because of the DB
connection, but don't be scared. It really is quite easy stuff. OK,
everyone ready? Lets dive in.

The problem is that you need to define a secure mapping for a servlet in
web.xml, but maintaining the automatic scanning of Servlet annotations.
Luckily, we can use both. The servlet mapping (or UI mapping, in our
case) will take care of combining the two. I am not actually defining a
Servlet anywhere, but using the Vaadin-CDI addon for this. I simply
annotate my UIs with the @CDIUI annotation, and they will be picked up
automatically. The idea is to have an unsecured root UI mapped to /, a
secured area mapped to /secure, and a login page mapped to /login. For
the root UI, it looks like this:

[source,java]
....
@CDIUI
public class UnsecureUI extends UI {

  @Override
  protected void init(VaadinRequest request) {
    final VerticalLayout layout = new VerticalLayout();
    layout.setMargin(true);
    setContent(layout);

    layout.addComponent(new Label("unsecure UI"));

    Button b = new Button("Go to secure part");
    b.addClickListener(new ClickListener() {
      @Override
      public void buttonClick(ClickEvent event) {
          String currentURI = getPage().getLocation().toString();
          getPage().setLocation(currentURI + "secure");
      }
    });
    layout.addComponent(b);
  }
}
....

The CDI addon (more exactly, the CDIUIProvider) will find the UI, and
will automatically deploy it. You can then start injecting things into
the UI class, such as a CDIViewProvider for the Navigator:

[source,java]
....
@Inject
private CDIViewProvider provider;

@Override
protected void init(VaadinRequest request) {
  Navigator n = new Navigator(this, this);
  n.addProvider(provider);
....

Please note that you can configure the Servlet in a multitude of ways;
you can map the Servlet in your web.xml file as well. Leave out any UI
definitions, and put this in instead:

[source,xml]
....
<init-param>
  <param-name>UIProvider</param-name>
  <param-value>com.vaadin.cdi.CDIUIProvider</param-value>
</init-param>
....

This snippet tells Vaadin to use the same classpath scanning as it would
with Servlet annotations.

So, thats it for the CDI part. What about JAAS? Well, we need to put
this in web.xml, so lets create the file and add some security
configuration:

[source,xml]
....
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
   http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">

  <security-constraint>
    <display-name>SecureApplicationConstraint</display-name>
    <web-resource-collection>
      <web-resource-name>SecureUI</web-resource-name>
      <description>Only this UI is protected</description>
      <url-pattern>/secure/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
      <description>Only valid users are allowed</description>
      <role-name>viewer</role-name>
    </auth-constraint>
  </security-constraint>
  <login-config>
    <auth-method>FORM</auth-method>
    <realm-name>ApplicationRealm</realm-name>
    <form-login-config>
      <form-login-page>/login</form-login-page>
      <form-error-page>/login</form-error-page>
    </form-login-config>
  </login-config>
  <security-role>
    <role-name>viewer</role-name>
  </security-role>

</web-app>
....

What happens is that the root of our application is not secured; here we
have an unsecured part of our application. When the user tries to access
the secure area under /secure, they get redirected to our login UI under
/login. Any attempt to go to /secure/ without logging in will be blocked
by the server. We also restrict that any user who tries to access the
application needs to have the 'viewer' role, or they won't get in.

The secure UI and the login UI are added below:

SecureUI:

[source,java]
....
@CDIUI("secure")
public class SecureUI extends UI {

  @Override
  protected void init(VaadinRequest request) {
    final VerticalLayout layout = new VerticalLayout();
    layout.setMargin(true);
    setContent(layout);

    layout.addComponent(new Label("This is a secure UI! Username is "
          + request.getUserPrincipal().getName()));
  }
}
....

The secure page isn’t anything special, but you can see how we access
the user name from the JAAS security context.

LoginUI:

[source,java]
....
@CDIUI("login")
public class LoginUI extends UI {

  @Override
  protected void init(VaadinRequest request) {
    final VerticalLayout layout = new VerticalLayout();
    layout.setMargin(true);
    setContent(layout);

    Button login = new Button("login");
    login.addClickListener(new ClickListener() {
      @Override
      public void buttonClick(ClickEvent event) {
        try {
          JaasAccessControl.login("demo", "demo");
          Page page = Page.getCurrent();
          page.setLocation(page.getLocation());
        } catch (ServletException e) {
          // TODO handle exception
          e.printStackTrace();
        }
      }
    });
    layout.addComponent(login);
  }
}
....

The interesting parts are these:

[source,java]
....
JaasAccessControl.login("demo", "demo");
Page page = Page.getCurrent();
page.setLocation(page.getLocation());
....

JaasAccessControl is a utility class from the Vaadin-CDI addon; we use
it to perform programmatic login. If the login succeeds, we refresh the
page the user is on. Why do we need to do this? Well, let’s consider why
the login page is visible. The user has tried to access /secure, but
isn’t logged in. Under the hood, the server realizes this, and serves
our login page instead without doing a proper redirect. This means the
users URL doesn’t change; it still says /secure. We refresh the page,
and since we are logged in, we get the real content of the secure UI.

Now, we could do login with other technologies as well. If you have a
single-sign-on of some sort, you might want to use the JaasAccessControl
class to integrate that into your app. You can also do form-based JSP
login, as you would do in the olden days. The possibilities are truly
many here. If you do decide on using JSP, here are a couple of helpers
for you:

Add the following into your login.jsp:

[source,html]
....
<!-- Vaadin-Refresh -->
....

Why is this line needed? To answer this I need to tell you what happens
when an application session times out. When Vaadin requests something
from the server, the server replies with something else. Typically
(without JAAS), it is a simple error message saying the session is
invalid. If we are using JAAS, however, what we get in the response from
the server is the login page HTML. Vaadin doesn't handle this too well;
it adds the HTML response to the red notification popup. To fix this, we
have added a feature to Vaadin that checks the HTML for a specific
keyword (you guessed it, 'Vaadin-Refresh'), and if it finds it, simply
reloads the complete page. You can also define a redirect url if you
want to, but we won't need it here since JAAS will redirect for us. So,
we add the comment to the JSP so that when a session timeouts, we want
to be redirected to the login page.

The second thing (still in login.jsp) is this:

[source,html]
....
<meta http-equiv="refresh" content="${pageContext.session.maxInactiveInterval}">
....

We add this line so that the login page itself doesn't timeout. Session
timeouts are active from the first access to the servlet; in our case
loading the login page. If the user doesn't fill in anything, and the
timer runs out, the user will get an ugly error message from the server.
To fix that we simply reload the page, extending the session (or
creating a new one).

OK, with us so far? We still need a couple of things for JBoss to
understand what we want to do:

I have a jboss-web.xml inside WEB-INF that tells JBoss which settings to
use:

[source,xml]
....
<jboss-web>
  <security-domain>DBAuth</security-domain>
</jboss-web>
....

Then in the JBoss standalone.xml configuration file, I add the security
domain params:

[source,xml]
....
<security-domain name="DBAuth">
  <authentication>
    <login-module code="org.jboss.security.auth.spi.DatabaseServerLoginModule" flag="required">
      <module-option name="dsJndiName" value="java:jboss/datasources/myappdb"/>
      <module-option name="principalsQuery" value="select password from PRINCIPLES where principal_id=?"/>
      <module-option name="rolesQuery" value="select user_role, 'Roles' from ROLES where principal_id=?"/>
    </login-module>
  </authentication>
</security-domain>
....

The domain that we specify tells the server where to find users and
passwords. In our case, they can be found in the PRINCIPLES table, with
roles added to the ROLES table. As you can see, you specify the SQL for
the query, so you have a lot of freedom in how you do this. Note that we
are not using any encryption or hashing for the passwords; please don't
use this configuration for real applications. Instead, you should use a
custom Login Module class that can compare hashes instead of pure
strings, and store salted hashes in your database. Implement your class
by extending the DatabaseServerLoginModule class and change the code
attribute in the login-module tag to point to your class instead.

Then we need the data source (still in standalone.xml):

[source,xml]
....
<datasources>
  <datasource jta="true" jndi-name="java:jboss/datasources/myappdb" pool-name="java:jboss/datasources/myappdb_pool"
  enabled="true" use-java-context="true" use-ccm="true">
    <connection-url>jdbc:postgresql://localhost:5432/myappdb</connection-url>
    <driver-class>org.postgresql.Driver</driver-class>
    <driver>postgresql-jdbc4</driver>
    <pool>
      <min-pool-size>2</min-pool-size>
      <max-pool-size>20</max-pool-size>
      <prefill>true</prefill>
    </pool>
    <security>
      <user-name>demo</user-name>
      <password>demo</password>
    </security>
    <validation>
      <check-valid-connection-sql>SELECT 1</check-valid-connection-sql>
      <validate-on-match>false</validate-on-match>
      <background-validation>false</background-validation>
      <use-fast-fail>false</use-fast-fail>
    </validation>
  </datasource>
  <drivers>
    <driver name="postgresql-jdbc4" module="org.postgresql"/>
  </drivers>
</datasources>
....

As you can see, I'm using a Postgres database. You will need the
postgres JDBC driver installed under the Wildfly modules directory for
this to work. And, of course an actual Postgres server with the
specified database created. In our application we use Hibernate with
container managed transactions to handle persistence; as this isn't a
JPA tutorial, so I'll leave that for another day.

But, for completeness sake, here is a short SQL script for the DB.
Create a database named ‘myappdb’, and run this:

[source,sql]
....
CREATE USER demo WITH PASSWORD 'demo';

CREATE TABLE PRINCIPLES ( principal_id VARCHAR(64) primary key,password VARCHAR(64));
CREATE TABLE ROLES ( role_item_id integer, principal_id VARCHAR(64),user_role VARCHAR(64));

Grant all privileges on table roles to demo;
Grant all privileges on table principles to demo;

--Initial data
Insert into principles values ('demo', 'demo');
insert into roles values (1, 'demo', 'viewer');
....

The only thing left is to get the username and roles from inside your
Vaadin app:

[source,java]
....
@Override
protected void init(VaadinRequest request) {
  String username = request.getUserPrincipal().toString();
  if (request.isUserInRole("viewer")) {
      // Add admin view to menu
  }
....

If you are using the CDI-based navigator, you can also use the
@RolesAllowed annotation on your views to automatically constrain
visibility of your views.

That's it, your app will now use database authentication with JAAS and
CDI. The provided configuration isn't complete, and there are small
pieces I didn't really cover, but it will work for basic cases. Feel
free to add comments below.

You might also check out
https://github.com/mstahv/vaadin-cdi-jaas-jbossas-example/[a related
full app example], that uses built in "FileRealm" in JBoss.