JBoss Seam w swojej najnowszej odsłonie (2.1.0.SP1) w porównaniu z wersją 2.0 posiada bardzo wiele zmian związanych z bezpieczeństwem. Omówione one zostały na przykładzie aplikacji SeamSpace w poście na blogu Shane’a Bryzaka – głównego deweolpera kwestii bezpieczeństwa w JBoss Seam. Osobom zainteresowanym polecam również przestudiować rozdział poświęcony bezpieczeństwu w oficjalnej dokumentacji Seama.
Nowością w Seam 2.1 jest możliwość definiowania uprawnień i przypisywania ich określonym rolom. Jest to rozwiązanie bardzo elastyczne, jak również bezpieczne, a co najważniejsze – wygodne! Postanowiłem sprawdzić czy sprawdza się to równiez w praktyce.
W swojej aplikacji postawiłem wykorzystać JpaPermissionStore, jak również JpaIdentityStore aby móc zapisywać uprawnienia i użytkowników w relacyjnej bazie danych. Po odpowiedniej konfiguracji (więcej info w poście na blogu Shane’a) oraz zabezpieczeniu odpowiednich stron okazało się, że każdorazowo (sic!) gdy jest sprawdzane jakieś uprawnienie – wykonywane jest zapytanie do bazy danych! W przypadku wielokrotnego sprawdzania uprawnień na jednej stronie (a ma to miejsce praktycznie zawsze) dochodziło do wykonywania nawet kilkudziesięciu zapytań – bardzo często takich samych.
Oczywiście trzeba było to zneutralizować. Jak? Nadpisując komponent org.jboss.seam.security.permission.PersistentPermissionResolver w ten sposób:
package pl.atlone.firma.auth;
import java.util.HashMap;
import java.util.Map;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Install;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Observer;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.Startup;
import org.jboss.seam.annotations.intercept.BypassInterceptors;
import org.jboss.seam.log.Log;
import org.jboss.seam.security.Identity;
import org.jboss.seam.security.permission.PersistentPermissionResolver;
@Scope(ScopeType.APPLICATION)
@BypassInterceptors
@Name("org.jboss.seam.security.persistentPermissionResolver")
@Install(precedence = Install.APPLICATION)
@Startup
public class SystemPermissionResolver extends PersistentPermissionResolver {
private static final long serialVersionUID = -9063212676529968783L;
@Logger
private Log log;
private Map<string , Map<String, String>> permissions = new HashMap</string><string , Map<String, String>>();
@Observer( { "System.Permission.Removed", "System.Permission.Updated" })
public void onSecurityUpdate() {
permissions.clear();
}
@Override
public boolean hasPermission(Object target, String action) {
log.trace("Checking permissions: [identity = "
+ Identity.instance().getCredentials().getUsername()
+ "], [target = " + target + "]. [action = " + action + "]");
Map</string><string , String> identityPermissions = permissions.get(Identity
.instance().getCredentials().getUsername());
if (identityPermissions == null) {
identityPermissions = new HashMap</string><string , String>();
permissions.put(Identity.instance().getCredentials().getUsername(),
identityPermissions);
}
String permission = identityPermissions.get(target.toString());
if (permission != null && permission.equals(action)) {
return true;
} else {
boolean hasPermission = super.hasPermission(target, action);
if (hasPermission) {
identityPermissions.put(target.toString(), action);
return true;
}
}
return false;
}
}
Otrzymujemy w ten sposób komponent pobierający informacje z bazy danych tylko wtedy, gdy żądana informacja nie została już wcześniej pobrana. Komponent bardzo prosty i przyjemny. Należy pamiętać, aby po usunięciu lub uaktualnieniu uprawnienia wywołać odpowiednio zdarzenia System.Permission.Removed lub System.Permission.Removed, co spowoduje wyczyszczenie listy uprawnień znajdujących się w pamięci, a zarazem wymusi załadowanie nowych, uaktualnionych uprawnień.


Wartościowy wpis! Można kolejny na temat „wywołać odpowiednio zdarzenia System.Permission.Removed lub System.Permission.Removed”? Poczekam cierpliwie.
@Jacek
Nie ma sprawy – napiszę post o wywoływaniu i obsłudze zdarzeń w JBoss Seam, który będzie zawierał przykład użycia powyższych zdarzeń.
Przydatny materiał.
Jednak do metody hasPermission należy dodać kontrolę parametru „action” ponieważ w obecnej postaci metoda zawsze zwróci true w przypadku tego samego „target” z innymi „action”.
@Mirek
Nie ma to jak sprawne oko! Dzięki za znalezienie tego błędu, bo faktycznie mógł on powodować poważne skutki, kod został poprawiony.