Java tutorial
/* * 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; import io.jsonwebtoken.*; import io.jsonwebtoken.SignatureException; import io.jsonwebtoken.impl.crypto.RsaProvider; import org.apache.commons.collections4.map.HashedMap; import org.apache.commons.lang.RandomStringUtils; import org.apache.usergrid.NewOrgAppAdminRule; import org.apache.usergrid.ServiceITSetup; import org.apache.usergrid.ServiceITSetupImpl; import org.apache.usergrid.cassandra.ClearShiroSubject; import org.apache.usergrid.management.ManagementService; import org.apache.usergrid.management.UserInfo; import org.apache.usergrid.persistence.Entity; import org.apache.usergrid.persistence.EntityManager; import org.apache.usergrid.persistence.SimpleEntityRef; import org.apache.usergrid.persistence.entities.User; import org.apache.usergrid.security.sso.ApigeeSSO2Provider; import org.apache.usergrid.security.tokens.TokenInfo; import org.apache.usergrid.security.tokens.exceptions.BadTokenException; import org.junit.*; import org.mockito.Mockito; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.security.*; import java.security.spec.X509EncodedKeySpec; import java.util.*; import static org.apache.commons.codec.binary.Base64.decodeBase64; /** * Created by Dave Johnson (snoopdave@apache.org) on 10/25/16. */ public class ApigeeSSO2ProviderIT { private static final Logger logger = LoggerFactory.getLogger(ApigeeSSO2ProviderIT.class); @ClassRule public static final ServiceITSetup setup = new ServiceITSetupImpl(); @Test public void testBasicOperation() throws Exception { // create keypair KeyPair kp = RsaProvider.generateKeyPair(1024); PublicKey publicKey = kp.getPublic(); PrivateKey privateKey = kp.getPrivate(); // create provider with private key ApigeeSSO2Provider provider = new MockApigeeSSO2Provider(); provider.setManagement(setup.getMgmtSvc()); provider.setPublicKey(publicKey); // create user, claims and a token for those things User user = createUser(); long exp = System.currentTimeMillis() + 10000; Map<String, Object> claims = createClaims(user.getUsername(), user.getEmail(), exp); String token = Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.RS256, privateKey).compact(); // test that provider can validate the token, get user, return token info TokenInfo tokenInfo = provider.validateAndReturnTokenInfo(token, 86400L); Assert.assertNotNull(tokenInfo); } @Test public void testExpiredToken() throws Exception { // create keypair KeyPair kp = RsaProvider.generateKeyPair(1024); PublicKey publicKey = kp.getPublic(); PrivateKey privateKey = kp.getPrivate(); // create provider with private key ApigeeSSO2Provider provider = new MockApigeeSSO2Provider(); provider.setManagement(setup.getMgmtSvc()); provider.setPublicKey(publicKey); // create user, claims and a token for those things User user = createUser(); long exp = System.currentTimeMillis() - 1500; Map<String, Object> claims = createClaims(user.getUsername(), user.getEmail(), exp); String token = Jwts.builder().setClaims(claims).setExpiration(new Date()) .signWith(SignatureAlgorithm.RS256, privateKey).compact(); Thread.sleep(500); // wait for claims to timeout // test that token is expired try { provider.validateAndReturnTokenInfo(token, 86400L); Assert.fail("Should have failed due to expired token"); } catch (BadTokenException e) { Assert.assertTrue(e.getCause() instanceof ExpiredJwtException); } } @Test public void testMalformedToken() throws Exception { // create keypair KeyPair kp = RsaProvider.generateKeyPair(1024); PublicKey publicKey = kp.getPublic(); // create provider with private key ApigeeSSO2Provider provider = new MockApigeeSSO2Provider(); provider.setManagement(setup.getMgmtSvc()); provider.setPublicKey(publicKey); // test that token is malformed try { provider.getClaims("{;aklsjd;fkajsd;fkjasd;lfkj}"); Assert.fail("Should have failed due to malformed token"); } catch (BadTokenException e) { Assert.assertTrue(e.getCause() instanceof MalformedJwtException); } } @Test public void testNewPublicKeyFetch() throws Exception { // create old keypair KeyPair kp = RsaProvider.generateKeyPair(1024); PublicKey publicKey = kp.getPublic(); PrivateKey privateKey = kp.getPrivate(); // create new keypair KeyPair kpNew = RsaProvider.generateKeyPair(1024); PublicKey publicKeyNew = kpNew.getPublic(); PrivateKey privateKeyNew = kpNew.getPrivate(); // create mock provider with old and old key MockApigeeSSO2ProviderNewKey provider = new MockApigeeSSO2ProviderNewKey(publicKey, publicKeyNew); provider.setManagement(setup.getMgmtSvc()); // create user, claims and a token for those things. Sign with new public key User user = createUser(); long exp = System.currentTimeMillis() + 10000; Map<String, Object> claims = createClaims(user.getUsername(), user.getEmail(), exp); String token = Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.RS256, privateKeyNew).compact(); // test that provider can validate the token, get user, return token info TokenInfo tokenInfo = provider.validateAndReturnTokenInfo(token, 86400L); Assert.assertNotNull(tokenInfo); // assert that provider called for new key Assert.assertTrue(provider.isGetPublicKeyCalled()); // try it again, but this time it should fail due to freshness value provider.setPublicKey(publicKey); // set old key // test that signature exception thrown try { provider.validateAndReturnTokenInfo(token, 86400L); Assert.fail("Should have failed due to bad signature"); } catch (BadTokenException e) { Assert.assertTrue(e.getCause() instanceof SignatureException); } } @Test public void testBadSignature() throws Exception { // create old keypair KeyPair kp = RsaProvider.generateKeyPair(1024); PublicKey publicKey = kp.getPublic(); PrivateKey privateKey = kp.getPrivate(); // create new keypair KeyPair kpNew = RsaProvider.generateKeyPair(1024); PrivateKey privateKeyNew = kpNew.getPrivate(); // create mock provider with old public key ApigeeSSO2Provider provider = new MockApigeeSSO2ProviderNewKey(publicKey, publicKey); provider.setManagement(setup.getMgmtSvc()); // create user, claims and a token for those things. Sign with new public key User user = createUser(); long exp = System.currentTimeMillis() + 10000; Map<String, Object> claims = createClaims(user.getUsername(), user.getEmail(), exp); String token = Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.RS256, privateKeyNew).compact(); // test that signature exception thrown try { provider.validateAndReturnTokenInfo(token, 86400L); Assert.fail("Should have failed due to bad signature"); } catch (BadTokenException e) { Assert.assertTrue(e.getCause() instanceof SignatureException); } } private User createUser() throws Exception { String rando = RandomStringUtils.randomAlphanumeric(10); String username = "user_" + rando; String email = username + "@example.com"; Map<String, Object> properties = new HashMap<String, Object>() { { put("username", username); put("email", email); } }; EntityManager em = setup.getEmf().getEntityManager(setup.getEmf().getManagementAppId()); Entity entity = em.create("user", properties); return em.get(new SimpleEntityRef(User.ENTITY_TYPE, entity.getUuid()), User.class); } private Map<String, Object> createClaims(final String username, final String email, long exp) { return new HashedMap<String, Object>() { { put("jti", "c7df0339-3847-450b-a925-628ef237953a"); put("sub", "b6d62259-217b-4e96-8f49-e00c366e4fed"); put("scope", "size = 5"); put("client_id", "dummy1"); put("azp", "dummy2"); put("grant_type", "password"); put("user_id", "b6d62259-217b-4e96-8f49-e00c366e4fed"); put("origin", "usergrid"); put("user_name", username); put("email", email); put("rev_sig", "dfe5d0d3"); put("exp", exp); put("iat", System.currentTimeMillis()); put("iss", "https://jwt.example.com/token"); put("zid", "uaa"); put("aud", " size = 6"); } }; } } class MockApigeeSSO2Provider extends ApigeeSSO2Provider { private static final Logger logger = LoggerFactory.getLogger(MockApigeeSSO2Provider.class); @Override public PublicKey getPublicKey(String keyUrl) { return publicKey; } @Override public void setPublicKey(PublicKey publicKey) { this.publicKey = publicKey; } } class MockApigeeSSO2ProviderNewKey extends ApigeeSSO2Provider { private static final Logger logger = LoggerFactory.getLogger(MockApigeeSSO2Provider.class); private PublicKey newKey; private boolean getPublicKeyCalled = false; public MockApigeeSSO2ProviderNewKey(PublicKey oldKey, PublicKey newKey) { this.publicKey = oldKey; this.newKey = newKey; this.properties = new Properties(); } @Override public PublicKey getPublicKey(String keyUrl) { getPublicKeyCalled = true; return newKey; } public boolean isGetPublicKeyCalled() { return getPublicKeyCalled; } }