/** * Copyright 2014 Eediom Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.araqne.logdb.impl; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.apache.felix.ipojo.annotations.Component; import org.apache.felix.ipojo.annotations.Invalidate; import org.apache.felix.ipojo.annotations.Provides; import org.apache.felix.ipojo.annotations.Requires; import org.apache.felix.ipojo.annotations.Validate; import org.araqne.confdb.Config; import org.araqne.confdb.ConfigDatabase; import org.araqne.confdb.ConfigService; import org.araqne.confdb.Predicates; import org.araqne.logdb.AbstractAccountEventListener; import org.araqne.logdb.Account; import org.araqne.logdb.AccountService; import org.araqne.logdb.Procedure; import org.araqne.logdb.ProcedureRegistry; import org.araqne.logdb.SecurityGroup; import org.araqne.logdb.Session; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Component(name = "logdb-procedure-registry") @Provides public class ProcedureRegistryImpl implements ProcedureRegistry { private final Logger slog = LoggerFactory.getLogger(ProcedureRegistryImpl.class); @Requires private ConfigService conf; @Requires private AccountService accountService; private OrphanCleaner cleaner = new OrphanCleaner(); private ConcurrentHashMap<String, Procedure> procedures = new ConcurrentHashMap<String, Procedure>(); private Object dbLock = new Object(); @Validate public void start() { ConfigDatabase db = conf.ensureDatabase("araqne-logdb"); for (Procedure p : db.findAll(Procedure.class).getDocuments(Procedure.class)) { procedures.put(p.getName(), p); } accountService.addListener(cleaner); } @Invalidate public void stop() { if (accountService != null) accountService.removeListener(cleaner); procedures.clear(); } @Override public boolean isGranted(String procedureName, String loginName) { Procedure p = procedures.get(procedureName); if (p == null) throw new IllegalStateException("procedure not found: " + procedureName); if (accountService.isAdmin(loginName)) return true; if (p.getOwner().equals(loginName) || p.getGrants().contains(loginName)) return true; for (String guid : p.getGrantGroups()) { SecurityGroup group = accountService.getSecurityGroup(guid); if (group != null && group.getAccounts().contains(loginName)) return true; } return false; } @Override public Set<String> getProcedureNames() { return new HashSet<String>(procedures.keySet()); } @Override public List<Procedure> getProcedures() { return getProcedures(null); } @Override public List<Procedure> getProcedures(String loginName) { List<Procedure> l = new ArrayList<Procedure>(); for (Procedure p : procedures.values()) { if (loginName != null && !isGranted(p.getName(), loginName)) continue; l.add(p.clone()); } return l; } @Override public Procedure getProcedure(String name) { if (name == null) return null; Procedure p = procedures.get(name); if (p == null) return null; return p.clone(); } @Override public void createProcedure(Procedure procedure) { if (procedure == null) throw new IllegalArgumentException("procedure should not be null"); Procedure old = procedures.putIfAbsent(procedure.getName(), procedure); if (old != null) throw new IllegalStateException("duplicated procedure name: " + procedure.getName()); filterGrants(procedure); synchronized (dbLock) { ConfigDatabase db = conf.ensureDatabase("araqne-logdb"); db.add(procedure); } } @Override public void updateProcedure(Procedure procedure) { if (procedure == null) throw new IllegalArgumentException("procedure should not be null"); if (procedures.get(procedure.getName()) == null) throw new IllegalStateException("procedure not found: " + procedure.getName()); filterGrants(procedure); procedures.put(procedure.getName(), procedure); synchronized (dbLock) { ConfigDatabase db = conf.ensureDatabase("araqne-logdb"); Config c = db.findOne(Procedure.class, Predicates.field("name", procedure.getName())); if (c != null) db.update(c, procedure); } } @Override public void removeProcedure(String name) { if (name == null) throw new IllegalArgumentException("procedure name should not be null"); Procedure old = procedures.remove(name); if (old == null) throw new IllegalStateException("procedure not found: " + name); synchronized (dbLock) { ConfigDatabase db = conf.ensureDatabase("araqne-logdb"); Config c = db.findOne(Procedure.class, Predicates.field("name", name)); if (c != null) { c.remove(); } } } private void filterGrants(Procedure p) { Set<String> filteredGrants = new HashSet<String>(); for (String s : p.getGrants()) { if (accountService.getAccount(s) != null) filteredGrants.add(s); } Set<String> filteredGrantGroups = new HashSet<String>(); for (String s : p.getGrantGroups()) { if (accountService.getSecurityGroup(s) != null) filteredGrantGroups.add(s); } p.setGrants(filteredGrants); p.setGrantGroups(filteredGrantGroups); } private class OrphanCleaner extends AbstractAccountEventListener { @Override public void onRemoveAccount(Session session, Account account) { for (Procedure p : procedures.values()) { if (p.getOwner().equals(account.getLoginName())) { removeProcedure(p.getName()); slog.info("araqne logdb: dropped procedure [{}] by remove cascade of user [{}]", p.getName(), account.getLoginName()); continue; } if (p.getGrants().contains(account.getLoginName())) { p.getGrants().remove(account.getLoginName()); updateProcedure(p); slog.info("araqne logdb: revoked procedure [{}] by remove cascade of user [{}]", p.getName(), account.getLoginName()); } } } @Override public void onRemoveAccounts(Session session, List<Account> accounts) { Set<String> loginNames = new HashSet<String>(); for (Account account : accounts) loginNames.add(account.getLoginName()); for (Procedure p : procedures.values()) { if (loginNames.contains(p.getOwner())) { removeProcedure(p.getName()); slog.info("araqne logdb: dropped procedure [{}] by remove cascade of user [{}]", p.getName(), p.getOwner()); continue; } boolean contains = false; for (String loginName : loginNames) { if (p.getGrants().contains(loginName)) { p.getGrants().remove(loginName); contains = true; } } if (contains) { updateProcedure(p); slog.info("araqne logdb: revoked procedure [{}] by remove cascade of users", p.getName()); } } } @Override public void onRemoveSecurityGroup(Session session, SecurityGroup group) { for (Procedure p : procedures.values()) { if (p.getGrantGroups().contains(group.getGuid())) { p.getGrantGroups().remove(group.getGuid()); updateProcedure(p); slog.info("araqne logdb: revoked procedure [{}] by remove cascade of security group [guid: {}, name: {}]", new Object[] { p.getName(), group.getGuid(), group.getName() }); } } } } }