/* * Copyright 2011 Future Systems * * 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.krakenapps.auth; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.krakenapps.api.PrimitiveConverter; import org.krakenapps.auth.api.AuthCallback; import org.krakenapps.auth.api.AuthProfile; import org.krakenapps.auth.api.AuthProvider; import org.krakenapps.auth.api.AuthService; import org.krakenapps.auth.api.AuthStrategy; import org.krakenapps.auth.api.EmptyAuthCallback; import org.krakenapps.auth.api.UserCredentials; import org.krakenapps.auth.api.UserPrincipal; import org.krakenapps.confdb.Config; import org.krakenapps.confdb.ConfigCollection; import org.krakenapps.confdb.ConfigDatabase; import org.krakenapps.confdb.ConfigIterator; import org.krakenapps.confdb.ConfigService; import org.krakenapps.confdb.Predicates; import org.osgi.framework.BundleContext; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceReference; import org.osgi.util.tracker.ServiceTracker; public class DefaultAuthService implements AuthService { private BundleContext bc; private ConfigService conf; private ConcurrentMap<String, AuthProvider> providers; private ConcurrentMap<String, AuthProfile> profiles; private AuthProviderTracker tracker; public DefaultAuthService(BundleContext bc, ConfigService conf) { this.bc = bc; this.conf = conf; this.providers = new ConcurrentHashMap<String, AuthProvider>(); this.tracker = new AuthProviderTracker(); // add providers and start tracker try { ServiceReference[] refs = bc.getAllServiceReferences(AuthProvider.class.getName(), null); if (refs != null) { for (ServiceReference ref : refs) { AuthProvider p = (AuthProvider) bc.getService(ref); providers.put(p.getName(), p); } } } catch (InvalidSyntaxException e) { } // read all profiles ConfigDatabase db = conf.ensureDatabase("kraken-core"); ConfigCollection col = db.ensureCollection("auth-profile"); ConfigIterator it = col.findAll(); while (it.hasNext()) { Config c = it.next(); AuthProfile p = c.getDocument(AuthProfile.class); profiles.put(p.getName(), p); } tracker.open(); } @Override public Collection<AuthProvider> getProviders() { return providers.values(); } @Override public AuthProvider getProvider(String name) { return providers.get(name); } @Override public Collection<AuthProfile> getProfiles() { return profiles.values(); } @Override public AuthProfile getProfile(String name) { return profiles.get(name); } @Override public void createProfile(AuthProfile p) { AuthProfile old = profiles.putIfAbsent(p.getName(), p); if (old != null) throw new IllegalStateException("duplicated profile name: " + p.getName()); ConfigDatabase db = conf.getDatabase("kraken-core"); ConfigCollection col = db.ensureCollection("auth-profile"); col.add(PrimitiveConverter.serialize(p), "kraken-core", "created auth profile: " + p.getName()); } @Override public void updateProfile(AuthProfile p) { if (!profiles.containsKey(p.getName())) throw new IllegalStateException("profile not found: " + p.getName()); profiles.put(p.getName(), p); ConfigDatabase db = conf.getDatabase("kraken-core"); ConfigCollection col = db.ensureCollection("auth-profile"); Config c = col.findOne(Predicates.field("name", p.getName())); if (c != null) { c.setDocument(PrimitiveConverter.serialize(p)); col.update(c, false, "kraken-core", "created auth profile: " + p.getName()); } } @Override public void removeProfile(String name) { ConfigDatabase db = conf.getDatabase("kraken-core"); ConfigCollection col = db.ensureCollection("auth-profile"); Config c = col.findOne(Predicates.field("name", name)); if (c != null) col.remove(c, false, "kraken-core", "removed auth profile: " + name); } @Override public void authenticate(String profile, UserPrincipal principal, UserCredentials credentials, AuthCallback callback) { AuthProfile p = profiles.get(profile); if (p == null) throw new IllegalStateException("profile not found: " + profile); ChainedAuthCallback chained = new ChainedAuthCallback(principal, credentials, p, callback); chained.check(); } private class ChainedAuthCallback extends EmptyAuthCallback { private UserPrincipal principal; private UserCredentials credentials; private AuthProfile profile; private List<String> providerNames; private AuthCallback userCallback; public ChainedAuthCallback(UserPrincipal principal, UserCredentials credentials, AuthProfile profile, AuthCallback userCallback) { this.principal = principal; this.credentials = credentials; this.profile = profile; this.providerNames = new ArrayList<String>(profile.getProviders()); this.userCallback = userCallback; } public void check() { String providerName = providerNames.remove(0); AuthProvider provider = providers.get(providerName); if (provider == null) throw new IllegalStateException("provider not found: " + providerName); provider.authenticate(principal, credentials, this); } @Override public void onSuccess(AuthProvider provider, UserPrincipal principal, UserCredentials credentials) { userCallback.onSuccess(provider, principal, credentials); if (profile.getStrategy() == AuthStrategy.MatchAny) userCallback.onFail(profile, principal, credentials); else if (providerNames.size() == 0) userCallback.onSuccess(profile, principal, credentials); else check(); } @Override public void onFail(AuthProvider provider, UserPrincipal principal, UserCredentials credentials) { userCallback.onFail(provider, principal, credentials); if (profile.getStrategy() == AuthStrategy.MatchAny) check(); else userCallback.onFail(profile, principal, credentials); } } private class AuthProviderTracker extends ServiceTracker { public AuthProviderTracker() { super(bc, AuthProvider.class.getName(), null); } @Override public Object addingService(ServiceReference reference) { AuthProvider p = (AuthProvider) bc.getService(reference); providers.put(p.getName(), p); return p; } @Override public void removedService(ServiceReference reference, Object service) { AuthProvider p = (AuthProvider) service; providers.remove(p); } } }