Merge pull request #1023 from patriot1burke/master

add oauth mappers
This commit is contained in:
Bill Burke 2015-03-05 12:14:54 -05:00
commit 42eed8145c
10 changed files with 521 additions and 6 deletions

View file

@ -491,6 +491,72 @@ module.config([ '$routeProvider', function($routeProvider) {
},
controller : 'ApplicationProtocolMapperCreateCtrl'
})
.when('/realms/:realm/oauth-clients/:oauth/mappers', {
templateUrl : resourceUrl + '/partials/oauth-client-mappers.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
oauth : function(OAuthClientLoader) {
return OAuthClientLoader();
},
serverInfo : function(ServerInfoLoader) {
return ServerInfoLoader();
}
},
controller : 'OAuthClientProtocolMapperListCtrl'
})
.when('/realms/:realm/oauth-clients/:oauth/add-mappers', {
templateUrl : resourceUrl + '/partials/oauth-client-mappers-add.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
oauth : function(OAuthClientLoader) {
return OAuthClientLoader();
},
serverInfo : function(ServerInfoLoader) {
return ServerInfoLoader();
}
},
controller : 'OAuthClientAddBuiltinProtocolMapperCtrl'
})
.when('/realms/:realm/oauth-clients/:oauth/mappers/:id', {
templateUrl : resourceUrl + '/partials/oauth-client-protocol-mapper-detail.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
oauth : function(OAuthClientLoader) {
return OAuthClientLoader();
},
serverInfo : function(ServerInfoLoader) {
return ServerInfoLoader();
},
mapper : function(OAuthClientProtocolMapperLoader) {
return OAuthClientProtocolMapperLoader();
}
},
controller : 'OAuthClientProtocolMapperCtrl'
})
.when('/create/oauth-client/:realm/:oauth/mappers', {
templateUrl : resourceUrl + '/partials/oauth-client-protocol-mapper-detail.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
serverInfo : function(ServerInfoLoader) {
return ServerInfoLoader();
},
oauth : function(OAuthClientLoader) {
return OAuthClientLoader();
}
},
controller : 'OAuthClientProtocolMapperCreateCtrl'
})
.when('/realms/:realm/applications/:application/sessions', {
templateUrl : resourceUrl + '/partials/application-sessions.html',
resolve : {
@ -1411,7 +1477,7 @@ module.directive('kcNavigationApplication', function () {
scope: true,
restrict: 'E',
replace: true,
templateUrl: resourceUrl + '/templates/kc-navigation-application.html',
templateUrl: resourceUrl + '/templates/kc-navigation-application.html'
}
});
@ -1420,7 +1486,7 @@ module.directive('kcNavigationOauthClient', function () {
scope: true,
restrict: 'E',
replace: true,
templateUrl: resourceUrl + '/templates/kc-navigation-oauth-client.html',
templateUrl: resourceUrl + '/templates/kc-navigation-oauth-client.html'
}
});

View file

@ -415,3 +415,197 @@ module.controller('OAuthClientIdentityProviderCtrl', function($scope, $route, re
}, true);
});
module.controller('OAuthClientProtocolMapperListCtrl', function($scope, realm, oauth, serverInfo,
OAuthClientProtocolMappersByProtocol,
$http, $location, Dialog, Notifications) {
$scope.realm = realm;
$scope.oauth = oauth;
if (oauth.protocol == null) {
oauth.protocol = 'openid-connect';
}
var protocolMappers = serverInfo.protocolMapperTypes[oauth.protocol];
var mapperTypes = {};
for (var i = 0; i < protocolMappers.length; i++) {
mapperTypes[protocolMappers[i].id] = protocolMappers[i];
}
$scope.mapperTypes = mapperTypes;
var updateMappers = function() {
$scope.mappers = OAuthClientProtocolMappersByProtocol.query({realm : realm.realm, oauth : oauth.id, protocol : oauth.protocol});
};
updateMappers();
});
module.controller('OAuthClientAddBuiltinProtocolMapperCtrl', function($scope, realm, oauth, serverInfo,
OAuthClientProtocolMappersByProtocol,
$http, $location, Dialog, Notifications) {
$scope.realm = realm;
$scope.oauth = oauth;
if (oauth.protocol == null) {
oauth.protocol = 'openid-connect';
}
var protocolMappers = serverInfo.protocolMapperTypes[oauth.protocol];
var mapperTypes = {};
for (var i = 0; i < protocolMappers.length; i++) {
mapperTypes[protocolMappers[i].id] = protocolMappers[i];
}
$scope.mapperTypes = mapperTypes;
var updateMappers = function() {
var appMappers = OAuthClientProtocolMappersByProtocol.query({realm : realm.realm, oauth : oauth.id, protocol : oauth.protocol}, function() {
var builtinMappers = serverInfo.builtinProtocolMappers[oauth.protocol];
for (var i = 0; i < appMappers.length; i++) {
for (var j = 0; j < builtinMappers.length; j++) {
if (builtinMappers[j].name == appMappers[i].name
&& builtinMappers[j].protocolMapper == appMappers[i].protocolMapper) {
console.log('removing: ' + builtinMappers[j].name);
builtinMappers.splice(j, 1);
break;
}
}
}
for (var j = 0; j < builtinMappers.length; j++) {
console.log('builtin left: ' + builtinMappers[j].name);
}
$scope.mappers = builtinMappers;
for (var i = 0; i < $scope.mappers.length; i++) {
$scope.mappers[i].isChecked = false;
}
});
};
updateMappers();
$scope.add = function() {
var toAdd = [];
for (var i = 0; i < $scope.mappers.length; i++) {
if ($scope.mappers[i].isChecked) {
delete $scope.mappers[i].isChecked;
toAdd.push($scope.mappers[i]);
}
}
$http.post(authUrl + '/admin/realms/' + realm.realm + '/oauth-clients-by-id/' + oauth.id + '/protocol-mappers/add-models',
toAdd).success(function() {
Notifications.success("Mappers added");
$location.url('/realms/' + realm.realm + '/oauth-clients/' + oauth.id + '/mappers');
}).error(function() {
Notifications.error("Error adding mappers");
$location.url('/realms/' + realm.realm + '/oauth-clients/' + oauth.id + '/mappers');
});
};
});
module.controller('OAuthClientProtocolMapperCtrl', function($scope, realm, serverInfo, oauth, mapper, OAuthClientProtocolMapper, Notifications, Dialog, $location) {
if (oauth.protocol == null) {
oauth.protocol = 'openid-connect';
}
$scope.realm = realm;
$scope.oauth = oauth;
$scope.create = false;
var protocol = oauth.protocol;
$scope.protocol = oauth.protocol;
$scope.mapper = angular.copy(mapper);
var oldCopy = angular.copy($scope.realm);
$scope.changed = false;
var protocolMappers = serverInfo.protocolMapperTypes[protocol];
for (var i = 0; i < protocolMappers.length; i++) {
if (protocolMappers[i].id == mapper.protocolMapper) {
$scope.mapperType = protocolMappers[i];
}
}
$scope.$watch(function() {
return $location.path();
}, function() {
$scope.path = $location.path().substring(1).split("/");
});
$scope.$watch('mapper', function() {
if (!angular.equals($scope.mapper, mapper)) {
$scope.changed = true;
}
}, true);
$scope.save = function() {
OAuthClientProtocolMapper.update({
realm : realm.realm,
oauth: oauth.id,
id : mapper.id
}, $scope.mapper, function() {
$scope.changed = false;
mapper = angular.copy($scope.mapper);
$location.url("/realms/" + realm.realm + '/oauth-clients/' + oauth.id + "/mappers/" + mapper.id);
Notifications.success("Your changes have been saved.");
});
};
$scope.reset = function() {
$scope.mapper = angular.copy(mapper);
$scope.changed = false;
};
$scope.cancel = function() {
//$location.url("/realms");
window.history.back();
};
$scope.remove = function() {
Dialog.confirmDelete($scope.mapper.name, 'mapper', function() {
OAuthClientProtocolMapper.remove({ realm: realm.realm, oauth: oauth.id, id : $scope.mapper.id }, function() {
Notifications.success("The mapper has been deleted.");
$location.url("/realms/" + realm.realm + '/oauth-clients/' + oauth.id + "/mappers");
});
});
};
});
module.controller('OAuthClientProtocolMapperCreateCtrl', function($scope, realm, serverInfo, oauth, OAuthClientProtocolMapper, Notifications, Dialog, $location) {
if (oauth.protocol == null) {
oauth.protocol = 'openid-connect';
}
$scope.realm = realm;
$scope.oauth = oauth;
$scope.create = true;
var protocol = oauth.protocol;
$scope.protocol = protocol;
$scope.mapper = { protocol : oauth.protocol, config: {}};
$scope.mapperTypes = serverInfo.protocolMapperTypes[protocol];
$scope.$watch(function() {
return $location.path();
}, function() {
$scope.path = $location.path().substring(1).split("/");
});
$scope.save = function() {
$scope.mapper.protocolMapper = $scope.mapperType.id;
OAuthClientProtocolMapper.save({
realm : realm.realm, oauth: oauth.id
}, $scope.mapper, function(data, headers) {
var l = headers().location;
var id = l.substring(l.lastIndexOf("/") + 1);
$location.url("/realms/" + realm.realm + '/oauth-clients/' + oauth.id + "/mappers/" + id);
Notifications.success("Mapper has been created.");
});
};
$scope.cancel = function() {
//$location.url("/realms");
window.history.back();
};
});

