/*******************************************************************************
* Copyright (c) 2005-2014, G. Weirich and Elexis
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* G. Weirich - initial implementation
*******************************************************************************/
package ch.elexis.admin;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import ch.elexis.core.constants.Preferences;
import ch.elexis.core.constants.StringConstants;
import ch.elexis.core.data.activator.CoreHub;
import ch.elexis.data.Anwender;
import ch.elexis.data.NamedBlob;
import ch.elexis.data.Role;
import ch.elexis.data.User;
import ch.rgw.io.InMemorySettings;
import ch.rgw.io.Settings;
import ch.rgw.tools.Log;
import ch.rgw.tools.StringTool;
/**
* Diese Klasse realisiert das Zugriffskontroll- und Rechteverwaltungskonzept von Elexis.
* <ul>
* <li>Es gibt Gruppen und Anwender.</li>
* <li>Jeder Anwender gehört zu mindestens einer Gruppe.</li>
* <li>Es existiert von Anfang an eine Gruppe "Alle" und ein Anwender "Jeder"</li>
* <li>Jedes Recht kann einer oder mehreren Gruppen und/oder einer onder mehreren Anwendern gewährt
* werden.</li>
* <li>Ein Anwender erhält alle Rechte, die ihm entweder individuell gewährt wurden, oder die einer
* der Gruppen gewährt wurden, zu denen er gehört.</li>
* </ul>
* Eine Ressource, die ein Zugriffsrecht realisieren will, muss für dieses Recht ein ACE erstellen,
* Zugriffsrechte können hierarchisch aufgebaut sein. Beispielsweise kann ein Recht foo/bar/baz
* definiert sein. Wenn keine Regel für baz existiert, dann wird nach einer Regel für bar gesucht
* und diese Angewandt. Wenn auch die nicht gefunden wird, wird nach einer Regel für foo gesucht.
* Wenn auch dies fehlschlägt, wird das Recht in jedem Fall verweigert. Das Zugriffsrecht kann dann
* mit grant(gruppe,recht) oder grant(Anwender,recht) gewährt resp. mit revoke(gruppe,Name) oder
* revoke(Anwender,name) entzogen werden. Um herauszufinden, ob ein Anwender bw. einer seiner
* Gruppen das Recht hat, auf eine ressource zuzugreifen, muss man request(anwedner,recht) fragen.
* Eine Abkürzung ist request(recht). Dies fragt, ob der aktuell eingeloggte Anwender das
* betreffende Recht hat.
*
* @author Gerry
* @see ACE
* @deprecated 3.1 replaced by {@link RoleBasedAccessControl}
*/
public class AccessControlImpl extends AbstractAccessControl {
public static final String KEY_GROUPS = "Groups";
public static final String DB_UID = "dbUID";
// Set this to true to make any user admin (e.g. to reset admin pwd
private final static boolean FORCE_ADMIN = false;
public static final String ALL_GROUP = StringConstants.ROLE_ALL;
public static final String USER_GROUP = StringConstants.ROLE_USERS;
public static final String ADMIN_GROUP = StringConstants.ROLE_ADMIN;
public static final String GROUP_FOR_PREFERENCEPAGE = "ch.elexis.preferences.acl"; //$NON-NLS-1$
private static final String BLOBNAME = "AccessControl"; //$NON-NLS-1$
private static final String ACLNAME = "AccessControlACL"; //$NON-NLS-1$
private static Hashtable<String, ACE> rights;
private static Hashtable<String, List<String>> usergroups;
private static Hashtable<String, ACE> acls;
private static final Log log = Log.get("AccessControl"); //$NON-NLS-1$
// TODO: Cleanup alte Gruppen/Anwender
/**
* Die Zugriffsrechte aus den globalen Settings laden.
*/
@SuppressWarnings("unchecked")
public void load(){
NamedBlob rset = NamedBlob.load(BLOBNAME);
if (rset == null) {
log.log("Warnung: ACEs nicht gefunden, erstelle neu ", Log.ERRORS); //$NON-NLS-1$
NamedBlob.createTable();
rset = NamedBlob.load(BLOBNAME);
}
NamedBlob aclset = NamedBlob.load(ACLNAME);
rights = rset.getHashtable();
System.out.println("========= RIGHTS");
rights.keySet().stream().forEach((k) -> {
ACE ace = rights.get(k);
System.out.println(k + ": " + ace.getCanonicalName());
});
acls = aclset.getHashtable();
if (rights.isEmpty() || acls.isEmpty()) {
reset();
}
System.out.println("========= ACLS");
acls.keySet().stream().forEach((k) -> {
ACE ace = acls.get(k);
System.out.println(k + ": " + ace.getCanonicalName());
});
usergroups = new Hashtable<String, List<String>>();
log.log("loaded AccessControl", Log.INFOS); //$NON-NLS-1$
for (String k1 : rights.keySet()) {
log.log(k1, Log.DEBUGMSG);
}
log.log("loaded ACLs", Log.INFOS); //$NON-NLS-1$
for (String k1 : acls.keySet()) {
log.log(k1, Log.DEBUGMSG);
}
}
/**
* Zugriffsrechte zurücksichern. Alle Rechte, die seit dem letzten flush geändert wurden, sind
* nur temporär bis zum nächsten flush()!
*/
public void flush(){
NamedBlob.load(BLOBNAME).put(rights);
NamedBlob.load(ACLNAME).put(acls);
}
/**
* Zugriffsrecht für den aktuell angemeldeten Anwender erfragen.
*
* @param right
* Das erfragte Recht
* @return true, wenn der Anwender (oder eine der Gruppen, zu denen der Anwender gehört) das
* Recht hat.
*/
public boolean request(ACE right){
return (request(CoreHub.actUser, right));
}
/**
* query the access right of a given user (that is {@link Anwender})
*
* @param user
* the user {@link Anwender}
* @param right
* the right to query for, if <code>null</code> always <code>true</code> is returned
* @return <code>true</code> if the user (or one of the groups this user belongs to) has the
* resp. right. Also <code>true</code> if the user belongs to group "Admin". Always
* <code>false</code> if no user is logged in
*/
@SuppressWarnings("unchecked")
public boolean request(Anwender user, ACE rightACE){
if (FORCE_ADMIN) {
return true;
} else {
if (rightACE == null) {
return true;
}
String right = rightACE.getCanonicalName();
if (rights == null) {
return false;
}
// Wenn alle dieses Recht haben-> ok
if (rights.get(Messages.AccessControl_GroupAll + right) != null) { //$NON-NLS-1$
return true;
}
// Wenn gar kein user angegeben ist -> verweigern
if (user == null) {
return false;
}
// Wenn das Recht für jeden User für sich besteht
if (rights.get("Self" + right) != null) { //$NON-NLS-1$
if (CoreHub.actUser.getId().equals(user.getId())) {
return true;
}
}
// Wenn das Recht für den genannten User individuell besteht
if (rights.get(user.getId() + right) != null) {
return true;
}
// Wenn das Recht für eine Gruppe, zu der der User gehört, besteht
List<String> list = usergroups.get(user.getId() + "#groups#"); //$NON-NLS-1$
// we cache the groups during runtime. If not yet in cache, load
// group
// membership from user data
if (list == null) {
// Anwender act=CoreHub.actUser;
list = new ArrayList<String>();
Map h = user.getMap("ExtInfo"); //$NON-NLS-1$
if (h != null) {
String grp = (String) h.get(KEY_GROUPS);
if (grp != null) {
String[] grps = grp.split(","); //$NON-NLS-1$
for (String g : grps) {
list.add(g);
}
usergroups.put(user.getId() + "#groups#", list); //$NON-NLS-1$
}
}
}
// The list is never null here, but might be empty
for (String g : list) {
if (ADMIN_GROUP.equals(g)) {
// If the user is member of the admin groups, he has any
// right
return true;
}
if (rights.get(g + right) != null) {
return true;
}
}
// Falls das gewünschte Recht nicht geregelt ist, eine
// Hierarchiestufe
// höher suchen
ACE parent = rightACE.getParent();
if (parent != null) {
return request(user, parent);
}
return false;
}
}
/**
* Zugriffsrecht(e) erteilen
*
* @param user
* Anwender, der diese Rechte erhalten soll
* @param elements
* ein oder mehrere Rechte
*/
public void grant(Anwender user, ACE... elements){
for (ACE right : elements) {
rights.put(user.getId() + right.getCanonicalName(), right);
acls.put(right.getCanonicalName(), right);
}
}
/**
* Zugriffsrechte entziehen
*
* @param user
* Anwender, dem diese Rechte entzogen werden sollen
* @param elements
* ein oder mehrere Rechte
*/
public void revoke(Anwender user, ACE... elements){
for (ACE right : elements) {
rights.remove(user.getId() + right.getCanonicalName());
}
}
/**
* Zugriffsrechte erteilen
*
* @param group
* Gruppe, der diese Rechte erteilt werden sollen
* @param elements
* ein oder mehrere Rechte
*/
public void grant(String group, ACE... elements){
for (ACE right : elements) {
rights.put(group + right.getCanonicalName(), right);
acls.put(right.getCanonicalName(), right);
}
}
/**
* Zugriffsrechte entziehem
*
* @param group
* Gruppe
* @param elements
* ein oder mehrere Rechte
*/
public void revoke(String group, ACE... elements){
for (ACE right : elements) {
rights.remove(group + right.getCanonicalName());
}
}
/**
*
* @param r
* @param ace
* @since 3.1
*/
public void grant(Role r, ACE ace) {
r.grantAccessRight(ace);
}
/**
*
* @param r
* @param ace
* @since 3.1
*/
public void revoke(Role r, ACE ace){
r.revokeAccessRight(ace);
}
/**
* Zugriffsrecht für "self" erteilen
*
*/
public void grantForSelf(ACE... elements){
for (ACE r : elements) {
rights.put("Self" + r.getCanonicalName(), r); //$NON-NLS-1$
acls.put(r.getCanonicalName(), r);
}
}
public void revokeFromSelf(ACE... strings){
for (ACE e : strings) {
rights.remove("Self" + e.getCanonicalName()); //$NON-NLS-1$
}
}
/**
* Einen Anwender einer Gruppe zufügen
*
* @param group
* Die Gruppe, der der Anwender angeschlossen werden soll
* @param user
* der Anwender
*/
public void addToGroup(String group, Anwender user){
String g = remove(group, user);
g = g + "," + group; //$NON-NLS-1$
user.setInfoElement(KEY_GROUPS, g);
}
/**
* Einen Anwender aus einer Gruppe entfernen
*
* @param group
* Gruppe, aus der der Anwender austreten soll
* @param user
* der Anwender
*/
public void removeFromGroup(String group, Anwender user){
String g = remove(group, user);
user.setInfoElement(KEY_GROUPS, g);
}
private String remove(String group, Anwender user){
String g = (String) user.getInfoElement(KEY_GROUPS);
if (g != null) {
g = g.replaceAll(user.getId(), ""); //$NON-NLS-1$
g = g.replaceAll("\\s*,*$", ""); //$NON-NLS-1$ //$NON-NLS-2$
return g;
}
return ""; //$NON-NLS-1$
}
/**
* Alle Gruppen holen
*
* @return eine Liste aller definierten Gruppen
*/
public List<String> getGroups(){
ArrayList<String> ret = new ArrayList<String>();
String grp = CoreHub.globalCfg.get(Preferences.ACC_GROUPS, ADMIN_GROUP);
for (String s : grp.split(",")) { //$NON-NLS-1$
ret.add(s);
}
return ret;
}
/**
* Eine Liste aller Gruppen holen, die in bestimmtes Recht haben
*
* @param right
* das zu erfragende Recht
* @return Alle Gruppen, deren Mitglieder dieses Recht haben
*/
public List<String> groupsForGrant(ACE rightACE){
ArrayList<String> ret = new ArrayList<String>();
String right = rightACE.getCanonicalName();
Pattern p = Pattern.compile("([a-zA-Z0-9]+)" + right); //$NON-NLS-1$
Enumeration<String> e = rights.keys();
while (e.hasMoreElements()) {
String k = e.nextElement();
Matcher m = p.matcher(k);
if (m.matches()) {
String grp = m.group(1);
Anwender an = Anwender.load(grp);
if (an == null) {
ret.add(grp);
}
}
}
return ret;
}
/**
* Alle Anwender efragen, die ein bestimmtes Recht haben
*
* @param right
* das zu erfragende Recht
* @return eine Liste aller Anwender, die das gesuchte Recht direkt (nicht über
* Gruppenmitgliedschaft) haben.
*/
public List<Anwender> usersForGrant(ACE rightACE){
ArrayList<Anwender> ret = new ArrayList<Anwender>();
String right = rightACE.getCanonicalName();
Pattern p = Pattern.compile("([a-zA-Z0-9]+)" + right); //$NON-NLS-1$
Enumeration<String> e = rights.keys();
while (e.hasMoreElements()) {
String k = e.nextElement();
Matcher m = p.matcher(k);
if (m.matches()) {
String grp = m.group(1);
Anwender an = Anwender.load(grp);
if ((an != null) && an.exists()) {
ret.add(an);
}
}
}
return ret;
}
public void deleteGrant(ACE grantACE){
String grant = grantACE.getCanonicalName();
Pattern p = Pattern.compile("([a-zA-Z0-9]+)" + grant); //$NON-NLS-1$
Enumeration<String> e = rights.keys();
while (e.hasMoreElements()) {
String k = e.nextElement();
Matcher m = p.matcher(k);
if (m.matches()) {
rights.remove(k);
}
}
acls.remove(grantACE);
}
public Settings asSettings(){
return new InMemorySettings(rights);
}
/** Alles auf Standard zurücksetzen und dbUID generieren */
public void reset(){
rights.clear();
grant(ALL_GROUP, AccessControlDefaults.getAlle());
grant(USER_GROUP, AccessControlDefaults.getAnwender());
acls.put(DB_UID, new ACE(ACE.ACE_ROOT, DB_UID, StringTool.unique("db%id")));
flush();
}
public String getDBUID(boolean bCreate){
ACE dbuid = acls.get(DB_UID);
if (bCreate && dbuid == null) {
dbuid = new ACE(ACE.ACE_ROOT, DB_UID, StringTool.unique("db%id"));
rights.put(DB_UID, dbuid);
flush();
}
return dbuid.getLocalizedName();
}
// for compatibility only - deprecated
@Override
public boolean request(Role r, ACE ace){
// TODO Auto-generated method stub
return false;
}
@Override
public boolean request(User u, ACE ace){
// TODO Auto-generated method stub
return false;
}
@Override
public void grant(String s, ACE ace){
// TODO Auto-generated method stub
}
@Override
public boolean request(String right){
// TODO Auto-generated method stub
return false;
}
}