Merge pull request #233 from patriot1burke/master

refactor token creation
This commit is contained in:
Bill Burke 2014-02-21 12:13:26 -05:00
commit 15968e9783
27 changed files with 243 additions and 184 deletions

View file

@ -11,13 +11,13 @@
<form class="form-horizontal" name="realmForm" novalidate> <form class="form-horizontal" name="realmForm" novalidate>
<fieldset class="border-top"> <fieldset class="border-top">
<div class="form-group input-select"> <div class="form-group input-select">
<label class="col-sm-2 control-label" for="tokenLifespan">Token lifespan</label> <label class="col-sm-2 control-label" for="accessTokenLifespan">Access Token lifespan</label>
<div class="col-sm-10"> <div class="col-sm-10">
<div class="row"> <div class="row">
<div class="col-sm-2"> <div class="col-sm-2">
<input class="form-control" type="number" required min="1" <input class="form-control" type="number" required min="1"
max="31536000" data-ng-model="realm.tokenLifespan" max="31536000" data-ng-model="realm.accessTokenLifespan"
id="tokenLifespan" name="tokenLifespan"/> id="accessTokenLifespan" name="accessTokenLifespan"/>
</div> </div>
<div class="col-sm-2 select-kc"> <div class="col-sm-2 select-kc">
<select name="tokenLifespanUnit" data-ng-model="realm.tokenLifespanUnit" > <select name="tokenLifespanUnit" data-ng-model="realm.tokenLifespanUnit" >

View file