View file

@ -89,6 +89,16 @@ module.factory('ApplicationProtocolMapperLoader', function(Loader, ApplicationPr
});
});
module.factory('OAuthClientProtocolMapperLoader', function(Loader, OAuthClientProtocolMapper, $route, $q) {
return Loader.get(OAuthClientProtocolMapper, function() {
return {
realm : $route.current.params.realm,
oauth : $route.current.params.oauth,
id: $route.current.params.id
}
});
});
module.factory('UserLoader', function(Loader, User, $route, $q) {
return Loader.get(User, function() {

View file

@ -188,6 +188,8 @@ module.factory('ServerInfo', function($resource) {
return $resource(authUrl + '/admin/serverinfo');
});
module.factory('ApplicationProtocolMapper', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/protocol-mappers/models/:id', {
realm : '@realm',
@ -200,6 +202,28 @@ module.factory('ApplicationProtocolMapper', function($resource) {
});
});
module.factory('OAuthClientProtocolMapper', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/oauth-clients-by-id/:oauth/protocol-mappers/models/:id', {
realm : '@realm',
oauth: '@oauth',
id : "@id"
}, {
update : {
method : 'PUT'
}
});
});
module.factory('OAuthClientProtocolMappersByProtocol', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/oauth-clients-by-id/:oauth/protocol-mappers/protocol/:protocol', {
realm : '@realm',
oauth : "@oauth",
protocol : "@protocol"
});
});
module.factory('User', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/users/:userId', {

View file

@ -35,7 +35,7 @@
</thead>
<tbody>
<tr ng-repeat="mapper in mappers | filter:search">
<td><a href="#/realms/{{realm.realm}}/protocols/{{application.protocol}}/mappers/{{mapper.id}}">{{mapper.name}}</a></td>
<td>{{mapper.name}}</td>
<td>{{mapperTypes[mapper.protocolMapper].category}}</td>
<td>{{mapperTypes[mapper.protocolMapper].name}}</td>
<td><input type="checkbox" ng-model="mapper.isChecked"></td>

View file

@ -0,0 +1,49 @@
<div class="bs-sidebar col-md-3 clearfix" data-ng-include data-src="resourceUrl + '/partials/realm-menu.html'"></div>
<div id="content-area" class="col-md-9" role="main">
<kc-navigation-oauth-client></kc-navigation-oauth-client>
<div id="content">
<ol class="breadcrumb" data-ng-hide="create">
<li><a href="#/realms/{{realm.realm}}/oauth-clients">OAuth Clients</a></li>
<li><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}">{{oauth.name}}</a></li>
<li><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/mappers">{{oauth.name}} Mappers</a></li>
<li class="active">Add Builtin Protocol Mappers</li>
</ol>
<h2>Add Builtin Protocol Mappers <span tooltip-placement="right" tooltip="Protocol mappers perform transformation on tokens and documents. They an do things like map user data into protocol claims, or just transform any requests going between the application and auth server." class="fa fa-info-circle"></span></h2>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th class="kc-table-actions" colspan="4">
<div class="search-comp clearfix">
<input type="text" placeholder="Search..." class="form-control search" data-ng-model="search.name"
onkeyup="if(event.keyCode == 13){$(this).next('button').click();}">
<button type="submit" class="kc-icon-search" tooltip-placement="right"
tooltip="Search by mapper name.">
Icon: search
</button>
</div>
<div class="pull-right">
<button class="btn btn-primary" data-ng-click="add()">Add Selected</button>
</div>
</th>
</tr>
<tr data-ng-hide="mappers.length == 0">
<th>Name</th>
<th>Category</th>
<th>Type</th>
<th>Add</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="mapper in mappers | filter:search">
<td>{{mapper.name}}</td>
<td>{{mapperTypes[mapper.protocolMapper].category}}</td>
<td>{{mapperTypes[mapper.protocolMapper].name}}</td>
<td><input type="checkbox" ng-model="mapper.isChecked"></td>
</tr>
<tr data-ng-show="mappers.length == 0">
<td>No mappers available</td>
</tr>
</tbody>
</table>
</div>
</div>

