usergrid-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mru...@apache.org
Subject [1/2] usergrid git commit: Finish convert token service persistence to Datastax. Add tests, and rename the package for TokenServiceImpl to impl from cassandra.
Date Sun, 18 Jun 2017 06:48:45 GMT
Repository: usergrid
Updated Branches:
  refs/heads/token-service-to-dstax 179f12db0 -> 146e47d66


http://git-wip-us.apache.org/repos/asf/usergrid/blob/146e47d6/stack/services/src/main/java/org/apache/usergrid/security/tokens/cassandra/TokenServiceImpl.java
----------------------------------------------------------------------
diff --git a/stack/services/src/main/java/org/apache/usergrid/security/tokens/cassandra/TokenServiceImpl.java b/stack/services/src/main/java/org/apache/usergrid/security/tokens/cassandra/TokenServiceImpl.java
deleted file mode 100644
index 6ea6de0..0000000
--- a/stack/services/src/main/java/org/apache/usergrid/security/tokens/cassandra/TokenServiceImpl.java
+++ /dev/null
@@ -1,848 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.apache.usergrid.security.tokens.cassandra;
-
-
-import com.google.inject.Injector;
-import me.prettyprint.hector.api.Keyspace;
-import me.prettyprint.hector.api.beans.HColumn;
-import me.prettyprint.hector.api.mutation.Mutator;
-import org.apache.usergrid.corepersistence.CpEntityManagerFactory;
-import org.apache.usergrid.corepersistence.util.CpNamingUtils;
-import org.apache.usergrid.management.ApplicationCreator;
-import org.apache.usergrid.management.ManagementService;
-import org.apache.usergrid.management.UserInfo;
-import org.apache.usergrid.persistence.EntityManagerFactory;
-import org.apache.usergrid.persistence.cassandra.CassandraService;
-import org.apache.usergrid.persistence.core.metrics.MetricsFactory;
-import org.apache.usergrid.persistence.entities.Application;
-import org.apache.usergrid.security.AuthPrincipalInfo;
-import org.apache.usergrid.security.AuthPrincipalType;
-import org.apache.usergrid.security.sso.SSOProviderFactory;
-import org.apache.usergrid.security.tokens.TokenCategory;
-import org.apache.usergrid.security.tokens.TokenInfo;
-import org.apache.usergrid.security.tokens.TokenService;
-import org.apache.usergrid.security.tokens.exceptions.BadTokenException;
-import org.apache.usergrid.security.tokens.exceptions.ExpiredTokenException;
-import org.apache.usergrid.security.tokens.exceptions.InvalidTokenException;
-import org.apache.usergrid.security.sso.ExternalSSOProvider;
-import org.apache.usergrid.utils.ConversionUtils;
-import org.apache.usergrid.utils.JsonUtils;
-import org.apache.usergrid.utils.UUIDUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.util.Assert;
-
-import javax.ws.rs.client.Client;
-import java.nio.ByteBuffer;
-import java.util.*;
-
-import static java.lang.System.currentTimeMillis;
-import static me.prettyprint.hector.api.factory.HFactory.createColumn;
-import static me.prettyprint.hector.api.factory.HFactory.createMutator;
-import static org.apache.commons.codec.binary.Base64.decodeBase64;
-import static org.apache.commons.codec.binary.Base64.encodeBase64URLSafeString;
-import static org.apache.commons.codec.digest.DigestUtils.sha;
-import static org.apache.usergrid.persistence.cassandra.CassandraPersistenceUtils.getColumnMap;
-import static org.apache.usergrid.persistence.cassandra.CassandraService.PRINCIPAL_TOKEN_CF;
-import static org.apache.usergrid.persistence.cassandra.CassandraService.TOKENS_CF;
-import static org.apache.usergrid.persistence.cassandra.Serializers.*;
-import static org.apache.usergrid.security.AuthPrincipalType.ADMIN_USER;
-import static org.apache.usergrid.security.tokens.TokenCategory.*;
-import static org.apache.usergrid.utils.ConversionUtils.*;
-import static org.apache.usergrid.utils.MapUtils.hasKeys;
-import static org.apache.usergrid.utils.MapUtils.hashMap;
-import static org.apache.usergrid.utils.UUIDUtils.getTimestampInMillis;
-
-
-public class TokenServiceImpl implements TokenService {
-
-    private static final Logger logger = LoggerFactory.getLogger( TokenServiceImpl.class );
-
-    public static final String PROPERTIES_AUTH_TOKEN_SECRET_SALT = "usergrid.auth.token_secret_salt";
-    public static final String PROPERTIES_AUTH_TOKEN_EXPIRES_FROM_LAST_USE =
-            "usergrid.auth.token_expires_from_last_use";
-    public static final String PROPERTIES_AUTH_TOKEN_REFRESH_REUSES_ID = "usergrid.auth.token_refresh_reuses_id";
-
-    private static final String TOKEN_UUID = "uuid";
-    private static final String TOKEN_TYPE = "type";
-    private static final String TOKEN_CREATED = "created";
-    private static final String TOKEN_ACCESSED = "accessed";
-    private static final String TOKEN_INACTIVE = "inactive";
-    private static final String TOKEN_DURATION = "duration";
-    private static final String TOKEN_PRINCIPAL_TYPE = "principal";
-    private static final String TOKEN_ENTITY = "entity";
-    private static final String TOKEN_APPLICATION = "application";
-    private static final String TOKEN_STATE = "state";
-    private static final String TOKEN_WORKFLOW_ORG_ID = "workflowOrgId";
-
-
-    private static final String TOKEN_TYPE_ACCESS = "access";
-
-
-    private static final Set<String> TOKEN_PROPERTIES;
-
-
-    static {
-        HashSet<String> set = new HashSet<String>();
-        set.add( TOKEN_UUID );
-        set.add( TOKEN_TYPE );
-        set.add( TOKEN_CREATED );
-        set.add( TOKEN_ACCESSED );
-        set.add( TOKEN_INACTIVE );
-        set.add( TOKEN_PRINCIPAL_TYPE );
-        set.add( TOKEN_ENTITY );
-        set.add( TOKEN_APPLICATION );
-        set.add( TOKEN_STATE );
-        set.add( TOKEN_DURATION );
-        set.add( TOKEN_WORKFLOW_ORG_ID );
-        TOKEN_PROPERTIES = Collections.unmodifiableSet(set);
-    }
-
-
-    private static final HashSet<String> REQUIRED_TOKEN_PROPERTIES = new HashSet<String>();
-
-
-    static {
-        REQUIRED_TOKEN_PROPERTIES.add( TOKEN_UUID );
-        REQUIRED_TOKEN_PROPERTIES.add( TOKEN_TYPE );
-        REQUIRED_TOKEN_PROPERTIES.add( TOKEN_CREATED );
-        REQUIRED_TOKEN_PROPERTIES.add( TOKEN_ACCESSED );
-        REQUIRED_TOKEN_PROPERTIES.add( TOKEN_INACTIVE );
-        REQUIRED_TOKEN_PROPERTIES.add( TOKEN_DURATION );
-    }
-
-
-    public static final String TOKEN_SECRET_SALT = "super secret token value";
-
-    // Short-lived token is good for 24 hours
-    public static final long SHORT_TOKEN_AGE = 24 * 60 * 60 * 1000;
-
-    // Long-lived token is good for 7 days
-    public static final long LONG_TOKEN_AGE = 7 * 24 * 60 * 60 * 1000;
-
-    String tokenSecretSalt = TOKEN_SECRET_SALT;
-
-    long maxPersistenceTokenAge = LONG_TOKEN_AGE;
-
-    Map<TokenCategory, Long> tokenExpirations =
-            hashMap( ACCESS, LONG_TOKEN_AGE ).map( REFRESH, LONG_TOKEN_AGE ).map( EMAIL, LONG_TOKEN_AGE )
-                    .map( OFFLINE, LONG_TOKEN_AGE );
-
-    long maxAccessTokenAge = SHORT_TOKEN_AGE;
-    long maxRefreshTokenAge = LONG_TOKEN_AGE;
-    long maxEmailTokenAge = LONG_TOKEN_AGE;
-    long maxOfflineTokenAge = LONG_TOKEN_AGE;
-
-    protected CassandraService cassandra;
-
-    protected Properties properties;
-
-    protected EntityManagerFactory emf;
-
-    protected MetricsFactory metricsFactory;
-
-
-    public TokenServiceImpl() {
-    }
-
-
-    long getExpirationProperty( String name, long default_expiration ) {
-        long expires = Long.parseLong(
-                properties.getProperty( "usergrid.auth.token." + name + ".expires", "" + default_expiration ) );
-        return expires > 0 ? expires : default_expiration;
-    }
-
-
-    long getExpirationForTokenType( TokenCategory tokenCategory ) {
-        Long l = tokenExpirations.get( tokenCategory );
-        if ( l != null ) {
-            return l;
-        }
-        return SHORT_TOKEN_AGE;
-    }
-
-
-    void setExpirationFromProperties( String name ) {
-        TokenCategory tokenCategory = TokenCategory.valueOf( name.toUpperCase() );
-        long expires = Long.parseLong( properties.getProperty( "usergrid.auth.token." + name + ".expires",
-                "" + getExpirationForTokenType( tokenCategory ) ) );
-        if ( expires > 0 ) {
-            tokenExpirations.put( tokenCategory, expires );
-        }
-        logger.info( "{} token expires after {} seconds", name, getExpirationForTokenType( tokenCategory ) / 1000 );
-    }
-
-
-    @Autowired
-    public void setProperties( Properties properties ) {
-        this.properties = properties;
-
-        if ( properties != null ) {
-            maxPersistenceTokenAge = getExpirationProperty( "persistence", maxPersistenceTokenAge );
-
-            setExpirationFromProperties( "access" );
-            setExpirationFromProperties( "refresh" );
-            setExpirationFromProperties( "email" );
-            setExpirationFromProperties( "offline" );
-
-            tokenSecretSalt = properties.getProperty( PROPERTIES_AUTH_TOKEN_SECRET_SALT, TOKEN_SECRET_SALT );
-        }
-    }
-
-
-    @Override
-    public String createToken( TokenCategory tokenCategory, String type, AuthPrincipalInfo principal,
-                               Map<String, Object> state, long duration ) throws Exception {
-        return createToken( tokenCategory, type, principal, state, duration, null, System.currentTimeMillis() );
-    }
-
-
-    @Override
-    public String createToken( TokenCategory tokenCategory, String type, AuthPrincipalInfo principal,
-                               Map<String, Object> state, long duration, UUID workflowOrgId ) throws Exception {
-        return createToken( tokenCategory, type, principal, state, duration, workflowOrgId, System.currentTimeMillis() );
-    }
-
-
-    /** Exposed for testing purposes. The interface does not allow creation timestamp checking */
-    public String createToken( TokenCategory tokenCategory, String type, AuthPrincipalInfo principal,
-                               Map<String, Object> state, long duration, UUID workflowOrgId,
-                               long creationTimestamp ) throws Exception {
-
-        long maxTokenTtl = getMaxTtl( tokenCategory, principal );
-
-        if ( duration > maxTokenTtl ) {
-            throw new IllegalArgumentException(
-                    String.format( "Your token age cannot be more than the maximum age of %d milliseconds",
-                            maxTokenTtl ) );
-        }
-
-        if ( duration == 0 ) {
-            duration = maxTokenTtl;
-        }
-
-        if ( principal != null ) {
-            Assert.notNull( principal.getType() );
-            Assert.notNull( principal.getApplicationId() );
-            Assert.notNull( principal.getUuid() );
-        }
-
-        // create UUID that we will use to store token info in our database
-        UUID uuid = UUIDUtils.newTimeUUID( creationTimestamp );
-
-        long timestamp = getTimestampInMillis( uuid );
-        if ( type == null ) {
-            type = TOKEN_TYPE_ACCESS;
-        }
-        TokenInfo tokenInfo = new TokenInfo( uuid, type, timestamp, timestamp, 0, duration, principal,
-                state, workflowOrgId );
-        putTokenInfo( tokenInfo );
-
-        // generate token from the UUID that we created
-        return getTokenForUUID(tokenInfo, tokenCategory, uuid);
-    }
-
-
-    @Override
-    public void importToken(String token, TokenCategory tokenCategory, String type, AuthPrincipalInfo principal,
-                            Map<String, Object> state, long duration) throws Exception {
-
-        importToken(token, tokenCategory, type, principal, state, duration, null);
-    }
-
-
-    @Override
-    public void importToken(String token, TokenCategory tokenCategory, String type, AuthPrincipalInfo principal,
-                            Map<String, Object> state, long duration, UUID workflowOrgId) throws Exception {
-
-        // same logic as create token
-
-        long maxTokenTtl = getMaxTtl( tokenCategory, principal );
-
-        if ( duration > maxTokenTtl ) {
-            throw new IllegalArgumentException(
-                    String.format( "Your token age cannot be more than the maximum age of %d milliseconds",
-                            maxTokenTtl ) );
-        }
-
-        if ( duration == 0 ) {
-            duration = maxTokenTtl;
-        }
-
-        if ( principal != null ) {
-            Assert.notNull( principal.getType() );
-            Assert.notNull( principal.getApplicationId() );
-            Assert.notNull( principal.getUuid() );
-        }
-
-        // except that we generate the UUID based on the token
-
-        UUID uuid = getUUIDForToken(token);
-
-        long timestamp = getTimestampInMillis( uuid );
-        if ( type == null ) {
-            type = TOKEN_TYPE_ACCESS;
-        }
-
-        TokenInfo tokenInfo = new TokenInfo( uuid, type, timestamp, timestamp, 0, duration, principal,
-                state, workflowOrgId );
-        putTokenInfo( tokenInfo );
-    }
-
-
-    @Override
-    public TokenInfo getTokenInfo( String token ) throws Exception {
-        return getTokenInfo(token, true);
-    }
-
-
-    @Override
-    public TokenInfo getTokenInfo( String token, boolean updateAccessTime ) throws Exception {
-
-        UUID uuid;
-
-
-        /** Pre-validation of the token string based on Usergrid's encoding scheme.
-         *
-         * If the token does not parse out a UUID, then it's not a Usergrid token.  Check if External SSO provider
-         * is configured, which is not Usergrid and immediately try to validate the token based on this parsing
-         * information.
-         */
-        try{
-            uuid = getUUIDForToken( token );
-        }
-        catch (ExpiredTokenException expiredTokenException){
-            throw new ExpiredTokenException(expiredTokenException.getMessage());
-        }
-        catch(Exception e){
-
-            // If the token doesn't parse as a Usergrid token, see if an external provider other than Usergrid is
-            // enabled.  If so, just validate the external token.
-            try{
-                if( isExternalSSOProviderEnabled() && !getExternalSSOProvider().equalsIgnoreCase("usergrid")) {
-                    return validateExternalToken(token, 1, getExternalSSOProvider());
-                }else{
-                    throw new IllegalArgumentException("invalid external provider : " + getExternalSSOProvider()); // re-throw the error
-                }
-            }
-            catch (NullPointerException npe){
-                throw new IllegalArgumentException("The SSO provider in the config is empty.");
-            }
-
-        }
-
-        final TokenInfo tokenInfo;
-
-        /**
-         * Now try actual Usergrid token validations.  First try locally.  If that fails and SSO is enabled with
-         * Usergrid being a provider, validate the external token.
-         */
-        try {
-            tokenInfo = getTokenInfo( uuid );
-        } catch (InvalidTokenException e){
-            // Try the request from Usergrid, conditions are specific so we don't incur perf hits for unncessary
-            // token validations that are known to not
-            if ( isExternalSSOProviderEnabled() && getExternalSSOProvider().equalsIgnoreCase("usergrid") ){
-                return validateExternalToken( token, maxPersistenceTokenAge, getExternalSSOProvider() );
-            }else{
-                throw e; // re-throw the error
-            }
-        }
-
-        if (updateAccessTime) {
-            //update the token
-            long now = currentTimeMillis();
-
-            long maxTokenTtl = getMaxTtl(TokenCategory.getFromBase64String(token), tokenInfo.getPrincipal());
-
-            Mutator<UUID> batch = createMutator(cassandra.getUsergridApplicationKeyspace(), ue);
-
-            HColumn<String, Long> col =
-                    createColumn(TOKEN_ACCESSED, now, calcTokenTime(tokenInfo.getExpiration(maxTokenTtl)),
-                            se, le);
-            batch.addInsertion(uuid, TOKENS_CF, col);
-
-            long inactive = now - tokenInfo.getAccessed();
-            if (inactive > tokenInfo.getInactive()) {
-                col = createColumn(TOKEN_INACTIVE, inactive, calcTokenTime(tokenInfo.getExpiration(maxTokenTtl)),
-                        se, le);
-                batch.addInsertion(uuid, TOKENS_CF, col);
-                tokenInfo.setInactive(inactive);
-            }
-
-            batch.execute();
-        }
-
-        return tokenInfo;
-    }
-
-
-    /** Get the max ttl per app. This is null safe,and will return the default in the case of missing data */
-    private long getMaxTtl( TokenCategory tokenCategory, AuthPrincipalInfo principal ) throws Exception {
-
-        if ( principal == null ) {
-            return maxPersistenceTokenAge;
-        }
-        long defaultMaxTtlForTokenType = getExpirationForTokenType( tokenCategory );
-
-        Application application = emf.getEntityManager( principal.getApplicationId() )
-                                     .get( principal.getApplicationId(), Application.class );
-
-        if ( application == null ) {
-            return defaultMaxTtlForTokenType;
-        }
-
-        // set the max to the default
-        long maxTokenTtl = defaultMaxTtlForTokenType;
-
-        // it's been defined on the expiration, override it
-        if ( application.getAccesstokenttl() != null ) {
-            maxTokenTtl = application.getAccesstokenttl();
-
-            // it's set to 0 which equals infinity, set our expiration to
-            // LONG.MAX
-            if ( maxTokenTtl == 0 ) {
-                maxTokenTtl = Long.MAX_VALUE;
-            }
-        }
-
-        return maxTokenTtl;
-    }
-
-
-    /*
-     * (non-Javadoc)
-     *
-     * @see
-     * org.apache.usergrid.security.tokens.TokenService#removeTokens(org.apache.usergrid.security
-     * .AuthPrincipalInfo)
-     */
-    @Override
-    public void removeTokens( AuthPrincipalInfo principal ) throws Exception {
-        List<UUID> tokenIds = getTokenUUIDS( principal );
-
-        Mutator<ByteBuffer> batch = createMutator( cassandra.getUsergridApplicationKeyspace(), be );
-
-        for ( UUID tokenId : tokenIds ) {
-            batch.addDeletion( bytebuffer( tokenId ), TOKENS_CF );
-        }
-
-        batch.addDeletion( principalKey( principal ), PRINCIPAL_TOKEN_CF );
-
-        batch.execute();
-    }
-
-
-    /*
-     * (non-Javadoc)
-     *
-     * @see
-     * org.apache.usergrid.security.tokens.TokenService#revokeToken(java.lang.String)
-     */
-    @Override
-    public void revokeToken( String token ) {
-
-        TokenInfo info;
-
-        try {
-            info = getTokenInfo( token );
-        }
-        catch ( Exception e ) {
-            logger.error( "Unable to find token with the specified value ignoring request.  Value : {}", token );
-            return;
-        }
-
-        UUID tokenId = info.getUuid();
-
-        Mutator<ByteBuffer> batch = createMutator( cassandra.getUsergridApplicationKeyspace(), be );
-
-        // clean up the link in the principal -> token index if the principal is
-        // on the token
-        if ( info.getPrincipal() != null ) {
-            batch.addDeletion( principalKey( info.getPrincipal() ), PRINCIPAL_TOKEN_CF, bytebuffer( tokenId ),
-                    be );
-        }
-
-        // remove the token from the tokens cf
-        batch.addDeletion( bytebuffer( tokenId ), TOKENS_CF );
-
-        batch.execute();
-    }
-
-
-    private TokenInfo getTokenInfo( UUID uuid ) throws Exception {
-        if ( uuid == null ) {
-            throw new InvalidTokenException( "No token specified" );
-        }
-        Map<String, ByteBuffer> columns = getColumnMap( cassandra
-                .getColumns( cassandra.getUsergridApplicationKeyspace(), TOKENS_CF, uuid, TOKEN_PROPERTIES, se,
-                        be ) );
-        if ( !hasKeys( columns, REQUIRED_TOKEN_PROPERTIES ) ) {
-            throw new InvalidTokenException( "Token not found in database" );
-        }
-        String type = string( columns.get( TOKEN_TYPE ) );
-        long created = getLong( columns.get( TOKEN_CREATED ) );
-        long accessed = getLong( columns.get( TOKEN_ACCESSED ) );
-        long inactive = getLong( columns.get( TOKEN_INACTIVE ) );
-        long duration = getLong( columns.get( TOKEN_DURATION ) );
-        String principalTypeStr = string( columns.get( TOKEN_PRINCIPAL_TYPE ) );
-        AuthPrincipalType principalType = null;
-        if ( principalTypeStr != null ) {
-            try {
-                principalType = AuthPrincipalType.valueOf( principalTypeStr.toUpperCase() );
-            }
-            catch ( IllegalArgumentException e ) {
-            }
-        }
-        AuthPrincipalInfo principal = null;
-        if ( principalType != null ) {
-            UUID entityId = uuid( columns.get( TOKEN_ENTITY ) );
-            UUID appId = uuid( columns.get( TOKEN_APPLICATION ) );
-            principal = new AuthPrincipalInfo( principalType, entityId, appId );
-        }
-        @SuppressWarnings("unchecked") Map<String, Object> state =
-                ( Map<String, Object> ) JsonUtils.fromByteBuffer( columns.get( TOKEN_STATE ) );
-
-        UUID workflowOrgId = null;
-        if (columns.containsKey(TOKEN_WORKFLOW_ORG_ID)) {
-            workflowOrgId = ConversionUtils.uuid(columns.get(TOKEN_WORKFLOW_ORG_ID));
-        }
-
-        return new TokenInfo( uuid, type, created, accessed, inactive, duration, principal, state, workflowOrgId );
-    }
-
-
-    private void putTokenInfo( TokenInfo tokenInfo ) throws Exception {
-
-        ByteBuffer tokenUUID = bytebuffer( tokenInfo.getUuid() );
-
-        Keyspace ko = cassandra.getUsergridApplicationKeyspace();
-
-        Mutator<ByteBuffer> m = createMutator( ko, be );
-
-        int ttl = calcTokenTime( tokenInfo.getDuration() );
-
-        m.addInsertion( tokenUUID, TOKENS_CF,
-                createColumn( TOKEN_UUID, bytebuffer( tokenInfo.getUuid() ), ttl, se, be ) );
-        m.addInsertion( tokenUUID, TOKENS_CF,
-                createColumn( TOKEN_TYPE, bytebuffer( tokenInfo.getType() ), ttl, se, be ) );
-        m.addInsertion( tokenUUID, TOKENS_CF,
-                createColumn( TOKEN_CREATED, bytebuffer( tokenInfo.getCreated() ), ttl, se, be ) );
-        m.addInsertion( tokenUUID, TOKENS_CF,
-                createColumn( TOKEN_ACCESSED, bytebuffer( tokenInfo.getAccessed() ), ttl, se, be ) );
-        m.addInsertion( tokenUUID, TOKENS_CF,
-                createColumn( TOKEN_INACTIVE, bytebuffer( tokenInfo.getInactive() ), ttl, se, be ) );
-        m.addInsertion( tokenUUID, TOKENS_CF,
-                createColumn( TOKEN_DURATION, bytebuffer( tokenInfo.getDuration() ), ttl, se, be ) );
-
-        if ( tokenInfo.getPrincipal() != null ) {
-
-            AuthPrincipalInfo principalInfo = tokenInfo.getPrincipal();
-
-            m.addInsertion( tokenUUID, TOKENS_CF,
-                    createColumn( TOKEN_PRINCIPAL_TYPE, bytebuffer( principalInfo.getType().toString().toLowerCase() ),
-                            ttl, se, be ) );
-            m.addInsertion( tokenUUID, TOKENS_CF,
-                    createColumn( TOKEN_ENTITY, bytebuffer( principalInfo.getUuid() ), ttl, se, be ) );
-            m.addInsertion( tokenUUID, TOKENS_CF,
-                    createColumn( TOKEN_APPLICATION, bytebuffer( principalInfo.getApplicationId() ), ttl, se,
-                            be ) );
-
-      /*
-       * write to the PRINCIPAL+TOKEN The format is as follow
-       *
-       * appid+principalId+principalType :{ tokenuuid: 0x00}
-       */
-
-            ByteBuffer rowKey = principalKey( principalInfo );
-            m.addInsertion( rowKey, PRINCIPAL_TOKEN_CF, createColumn( tokenUUID, HOLDER, ttl, be, be ) );
-        }
-
-        if ( tokenInfo.getState() != null ) {
-            m.addInsertion( tokenUUID, TOKENS_CF,
-                    createColumn( TOKEN_STATE, JsonUtils.toByteBuffer( tokenInfo.getState() ), ttl, se,
-                            be ) );
-        }
-
-        if ( tokenInfo.getWorkflowOrgId() != null ) {
-            m.addInsertion( tokenUUID, TOKENS_CF,
-                    createColumn( TOKEN_WORKFLOW_ORG_ID, bytebuffer( tokenInfo.getWorkflowOrgId() ), ttl, se, be ) );
-        }
-
-        m.execute();
-    }
-
-
-    /** Load all the token uuids for a principal info */
-    private List<UUID> getTokenUUIDS( AuthPrincipalInfo principal ) throws Exception {
-
-        ByteBuffer rowKey = principalKey( principal );
-
-        List<HColumn<ByteBuffer, ByteBuffer>> cols = cassandra
-                .getColumns( cassandra.getUsergridApplicationKeyspace(), PRINCIPAL_TOKEN_CF, rowKey, null, null, Integer.MAX_VALUE,
-                        false );
-
-        List<UUID> results = new ArrayList<UUID>( cols.size() );
-
-        for ( HColumn<ByteBuffer, ByteBuffer> col : cols ) {
-            results.add( uuid( col.getName() ) );
-        }
-
-        return results;
-    }
-
-
-    private ByteBuffer principalKey( AuthPrincipalInfo principalInfo ) {
-        // 66 bytes, 2 UUIDS + 2 chars for prefix
-        ByteBuffer buff = ByteBuffer.allocate( 32 * 2 + 2 );
-        buff.put( bytes( principalInfo.getApplicationId() ) );
-        buff.put( bytes( principalInfo.getUuid() ) );
-        buff.put( bytes( principalInfo.getType().getPrefix() ) );
-        buff.rewind();
-
-        return buff;
-    }
-
-
-    private UUID getUUIDForToken(String token ) throws ExpiredTokenException, BadTokenException {
-        TokenCategory tokenCategory = TokenCategory.getFromBase64String( token );
-        byte[] bytes = decodeBase64( token.substring( TokenCategory.BASE64_PREFIX_LENGTH ) );
-        UUID uuid = uuid( bytes );
-        int i = 16;
-        long expires = Long.MAX_VALUE;
-        if ( tokenCategory.getExpires() ) {
-            expires = ByteBuffer.wrap( bytes, i, 8 ).getLong();
-            i = 24;
-        }
-        ByteBuffer expected = ByteBuffer.allocate( 20 );
-        expected.put( sha( tokenCategory.getPrefix() + uuid + tokenSecretSalt + expires ) );
-        expected.rewind();
-        ByteBuffer signature = ByteBuffer.wrap( bytes, i, 20 );
-
-
-        if ( !signature.equals( expected ) ) {
-            throw new BadTokenException( "Invalid token signature" );
-        }
-
-
-        long expirationDelta = System.currentTimeMillis() - expires;
-
-        if ( expires != Long.MAX_VALUE && expirationDelta > 0 ) {
-            throw new ExpiredTokenException( String.format( "Token expired %d milliseconds ago.", expirationDelta ) );
-        }
-        return uuid;
-    }
-
-
-    @Override
-    public long getMaxTokenAge( String token ) {
-        TokenCategory tokenCategory = TokenCategory.getFromBase64String( token );
-        byte[] bytes = decodeBase64( token.substring( TokenCategory.BASE64_PREFIX_LENGTH ) );
-        UUID uuid = uuid( bytes );
-        long timestamp = getTimestampInMillis( uuid );
-        int i = 16;
-        if ( tokenCategory.getExpires() ) {
-            long expires = ByteBuffer.wrap( bytes, i, 8 ).getLong();
-            return expires - timestamp;
-        }
-        return Long.MAX_VALUE;
-    }
-
-
-    /*
-     * (non-Javadoc)
-     *
-     * @see
-     * org.apache.usergrid.security.tokens.TokenService#getMaxTokenAgeInSeconds(java.
-     * lang.String)
-     */
-    @Override
-    public long getMaxTokenAgeInSeconds( String token ) {
-        return getMaxTokenAge( token ) / 1000;
-    }
-
-
-    /**
-     * The maximum age a token can be saved for
-     *
-     * @return the maxPersistenceTokenAge
-     */
-    public long getMaxPersistenceTokenAge() {
-        return maxPersistenceTokenAge;
-    }
-
-
-    @Autowired
-    @Qualifier("cassandraService")
-    public void setCassandraService( CassandraService cassandra ) {
-        this.cassandra = cassandra;
-    }
-
-
-    @Autowired
-    public void setEntityManagerFactory( EntityManagerFactory emf ) {
-        this.emf = emf;
-        final Injector injector = ((CpEntityManagerFactory)emf).getApplicationContext().getBean( Injector.class );
-        metricsFactory = injector.getInstance(MetricsFactory.class);
-    }
-
-
-    private String getTokenForUUID( TokenInfo tokenInfo, TokenCategory tokenCategory, UUID uuid ) {
-        int l = 36;
-        if ( tokenCategory.getExpires() ) {
-            l += 8;
-        }
-        ByteBuffer bytes = ByteBuffer.allocate( l );
-        bytes.put( bytes( uuid ) );
-        long expires = Long.MAX_VALUE;
-        if ( tokenCategory.getExpires() ) {
-            expires = ( tokenInfo.getDuration() > 0 ) ?
-                      UUIDUtils.getTimestampInMillis( uuid ) + ( tokenInfo.getDuration() ) :
-                      UUIDUtils.getTimestampInMillis( uuid ) + getExpirationForTokenType( tokenCategory );
-            bytes.putLong( expires );
-        }
-        bytes.put( sha( tokenCategory.getPrefix() + uuid + tokenSecretSalt + expires ) );
-        return tokenCategory.getBase64Prefix() + encodeBase64URLSafeString( bytes.array() );
-    }
-
-
-    /** Calculate the column lifetime and account for long truncation to seconds */
-    private int calcTokenTime( long time ) {
-
-        long secondsDuration = time / 1000;
-
-        int ttl = ( int ) secondsDuration;
-
-        // we've had a ttl that's longer than Integer.MAX value
-        if ( ttl != secondsDuration ) {
-            // Something is up with cassandra... Setting ttl to integer.max
-            // makes the cols disappear.....
-
-            // this should be the line below once this issue is fixed.
-            // https://issues.apache.org/jira/browse/CASSANDRA-4771
-            // ttl = Integer.MAX_VALUE
-
-            // take the max value of an int, and substract the system time off
-            // (in seconds) ,then arbitrarily remove another 120 seconds for good
-            // measure.
-            // Cass calcs the expiration time as
-            // "(System.currentTimeMillis() / 1000) + timeToLive);", so we need
-            // to play nice otherwise it blows up on persist
-            ttl = Integer.MAX_VALUE - ( int ) ( System.currentTimeMillis() / 1000 ) - 120;
-        }
-        // hard cap at the max in o.a.c.db.IColumn
-        if ( ttl > MAX_TTL ) {
-            ttl = MAX_TTL;
-        }
-
-        return ttl;
-    }
-
-
-    private static final int MAX_TTL = 20 * 365 * 24 * 60 * 60;
-
-
-    //-------------------------------------------------------------------------------------------------------
-    //
-    // Central SSO implementation
-
-    public static final String CENTRAL_CONNECTION_POOL_SIZE = "usergrid.central.connection.pool.size";
-    public static final String CENTRAL_CONNECTION_TIMEOUT =   "usergrid.central.connection.timeout";
-    public static final String CENTRAL_READ_TIMEOUT =         "usergrid.central.read.timeout";
-
-
-    // names for metrics to be collected
-    private static final String SSO_TOKENS_REJECTED =         "sso.tokens_rejected";
-    private static final String SSO_TOKENS_VALIDATED =        "sso.tokens_validated";
-    private static final String SSO_CREATED_LOCAL_ADMINS =    "sso.created_local_admins";
-    private static final String SSO_PROCESSING_TIME =         "sso.processing_time";
-
-    //SSO2 implementation
-    public static final String USERGRID_EXTERNAL_SSO_ENABLED = "usergrid.external.sso.enabled";
-    public static final String USERGRID_EXTERNAL_SSO_PROVIDER =    "usergrid.external.sso.provider";
-    public static final String USERGRID_EXTERNAL_SSO_PROVIDER_URL = "usergrid.external.sso.url";
-    public static final String USERGRID_EXTERNAL_SSO_PROVIDER_USER_PROVISION_URL
-        = "usergrid.external.sso.userprovision.url";
-
-
-    private static Client jerseyClient = null;
-
-    @Autowired
-    private ApplicationCreator applicationCreator;
-
-    @Autowired
-    protected ManagementService management;
-
-    @Autowired
-    private SSOProviderFactory ssoProviderFactory;
-
-    MetricsFactory getMetricsFactory() {
-        return metricsFactory;
-    }
-
-
-    public boolean isExternalSSOProviderEnabled() {
-        return Boolean.valueOf(properties.getProperty( USERGRID_EXTERNAL_SSO_ENABLED ));
-    }
-
-    private String getExternalSSOProvider(){
-            return properties.getProperty(USERGRID_EXTERNAL_SSO_PROVIDER);
-    }
-
-    /**
-     * <p>
-     * Validates access token from other or "external" Usergrid system.
-     * Calls other system's /management/me endpoint to get the User
-     * associated with the access token. If user does not exist locally,
-     * then user and organizations will be created. If no user is returned
-     * from the other cluster, then return null.
-     * </p>
-     * <p/>
-     * <p> Part of Usergrid Central SSO feature.
-     * See <a href="https://issues.apache.org/jira/browse/USERGRID-567">USERGRID-567</a>
-     * for details about Usergrid Central SSO.
-     * </p>
-     *
-     * @param extAccessToken Access token from external Usergrid system.
-     * @param ttl            Time to live for token.
-     */
-    public TokenInfo validateExternalToken(String extAccessToken, long ttl, String provider) throws Exception {
-
-
-        ExternalSSOProvider ssoProvider = ssoProviderFactory.getProvider();
-
-        if(provider.equalsIgnoreCase("usergrid")){
-
-            UserInfo userinfo = ssoProvider.validateAndReturnUserInfo(extAccessToken,ttl);
-
-            // Store the external Usergrid access_token as if it were one of our own so we don't have to make the
-            // external HTTP validation call on subsequent requests
-            importToken( extAccessToken, TokenCategory.ACCESS, null, new AuthPrincipalInfo(
-                ADMIN_USER, userinfo.getUuid(), CpNamingUtils.MANAGEMENT_APPLICATION_ID), null, ttl );
-            return getTokenInfo( extAccessToken );
-
-        }else{
-
-            return ssoProvider.validateAndReturnTokenInfo(extAccessToken,ttl);
-        }
-
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/usergrid/blob/146e47d6/stack/services/src/main/java/org/apache/usergrid/security/tokens/impl/TokenServiceImpl.java
----------------------------------------------------------------------
diff --git a/stack/services/src/main/java/org/apache/usergrid/security/tokens/impl/TokenServiceImpl.java b/stack/services/src/main/java/org/apache/usergrid/security/tokens/impl/TokenServiceImpl.java
new file mode 100644
index 0000000..d93ecc0
--- /dev/null
+++ b/stack/services/src/main/java/org/apache/usergrid/security/tokens/impl/TokenServiceImpl.java
@@ -0,0 +1,731 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.usergrid.security.tokens.impl;
+
+
+import com.google.inject.Injector;
+import org.apache.usergrid.corepersistence.CpEntityManagerFactory;
+import org.apache.usergrid.corepersistence.util.CpNamingUtils;
+import org.apache.usergrid.management.ApplicationCreator;
+import org.apache.usergrid.management.ManagementService;
+import org.apache.usergrid.management.UserInfo;
+import org.apache.usergrid.persistence.EntityManagerFactory;
+import org.apache.usergrid.persistence.core.metrics.MetricsFactory;
+import org.apache.usergrid.persistence.entities.Application;
+import org.apache.usergrid.persistence.token.TokenSerialization;
+import org.apache.usergrid.security.AuthPrincipalInfo;
+import org.apache.usergrid.security.AuthPrincipalType;
+import org.apache.usergrid.security.sso.SSOProviderFactory;
+import org.apache.usergrid.security.tokens.TokenCategory;
+import org.apache.usergrid.security.tokens.TokenInfo;
+import org.apache.usergrid.security.tokens.TokenService;
+import org.apache.usergrid.security.tokens.exceptions.BadTokenException;
+import org.apache.usergrid.security.tokens.exceptions.ExpiredTokenException;
+import org.apache.usergrid.security.tokens.exceptions.InvalidTokenException;
+import org.apache.usergrid.security.sso.ExternalSSOProvider;
+import org.apache.usergrid.utils.UUIDUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.Assert;
+
+import javax.ws.rs.client.Client;
+import java.nio.ByteBuffer;
+import java.util.*;
+
+import static java.lang.System.currentTimeMillis;
+import static org.apache.commons.codec.binary.Base64.decodeBase64;
+import static org.apache.commons.codec.binary.Base64.encodeBase64URLSafeString;
+import static org.apache.commons.codec.digest.DigestUtils.sha;
+import static org.apache.usergrid.persistence.token.impl.TokenSerializationImpl.*;
+import static org.apache.usergrid.security.AuthPrincipalType.ADMIN_USER;
+import static org.apache.usergrid.security.tokens.TokenCategory.*;
+import static org.apache.usergrid.utils.ConversionUtils.*;
+import static org.apache.usergrid.utils.MapUtils.hasKeys;
+import static org.apache.usergrid.utils.MapUtils.hashMap;
+import static org.apache.usergrid.utils.UUIDUtils.getTimestampInMillis;
+
+
+public class TokenServiceImpl implements TokenService {
+
+    private static final Logger logger = LoggerFactory.getLogger( TokenServiceImpl.class );
+
+    public static final String PROPERTIES_AUTH_TOKEN_SECRET_SALT = "usergrid.auth.token_secret_salt";
+
+    public static final String TOKEN_SECRET_SALT = "super secret token value";
+
+    // Short-lived token is good for 24 hours
+    public static final long SHORT_TOKEN_AGE = 24 * 60 * 60 * 1000;
+
+    // Long-lived token is good for 7 days
+    public static final long LONG_TOKEN_AGE = 7 * 24 * 60 * 60 * 1000;
+
+    String tokenSecretSalt = TOKEN_SECRET_SALT;
+
+    long maxPersistenceTokenAge = LONG_TOKEN_AGE;
+
+    Map<TokenCategory, Long> tokenExpirations =
+            hashMap( ACCESS, LONG_TOKEN_AGE ).map( REFRESH, LONG_TOKEN_AGE ).map( EMAIL, LONG_TOKEN_AGE )
+                    .map( OFFLINE, LONG_TOKEN_AGE );
+
+    long maxAccessTokenAge = SHORT_TOKEN_AGE;
+    long maxRefreshTokenAge = LONG_TOKEN_AGE;
+    long maxEmailTokenAge = LONG_TOKEN_AGE;
+    long maxOfflineTokenAge = LONG_TOKEN_AGE;
+
+    protected Properties properties;
+
+    protected EntityManagerFactory emf;
+
+    private MetricsFactory metricsFactory;
+
+    private TokenSerialization tokenSerialization;
+
+
+    public TokenServiceImpl() {
+    }
+
+
+    private long getExpirationProperty( String name, long default_expiration ) {
+        long expires = Long.parseLong(
+                properties.getProperty( "usergrid.auth.token." + name + ".expires", "" + default_expiration ) );
+        return expires > 0 ? expires : default_expiration;
+    }
+
+
+    private long getExpirationForTokenType( TokenCategory tokenCategory ) {
+        Long l = tokenExpirations.get( tokenCategory );
+        if ( l != null ) {
+            return l;
+        }
+        return SHORT_TOKEN_AGE;
+    }
+
+
+    private void setExpirationFromProperties( String name ) {
+        TokenCategory tokenCategory = TokenCategory.valueOf( name.toUpperCase() );
+        long expires = Long.parseLong( properties.getProperty( "usergrid.auth.token." + name + ".expires",
+                "" + getExpirationForTokenType( tokenCategory ) ) );
+        if ( expires > 0 ) {
+            tokenExpirations.put( tokenCategory, expires );
+        }
+        logger.info( "{} token expires after {} seconds", name, getExpirationForTokenType( tokenCategory ) / 1000 );
+    }
+
+
+    @Autowired
+    public void setProperties( Properties properties ) {
+        this.properties = properties;
+
+        if ( properties != null ) {
+            maxPersistenceTokenAge = getExpirationProperty( "persistence", maxPersistenceTokenAge );
+
+            setExpirationFromProperties( "access" );
+            setExpirationFromProperties( "refresh" );
+            setExpirationFromProperties( "email" );
+            setExpirationFromProperties( "offline" );
+
+            tokenSecretSalt = properties.getProperty( PROPERTIES_AUTH_TOKEN_SECRET_SALT, TOKEN_SECRET_SALT );
+        }
+    }
+
+
+    @Override
+    public String createToken( TokenCategory tokenCategory, String type, AuthPrincipalInfo principal,
+                               Map<String, Object> state, long duration ) throws Exception {
+        return createToken( tokenCategory, type, principal, state, duration, null, System.currentTimeMillis() );
+    }
+
+
+    @Override
+    public String createToken( TokenCategory tokenCategory, String type, AuthPrincipalInfo principal,
+                               Map<String, Object> state, long duration, UUID workflowOrgId ) throws Exception {
+        return createToken( tokenCategory, type, principal, state, duration, workflowOrgId, System.currentTimeMillis() );
+    }
+
+
+    /** Exposed for testing purposes. The interface does not allow creation timestamp checking */
+    public String createToken( TokenCategory tokenCategory, String type, AuthPrincipalInfo principal,
+                               Map<String, Object> state, long duration, UUID workflowOrgId,
+                               long creationTimestamp ) throws Exception {
+
+        long maxTokenTtl = getMaxTtl( tokenCategory, principal );
+
+        if ( duration > maxTokenTtl ) {
+            throw new IllegalArgumentException(
+                    String.format( "Your token age cannot be more than the maximum age of %d milliseconds",
+                            maxTokenTtl ) );
+        }
+
+        if ( duration == 0 ) {
+            duration = maxTokenTtl;
+        }
+
+        if ( principal != null ) {
+            Assert.notNull( principal.getType() );
+            Assert.notNull( principal.getApplicationId() );
+            Assert.notNull( principal.getUuid() );
+        }
+
+        // create UUID that we will use to store token info in our database
+        UUID uuid = UUIDUtils.newTimeUUID( creationTimestamp );
+
+        long timestamp = getTimestampInMillis( uuid );
+        if ( type == null ) {
+            type = TOKEN_TYPE_ACCESS;
+        }
+        TokenInfo tokenInfo = new TokenInfo( uuid, type, timestamp, timestamp, 0, duration, principal,
+                state, workflowOrgId );
+        putTokenInfo( tokenInfo );
+
+        // generate token from the UUID that we created
+        return getTokenForUUID(tokenInfo, tokenCategory, uuid);
+    }
+
+
+    @Override
+    public void importToken(String token, TokenCategory tokenCategory, String type, AuthPrincipalInfo principal,
+                            Map<String, Object> state, long duration) throws Exception {
+
+        importToken(token, tokenCategory, type, principal, state, duration, null);
+    }
+
+
+    @Override
+    public void importToken(String token, TokenCategory tokenCategory, String type, AuthPrincipalInfo principal,
+                            Map<String, Object> state, long duration, UUID workflowOrgId) throws Exception {
+
+        // same logic as create token
+
+        long maxTokenTtl = getMaxTtl( tokenCategory, principal );
+
+        if ( duration > maxTokenTtl ) {
+            throw new IllegalArgumentException(
+                    String.format( "Your token age cannot be more than the maximum age of %d milliseconds",
+                            maxTokenTtl ) );
+        }
+
+        if ( duration == 0 ) {
+            duration = maxTokenTtl;
+        }
+
+        if ( principal != null ) {
+            Assert.notNull( principal.getType() );
+            Assert.notNull( principal.getApplicationId() );
+            Assert.notNull( principal.getUuid() );
+        }
+
+        // except that we generate the UUID based on the token
+
+        UUID uuid = getUUIDForToken(token);
+
+        long timestamp = getTimestampInMillis( uuid );
+        if ( type == null ) {
+            type = TOKEN_TYPE_ACCESS;
+        }
+
+        TokenInfo tokenInfo = new TokenInfo( uuid, type, timestamp, timestamp, 0, duration, principal,
+                state, workflowOrgId );
+        putTokenInfo( tokenInfo );
+    }
+
+
+    @Override
+    public TokenInfo getTokenInfo( String token ) throws Exception {
+        return getTokenInfo(token, true);
+    }
+
+
+    @Override
+    public TokenInfo getTokenInfo( String token, boolean updateAccessTime ) throws Exception {
+
+        UUID uuid;
+
+
+        /** Pre-validation of the token string based on Usergrid's encoding scheme.
+         *
+         * If the token does not parse out a UUID, then it's not a Usergrid token.  Check if External SSO provider
+         * is configured, which is not Usergrid and immediately try to validate the token based on this parsing
+         * information.
+         */
+        try{
+            uuid = getUUIDForToken( token );
+        }
+        catch (ExpiredTokenException expiredTokenException){
+            throw new ExpiredTokenException(expiredTokenException.getMessage());
+        }
+        catch(Exception e){
+
+            // If the token doesn't parse as a Usergrid token, see if an external provider other than Usergrid is
+            // enabled.  If so, just validate the external token.
+            try{
+                if( isExternalSSOProviderEnabled() && !getExternalSSOProvider().equalsIgnoreCase("usergrid")) {
+                    return validateExternalToken(token, 1, getExternalSSOProvider());
+                }else{
+                    throw new IllegalArgumentException("invalid external provider : " + getExternalSSOProvider()); // re-throw the error
+                }
+            }
+            catch (NullPointerException npe){
+                throw new IllegalArgumentException("The SSO provider in the config is empty.");
+            }
+
+        }
+
+        final TokenInfo tokenInfo;
+
+        /**
+         * Now try actual Usergrid token validations.  First try locally.  If that fails and SSO is enabled with
+         * Usergrid being a provider, validate the external token.
+         */
+        try {
+            tokenInfo = getTokenInfo( uuid );
+        } catch (InvalidTokenException e){
+            // Try the request from Usergrid, conditions are specific so we don't incur perf hits for unncessary
+            // token validations that are known to not
+            if ( isExternalSSOProviderEnabled() && getExternalSSOProvider().equalsIgnoreCase("usergrid") ){
+                return validateExternalToken( token, maxPersistenceTokenAge, getExternalSSOProvider() );
+            }else{
+                throw e; // re-throw the error
+            }
+        }
+
+        if (updateAccessTime) {
+            //update the token
+            long now = currentTimeMillis();
+
+            long maxTokenTtl = getMaxTtl(TokenCategory.getFromBase64String(token), tokenInfo.getPrincipal());
+
+            long inactive = now - tokenInfo.getAccessed();
+            // Long.MIN_VALUE indicates that nothing needs to be updated for token inactive property
+            if (inactive < tokenInfo.getInactive()) {
+               inactive = Long.MIN_VALUE;
+            }
+
+            tokenSerialization.updateTokenAccessTime(uuid, now, inactive, calcTokenTime(tokenInfo.getExpiration(maxTokenTtl)));
+        }
+
+        return tokenInfo;
+    }
+
+
+    /** Get the max ttl per app. This is null safe,and will return the default in the case of missing data */
+    private long getMaxTtl( TokenCategory tokenCategory, AuthPrincipalInfo principal ) throws Exception {
+
+        if ( principal == null ) {
+            return maxPersistenceTokenAge;
+        }
+        long defaultMaxTtlForTokenType = getExpirationForTokenType( tokenCategory );
+
+        Application application = emf.getEntityManager( principal.getApplicationId() )
+                                     .get( principal.getApplicationId(), Application.class );
+
+        if ( application == null ) {
+            return defaultMaxTtlForTokenType;
+        }
+
+        // set the max to the default
+        long maxTokenTtl = defaultMaxTtlForTokenType;
+
+        // it's been defined on the expiration, override it
+        if ( application.getAccesstokenttl() != null ) {
+            maxTokenTtl = application.getAccesstokenttl();
+
+            // it's set to 0 which equals infinity, set our expiration to
+            // LONG.MAX
+            if ( maxTokenTtl == 0 ) {
+                maxTokenTtl = Long.MAX_VALUE;
+            }
+        }
+
+        return maxTokenTtl;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.apache.usergrid.security.tokens.TokenService#removeTokens(org.apache.usergrid.security
+     * .AuthPrincipalInfo)
+     */
+    @Override
+    public void removeTokens( AuthPrincipalInfo principal ) throws Exception {
+
+        final List<UUID> tokenIds = getTokenUUIDS( principal );
+        tokenSerialization.deleteTokens(tokenIds, principalKey( principal ));
+
+    }
+
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.apache.usergrid.security.tokens.TokenService#revokeToken(java.lang.String)
+     */
+    @Override
+    public void revokeToken( String token ) {
+
+        TokenInfo info;
+
+        try {
+            info = getTokenInfo( token );
+        }
+        catch ( Exception e ) {
+            logger.error( "Unable to find token with the specified value ignoring request.  Value : {}", token );
+            return;
+        }
+
+        final UUID tokenId = info.getUuid();
+
+        // clean up the link in the principal -> token index if the principal is
+        // on the token
+        if ( info.getPrincipal() != null ) {
+            tokenSerialization.revokeToken(tokenId, principalKey( info.getPrincipal()));
+        }else{
+            tokenSerialization.revokeToken(tokenId, null);
+        }
+
+    }
+
+
+    private TokenInfo getTokenInfo( UUID uuid ) throws Exception {
+        if ( uuid == null ) {
+            throw new InvalidTokenException( "No token specified" );
+        }
+
+        Map<String, Object> tokenDetails = tokenSerialization.getTokenInfo(uuid);
+
+        if ( !hasKeys( tokenDetails, REQUIRED_TOKEN_PROPERTIES ) ) {
+            throw new InvalidTokenException( "Token not found in database" );
+        }
+
+        String type = (String) tokenDetails.get(TOKEN_TYPE);
+        long created = (long) tokenDetails.get(TOKEN_CREATED);
+        long accessed = (long) tokenDetails.get(TOKEN_ACCESSED);
+        long inactive = (long) tokenDetails.get(TOKEN_INACTIVE);
+        long duration = (long) tokenDetails.get(TOKEN_DURATION);
+
+        String principalTypeStr = (String) tokenDetails.get(TOKEN_PRINCIPAL_TYPE);
+
+        AuthPrincipalType principalType = null;
+        if ( principalTypeStr != null ) {
+            try {
+                principalType = AuthPrincipalType.valueOf( principalTypeStr.toUpperCase() );
+            }
+            catch ( IllegalArgumentException e ) {
+                logger.warn("Unable to convert authPrincipal Type from string to enum");
+            }
+        }
+        AuthPrincipalInfo principal = null;
+        if ( principalType != null ) {
+            UUID entityId = (UUID) tokenDetails.get(TOKEN_ENTITY);
+            UUID appId = (UUID) tokenDetails.get(TOKEN_APPLICATION);
+            principal = new AuthPrincipalInfo( principalType, entityId, appId );
+        }
+
+        @SuppressWarnings("unchecked")
+        Map<String, Object> state = ( Map<String, Object> ) tokenDetails.get( TOKEN_STATE );
+
+        UUID workflowOrgId = null;
+        if (tokenDetails.containsKey(TOKEN_WORKFLOW_ORG_ID)) {
+            workflowOrgId = (UUID) tokenDetails.get(TOKEN_WORKFLOW_ORG_ID);
+        }
+
+        return new TokenInfo( uuid, type, created, accessed, inactive, duration, principal, state, workflowOrgId );
+    }
+
+
+    private void putTokenInfo( TokenInfo tokenInfo ) throws Exception {
+
+        int ttl = calcTokenTime( tokenInfo.getDuration() );
+        final Map<String, Object> tokenDetails = new HashMap<>();
+
+        tokenDetails.put(TOKEN_UUID, tokenInfo.getUuid());
+        tokenDetails.put(TOKEN_TYPE, tokenInfo.getType());
+        tokenDetails.put(TOKEN_CREATED, tokenInfo.getCreated());
+        tokenDetails.put(TOKEN_ACCESSED, tokenInfo.getAccessed());
+        tokenDetails.put(TOKEN_INACTIVE, tokenInfo.getInactive());
+        tokenDetails.put(TOKEN_DURATION, tokenInfo.getDuration());
+
+        ByteBuffer principalKeyBuffer = null;
+        if ( tokenInfo.getPrincipal() != null ) {
+
+            AuthPrincipalInfo principalInfo = tokenInfo.getPrincipal();
+
+            tokenDetails.put(TOKEN_PRINCIPAL_TYPE, principalInfo.getType().toString().toLowerCase());
+            tokenDetails.put(TOKEN_ENTITY, principalInfo.getUuid());
+            tokenDetails.put(TOKEN_APPLICATION, principalInfo.getApplicationId());
+
+          /*
+           * write to the PRINCIPAL+TOKEN The format is as follow
+           *
+           * appid+principalId+principalType :{ tokenuuid: 0x00}
+           */
+            principalKeyBuffer = principalKey( principalInfo );
+
+        }
+
+        if ( tokenInfo.getState() != null ) {
+            tokenDetails.put(TOKEN_STATE, tokenInfo.getState());
+        }
+
+        if ( tokenInfo.getWorkflowOrgId() != null ) {
+            tokenDetails.put(TOKEN_WORKFLOW_ORG_ID, tokenInfo.getWorkflowOrgId());
+        }
+
+        tokenSerialization.putTokenInfo(tokenInfo.getUuid(), tokenDetails, principalKeyBuffer, ttl);
+    }
+
+
+    /** Load all the token uuids for a principal info */
+    private List<UUID> getTokenUUIDS( AuthPrincipalInfo principal ) throws Exception {
+
+        return tokenSerialization.getTokensForPrincipal(principalKey( principal ));
+    }
+
+
+    private ByteBuffer principalKey( AuthPrincipalInfo principalInfo ) {
+        // 66 bytes, 2 UUIDS + 2 chars for prefix
+        ByteBuffer buff = ByteBuffer.allocate( 32 * 2 + 2 );
+        buff.put( bytes( principalInfo.getApplicationId() ) );
+        buff.put( bytes( principalInfo.getUuid() ) );
+        buff.put( bytes( principalInfo.getType().getPrefix() ) );
+        buff.rewind();
+
+        return buff;
+    }
+
+
+    private UUID getUUIDForToken(String token ) throws ExpiredTokenException, BadTokenException {
+        TokenCategory tokenCategory = TokenCategory.getFromBase64String( token );
+        byte[] bytes = decodeBase64( token.substring( TokenCategory.BASE64_PREFIX_LENGTH ) );
+        UUID uuid = uuid( bytes );
+        int i = 16;
+        long expires = Long.MAX_VALUE;
+        if ( tokenCategory.getExpires() ) {
+            expires = ByteBuffer.wrap( bytes, i, 8 ).getLong();
+            i = 24;
+        }
+        ByteBuffer expected = ByteBuffer.allocate( 20 );
+        expected.put( sha( tokenCategory.getPrefix() + uuid + tokenSecretSalt + expires ) );
+        expected.rewind();
+        ByteBuffer signature = ByteBuffer.wrap( bytes, i, 20 );
+
+
+        if ( !signature.equals( expected ) ) {
+            throw new BadTokenException( "Invalid token signature" );
+        }
+
+
+        long expirationDelta = System.currentTimeMillis() - expires;
+
+        if ( expires != Long.MAX_VALUE && expirationDelta > 0 ) {
+            throw new ExpiredTokenException( String.format( "Token expired %d milliseconds ago.", expirationDelta ) );
+        }
+        return uuid;
+    }
+
+
+    @Override
+    public long getMaxTokenAge( String token ) {
+        TokenCategory tokenCategory = TokenCategory.getFromBase64String( token );
+        byte[] bytes = decodeBase64( token.substring( TokenCategory.BASE64_PREFIX_LENGTH ) );
+        UUID uuid = uuid( bytes );
+        long timestamp = getTimestampInMillis( uuid );
+        int i = 16;
+        if ( tokenCategory.getExpires() ) {
+            long expires = ByteBuffer.wrap( bytes, i, 8 ).getLong();
+            return expires - timestamp;
+        }
+        return Long.MAX_VALUE;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.apache.usergrid.security.tokens.TokenService#getMaxTokenAgeInSeconds(java.
+     * lang.String)
+     */
+    @Override
+    public long getMaxTokenAgeInSeconds( String token ) {
+        return getMaxTokenAge( token ) / 1000;
+    }
+
+
+    /**
+     * The maximum age a token can be saved for
+     *
+     * @return the maxPersistenceTokenAge
+     */
+    public long getMaxPersistenceTokenAge() {
+        return maxPersistenceTokenAge;
+    }
+
+    @Autowired
+    public void setEntityManagerFactory( EntityManagerFactory emf ) {
+        this.emf = emf;
+        final Injector injector = ((CpEntityManagerFactory)emf).getApplicationContext().getBean( Injector.class );
+        this.metricsFactory = injector.getInstance(MetricsFactory.class);
+        this.tokenSerialization = injector.getInstance(TokenSerialization.class);
+    }
+
+
+    private String getTokenForUUID( TokenInfo tokenInfo, TokenCategory tokenCategory, UUID uuid ) {
+        int l = 36;
+        if ( tokenCategory.getExpires() ) {
+            l += 8;
+        }
+        ByteBuffer bytes = ByteBuffer.allocate( l );
+        bytes.put( bytes( uuid ) );
+        long expires = Long.MAX_VALUE;
+        if ( tokenCategory.getExpires() ) {
+            expires = ( tokenInfo.getDuration() > 0 ) ?
+                      UUIDUtils.getTimestampInMillis( uuid ) + ( tokenInfo.getDuration() ) :
+                      UUIDUtils.getTimestampInMillis( uuid ) + getExpirationForTokenType( tokenCategory );
+            bytes.putLong( expires );
+        }
+        bytes.put( sha( tokenCategory.getPrefix() + uuid + tokenSecretSalt + expires ) );
+        return tokenCategory.getBase64Prefix() + encodeBase64URLSafeString( bytes.array() );
+    }
+
+
+    /** Calculate the column lifetime and account for long truncation to seconds */
+    private int calcTokenTime( long time ) {
+
+        long secondsDuration = time / 1000;
+
+        int ttl = ( int ) secondsDuration;
+
+        // we've had a ttl that's longer than Integer.MAX value
+        if ( ttl != secondsDuration ) {
+            // Something is up with cassandra... Setting ttl to integer.max
+            // makes the cols disappear.....
+
+            // this should be the line below once this issue is fixed.
+            // https://issues.apache.org/jira/browse/CASSANDRA-4771
+            // ttl = Integer.MAX_VALUE
+
+            // take the max value of an int, and substract the system time off
+            // (in seconds) ,then arbitrarily remove another 120 seconds for good
+            // measure.
+            // Cass calcs the expiration time as
+            // "(System.currentTimeMillis() / 1000) + timeToLive);", so we need
+            // to play nice otherwise it blows up on persist
+            ttl = Integer.MAX_VALUE - ( int ) ( System.currentTimeMillis() / 1000 ) - 120;
+        }
+        // hard cap at the max in o.a.c.db.IColumn
+        if ( ttl > MAX_TTL ) {
+            ttl = MAX_TTL;
+        }
+
+        return ttl;
+    }
+
+
+    private static final int MAX_TTL = 20 * 365 * 24 * 60 * 60;
+
+
+    //-------------------------------------------------------------------------------------------------------
+    //
+    // Central SSO implementation
+
+    public static final String CENTRAL_CONNECTION_POOL_SIZE = "usergrid.central.connection.pool.size";
+    public static final String CENTRAL_CONNECTION_TIMEOUT =   "usergrid.central.connection.timeout";
+    public static final String CENTRAL_READ_TIMEOUT =         "usergrid.central.read.timeout";
+
+
+    // names for metrics to be collected
+    private static final String SSO_TOKENS_REJECTED =         "sso.tokens_rejected";
+    private static final String SSO_TOKENS_VALIDATED =        "sso.tokens_validated";
+    private static final String SSO_CREATED_LOCAL_ADMINS =    "sso.created_local_admins";
+    private static final String SSO_PROCESSING_TIME =         "sso.processing_time";
+
+    //SSO2 implementation
+    public static final String USERGRID_EXTERNAL_SSO_ENABLED = "usergrid.external.sso.enabled";
+    public static final String USERGRID_EXTERNAL_SSO_PROVIDER =    "usergrid.external.sso.provider";
+    public static final String USERGRID_EXTERNAL_SSO_PROVIDER_URL = "usergrid.external.sso.url";
+    public static final String USERGRID_EXTERNAL_SSO_PROVIDER_USER_PROVISION_URL
+        = "usergrid.external.sso.userprovision.url";
+
+
+    private static Client jerseyClient = null;
+
+    @Autowired
+    private ApplicationCreator applicationCreator;
+
+    @Autowired
+    protected ManagementService management;
+
+    @Autowired
+    private SSOProviderFactory ssoProviderFactory;
+
+    MetricsFactory getMetricsFactory() {
+        return metricsFactory;
+    }
+
+
+    public boolean isExternalSSOProviderEnabled() {
+        return Boolean.valueOf(properties.getProperty( USERGRID_EXTERNAL_SSO_ENABLED ));
+    }
+
+    private String getExternalSSOProvider(){
+            return properties.getProperty(USERGRID_EXTERNAL_SSO_PROVIDER);
+    }
+
+    /**
+     * <p>
+     * Validates access token from other or "external" Usergrid system.
+     * Calls other system's /management/me endpoint to get the User
+     * associated with the access token. If user does not exist locally,
+     * then user and organizations will be created. If no user is returned
+     * from the other cluster, then return null.
+     * </p>
+     * <p/>
+     * <p> Part of Usergrid Central SSO feature.
+     * See <a href="https://issues.apache.org/jira/browse/USERGRID-567">USERGRID-567</a>
+     * for details about Usergrid Central SSO.
+     * </p>
+     *
+     * @param extAccessToken Access token from external Usergrid system.
+     * @param ttl            Time to live for token.
+     */
+    public TokenInfo validateExternalToken(String extAccessToken, long ttl, String provider) throws Exception {
+
+
+        ExternalSSOProvider ssoProvider = ssoProviderFactory.getProvider();
+
+        if(provider.equalsIgnoreCase("usergrid")){
+
+            UserInfo userinfo = ssoProvider.validateAndReturnUserInfo(extAccessToken,ttl);
+
+            // Store the external Usergrid access_token as if it were one of our own so we don't have to make the
+            // external HTTP validation call on subsequent requests
+            importToken( extAccessToken, TokenCategory.ACCESS, null, new AuthPrincipalInfo(
+                ADMIN_USER, userinfo.getUuid(), CpNamingUtils.MANAGEMENT_APPLICATION_ID), null, ttl );
+            return getTokenInfo( extAccessToken );
+
+        }else{
+
+            return ssoProvider.validateAndReturnTokenInfo(extAccessToken,ttl);
+        }
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/usergrid/blob/146e47d6/stack/services/src/main/resources/usergrid-services-context.xml
----------------------------------------------------------------------
diff --git a/stack/services/src/main/resources/usergrid-services-context.xml b/stack/services/src/main/resources/usergrid-services-context.xml
index 268b5ec..45f4c48 100644
--- a/stack/services/src/main/resources/usergrid-services-context.xml
+++ b/stack/services/src/main/resources/usergrid-services-context.xml
@@ -63,8 +63,7 @@
 
     <bean id="taskExecutor" class="org.springframework.core.task.SyncTaskExecutor"/>
 
-    <bean id="tokenService" class="org.apache.usergrid.security.tokens.cassandra.TokenServiceImpl">
-        <property name="cassandraService" ref="cassandraService"/>
+    <bean id="tokenService" class="org.apache.usergrid.security.tokens.impl.TokenServiceImpl">
         <property name="entityManagerFactory" ref="entityManagerFactory"/>
     </bean>
 

http://git-wip-us.apache.org/repos/asf/usergrid/blob/146e47d6/stack/services/src/test/java/org/apache/usergrid/security/tokens/TokenServiceIT.java
----------------------------------------------------------------------
diff --git a/stack/services/src/test/java/org/apache/usergrid/security/tokens/TokenServiceIT.java b/stack/services/src/test/java/org/apache/usergrid/security/tokens/TokenServiceIT.java
index c4cc10c..452ee61 100644
--- a/stack/services/src/test/java/org/apache/usergrid/security/tokens/TokenServiceIT.java
+++ b/stack/services/src/test/java/org/apache/usergrid/security/tokens/TokenServiceIT.java
@@ -38,7 +38,7 @@ import org.apache.usergrid.persistence.EntityManager;
 import org.apache.usergrid.persistence.entities.Application;
 import org.apache.usergrid.security.AuthPrincipalInfo;
 import org.apache.usergrid.security.AuthPrincipalType;
-import org.apache.usergrid.security.tokens.cassandra.TokenServiceImpl;
+import org.apache.usergrid.security.tokens.impl.TokenServiceImpl;
 import org.apache.usergrid.security.tokens.exceptions.ExpiredTokenException;
 import org.apache.usergrid.security.tokens.exceptions.InvalidTokenException;
 import org.apache.usergrid.utils.UUIDUtils;

http://git-wip-us.apache.org/repos/asf/usergrid/blob/146e47d6/stack/websocket/src/test/resources/testApplicationContext.xml
----------------------------------------------------------------------
diff --git a/stack/websocket/src/test/resources/testApplicationContext.xml b/stack/websocket/src/test/resources/testApplicationContext.xml
index d92f4fb..bbf7b93 100644
--- a/stack/websocket/src/test/resources/testApplicationContext.xml
+++ b/stack/websocket/src/test/resources/testApplicationContext.xml
@@ -130,7 +130,7 @@
 		</constructor-arg>
 	</bean>
 
-	<bean id="tokenService" class="org.apache.usergrid.security.tokens.cassandra.TokenServiceImpl"/>
+	<bean id="tokenService" class="org.apache.usergrid.security.tokens.impl.TokenServiceImpl"/>
 
 	<bean id="managementService" class="org.apache.usergrid.management.cassandra.ManagementServiceImpl" />
 


Mime
View raw message