@ -19,6 +19,19 @@ public class AccessToken extends JsonWebToken {
@JsonProperty("verify_caller") @JsonProperty("verify_caller")
protected Boolean verifyCaller; protected Boolean verifyCaller;
public Access() {
}
public Access clone() {
Access access = new Access();
access.verifyCaller = verifyCaller;
if (roles != null) {
access.roles = new HashSet<String>();
access.roles.addAll(roles);
}
return access;
}
public Set<String> getRoles() { public Set<String> getRoles() {
return roles; return roles;
} }

View file

@ -0,0 +1,36 @@
package org.keycloak.representations;
import java.util.HashMap;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class RefreshToken extends AccessToken {
public RefreshToken() {
type("REFRESH");
}
/**
* Deep copies issuer, subject, issuedFor, realmAccess, and resourceAccess
* from AccessToken.
*
* @param token
*/
public RefreshToken(AccessToken token) {
this();
this.issuer = token.issuer;
this.subject = token.subject;
this.issuedFor = token.issuedFor;
if (token.realmAccess != null) {
realmAccess = token.realmAccess.clone();
}
if (token.resourceAccess != null) {
resourceAccess = new HashMap<String, Access>();
for (Map.Entry<String, Access> entry : token.resourceAccess.entrySet()) {
resourceAccess.put(entry.getKey(), entry.getValue().clone());
}
}
}
}

View file

@ -29,9 +29,6 @@ public class PublishedRealmRepresentation {
@JsonProperty("grants") @JsonProperty("grants")
protected String grantUrl; protected String grantUrl;
@JsonProperty("identity-grants")
protected String identityGrantUrl;
@JsonProperty("admin-role") @JsonProperty("admin-role")
protected String adminRole; protected String adminRole;
@ -124,12 +121,4 @@ public class PublishedRealmRepresentation {
public void setGrantUrl(String grantUrl) { public void setGrantUrl(String grantUrl) {
this.grantUrl = grantUrl; this.grantUrl = grantUrl;
} }
public String getIdentityGrantUrl() {
return identityGrantUrl;
}
public void setIdentityGrantUrl(String identityGrantUrl) {
this.identityGrantUrl = identityGrantUrl;
}
} }

View file

@ -13,7 +13,7 @@ public class RealmRepresentation {
protected String self; // link protected String self; // link
protected String id; protected String id;
protected String realm; protected String realm;
protected Integer tokenLifespan; protected Integer accessTokenLifespan;
protected Integer accessCodeLifespan; protected Integer accessCodeLifespan;
protected Integer accessCodeLifespanUserAction; protected Integer accessCodeLifespanUserAction;
protected Boolean enabled; protected Boolean enabled;
@ -114,12 +114,12 @@ public class RealmRepresentation {
this.sslNotRequired = sslNotRequired; this.sslNotRequired = sslNotRequired;
} }
public Integer getTokenLifespan() { public Integer getAccessTokenLifespan() {
return tokenLifespan; return accessTokenLifespan;
} }
public void setTokenLifespan(Integer tokenLifespan) { public void setAccessTokenLifespan(Integer accessTokenLifespan) {
this.tokenLifespan = tokenLifespan; this.accessTokenLifespan = accessTokenLifespan;
} }
public List<UserRoleMappingRepresentation> getRoleMappings() { public List<UserRoleMappingRepresentation> getRoleMappings() {

View file

@ -1,7 +1,7 @@
{ {
"realm": "demo", "realm": "demo",
"enabled": true, "enabled": true,
"tokenLifespan": 3000, "accessTokenLifespan": 3000,
"accessCodeLifespan": 10, "accessCodeLifespan": 10,
"accessCodeLifespanUserAction": 6000, "accessCodeLifespanUserAction": 6000,
"sslNotRequired": true, "sslNotRequired": true,

View file

@ -38,9 +38,13 @@ public interface RealmModel extends RoleContainerModel, RoleMapperModel, ScopeMa
void setResetPasswordAllowed(boolean resetPasswordAllowed); void setResetPasswordAllowed(boolean resetPasswordAllowed);
int getTokenLifespan(); int getAccessTokenLifespan();
void setTokenLifespan(int tokenLifespan); void setAccessTokenLifespan(int tokenLifespan);
int getRefreshTokenLifespan();
void setRefreshTokenLifespan(int tokenLifespan);
int getAccessCodeLifespan(); int getAccessCodeLifespan();

View file

@ -128,13 +128,24 @@ public class RealmAdapter implements RealmModel {
} }
@Override @Override
public int getTokenLifespan() { public int getAccessTokenLifespan() {
return realm.getTokenLifespan(); return realm.getAccessTokenLifespan();
} }
@Override @Override
public void setTokenLifespan(int tokenLifespan) { public void setAccessTokenLifespan(int tokenLifespan) {
realm.setTokenLifespan(tokenLifespan); realm.setAccessTokenLifespan(tokenLifespan);
em.flush();
}
@Override
public int getRefreshTokenLifespan() {
return realm.getRefreshTokenLifespan();
}
@Override
public void setRefreshTokenLifespan(int tokenLifespan) {
realm.setRefreshTokenLifespan(tokenLifespan);
em.flush(); em.flush();
} }

View file

@ -43,9 +43,10 @@ public class RealmEntity {
protected boolean updateProfileOnInitialSocialLogin; protected boolean updateProfileOnInitialSocialLogin;
protected String passwordPolicy; protected String passwordPolicy;
protected int tokenLifespan; protected int accessTokenLifespan;
protected int accessCodeLifespan; protected int accessCodeLifespan;
protected int accessCodeLifespanUserAction; protected int accessCodeLifespanUserAction;
protected int refreshTokenLifespan;
@Column(length = 2048) @Column(length = 2048)
protected String publicKeyPem; protected String publicKeyPem;
@ -161,12 +162,20 @@ public class RealmEntity {
this.updateProfileOnInitialSocialLogin = updateProfileOnInitialSocialLogin; this.updateProfileOnInitialSocialLogin = updateProfileOnInitialSocialLogin;
} }
public int getTokenLifespan() { public int getRefreshTokenLifespan() {
return tokenLifespan; return refreshTokenLifespan;
} }
public void setTokenLifespan(int tokenLifespan) { public void setRefreshTokenLifespan(int refreshTokenLifespan) {
this.tokenLifespan = tokenLifespan; this.refreshTokenLifespan = refreshTokenLifespan;
}
public int getAccessTokenLifespan() {
return accessTokenLifespan;
}
public void setAccessTokenLifespan(int accessTokenLifespan) {
this.accessTokenLifespan = accessTokenLifespan;
} }
public int getAccessCodeLifespan() { public int getAccessCodeLifespan() {

View file

@ -167,13 +167,24 @@ public class RealmAdapter extends AbstractAdapter implements RealmModel {
} }
@Override @Override
public int getTokenLifespan() { public int getAccessTokenLifespan() {
return realm.getTokenLifespan(); return realm.getAccessTokenLifespan();
} }
@Override @Override
public void setTokenLifespan(int tokenLifespan) { public void setAccessTokenLifespan(int tokenLifespan) {
realm.setTokenLifespan(tokenLifespan); realm.setAccessTokenLifespan(tokenLifespan);
updateRealm();
}
@Override
public int getRefreshTokenLifespan() {
return realm.getRefreshTokenLifespan();
}
@Override
public void setRefreshTokenLifespan(int tokenLifespan) {
realm.setRefreshTokenLifespan(tokenLifespan);
updateRealm(); updateRealm();
} }

View file

@ -29,9 +29,10 @@ public class RealmEntity extends AbstractMongoIdentifiableEntity implements Mong
private boolean updateProfileOnInitialSocialLogin; private boolean updateProfileOnInitialSocialLogin;
private String passwordPolicy; private String passwordPolicy;
private int tokenLifespan; private int accessTokenLifespan;
private int accessCodeLifespan; private int accessCodeLifespan;
private int accessCodeLifespanUserAction; private int accessCodeLifespanUserAction;
private int refreshTokenLifespan;
private String publicKeyPem; private String publicKeyPem;
private String privateKeyPem; private String privateKeyPem;
@ -131,12 +132,21 @@ public class RealmEntity extends AbstractMongoIdentifiableEntity implements Mong
} }
@MongoField @MongoField
public int getTokenLifespan() { public int getAccessTokenLifespan() {
return tokenLifespan; return accessTokenLifespan;
} }
public void setTokenLifespan(int tokenLifespan) { public void setAccessTokenLifespan(int accessTokenLifespan) {
this.tokenLifespan = tokenLifespan; this.accessTokenLifespan = accessTokenLifespan;
}
@MongoField
public int getRefreshTokenLifespan() {
return refreshTokenLifespan;
}
public void setRefreshTokenLifespan(int refreshTokenLifespan) {
this.refreshTokenLifespan = refreshTokenLifespan;
} }
@MongoField @MongoField

View file

@ -47,7 +47,7 @@ public class AdapterTest extends AbstractModelTest {
realmModel.setName("JUGGLER"); realmModel.setName("JUGGLER");
realmModel.setPrivateKeyPem("0234234"); realmModel.setPrivateKeyPem("0234234");
realmModel.setPublicKeyPem("0234234"); realmModel.setPublicKeyPem("0234234");
realmModel.setTokenLifespan(1000); realmModel.setAccessTokenLifespan(1000);
realmModel.setUpdateProfileOnInitialSocialLogin(true); realmModel.setUpdateProfileOnInitialSocialLogin(true);
realmModel.addDefaultRole("foo"); realmModel.addDefaultRole("foo");
@ -56,7 +56,7 @@ public class AdapterTest extends AbstractModelTest {
Assert.assertNotNull(realmModel); Assert.assertNotNull(realmModel);
Assert.assertEquals(realmModel.getAccessCodeLifespan(), 100); Assert.assertEquals(realmModel.getAccessCodeLifespan(), 100);
Assert.assertEquals(600, realmModel.getAccessCodeLifespanUserAction()); Assert.assertEquals(600, realmModel.getAccessCodeLifespanUserAction());
Assert.assertEquals(realmModel.getTokenLifespan(), 1000); Assert.assertEquals(realmModel.getAccessTokenLifespan(), 1000);
Assert.assertEquals(realmModel.isEnabled(), true); Assert.assertEquals(realmModel.isEnabled(), true);
Assert.assertEquals(realmModel.getName(), "JUGGLER"); Assert.assertEquals(realmModel.getName(), "JUGGLER");
Assert.assertEquals(realmModel.getPrivateKeyPem(), "0234234"); Assert.assertEquals(realmModel.getPrivateKeyPem(), "0234234");
@ -75,7 +75,7 @@ public class AdapterTest extends AbstractModelTest {
realmModel.setName("JUGGLER"); realmModel.setName("JUGGLER");
realmModel.setPrivateKeyPem("0234234"); realmModel.setPrivateKeyPem("0234234");
realmModel.setPublicKeyPem("0234234"); realmModel.setPublicKeyPem("0234234");
realmModel.setTokenLifespan(1000); realmModel.setAccessTokenLifespan(1000);
realmModel.setUpdateProfileOnInitialSocialLogin(true); realmModel.setUpdateProfileOnInitialSocialLogin(true);
realmModel.addDefaultRole("foo"); realmModel.addDefaultRole("foo");
@ -84,7 +84,7 @@ public class AdapterTest extends AbstractModelTest {
Assert.assertNotNull(realmModel); Assert.assertNotNull(realmModel);
Assert.assertEquals(realmModel.getAccessCodeLifespan(), 100); Assert.assertEquals(realmModel.getAccessCodeLifespan(), 100);
Assert.assertEquals(600, realmModel.getAccessCodeLifespanUserAction()); Assert.assertEquals(600, realmModel.getAccessCodeLifespanUserAction());
Assert.assertEquals(realmModel.getTokenLifespan(), 1000); Assert.assertEquals(realmModel.getAccessTokenLifespan(), 1000);
Assert.assertEquals(realmModel.isEnabled(), true); Assert.assertEquals(realmModel.isEnabled(), true);
Assert.assertEquals(realmModel.getName(), "JUGGLER"); Assert.assertEquals(realmModel.getName(), "JUGGLER");
Assert.assertEquals(realmModel.getPrivateKeyPem(), "0234234"); Assert.assertEquals(realmModel.getPrivateKeyPem(), "0234234");

View file

@ -127,7 +127,7 @@ public class AuthenticationManagerTest extends AbstractModelTest {
realm.setName("Test"); realm.setName("Test");
realm.setPrivateKeyPem("0234234"); realm.setPrivateKeyPem("0234234");
realm.setPublicKeyPem("0234234"); realm.setPublicKeyPem("0234234");
realm.setTokenLifespan(1000); realm.setAccessTokenLifespan(1000);
realm.addRequiredCredential(CredentialRepresentation.PASSWORD); realm.addRequiredCredential(CredentialRepresentation.PASSWORD);
am = new AuthenticationManager(); am = new AuthenticationManager();

View file

@ -22,7 +22,7 @@ public class ModelTest extends AbstractModelTest {
realm.setSocial(true); realm.setSocial(true);
realm.setSslNotRequired(true); realm.setSslNotRequired(true);
realm.setVerifyEmail(true); realm.setVerifyEmail(true);
realm.setTokenLifespan(1000); realm.setAccessTokenLifespan(1000);
realm.setPasswordPolicy(new PasswordPolicy("length")); realm.setPasswordPolicy(new PasswordPolicy("length"));
realm.setAccessCodeLifespan(1001); realm.setAccessCodeLifespan(1001);
realm.setAccessCodeLifespanUserAction(1002); realm.setAccessCodeLifespanUserAction(1002);
@ -55,7 +55,7 @@ public class ModelTest extends AbstractModelTest {
Assert.assertEquals(expected.isSocial(), actual.isSocial()); Assert.assertEquals(expected.isSocial(), actual.isSocial());
Assert.assertEquals(expected.isSslNotRequired(), actual.isSslNotRequired()); Assert.assertEquals(expected.isSslNotRequired(), actual.isSslNotRequired());
Assert.assertEquals(expected.isVerifyEmail(), actual.isVerifyEmail()); Assert.assertEquals(expected.isVerifyEmail(), actual.isVerifyEmail());
Assert.assertEquals(expected.getTokenLifespan(), actual.getTokenLifespan()); Assert.assertEquals(expected.getAccessTokenLifespan(), actual.getAccessTokenLifespan());
Assert.assertEquals(expected.getAccessCodeLifespan(), actual.getAccessCodeLifespan()); Assert.assertEquals(expected.getAccessCodeLifespan(), actual.getAccessCodeLifespan());
Assert.assertEquals(expected.getAccessCodeLifespanUserAction(), actual.getAccessCodeLifespanUserAction()); Assert.assertEquals(expected.getAccessCodeLifespanUserAction(), actual.getAccessCodeLifespanUserAction());

View file

@ -2,7 +2,7 @@
"id": "Test", "id": "Test",
"realm": "Test", "realm": "Test",
"enabled": true, "enabled": true,
"tokenLifespan": 600, "accessTokenLifespan": 600,
"accessCodeLifespan": 600, "accessCodeLifespan": 600,
"accessCodeLifespanUserAction": 600, "accessCodeLifespanUserAction": 600,
"sslNotRequired": true, "sslNotRequired": true,

View file

@ -1,7 +1,7 @@
{ {
"realm": "demo", "realm": "demo",
"enabled": true, "enabled": true,
"tokenLifespan": 300, "accessTokenLifespan": 300,
"accessCodeLifespan": 10, "accessCodeLifespan": 10,
"accessCodeLifespanUserAction": 600, "accessCodeLifespanUserAction": 600,
"sslNotRequired": true, "sslNotRequired": true,

View file

@ -1,7 +1,7 @@
{ {
"realm": "test-realm", "realm": "test-realm",
"enabled": true, "enabled": true,
"tokenLifespan": 6000, "accessTokenLifespan": 6000,
"accessCodeLifespan": 30, "accessCodeLifespan": 30,
"accessCodeLifespanUserAction": 600, "accessCodeLifespanUserAction": 600,
"requiredCredentials": [ "password" ], "requiredCredentials": [ "password" ],

View file

@ -11,8 +11,6 @@ import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.representations.idm.CredentialRepresentation;
import java.util.UUID;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
@ -48,7 +46,7 @@ public class ApplianceBootstrap {
realm.addRequiredCredential(CredentialRepresentation.PASSWORD); realm.addRequiredCredential(CredentialRepresentation.PASSWORD);
realm.addRequiredOAuthClientCredential(CredentialRepresentation.PASSWORD); realm.addRequiredOAuthClientCredential(CredentialRepresentation.PASSWORD);
realm.addRequiredResourceCredential(CredentialRepresentation.PASSWORD); realm.addRequiredResourceCredential(CredentialRepresentation.PASSWORD);
realm.setTokenLifespan(300); realm.setAccessTokenLifespan(300);
realm.setAccessCodeLifespan(60); realm.setAccessCodeLifespan(60);
realm.setAccessCodeLifespanUserAction(300); realm.setAccessCodeLifespanUserAction(300);
realm.setSslNotRequired(true); realm.setSslNotRequired(true);

View file

@ -46,8 +46,8 @@ public class AuthenticationManager {
token.issuedNow(); token.issuedNow();
token.subject(user.getId()); token.subject(user.getId());
token.audience(realm.getName()); token.audience(realm.getName());
if (realm.getTokenLifespan() > 0) { if (realm.getAccessTokenLifespan() > 0) {
token.expiration((System.currentTimeMillis() / 1000) + realm.getTokenLifespan()); token.expiration((System.currentTimeMillis() / 1000) + realm.getAccessTokenLifespan());
} }
return token; return token;
} }

View file

@ -71,7 +71,7 @@ public class ModelToRepresentation {
rep.setRegistrationAllowed(realm.isRegistrationAllowed()); rep.setRegistrationAllowed(realm.isRegistrationAllowed());
rep.setVerifyEmail(realm.isVerifyEmail()); rep.setVerifyEmail(realm.isVerifyEmail());
rep.setResetPasswordAllowed(realm.isResetPasswordAllowed()); rep.setResetPasswordAllowed(realm.isResetPasswordAllowed());
rep.setTokenLifespan(realm.getTokenLifespan()); rep.setAccessTokenLifespan(realm.getAccessTokenLifespan());
rep.setAccessCodeLifespan(realm.getAccessCodeLifespan()); rep.setAccessCodeLifespan(realm.getAccessCodeLifespan());
rep.setAccessCodeLifespanUserAction(realm.getAccessCodeLifespanUserAction()); rep.setAccessCodeLifespanUserAction(realm.getAccessCodeLifespanUserAction());
rep.setSmtpServer(realm.getSmtpConfig()); rep.setSmtpServer(realm.getSmtpConfig());

View file

@ -7,7 +7,6 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.OAuthClientModel; import org.keycloak.models.OAuthClientModel;
import org.keycloak.models.PasswordPolicy; import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredCredentialModel;
import org.keycloak.models.RoleModel; import org.keycloak.models.RoleModel;
import org.keycloak.models.SocialLinkModel; import org.keycloak.models.SocialLinkModel;
import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserCredentialModel;
@ -28,14 +27,10 @@ import org.keycloak.representations.idm.UserRoleMappingRepresentation;
import java.security.KeyPair; import java.security.KeyPair;
import java.security.KeyPairGenerator; import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
/** /**
* Per request object * Per request object
@ -110,7 +105,7 @@ public class RealmManager {
if (rep.getAccessCodeLifespan() != null) realm.setAccessCodeLifespan(rep.getAccessCodeLifespan()); if (rep.getAccessCodeLifespan() != null) realm.setAccessCodeLifespan(rep.getAccessCodeLifespan());
if (rep.getAccessCodeLifespanUserAction() != null) if (rep.getAccessCodeLifespanUserAction() != null)
realm.setAccessCodeLifespanUserAction(rep.getAccessCodeLifespanUserAction()); realm.setAccessCodeLifespanUserAction(rep.getAccessCodeLifespanUserAction());
if (rep.getTokenLifespan() != null) realm.setTokenLifespan(rep.getTokenLifespan()); if (rep.getAccessTokenLifespan() != null) realm.setAccessTokenLifespan(rep.getAccessTokenLifespan());
if (rep.getRequiredCredentials() != null) { if (rep.getRequiredCredentials() != null) {
realm.updateRequiredCredentials(rep.getRequiredCredentials()); realm.updateRequiredCredentials(rep.getRequiredCredentials());
} }
@ -163,8 +158,8 @@ public class RealmManager {
if (rep.isEnabled() != null) newRealm.setEnabled(rep.isEnabled()); if (rep.isEnabled() != null) newRealm.setEnabled(rep.isEnabled());
if (rep.isSocial() != null) newRealm.setSocial(rep.isSocial()); if (rep.isSocial() != null) newRealm.setSocial(rep.isSocial());
if (rep.getTokenLifespan() != null) newRealm.setTokenLifespan(rep.getTokenLifespan()); if (rep.getAccessTokenLifespan() != null) newRealm.setAccessTokenLifespan(rep.getAccessTokenLifespan());
else newRealm.setTokenLifespan(300); else newRealm.setAccessTokenLifespan(300);
if (rep.getAccessCodeLifespan() != null) newRealm.setAccessCodeLifespan(rep.getAccessCodeLifespan()); if (rep.getAccessCodeLifespan() != null) newRealm.setAccessCodeLifespan(rep.getAccessCodeLifespan());
else newRealm.setAccessCodeLifespan(60); else newRealm.setAccessCodeLifespan(60);

View file

@ -12,10 +12,12 @@ import org.keycloak.representations.AccessToken;
import org.keycloak.util.Base64Url; import org.keycloak.util.Base64Url;
import org.keycloak.util.JsonSerialization; import org.keycloak.util.JsonSerialization;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.MultivaluedMap;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -78,12 +80,44 @@ public class TokenManager {
public AccessCodeEntry createAccessCode(String scopeParam, String state, String redirect, RealmModel realm, UserModel client, UserModel user) { public AccessCodeEntry createAccessCode(String scopeParam, String state, String redirect, RealmModel realm, UserModel client, UserModel user) {
AccessCodeEntry code = createAccessCodeEntry(scopeParam, state, redirect, realm, client, user);
accessCodeMap.put(code.getId(), code);
return code;
}
private AccessCodeEntry createAccessCodeEntry(String scopeParam, String state, String redirect, RealmModel realm, UserModel client, UserModel user) {
AccessCodeEntry code = new AccessCodeEntry(); AccessCodeEntry code = new AccessCodeEntry();
AccessScope scopeMap = null;
if (scopeParam != null) scopeMap = decodeScope(scopeParam);
List<RoleModel> realmRolesRequested = code.getRealmRolesRequested(); List<RoleModel> realmRolesRequested = code.getRealmRolesRequested();
MultivaluedMap<String, RoleModel> resourceRolesRequested = code.getResourceRolesRequested(); MultivaluedMap<String, RoleModel> resourceRolesRequested = code.getResourceRolesRequested();
AccessToken token = createClientAccessToken(scopeParam, realm, client, user, realmRolesRequested, resourceRolesRequested);
code.setToken(token);
code.setRealm(realm);
code.setExpiration((System.currentTimeMillis() / 1000) + realm.getAccessCodeLifespan());
code.setClient(client);
code.setUser(user);
code.setState(state);
code.setRedirectUri(redirect);
String accessCode = null;
try {
accessCode = new JWSBuilder().content(code.getId().getBytes("UTF-8")).rsa256(realm.getPrivateKey());
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
code.setCode(accessCode);
return code;
}
public AccessToken createClientAccessToken(String scopeParam, RealmModel realm, UserModel client, UserModel user) {
return createClientAccessToken(scopeParam, realm, client, user, new LinkedList<RoleModel>(), new MultivaluedHashMap<String, RoleModel>());
}
public AccessToken createClientAccessToken(String scopeParam, RealmModel realm, UserModel client, UserModel user, List<RoleModel> realmRolesRequested, MultivaluedMap<String, RoleModel> resourceRolesRequested) {
AccessScope scopeMap = null;
if (scopeParam != null) scopeMap = decodeScope(scopeParam);
Set<RoleModel> roleMappings = realm.getRoleMappings(user); Set<RoleModel> roleMappings = realm.getRoleMappings(user);
Set<RoleModel> scopeMappings = realm.getScopeMappings(client); Set<RoleModel> scopeMappings = realm.getScopeMappings(client);
@ -113,22 +147,22 @@ public class TokenManager {
} }
} }
createToken(code, realm, client, user); AccessToken token = initToken(realm, client, user);
code.setRealm(realm);
code.setExpiration((System.currentTimeMillis() / 1000) + realm.getAccessCodeLifespan()); if (realmRolesRequested.size() > 0) {
code.setClient(client); for (RoleModel role : realmRolesRequested) {
code.setUser(user); addComposites(token, role);
code.setState(state); }
code.setRedirectUri(redirect);
accessCodeMap.put(code.getId(), code);
String accessCode = null;
try {
accessCode = new JWSBuilder().content(code.getId().getBytes("UTF-8")).rsa256(realm.getPrivateKey());
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
} }
code.setCode(accessCode);
return code; if (resourceRolesRequested.size() > 0) {
for (List<RoleModel> roles : resourceRolesRequested.values()) {
for (RoleModel role : roles) {
addComposites(token, role);
}
}
}
return token;
} }
protected AccessToken initToken(RealmModel realm, UserModel client, UserModel user) { protected AccessToken initToken(RealmModel realm, UserModel client, UserModel user) {
@ -138,8 +172,8 @@ public class TokenManager {
token.audience(realm.getName()); token.audience(realm.getName());
token.issuedNow(); token.issuedNow();
token.issuedFor(client.getLoginName()); token.issuedFor(client.getLoginName());
if (realm.getTokenLifespan() > 0) { if (realm.getAccessTokenLifespan() > 0) {
token.expiration((System.currentTimeMillis() / 1000) + realm.getTokenLifespan()); token.expiration((System.currentTimeMillis() / 1000) + realm.getAccessTokenLifespan());
} }
Set<String> allowedOrigins = client.getWebOrigins(); Set<String> allowedOrigins = client.getWebOrigins();
if (allowedOrigins != null) { if (allowedOrigins != null) {
@ -176,26 +210,6 @@ public class TokenManager {
} }
protected void createToken(AccessCodeEntry accessCodeEntry, RealmModel realm, UserModel client, UserModel user) {
AccessToken token = initToken(realm, client, user);
if (accessCodeEntry.getRealmRolesRequested().size() > 0) {
for (RoleModel role : accessCodeEntry.getRealmRolesRequested()) {
addComposites(token, role);
}
}
if (accessCodeEntry.getResourceRolesRequested().size() > 0) {
for (List<RoleModel> roles : accessCodeEntry.getResourceRolesRequested().values()) {
for (RoleModel role : roles) {
addComposites(token, role);
}
}
}
accessCodeEntry.setToken(token);
}
public String encodeScope(AccessScope scope) { public String encodeScope(AccessScope scope) {
String token = null; String token = null;
try { try {
@ -224,8 +238,8 @@ public class TokenManager {
token.issuedNow(); token.issuedNow();
token.subject(user.getId()); token.subject(user.getId());
token.audience(realm.getName()); token.audience(realm.getName());
if (realm.getTokenLifespan() > 0) { if (realm.getAccessTokenLifespan() > 0) {
token.expiration((System.currentTimeMillis() / 1000) + realm.getTokenLifespan()); token.expiration((System.currentTimeMillis() / 1000) + realm.getAccessTokenLifespan());
} }
for (RoleModel role : realm.getRoleMappings(user)) { for (RoleModel role : realm.getRoleMappings(user)) {
addComposites(token, role); addComposites(token, role);

View file

@ -53,13 +53,11 @@ public class PublicRealmResource {
String authUri = TokenService.loginPageUrl(uriInfo).build(realm.getName()).toString(); String authUri = TokenService.loginPageUrl(uriInfo).build(realm.getName()).toString();
String codeUri = TokenService.accessCodeToTokenUrl(uriInfo).build(realm.getName()).toString(); String codeUri = TokenService.accessCodeToTokenUrl(uriInfo).build(realm.getName()).toString();
String grantUrl = TokenService.grantAccessTokenUrl(uriInfo).build(realm.getName()).toString(); String grantUrl = TokenService.grantAccessTokenUrl(uriInfo).build(realm.getName()).toString();
String idGrantUrl = TokenService.grantIdentityTokenUrl(uriInfo).build(realm.getName()).toString();
html.append("<html><body><h1>Realm: ").append(realm.getName()).append("</h1>"); html.append("<html><body><h1>Realm: ").append(realm.getName()).append("</h1>");
html.append("<p>auth: ").append(authUri).append("</p>"); html.append("<p>auth: ").append(authUri).append("</p>");
html.append("<p>code: ").append(codeUri).append("</p>"); html.append("<p>code: ").append(codeUri).append("</p>");
html.append("<p>grant: ").append(grantUrl).append("</p>"); html.append("<p>grant: ").append(grantUrl).append("</p>");
html.append("<p>identity grant: ").append(idGrantUrl).append("</p>");
html.append("<p>public key: ").append(realm.getPublicKeyPem()).append("</p>"); html.append("<p>public key: ").append(realm.getPublicKeyPem()).append("</p>");
html.append("</body></html>"); html.append("</body></html>");
@ -77,8 +75,6 @@ public class PublicRealmResource {
rep.setAuthorizationUrl(TokenService.loginPageUrl(uriInfo).build(realm.getName()).toString()); rep.setAuthorizationUrl(TokenService.loginPageUrl(uriInfo).build(realm.getName()).toString());
rep.setCodeUrl(TokenService.accessCodeToTokenUrl(uriInfo).build(realm.getName()).toString()); rep.setCodeUrl(TokenService.accessCodeToTokenUrl(uriInfo).build(realm.getName()).toString());
rep.setGrantUrl(TokenService.grantAccessTokenUrl(uriInfo).build(realm.getName()).toString()); rep.setGrantUrl(TokenService.grantAccessTokenUrl(uriInfo).build(realm.getName()).toString());
String idGrantUrl = TokenService.grantIdentityTokenUrl(uriInfo).build(realm.getName()).toString();
rep.setIdentityGrantUrl(idGrantUrl);
return rep; return rep;
} }

View file

@ -29,6 +29,7 @@ import org.keycloak.services.resources.flows.OAuthFlows;
import org.keycloak.services.validation.Validation; import org.keycloak.services.validation.Validation;
import org.keycloak.util.BasicAuthHelper; import org.keycloak.util.BasicAuthHelper;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam; import javax.ws.rs.HeaderParam;
@ -108,11 +109,6 @@ public class TokenService {
} }
public static UriBuilder grantIdentityTokenUrl(UriInfo uriInfo) {
return tokenServiceBaseUrl(uriInfo).path(TokenService.class, "grantIdentityToken");
}
public static UriBuilder loginPageUrl(UriInfo uriInfo) { public static UriBuilder loginPageUrl(UriInfo uriInfo) {
return tokenServiceBaseUrl(uriInfo).path(TokenService.class, "loginPage"); return tokenServiceBaseUrl(uriInfo).path(TokenService.class, "loginPage");
} }
@ -129,45 +125,19 @@ public class TokenService {
return tokenServiceBaseUrl(uriInfo).path(TokenService.class, "processOAuth"); return tokenServiceBaseUrl(uriInfo).path(TokenService.class, "processOAuth");
} }
@Path("grants/identity-token")
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.APPLICATION_JSON)
public Response grantIdentityToken(final MultivaluedMap<String, String> form) {
if (!checkSsl()) {
throw new NotAcceptableException("HTTPS required");
}
String username = form.getFirst(AuthenticationManager.FORM_USERNAME);
if (username == null) {
throw new NotAuthorizedException("No user");
}
if (!realm.isEnabled()) {
throw new NotAuthorizedException("Disabled realm");
}
UserModel user = realm.getUser(username);
AuthenticationStatus status = authManager.authenticateForm(realm, user, form);
if (status != AuthenticationStatus.SUCCESS) {
throw new NotAuthorizedException(status);
}
tokenManager = new TokenManager();
AccessToken token = authManager.createIdentityToken(realm, user);
String encoded = tokenManager.encodeToken(realm, token);
AccessTokenResponse res = accessTokenResponse(token, encoded);
return Response.ok(res, MediaType.APPLICATION_JSON_TYPE).build();
}
@Path("grants/access") @Path("grants/access")
@POST @POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Response grantAccessToken(final MultivaluedMap<String, String> form) { public Response grantAccessToken(final @HeaderParam(HttpHeaders.AUTHORIZATION) String authorizationHeader,
final MultivaluedMap<String, String> form) {
if (!checkSsl()) { if (!checkSsl()) {
throw new NotAcceptableException("HTTPS required"); throw new NotAcceptableException("HTTPS required");
} }
UserModel client = authorizeClient(authorizationHeader);
String username = form.getFirst(AuthenticationManager.FORM_USERNAME); String username = form.getFirst(AuthenticationManager.FORM_USERNAME);
if (username == null) { if (username == null) {
throw new NotAuthorizedException("No user"); throw new NotAuthorizedException("No user");
@ -185,7 +155,8 @@ public class TokenService {
if (authManager.authenticateForm(realm, user, form) != AuthenticationStatus.SUCCESS) { if (authManager.authenticateForm(realm, user, form) != AuthenticationStatus.SUCCESS) {
throw new NotAuthorizedException("Auth failed"); throw new NotAuthorizedException("Auth failed");
} }
AccessToken token = tokenManager.createAccessToken(realm, user); String scope = form.getFirst("scope");
AccessToken token = tokenManager.createClientAccessToken(scope, realm, client, user);
String encoded = tokenManager.encodeToken(realm, token); String encoded = tokenManager.encodeToken(realm, token);
AccessTokenResponse res = accessTokenResponse(token, encoded); AccessTokenResponse res = accessTokenResponse(token, encoded);
return Response.ok(res, MediaType.APPLICATION_JSON_TYPE).build(); return Response.ok(res, MediaType.APPLICATION_JSON_TYPE).build();
@ -341,47 +312,14 @@ public class TokenService {
throw new NotAuthorizedException("Realm not enabled"); throw new NotAuthorizedException("Realm not enabled");
} }
if (authorizationHeader == null) { UserModel client = authorizeClient(authorizationHeader);
throw new NotAuthorizedException("No Authorization header to authenticate client", "Basic realm=\"" + realm.getName() + "\"");
}
String[] usernameSecret = BasicAuthHelper.parseHeader(authorizationHeader);
if (usernameSecret == null) {
throw new NotAuthorizedException("No Authorization header to authenticate client", "Basic realm=\"" + realm.getName() + "\"");
}
String client_id = usernameSecret[0];
String clientSecret = usernameSecret[1];
UserModel client = realm.getUser(client_id);
if (client == null) {
logger.debug("Could not find user");
Map<String, String> error = new HashMap<String, String>();
error.put("error", "invalid_client");
error.put("error_description", "Could not find user");
return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build();
}
if (!client.isEnabled()) {
logger.debug("user is not enabled");
Map<String, String> error = new HashMap<String, String>();
error.put("error", "invalid_client");
error.put("error_description", "User is not enabled");
return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build();
}
if (!realm.validateSecret(client, clientSecret)) {
Map<String, String> error = new HashMap<String, String>();
error.put("error", "unauthorized_client");
return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build();
}
String code = formData.getFirst("code"); String code = formData.getFirst("code");
if (code == null) { if (code == null) {
logger.debug("code not specified");
Map<String, String> error = new HashMap<String, String>(); Map<String, String> error = new HashMap<String, String>();
error.put("error", "invalid_request"); error.put("error", "invalid_request");
error.put("error_description", "code not specified"); error.put("error_description", "code not specified");
return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build(); throw new BadRequestException("Code not specified", Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build());
} }
@ -435,6 +373,41 @@ public class TokenService {
return Cors.add(request, Response.ok(res)).allowedOrigins(client).allowedMethods("POST").build(); return Cors.add(request, Response.ok(res)).allowedOrigins(client).allowedMethods("POST").build();
} }
protected UserModel authorizeClient(String authorizationHeader) {
if (authorizationHeader == null) {
throw new NotAuthorizedException("No Authorization header to authenticate client", "Basic realm=\"" + realm.getName() + "\"");
}
String[] usernameSecret = BasicAuthHelper.parseHeader(authorizationHeader);
if (usernameSecret == null) {
throw new NotAuthorizedException("No Authorization header to authenticate client", "Basic realm=\"" + realm.getName() + "\"");
}
String client_id = usernameSecret[0];
String clientSecret = usernameSecret[1];
UserModel client = realm.getUser(client_id);
if (client == null) {
Map<String, String> error = new HashMap<String, String>();
error.put("error", "invalid_client");
error.put("error_description", "Could not find client");
throw new BadRequestException("Could not find client", Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build());
}
if (!client.isEnabled()) {
Map<String, String> error = new HashMap<String, String>();
error.put("error", "invalid_client");
error.put("error_description", "Client is not enabled");
throw new BadRequestException("Client is not enabled", Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build());
}
if (!realm.validateSecret(client, clientSecret)) {
Map<String, String> error = new HashMap<String, String>();
error.put("error", "unauthorized_client");
throw new BadRequestException("Unauthorized Client", Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build());
}
return client;
}
protected AccessTokenResponse accessTokenResponse(PrivateKey privateKey, AccessToken token) { protected AccessTokenResponse accessTokenResponse(PrivateKey privateKey, AccessToken token) {
String encodedToken = new JWSBuilder().jsonContent(token).rsa256(privateKey); String encodedToken = new JWSBuilder().jsonContent(token).rsa256(privateKey);

View file

@ -57,7 +57,7 @@ public class CompositeRoleTest {
RealmModel realm = manager.createRealm("Test"); RealmModel realm = manager.createRealm("Test");
manager.generateRealmKeys(realm); manager.generateRealmKeys(realm);
realmPublicKey = realm.getPublicKey(); realmPublicKey = realm.getPublicKey();
realm.setTokenLifespan(10000); realm.setAccessTokenLifespan(10000);
realm.setAccessCodeLifespanUserAction(1000); realm.setAccessCodeLifespanUserAction(1000);
realm.setAccessCodeLifespan(1000); realm.setAccessCodeLifespan(1000);
realm.setSslNotRequired(true); realm.setSslNotRequired(true);

View file

@ -2,7 +2,7 @@
"id": "Test", "id": "Test",
"realm": "Test", "realm": "Test",
"enabled": true, "enabled": true,
"tokenLifespan": 600, "accessTokenLifespan": 600,
"accessCodeLifespan": 600, "accessCodeLifespan": 600,
"accessCodeLifespanUserAction": 600, "accessCodeLifespanUserAction": 600,
"sslNotRequired": true, "sslNotRequired": true,

View file

@ -2,7 +2,7 @@
"id": "test", "id": "test",
"realm": "test", "realm": "test",
"enabled": true, "enabled": true,
"tokenLifespan": 600, "accessTokenLifespan": 600,
"accessCodeLifespan": 600, "accessCodeLifespan": 600,
"accessCodeLifespanUserAction": 600, "accessCodeLifespanUserAction": 600,
"sslNotRequired": true, "sslNotRequired": true,