View file

@ -0,0 +1,47 @@
<div class="bs-sidebar col-md-3 clearfix" data-ng-include data-src="resourceUrl + '/partials/realm-menu.html'"></div>
<div id="content-area" class="col-md-9" role="main">
<kc-navigation-oauth-client></kc-navigation-oauth-client>
<div id="content">
<ol class="breadcrumb" data-ng-hide="create">
<li><a href="#/realms/{{realm.realm}}/oauth-clients">OAuth Clients</a></li>
<li><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}">{{oauth.name}}</a></li>
<li class="active">Protocol Mappers</li>
</ol>
<h2><span>{{realm.realm}} </span> {{oauth.name}} {{oauth.protocol}} Protocol Mappers <span tooltip-placement="right" tooltip="Protocol mappers perform transformation on tokens and documents. They an do things like map user data into protocol claims, or just transform any requests going between the application and auth server." class="fa fa-info-circle"></span></h2>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th class="kc-table-actions" colspan="4">
<div class="search-comp clearfix">
<input type="text" placeholder="Search..." class="form-control search" data-ng-model="search.name"
onkeyup="if(event.keyCode == 13){$(this).next('button').click();}">
<button type="submit" class="kc-icon-search" tooltip-placement="right"
tooltip="Search by mapper name.">
Icon: search
</button>
</div>
<div class="pull-right">
<a class="btn btn-primary" href="#/create/oauth-client/{{realm.realm}}/{{oauth.id}}/mappers">Create</a>
<a class="btn btn-primary" href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/add-mappers">Add Builtin</a>
</div>
</th>
</tr>
<tr data-ng-hide="mappers.length == 0">
<th>Name</th>
<th>Category</th>
<th>Type</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="mapper in mappers | filter:search">
<td><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/mappers/{{mapper.id}}">{{mapper.name}}</a></td>
<td>{{mapperTypes[mapper.protocolMapper].category}}</td>
<td>{{mapperTypes[mapper.protocolMapper].name}}</td>
</tr>
<tr data-ng-show="mappers.length == 0">
<td>No mappers available</td>
</tr>
</tbody>
</table>
</div>
</div>

