/* * Copyright 2014 Bevbot LLC <info@bevbot.com> * * This file is part of the Kegtab package from the Kegbot project. For * more information on Kegtab or Kegbot, see <http://kegbot.org/>. * * Kegtab is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation, version 2. * * Kegtab is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with Kegtab. If not, see <http://www.gnu.org/licenses/>. */ package org.kegbot.core; import android.content.Context; import android.util.Log; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.Sets; import com.squareup.otto.Bus; import org.kegbot.app.config.AppConfiguration; import org.kegbot.backend.Backend; import org.kegbot.backend.BackendException; import org.kegbot.backend.NotFoundException; import org.kegbot.proto.Models.User; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; /** * @author mike wakerly (mike@wakerly.com) */ public class AuthenticationManager extends Manager { private static final String TAG = AuthenticationManager.class.getSimpleName(); private static final long CACHE_EXPIRE_HOURS = 3; private final Backend mApi; private final AppConfiguration mConfig; private final ExecutorService mExecutorService = Executors.newSingleThreadExecutor(); private User fetchUserForToken(AuthenticationToken token) throws BackendException { Log.d(TAG, "Loading token"); org.kegbot.proto.Models.AuthenticationToken tok = mApi.getAuthToken(token .getAuthDevice(), token.getTokenValue()); Log.d(TAG, "Got auth token: " + tok); if (tok == null) { throw new NotFoundException("Unknown token."); } if (!tok.getEnabled()) { throw new NotFoundException("Token not enabled."); } else if (!tok.hasUser()) { throw new NotFoundException("Token not assigned."); } return tok.getUser(); } private final LoadingCache<AuthenticationToken, User> mAuthTokenCache = CacheBuilder .newBuilder().expireAfterWrite(CACHE_EXPIRE_HOURS, TimeUnit.HOURS).build( new CacheLoader<AuthenticationToken, User>() { @Override public User load(AuthenticationToken token) throws Exception { final User user = fetchUserForToken(token); if (user != null) { mUserDetailCache.put(user.getUsername(), user); } return user; } } ); private final LoadingCache<String, User> mUserDetailCache = CacheBuilder.newBuilder() .expireAfterWrite(CACHE_EXPIRE_HOURS, TimeUnit.HOURS).build( new CacheLoader<String, User>() { @Override public User load(String username) throws Exception { Log.d(TAG, "Loading user: " + username); return mApi.getUser(username); } } ); AuthenticationManager(Bus bus, Context context, Backend api, AppConfiguration prefs) { super(bus); mApi = api; mConfig = prefs; } @Override protected void stop() { clearCache(); super.stop(); } public User authenticateToken(AuthenticationToken token) { if (!mConfig.getCacheCredentials()) { try { return fetchUserForToken(token); } catch (NotFoundException e) { Log.d(TAG, "Token is not assigned to anyone: " + token); return null; } catch (BackendException e) { Log.w(TAG, "Error fetching token: " + e.getCause(), e); return null; } } try { return mAuthTokenCache.get(token); } catch (ExecutionException e) { Throwable cause = e.getCause(); if (cause != null && cause instanceof NotFoundException) { Log.d(TAG, "Token is not assigned to anyone: " + token); return null; } else { Log.w(TAG, "Error fetching token: " + e.getCause(), e); } return null; } } public User authenticateUsername(String username) { // TODO(mikey): use pin try { final User user = mUserDetailCache.get(username); if (user != null) { mUserDetailCache.put(user.getUsername(), user); } return user; } catch (ExecutionException e) { Log.w(TAG, "Error fetching user: " + e, e); return null; } } public Future<User> authenticateUsernameAsync(final String username) { return mExecutorService.submit(new Callable<User>() { @Override public User call() { return authenticateUsername(username); } }); } public User getUserDetail(String username) { return mUserDetailCache.getIfPresent(username); } public Set<User> getAllRecent() { return Sets.newLinkedHashSet(mUserDetailCache.asMap().values()); } public void clearCache() { // TODO(mikey): clear me on config change broadcast mUserDetailCache.invalidateAll(); mAuthTokenCache.invalidateAll(); } }