use secrets

This commit is contained in:
Bill Burke 2014-02-13 18:36:41 -05:00
parent f66060e1be
commit 0cdd5e857e
43 changed files with 435 additions and 398 deletions

View file

@ -329,9 +329,6 @@ module.config([ '$routeProvider', function($routeProvider) {
},
application : function(ApplicationLoader) {
return ApplicationLoader();
},
installation : function(ApplicationInstallationLoader) {
return ApplicationInstallationLoader();
}
},
controller : 'ApplicationInstallationCtrl'
@ -399,9 +396,6 @@ module.config([ '$routeProvider', function($routeProvider) {
realm : function(RealmLoader) {
return RealmLoader();
},
application : function(OAuthClientLoader) {
return OAuthClientLoader();
},
oauth : function(OAuthClientLoader) {
return OAuthClientLoader();
}

View file

@ -13,80 +13,25 @@ module.controller('ApplicationRoleListCtrl', function($scope, $location, realm,
module.controller('ApplicationCredentialsCtrl', function($scope, $location, realm, application, ApplicationCredentials, Notifications) {
$scope.realm = realm;
$scope.application = application;
var required = realm.requiredApplicationCredentials;
for (var i = 0; i < required.length; i++) {
if (required[i] == 'password') {
$scope.passwordRequired = true;
} else if (required[i] == 'totp') {
$scope.totpRequired = true;
} else if (required[i] == 'cert') {
$scope.certRequired = true;
var secret = ApplicationCredentials.get({ realm : realm.realm, application : application.name },
function() {
$scope.secret = secret.value;
}
}
function randomString(len) {
var charSet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var randomString = '';
for (var i = 0; i < len; i++) {
var randomPoz = Math.floor(Math.random() * charSet.length);
randomString += charSet.substring(randomPoz,randomPoz+1);
}
return randomString;
}
$scope.generateTotp = function() {
$scope.totp = randomString(5) + '-' + randomString(5) + '-' + randomString(5);
}
);
$scope.changePassword = function() {
if ($scope.password != $scope.confirmPassword) {
Notifications.error("Password and confirmation does not match.");
$scope.password = "";
$scope.confirmPassword = "";
return;
}
var creds = [
{
type : "password",
value : $scope.password
}
];
ApplicationCredentials.update({ realm : realm.realm, application : application.name }, creds,
var secret = ApplicationCredentials.update({ realm : realm.realm, application : application.name },
function() {
Notifications.success('The password has been changed.');
$scope.password = null;
$scope.confirmPassword = null;
Notifications.success('The secret has been changed.');
$scope.secret = secret.value;
},
function() {
Notifications.error("The password was not changed due to a problem.");
$scope.password = null;
$scope.confirmPassword = null;
Notifications.error("The secret was not changed due to a problem.");
$scope.secret = "error";
}
);
};
$scope.changeTotp = function() {
var creds = [
{
type : "totp",
value : $scope.totp
}
];
ApplicationCredentials.update({ realm : realm.realm, application : application.name }, creds,
function() {
Notifications.success('The totp was changed.');
$scope.totp = null;
},
function() {
Notifications.error("The totp was not changed due to a problem.");
$scope.totp = null;
}
);
};
$scope.$watch(function() {
return $location.path();
}, function() {
@ -163,12 +108,37 @@ module.controller('ApplicationListCtrl', function($scope, realm, applications, A
});
});
module.controller('ApplicationInstallationCtrl', function($scope, realm, installation, application, ApplicationInstallation, $routeParams) {
module.controller('ApplicationInstallationCtrl', function($scope, realm, application, ApplicationInstallation,ApplicationInstallationJBoss, $http, $routeParams) {
console.log('ApplicationInstallationCtrl');
$scope.realm = realm;
$scope.application = application;
$scope.installation = installation;
$scope.download = ApplicationInstallation.url({ realm: $routeParams.realm, application: $routeParams.application });
$scope.installation = null;
$scope.download = null;
$scope.configFormat = null;
$scope.configFormats = [
"keycloak.json",
"Wildfly/JBoss Subsystem XML"
];
$scope.changeFormat = function() {
if ($scope.configFormat == "keycloak.json") {
var url = ApplicationInstallation.url({ realm: $routeParams.realm, application: $routeParams.application });
var installation = $http.get(url).success(function(data) {
var tmp = angular.fromJson(data);
$scope.installation = angular.toJson(tmp, true);
})
$scope.download = url;
} else if ($scope.configFormat == "Wildfly/JBoss Subsystem XML") {
var url = ApplicationInstallationJBoss.url({ realm: $routeParams.realm, application: $routeParams.application });
var installation = $http.get(url).success(function(data) {
$scope.installation = data;
})
$scope.download = url;
}
};
});
module.controller('ApplicationDetailCtrl', function($scope, realm, application, Application, $location, Dialog, Notifications) {

View file

@ -2,84 +2,31 @@ module.controller('OAuthClientCredentialsCtrl', function($scope, $location, real
$scope.realm = realm;
$scope.oauth = oauth;
var required = realm.requiredOAuthClientCredentials;
for (var i = 0; i < required.length; i++) {
if (required[i] == 'password') {
$scope.passwordRequired = true;
} else if (required[i] == 'totp') {
$scope.totpRequired = true;
} else if (required[i] == 'cert') {
$scope.certRequired = true;
var secret = OAuthClientCredentials.get({ realm : realm.realm, oauth : oauth.id },
function() {
$scope.secret = secret.value;
}
}
function randomString(len) {
var charSet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var randomString = '';
for (var i = 0; i < len; i++) {
var randomPoz = Math.floor(Math.random() * charSet.length);
randomString += charSet.substring(randomPoz,randomPoz+1);
}
return randomString;
}
$scope.generateTotp = function() {
$scope.totp = randomString(5) + '-' + randomString(5) + '-' + randomString(5);
}
);
$scope.changePassword = function() {
if ($scope.password != $scope.confirmPassword) {
Notifications.error("Password and confirmation does not match.");
$scope.password = "";
$scope.confirmPassword = "";
return;
}
var creds = [
{
type : "password",
value : $scope.password
}
];
OAuthClientCredentials.update({ realm : realm.realm, oauth : oauth.id }, creds,
var secret = OAuthClientCredentials.update({ realm : realm.realm, oauth : oauth.id },
function() {
Notifications.success('The password has been changed.');
$scope.password = null;
$scope.confirmPassword = null;
Notifications.success('The secret has been changed.');
$scope.secret = secret.value;
},
function() {
Notifications.error("The password was not changed due to a problem.");
$scope.password = null;
$scope.confirmPassword = null;
Notifications.error("The secret was not changed due to a problem.");
$scope.secret = "error";
}
);
};
$scope.changeTotp = function() {
var creds = [
{
type : "totp",
value : $scope.totp
}
];
OAuthClientCredentials.update({ realm : realm.realm, oauth : oauth.id }, creds,
function() {
Notifications.success('The totp was changed.');
$scope.totp = null;
},
function() {
Notifications.error("The totp was not changed due to a problem.");
$scope.totp = null;
}
);
};
$scope.$watch(function() {
return $location.path();
}, function() {
$scope.path = $location.path().substring(1).split("/");
});
});
module.controller('OAuthClientListCtrl', function($scope, realm, oauthClients, OAuthClient, $location) {

View file

@ -464,29 +464,31 @@ module.factory('Application', function($resource) {
});
module.factory('ApplicationInstallation', function($resource) {
var url = '/auth/rest/admin/realms/:realm/applications/:application/installation';
var resource = $resource('/auth/rest/admin/realms/:realm/applications/:application/installation', {
realm : '@realm',
application : '@application'
}, {
update : {
method : 'PUT'
var url = '/auth/rest/admin/realms/:realm/applications/:application/installation/json';
return {
url : function(parameters)
{
return url.replace(':realm', parameters.realm).replace(':application', parameters.application);
}
});
resource.url = function(parameters) {
}
});
module.factory('ApplicationInstallationJBoss', function($resource) {
var url = '/auth/rest/admin/realms/:realm/applications/:application/installation/jboss';
return {
url : function(parameters)
{
return url.replace(':realm', parameters.realm).replace(':application', parameters.application);
}
return resource;
}
});
module.factory('ApplicationCredentials', function($resource) {
return $resource('/auth/rest/admin/realms/:realm/applications/:application/credentials', {
return $resource('/auth/rest/admin/realms/:realm/applications/:application/client-secret', {
realm : '@realm',
application : '@application'
}, {
update : {
method : 'PUT',
isArray : true
method : 'POST'
}
});
});
@ -515,15 +517,15 @@ module.factory('OAuthClient', function($resource) {
});
module.factory('OAuthClientCredentials', function($resource) {
return $resource('/auth/rest/admin/realms/:realm/oauth-clients/:oauth/credentials', {
return $resource('/auth/rest/admin/realms/:realm/oauth-clients/:oauth/client-secret', {
realm : '@realm',
oauth : '@oauth'
}, {
update : {
method : 'PUT',
isArray : true
method : 'POST'
}
});
});
module.factory('OAuthClientRealmScopeMapping', function($resource) {

View file

@ -17,42 +17,20 @@
</ol>
<h2 data-ng-hide="create"><span>{{application.name}}</span> Credentials</h2>
<form class="form-horizontal" name="credentialForm" novalidate>
<fieldset data-ng-show="passwordRequired">
<legend><span class="text">Change Password</span></legend>
<fieldset >
<legend><span class="text">Client Secret</span></legend>
<div class="form-group">
<label class="col-sm-2 control-label" for="password">New Password</label>
<label class="col-sm-2 control-label" for="secret">Secret</label>
<div class="col-sm-4">
<input class="form-control" type="password" id="password" name="password" data-ng-model="password" autofocus
required>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="password">New Password Confirmation</label>
<div class="col-sm-4">
<input class="form-control" type="password" id="confirmPassword" name="confirmPassword" data-ng-model="confirmPassword"
<input ng-disabled="true" class="form-control" type="text" id="secret" name="secret" data-ng-model="secret" autofocus
required>
</div>
</div>
</fieldset>
<div class="pull-right form-actions" ng-show="password != null">
<button type="submit" data-ng-click="changePassword()" class="btn btn-primary btn-lg">Change Password
<div class="pull-right form-actions">
<button type="submit" data-ng-click="changePassword()" class="btn btn-primary btn-lg">Regenerate Secret
</button>
</div>
<fieldset data-ng-show="totpRequired">
<legend uncollapsed><span class="text">Change TOTP Key</span></legend>
<div class="form-group">
<label class="col-sm-2 control-label" for="totp">New Key</label>
<div class="col-sm-4">
<input class="form-control" type="text" id="totp" name="totp" data-ng-model="totp" autofocus
required>
<button class="btn btn-primary" type="submit" data-ng-click="generateTotp()">Generate
</button>
</div>
</div>
</fieldset>
<div class="pull-right form-actions" ng-show="totp != null">
<button type="submit" data-ng-click="changeTotp()" class="btn btn-primary btn-lg">Save</button>
</div>
</form>
</div>
</div>

View file

@ -19,15 +19,27 @@
<h2>Application Installation</h2>
<form class="form-horizontal" name="realmForm" novalidate>
<fieldset>
<div class="form-group">
<div class="form-group input-select">
<label class="col-sm-2 control-label" for="configFormats">Format Option</label>
<div class="col-sm-4">
<div class="input-group">
<div class="select-kc">
<select id="configFormats" name="configFormats" ng-change="changeFormat()" ng-model="configFormat" ng-options="a for a in configFormats">
<option value="" selected> Select a Format </option>
</select>
</div>
</div>
</div>
</div>
<div class="form-group" ng-show="installation">
<div class="col-sm-12">
<textarea class="form-control" rows="20" kc-select-action="click">{{installation | json}}</textarea>
<textarea class="form-control" rows="20" kc-select-action="click">{{installation}}</textarea>
</div>
</div>
</fieldset>
</form>
<div class="pull-right form-actions">
<div class="pull-right form-actions" ng-show="installation">
<a class="btn btn-primary btn-lg" href="{{download}}" download="keycloak.json" type="submit">Download</a>
</div>
</div>

View file

@ -16,43 +16,19 @@
</ol>
<h2 data-ng-hide="create"><span>{{oauth.name}}</span> Credentials</h2>
<form class="form-horizontal" name="credentialForm" novalidate>
<fieldset data-ng-show="passwordRequired">
<legend><span class="text">Change Password</span></legend>
<fieldset >
<legend><span class="text">Client Secret</span></legend>
<div class="form-group">
<label class="col-sm-2 control-label" for="password">New Password</label>
<label class="col-sm-2 control-label" for="secret">Secret</label>
<div class="col-sm-4">
<input class="form-control" type="password" id="password" name="password" data-ng-model="password" autofocus
required>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label two-lines" for="password">New Password Confirmation</label>
<div class="col-sm-4">
<input class="form-control" type="password" id="confirmPassword" name="confirmPassword" data-ng-model="confirmPassword"
<input ng-disabled="true" class="form-control" type="text" id="secret" name="secret" data-ng-model="secret" autofocus
required>
</div>
</div>
</fieldset>
<div class="pull-right form-actions" ng-show="password != null">
<button class="btn btn-primary btn-lg" type="submit" data-ng-click="changePassword()" ng-show="password != null">Change Password</button>
</div>
<fieldset data-ng-show="totpRequired">
<legend uncollapsed><span class="text">Change TOTP Key</span></legend>
<div class="form-group">
<label class="col-sm-2 control-label" for="totp">New Key</label>
<div class="col-sm-4">
<input class="form-control" type="text" id="totp" name="totp" data-ng-model="totp" autofocus
required>
<button class="btn btn-primary" type="submit" data-ng-click="generateTotp()">Generate
</button>
</div>
</div>
</fieldset>
<div class="pull-right form-actions" ng-show="totp != null">
<button class="btn btn-primary btn-lg" type="submit" data-ng-click="changeTotp()" ng-show="totp != null">Save</button>
<div class="pull-right form-actions">
<button type="submit" data-ng-click="changePassword()" class="btn btn-primary btn-lg">Regenerate Secret
</button>
</div>
</form>
</div>

View file

@ -19,20 +19,6 @@
<input id="user" type="text" ui-select2="userCredentialOptions" ng-model="realm.requiredCredentials" placeholder="Type a role and enter">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="application" class="control-label two-lines">Required Application Credentials</label>
<div class="col-sm-4">
<input id="application" type="text" ui-select2="userCredentialOptions" ng-model="realm.requiredApplicationCredentials" placeholder="Type a role and enter">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="oauth" class="control-label two-lines">Required OAuth Credentials</label>
<div class="col-sm-4">
<input id="oauth" type="text" ui-select2="userCredentialOptions" ng-model="realm.requiredOAuthClientCredentials" placeholder="Type a role and enter">
</div>
</div>
</fieldset>
<fieldset class="border-top">
<legend uncollapsed><span class="text">Realm Password Policy</span></legend>

View file

@ -3,6 +3,7 @@ package org.keycloak;
import org.keycloak.util.KeycloakUriBuilder;
import java.security.KeyStore;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
@ -13,7 +14,7 @@ import java.util.concurrent.atomic.AtomicLong;
public class AbstractOAuthClient {
public static final String OAUTH_TOKEN_REQUEST_STATE = "OAuth_Token_Request_State";
protected String clientId;
protected String password;
protected Map<String, String> credentials;
protected KeyStore truststore;
protected String authUrl;
protected String codeUrl;
@ -35,12 +36,12 @@ public class AbstractOAuthClient {
this.clientId = clientId;
}
public String getPassword() {
return password;
public Map<String, String> getCredentials() {
return credentials;
}
public void setPassword(String password) {
this.password = password;
public void setCredentials(Map<String, String> credentials) {
this.credentials = credentials;
}
public KeyStore getTruststore() {

View file

@ -5,6 +5,7 @@ package org.keycloak.representations.idm;
* @version $Revision: 1 $
*/
public class CredentialRepresentation {
public static final String SECRET = "secret";
public static final String PASSWORD = "password";
public static final String TOTP = "totp";
public static final String CLIENT_CERT = "cert";

View file

@ -28,8 +28,6 @@ public class RealmRepresentation {
protected RolesRepresentation roles;
protected List<String> defaultRoles;
protected Set<String> requiredCredentials;
protected Set<String> requiredApplicationCredentials;
protected Set<String> requiredOAuthClientCredentials;
protected String passwordPolicy;
protected List<UserRepresentation> users;
protected List<UserRoleMappingRepresentation> roleMappings;
@ -168,22 +166,6 @@ public class RealmRepresentation {
this.requiredCredentials = requiredCredentials;
}
public Set<String> getRequiredApplicationCredentials() {
return requiredApplicationCredentials;
}
public void setRequiredApplicationCredentials(Set<String> requiredApplicationCredentials) {
this.requiredApplicationCredentials = requiredApplicationCredentials;
}
public Set<String> getRequiredOAuthClientCredentials() {
return requiredOAuthClientCredentials;
}
public void setRequiredOAuthClientCredentials(Set<String> requiredOAuthClientCredentials) {
this.requiredOAuthClientCredentials = requiredOAuthClientCredentials;
}
public String getPasswordPolicy() {
return passwordPolicy;
}

View file

@ -6,6 +6,6 @@
"ssl-not-required": true,
"expose-token": true,
"credentials": {
"password": "password"
"secret": "password"
}
}

View file

@ -5,6 +5,6 @@
"auth-server-url" : "http://localhost:8080/auth",
"ssl-not-required" : true,
"credentials" : {
"password" : "password"
"secret": "password"
}
}

View file

@ -8,12 +8,12 @@
<secure-deployment name="customer-portal.war">
<realm>demo</realm>
<resource>customer-portal</resource>
<credential name="password">password</credential>
<credential name="secret">password</credential>
</secure-deployment>
<secure-deployment name="product-portal.war">
<realm>demo</realm>
<resource>product-portal</resource>
<credential name="password">password</credential>
<credential name="secret">password</credential>
</secure-deployment>
<secure-deployment name="database.war">
<realm>demo</realm>

View file

@ -11,8 +11,6 @@
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"requiredCredentials": [ "password" ],
"requiredApplicationCredentials": [ "password" ],
"requiredOAuthClientCredentials": [ "password" ],
"users" : [
{
"username" : "bburke@redhat.com",
@ -66,7 +64,7 @@
"adminUrl": "http://localhost:8080/customer-portal",
"credentials": [
{
"type": "password",
"type": "secret",
"value": "password"
}
]
@ -77,7 +75,7 @@
"adminUrl": "http://localhost:8080/product-portal",
"credentials": [
{
"type": "password",
"type": "secret",
"value": "password"
}
]
@ -89,7 +87,7 @@
"enabled": true,
"credentials": [
{
"type": "password",
"type": "secret",
"value": "password"
}
]

View file

@ -4,7 +4,7 @@
"auth-server-url" : "http://localhost:8080/auth",
"ssl-not-required" : true,
"credentials" : {
"password" : "password"
"secret": "password"
},
"scope": {
"realm": [ "user" ]

View file

@ -4,7 +4,7 @@
"auth-server-url" : "http://localhost:8080/auth",
"ssl-not-required" : true,
"credentials" : {
"password" : "password"
"secret": "password"
},
"scope": {
"realm": [ "user" ]

View file

@ -57,11 +57,12 @@ public class TokenGrantRequest {
public static AccessTokenResponse invoke(HttpClient client, String code, String codeUrl, String redirectUri, String client_id, Map<String, String> credentials) throws IOException, HttpFailure {
List<NameValuePair> formparams = new ArrayList<NameValuePair>();
redirectUri = stripOauthParametersFromRedirect(redirectUri);
String password = credentials.get("password");
for (Map.Entry<String, String> entry : credentials.entrySet()) {
formparams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
formparams.add(new BasicNameValuePair("grant_type", "authorization_code"));
formparams.add(new BasicNameValuePair("code", code));
formparams.add(new BasicNameValuePair("client_id", client_id));
formparams.add(new BasicNameValuePair(CredentialRepresentation.PASSWORD, password));
formparams.add(new BasicNameValuePair("redirect_uri", redirectUri));
HttpResponse response = null;
UrlEncodedFormEntity form = new UrlEncodedFormEntity(formparams, "UTF-8");

View file

@ -28,7 +28,7 @@ public abstract class OAuthClientConfigLoader extends RealmConfigurationLoader {
public void configureOAuthClient(AbstractOAuthClient oauthClient) {
oauthClient.setClientId(adapterConfig.getResource());
oauthClient.setPassword(adapterConfig.getCredentials().get("password"));
oauthClient.setCredentials(adapterConfig.getCredentials());
if (adapterConfig.getAuthServerUrl() == null) {
throw new RuntimeException("You must specify auth-url");
}

View file

@ -5,6 +5,7 @@ import org.jboss.resteasy.logging.Logger;
import org.jboss.resteasy.util.BasicAuthHelper;
import org.keycloak.AbstractOAuthClient;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.idm.CredentialRepresentation;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.InternalServerErrorException;
@ -19,6 +20,7 @@ import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import java.net.URI;
import java.net.URL;
import java.util.Map;
/**
* Helper code to obtain oauth access tokens via browser redirects
@ -58,14 +60,15 @@ public class JaxrsOAuthClient extends AbstractOAuthClient {
public String resolveBearerToken(String redirectUri, String code) {
redirectUri = stripOauthParametersFromRedirect(redirectUri);
String authHeader = BasicAuthHelper.createHeader(clientId, password);
Form codeForm = new Form()
.param("grant_type", "authorization_code")
.param("code", code)
.param("client_id", clientId)
.param("password", password)
.param("redirect_uri", redirectUri);
Response res = client.target(codeUrl).request().header(HttpHeaders.AUTHORIZATION, authHeader).post(Entity.form(codeForm));
for (Map.Entry<String, String> entry : credentials.entrySet()) {
codeForm.param(entry.getKey(), entry.getValue());
}
Response res = client.target(codeUrl).request().post(Entity.form(codeForm));
try {
if (res.getStatus() == 400) {
throw new BadRequestException();

View file

@ -48,8 +48,6 @@ public class ServletOAuthClient extends AbstractOAuthClient {
}
public String resolveBearerToken(String redirectUri, String code) throws IOException, TokenGrantRequest.HttpFailure {
Map<String, String> credentials = new HashMap<String, String>();
credentials.put("password", password);
return TokenGrantRequest.invoke(client, code, codeUrl, redirectUri, clientId, credentials).getToken();
}

View file

@ -170,4 +170,13 @@ public interface RealmModel extends RoleContainerModel, RoleMapperModel, ScopeMa
void setAccountTheme(String name);
boolean validateSecret(UserModel user, String secret);
/**
* Secrets can be viewed. They are used by confidential Applications and OAuth clients
*
* @param user
* @return
*/
UserCredentialModel getSecret(UserModel user);
}

View file

@ -53,6 +53,7 @@ public class RequiredCredentialModel {
public static final RequiredCredentialModel PASSWORD;
public static final RequiredCredentialModel TOTP;
public static final RequiredCredentialModel CLIENT_CERT;
public static final RequiredCredentialModel SECRET;
static {
Map<String, RequiredCredentialModel> map = new HashMap<String, RequiredCredentialModel>();
@ -62,6 +63,12 @@ public class RequiredCredentialModel {
PASSWORD.setSecret(true);
PASSWORD.setFormLabel("password");
map.put(PASSWORD.getType(), PASSWORD);
SECRET = new RequiredCredentialModel();
SECRET.setType(UserCredentialModel.SECRET);
SECRET.setInput(false);
SECRET.setSecret(true);
SECRET.setFormLabel("secret");
map.put(SECRET.getType(), SECRET);
TOTP = new RequiredCredentialModel();
TOTP.setType(UserCredentialModel.TOTP);
TOTP.setInput(true);

View file

@ -1,11 +1,16 @@
package org.keycloak.models;
import java.util.UUID;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class UserCredentialModel {
public static final String PASSWORD = "password";
// Secret is same as password but it is not hashed
public static final String SECRET = "secret";
public static final String TOTP = "totp";
public static final String CLIENT_CERT = "cert";
@ -23,6 +28,20 @@ public class UserCredentialModel {
return model;
}
public static UserCredentialModel secret(String password) {
UserCredentialModel model = new UserCredentialModel();
model.setType(SECRET);
model.setValue(password);
return model;
}
public static UserCredentialModel generateSecret() {
UserCredentialModel model = new UserCredentialModel();
model.setType(SECRET);
model.setValue(UUID.randomUUID().toString());
return model;
}
public String getType() {
return type;

View file

@ -996,6 +996,17 @@ public class RealmAdapter implements RealmModel {
return query;
}
@Override
public UserCredentialModel getSecret(UserModel user) {
for (CredentialEntity cred : ((UserAdapter)user).getUser().getCredentials()) {
if (cred.getType().equals(UserCredentialModel.SECRET)) {
return UserCredentialModel.secret(cred.getValue());
}
}
return null;
}
@Override
public boolean validatePassword(UserModel user, String password) {
for (CredentialEntity cred : ((UserAdapter)user).getUser().getCredentials()) {
@ -1006,6 +1017,18 @@ public class RealmAdapter implements RealmModel {
return false;
}
@Override
public boolean validateSecret(UserModel user, String secret) {
for (CredentialEntity cred : ((UserAdapter)user).getUser().getCredentials()) {
if (cred.getType().equals(UserCredentialModel.SECRET)) {
return secret.equals(cred.getValue());
}
}
return false;
}
@Override
public boolean validateTOTP(UserModel user, String password, String token) {
if (!validatePassword(user, password)) return false;

View file

@ -769,6 +769,29 @@ public class RealmAdapter extends AbstractAdapter implements RealmModel {
return false;
}
@Override
public boolean validateSecret(UserModel user, String secret) {
for (CredentialEntity cred : ((UserAdapter)user).getUser().getCredentials()) {
if (cred.getType().equals(UserCredentialModel.SECRET)) {
return secret.equals(cred.getValue());
}
}
return false;
}
@Override
public UserCredentialModel getSecret(UserModel user) {
for (CredentialEntity cred : ((UserAdapter)user).getUser().getCredentials()) {
if (cred.getType().equals(UserCredentialModel.SECRET)) {
return UserCredentialModel.secret(cred.getValue());
}
}
return null;
}
@Override
public void updateCredential(UserModel user, UserCredentialModel cred) {
CredentialEntity credentialEntity = null;

View file

@ -151,8 +151,8 @@ public class ImportTest extends AbstractModelTest {
Assert.assertFalse(realm.isUpdateProfileOnInitialSocialLogin());
Assert.assertEquals(600, realm.getAccessCodeLifespanUserAction());
verifyRequiredCredentials(realm.getRequiredCredentials(), "password");
verifyRequiredCredentials(realm.getRequiredApplicationCredentials(), "totp");
verifyRequiredCredentials(realm.getRequiredOAuthClientCredentials(), "cert");
verifyRequiredCredentials(realm.getRequiredApplicationCredentials(), "secret");
verifyRequiredCredentials(realm.getRequiredOAuthClientCredentials(), "secret");
}
private void verifyRequiredCredentials(List<RequiredCredentialModel> requiredCreds, String expectedType) {

12
model/tests/src/test/resources/testcomposites.json Normal file → Executable file
View file

@ -9,8 +9,6 @@
"registrationAllowed": true,
"resetPasswordAllowed": true,
"requiredCredentials": [ "password" ],
"requiredApplicationCredentials": [ "password" ],
"requiredOAuthClientCredentials": [ "password" ],
"smtpServer": {
"from": "auto@keycloak.org",
"host": "localhost",
@ -68,7 +66,7 @@
"name" : "third-party",
"enabled": true,
"credentials" : [
{ "type" : "password",
{ "type" : "secret",
"value" : "password" }
]
}
@ -109,7 +107,7 @@
"adminUrl": "http://localhost:8081/app/logout",
"credentials": [
{
"type": "password",
"type": "secret",
"value": "password"
}
]
@ -121,7 +119,7 @@
"adminUrl": "http://localhost:8081/app/logout",
"credentials": [
{
"type": "password",
"type": "secret",
"value": "password"
}
]
@ -133,7 +131,7 @@
"adminUrl": "http://localhost:8081/app/logout",
"credentials": [
{
"type": "password",
"type": "secret",
"value": "password"
}
]
@ -145,7 +143,7 @@
"adminUrl": "http://localhost:8081/app/logout",
"credentials": [
{
"type": "password",
"type": "secret",
"value": "password"
}
]

View file

@ -9,8 +9,6 @@
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"requiredCredentials": [ "password" ],
"requiredApplicationCredentials": [ "totp" ],
"requiredOAuthClientCredentials": [ "cert" ],
"users" : [
{
"username" : "bburke@redhat.com",
@ -29,7 +27,7 @@
"name" : "third-party",
"enabled": true,
"credentials" : [
{ "type" : "Password",
{ "type" : "secret",
"value" : "password" }
]
}
@ -66,9 +64,8 @@
"adminUrl": "http://localhost:8080/customer-portal/j_admin_request",
"credentials": [
{
"type": "totp",
"value": "12345",
"device": "67890"
"type": "secret",
"value": "12345"
}
]
},
@ -78,9 +75,8 @@
"adminUrl": "http://localhost:8080/product-portal/j_admin_request",
"credentials": [
{
"type": "totp",
"value": "12345",
"device": "67890"
"type": "secret",
"value": "12345"
}
]
}

View file

@ -5,8 +5,6 @@
"accessCodeLifespan": 30,
"accessCodeLifespanUserAction": 600,
"requiredCredentials": [ "password" ],
"requiredApplicationCredentials": [ "password" ],
"requiredOAuthClientCredentials": [ "password" ],
"defaultRoles": [ "foo", "bar" ],
"verifyEmail" : "true",
"users": [
@ -83,7 +81,7 @@
"name" : "oauthclient",
"enabled": true,
"credentials" : [
{ "type" : "password",
{ "type" : "secret",
"value" : "clientpassword" }
]
}

View file

@ -58,16 +58,10 @@ public class ApplianceBootstrap {
realm.setLoginTheme("keycloak");
realm.setAccountTheme("keycloak");
ApplicationModel adminConsole = realm.addApplication(Constants.ADMIN_CONSOLE_APPLICATION);
ApplicationModel adminConsole = new ApplicationManager(manager).createApplication(realm, Constants.ADMIN_CONSOLE_APPLICATION);
adminConsole.setBaseUrl("/auth/admin/index.html");
adminConsole.setEnabled(true);
UserCredentialModel adminConsolePassword = new UserCredentialModel();
adminConsolePassword.setType(UserCredentialModel.PASSWORD);
adminConsolePassword.setValue(UUID.randomUUID().toString()); // just a random password as we'll never access it
realm.updateCredential(adminConsole.getApplicationUser(), adminConsolePassword);
RoleModel applicationRole = realm.getRole(Constants.APPLICATION_ROLE);
realm.grantRole(adminConsole.getApplicationUser(), applicationRole);
RoleModel adminRole = adminConsole.addRole(Constants.ADMIN_CONSOLE_ADMIN_ROLE);
UserModel adminUser = realm.addUser("admin");

View file

@ -1,5 +1,7 @@
package org.keycloak.services.managers;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.annotate.JsonPropertyOrder;
import org.jboss.resteasy.logging.Logger;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.Constants;
@ -7,7 +9,9 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.representations.SkeletonKeyScope;
import org.keycloak.representations.adapters.config.BaseAdapterConfig;
import org.keycloak.representations.adapters.config.BaseRealmConfig;
import org.keycloak.representations.idm.ApplicationRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
@ -22,6 +26,7 @@ import java.util.List;
import java.util.Set;
import java.util.Map;
import java.util.HashMap;
import java.util.UUID;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -36,6 +41,10 @@ public class ApplicationManager {
this.realmManager = realmManager;
}
public ApplicationManager() {
}
/**
* Does not create scope or role mappings!
*
@ -54,14 +63,18 @@ public class ApplicationManager {
applicationModel.updateApplication();
UserModel resourceUser = applicationModel.getApplicationUser();
if (resourceRep.getCredentials() != null) {
if (resourceRep.getCredentials() != null && resourceRep.getCredentials().size() > 0) {
for (CredentialRepresentation cred : resourceRep.getCredentials()) {
UserCredentialModel credential = new UserCredentialModel();
credential.setType(cred.getType());
credential.setValue(cred.getValue());
realm.updateCredential(resourceUser, credential);
}
} else {
generateSecret(realm, applicationModel);
}
if (resourceRep.getRedirectUris() != null) {
for (String redirectUri : resourceRep.getRedirectUris()) {
resourceUser.addRedirectUri(redirectUri);
@ -122,9 +135,17 @@ public class ApplicationManager {
RoleModel loginRole = realm.getRole(Constants.APPLICATION_ROLE);
ApplicationModel app = realm.addApplication(name);
realm.grantRole(app.getApplicationUser(), loginRole);
generateSecret(realm, app);
return app;
}
public UserCredentialModel generateSecret(RealmModel realm, ApplicationModel app) {
UserCredentialModel secret = UserCredentialModel.generateSecret();
realm.updateCredential(app.getApplicationUser(), secret);
return secret;
}
public void updateApplication(ApplicationRepresentation rep, ApplicationModel resource) {
resource.setName(rep.getName());
resource.setEnabled(rep.isEnabled());
@ -175,8 +196,45 @@ public class ApplicationManager {
}
public BaseAdapterConfig toInstallationRepresentation(RealmModel realmModel, ApplicationModel applicationModel, URI baseUri) {
BaseAdapterConfig rep = new BaseAdapterConfig();
@JsonPropertyOrder({"realm", "realm-public-key", "auth-server-url", "ssl-not-required",
"resource", "credentials",
"use-resource-role-mappings"})
public static class InstallationAdapterConfig extends BaseRealmConfig {
@JsonProperty("resource")
protected String resource;
@JsonProperty("use-resource-role-mappings")
protected boolean useResourceRoleMappings;
@JsonProperty("credentials")
protected Map<String, String> credentials = new HashMap<String, String>();
public boolean isUseResourceRoleMappings() {
return useResourceRoleMappings;
}
public void setUseResourceRoleMappings(boolean useResourceRoleMappings) {
this.useResourceRoleMappings = useResourceRoleMappings;
}
public String getResource() {
return resource;
}
public void setResource(String resource) {
this.resource = resource;
}
public Map<String, String> getCredentials() {
return credentials;
}
public void setCredentials(Map<String, String> credentials) {
this.credentials = credentials;
}
}
public InstallationAdapterConfig toInstallationRepresentation(RealmModel realmModel, ApplicationModel applicationModel, URI baseUri) {
InstallationAdapterConfig rep = new InstallationAdapterConfig();
rep.setRealm(realmModel.getName());
rep.setRealmKey(realmModel.getPublicKeyPem());
rep.setSslNotRequired(realmModel.isSslNotRequired());
@ -187,12 +245,25 @@ public class ApplicationManager {
rep.setResource(applicationModel.getName());
Map<String, String> creds = new HashMap<String, String>();
creds.put(CredentialRepresentation.PASSWORD, "INSERT APPLICATION PASSWORD");
if (applicationModel.getApplicationUser().isTotp()) {
creds.put(CredentialRepresentation.TOTP, "INSERT APPLICATION TOTP");
}
String cred = realmModel.getSecret(applicationModel.getApplicationUser()).getValue();
creds.put(CredentialRepresentation.SECRET, cred);
rep.setCredentials(creds);
return rep;
}
public String toJBossSubsystemConfig(RealmModel realmModel, ApplicationModel applicationModel, URI baseUri) {
StringBuffer buffer = new StringBuffer();
buffer.append("<secure-deployment name=\"WAR MODULE NAME.war\">\n");
buffer.append(" <realm>").append(realmModel.getName()).append("</realm>\n");
buffer.append(" <realm-public-key>").append(realmModel.getPublicKeyPem()).append("</realm-public-key>\n");
buffer.append(" <auth-server-url>").append(baseUri.toString()).append("</auth-server-url>\n");
buffer.append(" <ssl-not-required>").append(realmModel.isSslNotRequired()).append("</ssl-not-required>\n");
buffer.append(" <resource>").append(applicationModel.getName()).append("</resource>\n");
String cred = realmModel.getSecret(applicationModel.getApplicationUser()).getValue();
buffer.append(" <credential name=\"secret\">").append(cred).append("</credential>\n");
buffer.append("</secure-deployment>\n");
return buffer.toString();
}
}

View file

@ -298,6 +298,21 @@ public class AuthenticationManager {
}
}
if (!user.getRequiredActions().isEmpty()) {
return AuthenticationStatus.ACTIONS_REQUIRED;
} else {
return AuthenticationStatus.SUCCESS;
}
} else if (types.contains(CredentialRepresentation.SECRET)) {
String secret = formData.getFirst(CredentialRepresentation.SECRET);
if (secret == null) {
logger.warn("Secret not provided");
return AuthenticationStatus.MISSING_PASSWORD;
}
if (!realm.validateSecret(user, secret)) {
logger.debug("invalid secret for user: " + user.getLoginName());
return AuthenticationStatus.INVALID_CREDENTIALS;
}
if (!user.getRequiredActions().isEmpty()) {
return AuthenticationStatus.ACTIONS_REQUIRED;
} else {

View file

@ -5,7 +5,9 @@ import org.keycloak.models.Constants;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredCredentialModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
@ -95,20 +97,13 @@ public class ModelToRepresentation {
rep.getRequiredCredentials().add(cred.getType());
}
}
List<RequiredCredentialModel> requiredResourceCredentialModels = realm.getRequiredApplicationCredentials();
if (requiredResourceCredentialModels.size() > 0) {
rep.setRequiredApplicationCredentials(new HashSet<String>());
for (RequiredCredentialModel cred : requiredResourceCredentialModels) {
rep.getRequiredApplicationCredentials().add(cred.getType());
}
}
List<RequiredCredentialModel> requiredOAuthCredentialModels = realm.getRequiredOAuthClientCredentials();
if (requiredOAuthCredentialModels.size() > 0) {
rep.setRequiredOAuthClientCredentials(new HashSet<String>());
for (RequiredCredentialModel cred : requiredOAuthCredentialModels) {
rep.getRequiredOAuthClientCredentials().add(cred.getType());
}
}
return rep;
}
public static CredentialRepresentation toRepresentation(UserCredentialModel cred) {
CredentialRepresentation rep = new CredentialRepresentation();
rep.setType(CredentialRepresentation.SECRET);
rep.setValue(cred.getValue());
return rep;
}
}

View file

@ -1,5 +1,7 @@
package org.keycloak.services.managers;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.annotate.JsonPropertyOrder;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.Constants;
import org.keycloak.models.OAuthClientModel;
@ -8,6 +10,7 @@ import org.keycloak.models.RoleModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.representations.adapters.config.BaseAdapterConfig;
import org.keycloak.representations.adapters.config.BaseRealmConfig;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.OAuthClientRepresentation;
import org.keycloak.services.resources.flows.Urls;
@ -83,21 +86,43 @@ public class OAuthClientManager {
return rep;
}
public BaseAdapterConfig toInstallationRepresentation(RealmModel realmModel, OAuthClientModel model, URI baseUri) {
BaseAdapterConfig rep = new BaseAdapterConfig();
@JsonPropertyOrder({"realm", "realm-public-key", "auth-server-url", "ssl-not-required",
"resource", "credentials"})
public static class InstallationAdapterConfig extends BaseRealmConfig {
@JsonProperty("resource")
protected String resource;
@JsonProperty("credentials")
protected Map<String, String> credentials = new HashMap<String, String>();
public String getResource() {
return resource;
}
public void setResource(String resource) {
this.resource = resource;
}
public Map<String, String> getCredentials() {
return credentials;
}
public void setCredentials(Map<String, String> credentials) {
this.credentials = credentials;
}
}
public InstallationAdapterConfig toInstallationRepresentation(RealmModel realmModel, OAuthClientModel model, URI baseUri) {
InstallationAdapterConfig rep = new InstallationAdapterConfig();
rep.setRealm(realmModel.getName());
rep.setRealmKey(realmModel.getPublicKeyPem());
rep.setSslNotRequired(realmModel.isSslNotRequired());
rep.setAuthServerUrl(baseUri.toString());
rep.setUseResourceRoleMappings(false);
rep.setResource(model.getOAuthAgent().getLoginName());
Map<String, String> creds = new HashMap<String, String>();
creds.put(CredentialRepresentation.PASSWORD, "INSERT CLIENT PASSWORD");
if (model.getOAuthAgent().isTotp()) {
creds.put(CredentialRepresentation.TOTP, "INSERT CLIENT TOTP");
}
creds.put(CredentialRepresentation.SECRET, realmModel.getSecret(model.getOAuthAgent()).getValue());
rep.setCredentials(creds);
return rep;

View file

@ -64,6 +64,7 @@ public class RealmManager {
return identitySession.getRealmByName(name);
}
public RealmModel createRealm(String name) {
return createRealm(name, name);
}
@ -76,6 +77,8 @@ public class RealmManager {
realm.addRole(Constants.IDENTITY_REQUESTER_ROLE);
setupAccountManagement(realm);
realm.addRequiredOAuthClientCredential(UserCredentialModel.SECRET);
realm.addRequiredResourceCredential(UserCredentialModel.SECRET);
return realm;
}
@ -108,15 +111,9 @@ public class RealmManager {
if (rep.getAccessCodeLifespanUserAction() != null)
realm.setAccessCodeLifespanUserAction(rep.getAccessCodeLifespanUserAction());
if (rep.getTokenLifespan() != null) realm.setTokenLifespan(rep.getTokenLifespan());
if (rep.getRequiredOAuthClientCredentials() != null) {
realm.updateRequiredOAuthClientCredentials(rep.getRequiredOAuthClientCredentials());
}
if (rep.getRequiredCredentials() != null) {
realm.updateRequiredCredentials(rep.getRequiredCredentials());
}
if (rep.getRequiredApplicationCredentials() != null) {
realm.updateRequiredApplicationCredentials(rep.getRequiredApplicationCredentials());
}
realm.setLoginTheme(rep.getLoginTheme());
realm.setAccountTheme(rep.getAccountTheme());
@ -142,20 +139,12 @@ public class RealmManager {
private void setupAccountManagement(RealmModel realm) {
ApplicationModel application = realm.getApplicationNameMap().get(Constants.ACCOUNT_APPLICATION);
if (application == null) {
application = realm.addApplication(Constants.ACCOUNT_APPLICATION);
application = new ApplicationManager(this).createApplication(realm, Constants.ACCOUNT_APPLICATION);
application.setEnabled(true);
application.addDefaultRole(Constants.ACCOUNT_PROFILE_ROLE);
application.addDefaultRole(Constants.ACCOUNT_MANAGE_ROLE);
UserCredentialModel password = new UserCredentialModel();
password.setType(UserCredentialModel.PASSWORD);
password.setValue(UUID.randomUUID().toString()); // just a random password as we'll never access it
realm.updateCredential(application.getApplicationUser(), password);
RoleModel applicationRole = realm.getRole(Constants.APPLICATION_ROLE);
realm.grantRole(application.getApplicationUser(), applicationRole);
}
}
@ -209,22 +198,6 @@ public class RealmManager {
addRequiredCredential(newRealm, CredentialRepresentation.PASSWORD);
}
if (rep.getRequiredApplicationCredentials() != null) {
for (String requiredCred : rep.getRequiredApplicationCredentials()) {
addResourceRequiredCredential(newRealm, requiredCred);
}
} else {
addResourceRequiredCredential(newRealm, CredentialRepresentation.PASSWORD);
}
if (rep.getRequiredOAuthClientCredentials() != null) {
for (String requiredCred : rep.getRequiredOAuthClientCredentials()) {
addOAuthClientRequiredCredential(newRealm, requiredCred);
}
} else {
addOAuthClientRequiredCredential(newRealm, CredentialRepresentation.PASSWORD);
}
newRealm.setPasswordPolicy(new PasswordPolicy(rep.getPasswordPolicy()));
if (rep.getUsers() != null) {

View file

@ -10,6 +10,7 @@ import org.keycloak.representations.adapters.config.BaseAdapterConfig;
import org.keycloak.representations.idm.ApplicationRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.managers.ApplicationManager;
import org.keycloak.services.managers.ModelToRepresentation;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.resources.KeycloakApplication;
import org.keycloak.util.JsonSerialization;
@ -17,6 +18,8 @@ import org.keycloak.util.JsonSerialization;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
@ -25,7 +28,6 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
import java.io.IOException;
import java.util.List;
import java.util.Set;
/**
@ -73,35 +75,53 @@ public class ApplicationResource extends RoleContainerResource {
@GET
@NoCache
@Path("installation")
@Path("installation/json")
@Produces(MediaType.APPLICATION_JSON)
public String getInstallation() throws IOException {
ApplicationManager applicationManager = new ApplicationManager(new RealmManager(session));
BaseAdapterConfig rep = applicationManager.toInstallationRepresentation(realm, application, getKeycloakApplication().getBaseUri(uriInfo));
Object rep = applicationManager.toInstallationRepresentation(realm, application, getKeycloakApplication().getBaseUri(uriInfo));
// TODO Temporary solution to pretty-print
return JsonSerialization.mapper.writerWithDefaultPrettyPrinter().writeValueAsString(rep);
}
@GET
@NoCache
@Path("installation/jboss")
@Produces(MediaType.TEXT_PLAIN)
public String getJBossInstallation() throws IOException {
ApplicationManager applicationManager = new ApplicationManager(new RealmManager(session));
return applicationManager.toJBossSubsystemConfig(realm, application, getKeycloakApplication().getBaseUri(uriInfo));
}
@DELETE
@NoCache
public void deleteApplication() {
realm.removeApplication(application.getId());
}
@Path("credentials")
@PUT
@Path("client-secret")
@POST
@Produces("application/json")
@Consumes("application/json")
public void updateCredentials(List<CredentialRepresentation> credentials) {
logger.debug("updateCredentials");
if (credentials == null) return;
for (CredentialRepresentation rep : credentials) {
UserCredentialModel cred = RealmManager.fromRepresentation(rep);
realm.updateCredential(application.getApplicationUser(), cred);
}
public CredentialRepresentation regenerateSecret() {
logger.debug("regenerateSecret");
UserCredentialModel cred = new ApplicationManager().generateSecret(realm, application);
CredentialRepresentation rep = ModelToRepresentation.toRepresentation(cred);
return rep;
}
@Path("client-secret")
@GET
@Produces("application/json")
public CredentialRepresentation getClientSecret() {
logger.debug("getClientSecret");
UserCredentialModel model = realm.getSecret(application.getApplicationUser());
if (model == null) throw new NotFoundException("Application does not have a secret");
return ModelToRepresentation.toRepresentation(model);
}
@Path("scope-mappings")
public ScopeMappedResource getScopeMappedResource() {
return new ScopeMappedResource(realm, application.getApplicationUser(), session);

View file

@ -10,6 +10,7 @@ import org.keycloak.representations.adapters.config.BaseAdapterConfig;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.OAuthClientRepresentation;
import org.keycloak.services.managers.ApplicationManager;
import org.keycloak.services.managers.ModelToRepresentation;
import org.keycloak.services.managers.OAuthClientManager;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.resources.KeycloakApplication;
@ -18,6 +19,8 @@ import org.keycloak.util.JsonSerialization;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
@ -74,7 +77,7 @@ public class OAuthClientResource {
@Produces(MediaType.APPLICATION_JSON)
public String getInstallation() throws IOException {
OAuthClientManager manager = new OAuthClientManager(realm);
BaseAdapterConfig rep = manager.toInstallationRepresentation(realm, oauthClient, getApplication().getBaseUri(uriInfo));
Object rep = manager.toInstallationRepresentation(realm, oauthClient, getApplication().getBaseUri(uriInfo));
// TODO Temporary solution to pretty-print
return JsonSerialization.mapper.writerWithDefaultPrettyPrinter().writeValueAsString(rep);
@ -86,17 +89,26 @@ public class OAuthClientResource {
realm.removeOAuthClient(oauthClient.getId());
}
@Path("credentials")
@PUT
@Path("client-secret")
@POST
@Produces("application/json")
@Consumes("application/json")
public void updateCredentials(List<CredentialRepresentation> credentials) {
logger.debug("updateCredentials");
if (credentials == null) return;
public CredentialRepresentation regenerateSecret() {
logger.debug("regenerateSecret");
UserCredentialModel cred = UserCredentialModel.generateSecret();
realm.updateCredential(oauthClient.getOAuthAgent(), cred);
CredentialRepresentation rep = ModelToRepresentation.toRepresentation(cred);
return rep;
}
for (CredentialRepresentation rep : credentials) {
UserCredentialModel cred = RealmManager.fromRepresentation(rep);
realm.updateCredential(oauthClient.getOAuthAgent(), cred);
}
@Path("client-secret")
@GET
@Produces("application/json")
public CredentialRepresentation getClientSecret() {
logger.debug("getClientSecret");
UserCredentialModel model = realm.getSecret(oauthClient.getOAuthAgent());
if (model == null) throw new NotFoundException("Application does not have a secret");
return ModelToRepresentation.toRepresentation(model);
}
@Path("scope-mappings")

View file

@ -6,6 +6,7 @@ import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.util.JsonSerialization;
import javax.mail.MessagingException;
import javax.mail.internet.AddressException;
@ -14,12 +15,27 @@ import java.io.IOException;
import java.lang.Thread.UncaughtExceptionHandler;
import java.net.SocketException;
import java.util.HashMap;
import java.util.UUID;
public class EmailSenderTest {
private GreenMail greenMail;
private EmailSender emailSender;
@Test
public void testUUID() throws Exception{
System.out.println(UUID.randomUUID());
HashMap<String,String> config = new HashMap<String, String>();
config.put("from", "auto@keycloak.org");
config.put("host", "localhost");
config.put("port", "3025");
System.out.println(JsonSerialization.mapper.writerWithDefaultPrettyPrinter().writeValueAsString(config));
}
@Before
public void before() {
ServerSetup setup = new ServerSetup(3025, "localhost", "smtp");

View file

@ -125,7 +125,7 @@ public class OAuthClient {
parameters.add(new BasicNameValuePair("client_id", clientId));
}
if (password != null) {
parameters.add(new BasicNameValuePair("password", password));
parameters.add(new BasicNameValuePair("secret", password));
}
UrlEncodedFormEntity formEntity = null;

View file

@ -62,8 +62,6 @@ public class CompositeRoleTest {
realm.setAccessCodeLifespan(1000);
realm.setSslNotRequired(true);
realm.setEnabled(true);
realm.addRequiredResourceCredential(UserCredentialModel.PASSWORD);
realm.addRequiredOAuthClientCredential(UserCredentialModel.PASSWORD);
realm.addRequiredCredential(UserCredentialModel.PASSWORD);
final RoleModel realmRole1 = realm.addRole("REALM_ROLE_1");
final RoleModel realmRole2 = realm.addRole("REALM_ROLE_2");
@ -86,21 +84,21 @@ public class CompositeRoleTest {
realmComposite1Application.addScope(realmComposite1);
realmComposite1Application.setBaseUrl("http://localhost:8081/app");
realmComposite1Application.setManagementUrl("http://localhost:8081/app/logout");
realm.updateCredential(realmComposite1Application.getApplicationUser(), UserCredentialModel.password("password"));
realm.updateCredential(realmComposite1Application.getApplicationUser(), UserCredentialModel.secret("password"));
final ApplicationModel realmRole1Application = new ApplicationManager(manager).createApplication(realm, "REALM_ROLE_1_APPLICATION");
realmRole1Application.setEnabled(true);
realmRole1Application.addScope(realmRole1);
realmRole1Application.setBaseUrl("http://localhost:8081/app");
realmRole1Application.setManagementUrl("http://localhost:8081/app/logout");
realm.updateCredential(realmRole1Application.getApplicationUser(), UserCredentialModel.password("password"));
realm.updateCredential(realmRole1Application.getApplicationUser(), UserCredentialModel.secret("password"));
final ApplicationModel appRoleApplication = new ApplicationManager(manager).createApplication(realm, "APP_ROLE_APPLICATION");
appRoleApplication.setEnabled(true);
appRoleApplication.setBaseUrl("http://localhost:8081/app");
appRoleApplication.setManagementUrl("http://localhost:8081/app/logout");
realm.updateCredential(appRoleApplication.getApplicationUser(), UserCredentialModel.password("password"));
realm.updateCredential(appRoleApplication.getApplicationUser(), UserCredentialModel.secret("password"));
final RoleModel appRole1 = appRoleApplication.addRole("APP_ROLE_1");
final RoleModel appRole2 = appRoleApplication.addRole("APP_ROLE_2");
@ -121,7 +119,7 @@ public class CompositeRoleTest {
appCompositeApplication.setEnabled(true);
appCompositeApplication.setBaseUrl("http://localhost:8081/app");
appCompositeApplication.setManagementUrl("http://localhost:8081/app/logout");
realm.updateCredential(appCompositeApplication.getApplicationUser(), UserCredentialModel.password("password"));
realm.updateCredential(appCompositeApplication.getApplicationUser(), UserCredentialModel.secret("password"));
final RoleModel appCompositeRole = appCompositeApplication.addRole("APP_COMPOSITE_ROLE");
appCompositeApplication.addScope(appRole2);
appCompositeRole.addCompositeRole(realmRole1);

View file

@ -9,8 +9,6 @@
"registrationAllowed": true,
"resetPasswordAllowed": true,
"requiredCredentials": [ "password" ],
"requiredApplicationCredentials": [ "password" ],
"requiredOAuthClientCredentials": [ "password" ],
"smtpServer": {
"from": "auto@keycloak.org",
"host": "localhost",
@ -68,7 +66,7 @@
"name" : "third-party",
"enabled": true,
"credentials" : [
{ "type" : "password",
{ "type" : "secret",
"value" : "password" }
]
}
@ -109,7 +107,7 @@
"adminUrl": "http://localhost:8081/app/logout",
"credentials": [
{
"type": "password",
"type": "secret",
"value": "password"
}
]
@ -121,7 +119,7 @@
"adminUrl": "http://localhost:8081/app/logout",
"credentials": [
{
"type": "password",
"type": "secret",
"value": "password"
}
]
@ -133,7 +131,7 @@
"adminUrl": "http://localhost:8081/app/logout",
"credentials": [
{
"type": "password",
"type": "secret",
"value": "password"
}
]
@ -145,7 +143,7 @@
"adminUrl": "http://localhost:8081/app/logout",
"credentials": [
{
"type": "password",
"type": "secret",
"value": "password"
}
]

View file

@ -11,8 +11,6 @@
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"requiredCredentials": [ "password" ],
"requiredApplicationCredentials": [ "password" ],
"requiredOAuthClientCredentials": [ "password" ],
"defaultRoles": [ "user" ],
"smtpServer": {
"from": "auto@keycloak.org",
@ -35,7 +33,7 @@
"name" : "third-party",
"enabled": true,
"credentials" : [
{ "type" : "password",
{ "type" : "secret",
"value" : "password" }
]
}
@ -64,7 +62,7 @@
"adminUrl": "http://localhost:8081/app/logout",
"credentials": [
{
"type": "password",
"type": "secret",
"value": "password"
}
]