View file

@ -0,0 +1,103 @@
<div class="bs-sidebar col-sm-3 " data-ng-include data-src="resourceUrl + '/partials/realm-menu.html'"></div>
<div id="content-area" class="col-sm-9" role="main">
<kc-navigation-oauth-client></kc-navigation-oauth-client>
<div id="content">
<ol class="breadcrumb" data-ng-show="create">
<li><a href="#/realms/{{realm.realm}}/oauth-clients">OAuth Clients</a></li>
<li><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}">{{oauth.name}}</a></li>
<li><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/mappers">{{oauth.name}} Mappers</a></li>
<li class="active">Create Protocol Mapper</li>
</ol>
<ol class="breadcrumb" data-ng-hide="create">
<li><a href="#/realms/{{realm.realm}}/oauth-clients">OAuth Clients</a></li>
<li><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}">{{oauth.name}}</a></li>
<li><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/mappers">{{oauth.name}} Mappers</a></li>
<li class="active">{{mapper.name}}</li>
</ol>
<h2 class="pull-left" data-ng-hide="create">{{mapper.name}} Protocol Mapper</h2>
<h2 class="pull-left" data-ng-show="create">Create Protocol Mapper</h2>
<p class="subtitle"><span class="required">*</span> Required fields</p>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
<fieldset>
<div class="form-group clearfix">
<label class="col-sm-2 control-label" for="protocol">Protocol</label>
<div class="col-sm-4">
<input class="form-control" id="protocol" type="text" ng-model="protocol" readonly>
</div>
<span tooltip-placement="right" tooltip="Protocol." class="fa fa-info-circle"></span>
</div>
<div class="form-group clearfix" data-ng-show="!create">
<label class="col-sm-2 control-label" for="mapperId">ID </label>
<div class="col-sm-4">
<input class="form-control" id="mapperId" type="text" ng-model="mapper.id" readonly>
</div>
</div>
<div class="form-group clearfix">
<label class="col-sm-2 control-label" for="name">Name</label>
<div class="col-sm-4">
<input class="form-control" id="name" type="text" ng-model="mapper.name" data-ng-readonly="!create">
</div>
<span tooltip-placement="right" tooltip="Name of the mapper." class="fa fa-info-circle"></span>
</div>
<div class="form-group">
<label for="consentRequired" class="col-sm-2 control-label">Consent required</label>
<div class="col-sm-4">
<input ng-model="mapper.consentRequired" name="consentRequired" id="consentRequired" onoffswitch />
</div>
<span tooltip-placement="right" tooltip="When granting temporary access, must the user consent to providing this data to the client?" class="fa fa-info-circle"></span>
</div>
<div class="form-group" data-ng-show="mapper.consentRequired">
<label class="col-sm-2 control-label" for="consentText">Consent Text </label>
<div class="col-sm-4">
<textarea class="form-control" rows="5" cols="50" id="consentText" name="consentText" data-ng-model="mapper.consentText"></textarea>
</div>
<span tooltip-placement="right" tooltip="Text to display on consent page" class="fa fa-info-circle"></span>
</div>
<div class="form-group" data-ng-show="create">
<label class="col-sm-2 control-label" for="mapperTypeCreate">Mapper Type</label>
<div class="col-sm-6">
<div class="select-kc">
<select id="mapperTypeCreate"
ng-model="mapperType"
ng-options="mapperType.name for mapperType in mapperTypes">
</select>
</div>
</div>
<span tooltip-placement="right" tooltip="{{mapperType.helpText}}" class="fa fa-info-circle"></span>
</div>
<div class="form-group clearfix" data-ng-hide="create">
<label class="col-sm-2 control-label" for="mapperType">Mapper Type</label>
<div class="col-sm-4">
<input class="form-control" id="mapperType" type="text" ng-model="mapperType.name" data-ng-readonly="true">
</div>
<span tooltip-placement="right" tooltip="{{mapperType.helpText}}" class="fa fa-info-circle"></span>
</div>
<div data-ng-repeat="option in mapperType.properties" class="form-group">
<label class="col-sm-2 control-label">{{option.label}} </label>
<div class="col-sm-4" data-ng-hide="option.type == 'boolean'">
<input class="form-control" type="text" data-ng-model="mapper.config[ option.name ]" >
</div>
<div class="col-sm-4" data-ng-show="option.type == 'boolean'">
<input ng-model="mapper.config[ option.name ]" value="'true'" name="option.name" id="option.name" onoffswitchmodel />
</div>
<span tooltip-placement="right" tooltip="{{option.helpText}}" class="fa fa-info-circle"></span>
</div>
</fieldset>
<div class="pull-right form-actions" data-ng-show="create && access.manageRealm">
<button kc-cancel data-ng-click="cancel()">Cancel</button>
<button kc-save>Save</button>
</div>
<div class="pull-right form-actions" data-ng-show="!create && access.manageRealm">
<button kc-reset data-ng-show="changed">Clear changes</button>
<button kc-save data-ng-show="changed">Save</button>
<button kc-delete data-ng-click="remove()" data-ng-hide="changed">Delete</button>
</div>
</form>
</div>
</div>

View file

@ -1,7 +1,7 @@
<ul class="nav nav-tabs nav-tabs-pf" data-ng-show="!create">
<li ng-class="{active: !path[4]}"><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}">Settings</a></li>
<li ng-class="{active: path[4] == 'credentials'}" data-ng-show="!oauth.publicClient"><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/credentials">Credentials</a></li>
<li ng-class="{active: path[4] == 'claims'}"><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/claims">Claims</a></li>
<li ng-class="{active: path[4] == 'mappers'}" data-ng-show="!oauth.bearerOnly"><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/mappers">Mappers</a></li>
<li ng-class="{active: path[4] == 'scope-mappings'}"><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/scope-mappings">Scope</a></li>
<li ng-class="{active: path[4] == 'revocation'}"><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/revocation">Revocation</a></li>
<li ng-class="{active: path[4] == 'identity-provider'}" data-ng-show="realm.identityFederationEnabled"><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/identity-provider">Identity Provider</a></li>

View file

@ -635,7 +635,18 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
appData.setEnabled(true);
getMongoStore().insertEntity(appData, invocationContext);
ApplicationModel model = new ApplicationAdapter(session, this, appData, invocationContext);
final ApplicationModel model = new ApplicationAdapter(session, this, appData, invocationContext);
session.getKeycloakSessionFactory().publish(new ApplicationCreationEvent() {
@Override
public ApplicationModel getCreatedApplication() {
return model;
}
@Override
public ClientModel getCreatedClient() {
return model;
}
});
return model;
}
@ -657,7 +668,18 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
oauthClient.setName(name);
getMongoStore().insertEntity(oauthClient, invocationContext);
OAuthClientAdapter model = new OAuthClientAdapter(session, this, oauthClient, invocationContext);
final OAuthClientAdapter model = new OAuthClientAdapter(session, this, oauthClient, invocationContext);
session.getKeycloakSessionFactory().publish(new OAuthClientCreationEvent() {
@Override
public OAuthClientModel getCreatedOAuthClient() {
return model;
}
@Override
public ClientModel getCreatedClient() {
return model;
}
});
return model;
}