[KEYCLOAK-2999] - Changes to authz examples for integration test

This commit is contained in:
Pedro Igor 2016-06-28 11:43:43 -03:00
parent afa9471c7c
commit 2db41ef052
18 changed files with 85 additions and 77 deletions

View file

@ -49,7 +49,7 @@
*/
this.authorize = function (wwwAuthenticateHeader) {
this.then = function (onGrant, onDeny, onError) {
if (wwwAuthenticateHeader.startsWith('UMA')) {
if (wwwAuthenticateHeader.indexOf('UMA') != -1) {
var params = wwwAuthenticateHeader.split(',');
for (i = 0; i < params.length; i++) {
@ -96,7 +96,7 @@
));
}
}
} else if (wwwAuthenticateHeader.startsWith('KC_ETT')) {
} else if (wwwAuthenticateHeader.indexOf('KC_ETT') != -1) {
var params = wwwAuthenticateHeader.substring('KC_ETT'.length).trim().split(',');
var clientId = null;

View file

@ -1,9 +1,8 @@
{
"realm" : "hello-world-authz",
"enabled" : true,
"privateKey" : "MIIEpQIBAAKCAQEAzMhNM9HXNQWhVf1m64zS67SIyQjj+tV5GR+MqlRTWDXdo8GAWHd+alY1urRhfRoqMy4F499+8wh2REKFykNt0ng6s6wWnEaKDboS3SAUV6lybcOAkwIOCtCZj1ItddKG3m64fzxDDQrcpkbiAvw3S8KJ4UJK+pyh9iX01duSDtM/HhPawsPdY8JSMfuo1IxQ2Vxw+8RKwbbdUeew6cyYGYAeFYwA66mlM3otB0RBHh4bjwg8297+2g53TdwM2rbCHRbrorMQD3031OTyFSp7lXCtoMLWRfAFnOP/2yZWZMXbiJheC0R3sLbU7Ef0/cUbYyk4Ckfq6pcYDR+VZBF7AwIDAQABAoIBAAwa4wVnKBOIS6srmYPfBTDNsTBBCEjxiYEErmn7JhoWxQ1DCPUxyxU6F177/q9Idqoj1FFOCtEO9P6/9+ym470HQmEQkR2Xxd1d3HOZy9oKuCro3ZbTDkVxY0JnlyxZz4MihGFxDH2e4MArfHy0sAgYbdIU+x2pWKGWSMzDd/TMSOExhc/sIQAg6ljbPCLLXCPQFAncoHRyGPrkRZs6UTZi5SJuCglVa2/3G+0drDdPuA83/mwsZfIBqQgbGbFgtq5T5C6CKMkPOQ42Rcclm7kEr6riTkJRo23EO1iOJVpxzI0tbxZsJAsW7zeqv0wWRyUgVfQAje6OdsNexp5aCtECgYEA6nMHCQ9xXvufCyzpIbYGxdAGqH6m1AR5gXerHqRiGNx+8UUt/E9cy/HTOhmZDK/eC4BT9tImeF01l1oSU/+wGKfux0SeAQchBhhq8GD6jmrtgczKAfZHp0Zrht7o9qu9KE7ZNWRmY1foJN9yNYmzY6qqHEy+zNo9amcqT7UZKO8CgYEA35sp9fMpMqkJE+NEJ9Ph/t2081BEkC0DYIuETZRSi+Ek5AliWTyEkg+oisTbWzi6fMQHS7W+M1SQP6djksLQNPP+353DKgup5gtKS+K/y2xNd7fSsNmkjW1bdJJpID7WzwwmwdahHxpcnFFuEXi5FkG3Vqmtd3cD0TYL33JlRy0CgYEA0+a3eybsDy9Zpp4m8IM3R98nxW8DlimdMLlafs2QpGvWiHdAgwWwF90wTxkHzgG+raKFQVbb0npcj7mnSyiUnxRZqt2H+eHZpUq4jR76F3LpzCGui2tvg+8QDMy4vwqmYyIxDCL8r9mqRnl3HpChBPoh2oY7BahTTjKEeZpzbR0CgYEAoNnVjX+mGzNNvGi4Fo5s/BIwoPcU20IGM+Uo/0W7O7Rx/Thi7x6BnzB0ZZ7GzRA51paNSQEsGXCzc5bOIjzR2cXLisDKK+zIAxwMDhrHLWZzM7OgdGeb38DTEUBhLzkE/VwYZUgoD1+/TxOkwhy9yCzt3gGhL1cF//GJCOwZvuECgYEAgsO4rdYScgCpsyePnHsFk+YtqtdORnmttF3JFcL3w2QneXuRwg2uW2Kfz8CVphrR9eOU0tiw38w6QTHIVeyRY8qqlHtiXj6dEYz7frh/k4hI29HwFx43rRpnAnN8kBEJYBYdbjaQ35Wsqkfu1tvHJ+6fxSwvQu/TVdGp0OfilAY=",
"publicKey" : "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzMhNM9HXNQWhVf1m64zS67SIyQjj+tV5GR+MqlRTWDXdo8GAWHd+alY1urRhfRoqMy4F499+8wh2REKFykNt0ng6s6wWnEaKDboS3SAUV6lybcOAkwIOCtCZj1ItddKG3m64fzxDDQrcpkbiAvw3S8KJ4UJK+pyh9iX01duSDtM/HhPawsPdY8JSMfuo1IxQ2Vxw+8RKwbbdUeew6cyYGYAeFYwA66mlM3otB0RBHh4bjwg8297+2g53TdwM2rbCHRbrorMQD3031OTyFSp7lXCtoMLWRfAFnOP/2yZWZMXbiJheC0R3sLbU7Ef0/cUbYyk4Ckfq6pcYDR+VZBF7AwIDAQAB",
"certificate" : "MIICsTCCAZkCBgFVETX4AzANBgkqhkiG9w0BAQsFADAcMRowGAYDVQQDDBFIZWxsbyBXb3JsZCBBdXRoWjAeFw0xNjA2MDIxMzAxMzdaFw0yNjA2MDIxMzAzMTdaMBwxGjAYBgNVBAMMEUhlbGxvIFdvcmxkIEF1dGhaMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzMhNM9HXNQWhVf1m64zS67SIyQjj+tV5GR+MqlRTWDXdo8GAWHd+alY1urRhfRoqMy4F499+8wh2REKFykNt0ng6s6wWnEaKDboS3SAUV6lybcOAkwIOCtCZj1ItddKG3m64fzxDDQrcpkbiAvw3S8KJ4UJK+pyh9iX01duSDtM/HhPawsPdY8JSMfuo1IxQ2Vxw+8RKwbbdUeew6cyYGYAeFYwA66mlM3otB0RBHh4bjwg8297+2g53TdwM2rbCHRbrorMQD3031OTyFSp7lXCtoMLWRfAFnOP/2yZWZMXbiJheC0R3sLbU7Ef0/cUbYyk4Ckfq6pcYDR+VZBF7AwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQANm5gIT/c50lwjawM686gNXpppLA928WsCOn9NIIWjSKekP8Bf9S73kf7vWcsEppm5B8rRyRxolXmzwghv74L7uVDg8Injjgj+XbPVQP+cJqWpSaMZHF7UfWe0/4M945Xcbmsl5q+m9PmrPG0AaaZhqXHcp4ehB1H+awyRqiERpJUuwZNycw2+2kjDADpsFf8hZVUd1F6ReYyOkqUyUjbL+jYTC7ZBNa7Ok+w6HCXWgkgVATAgQXJRM3w14IOc5MH/vfMCrCl/eNQLbjGl9y7u8PKwh3MXHDO2OLqtg6hOTSrOGUPJZGmGtUAl+2/R7FzoWkML/BNe2hjsL6UJwg91",
"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" ],
"users" :
[

View file

@ -34,7 +34,6 @@
<name>Keycloak Authz: Hello World Example</name>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.jboss.as.plugins</groupId>

View file

@ -1,6 +1,6 @@
{
"realm": "hello-world-authz",
"realm-public-key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzMhNM9HXNQWhVf1m64zS67SIyQjj+tV5GR+MqlRTWDXdo8GAWHd+alY1urRhfRoqMy4F499+8wh2REKFykNt0ng6s6wWnEaKDboS3SAUV6lybcOAkwIOCtCZj1ItddKG3m64fzxDDQrcpkbiAvw3S8KJ4UJK+pyh9iX01duSDtM/HhPawsPdY8JSMfuo1IxQ2Vxw+8RKwbbdUeew6cyYGYAeFYwA66mlM3otB0RBHh4bjwg8297+2g53TdwM2rbCHRbrorMQD3031OTyFSp7lXCtoMLWRfAFnOP/2yZWZMXbiJheC0R3sLbU7Ef0/cUbYyk4Ckfq6pcYDR+VZBF7AwIDAQAB",
"realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "http://localhost:8080/auth",
"ssl-required": "external",
"resource": "hello-world-authz-service",

View file

@ -13,7 +13,6 @@
<script src="http://localhost:8080/auth/js/keycloak.js"></script>
<script src="http://localhost:8080/auth/js/keycloak-authz.js"></script>
<script src="js/security/keycloak-authorization.js" type="text/javascript"></script>
<script src="js/identity.js" type="text/javascript"></script>
<script src="js/app.js" type="text/javascript"></script>
</head>

View file

@ -1,12 +1,8 @@
{
"realm": "photoz",
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url" : "http://localhost:8080/auth",
"ssl-required" : "external",
"resource" : "photoz-html5-client",
"public-client" : true,
"use-resource-role-mappings": "false",
"scope" : {
"realm" : [ "user" ]
}
"public-client" : true
}

View file

@ -10,7 +10,7 @@
<td>
<ul>
<li data-ng-repeat="p in value">
<a href="#/album/{{p.id}}">{{p.name}}</a> - [<a href="#" ng-click="deleteAlbum(p)">X</a>]
<a href="#/album/{{p.id}}">{{p.name}}</a> - [<a href="#" id="delete-{{p.name}}" ng-click="deleteAlbum(p)">X</a>]
</li>
</ul>
</td>

View file

@ -1,7 +1,7 @@
<h1>Create an Album</h1>
<form>
Name: <input type="text" ng-model="album.name"/>
Name: <input type="text" id="album.name" ng-model="album.name"/>
<button ng-click="create()">Save</button>
<button ng-click="create()" id="save-album">Save</button>
</form>

View file

@ -1,12 +1,12 @@
<h2><span>Welcome To Photoz, {{Identity.claims.name}}</span> [<a href="" ng-click="Identity.logout()">Sign Out</a>]</h2>
<div data-ng-show="Identity.isAdmin()"><b>Administration: </b> [<a href="#/admin/album">All Albums</a>]</div>
<div data-ng-show="Identity.isAdmin()"><b>Administration: </b> [<a href="#/admin/album" id="admin-albums">All Albums</a>]</div>
<hr/>
<br/>
<div data-ng-show="!Identity.isAdmin()">
<a href="#/album/create">Create Album</a> | <a href="#/profile">My Profile</a>
<a href="#/album/create" id="create-album">Create Album</a> | <a href="#/profile">My Profile</a>
<br/>
<br/>
<span data-ng-show="albums.length == 0">You don't have any albums, yet.</span>
<span data-ng-show="albums.length == 0" id="resource-list-empty">You don't have any albums, yet.</span>
<table class="table" data-ng-show="albums.length > 0">
<thead>
<tr>
@ -15,7 +15,7 @@
</thead>
<tbody>
<tr data-ng-repeat="p in albums">
<td><a href="#/album/{{p.id}}">{{p.name}}</a> - [<a href="#" ng-click="deleteAlbum(p)">X</a>]</td>
<td><a href="#/album/{{p.id}}">{{p.name}}</a> - [<a href="#" id="delete-{{p.name}}" ng-click="deleteAlbum(p)">X</a>]</td>
</tr>
</tbody>
</table>

View file

@ -92,17 +92,19 @@
"publicClient": true,
"redirectUris": [
"/photoz-html5-client/*"
]
],
"webOrigins": ["*"]
},
{
"clientId": "photoz-restful-api",
"secret": "secret",
"enabled": true,
"baseUrl": "/photoz-restful-api",
"authorizationServicesEnabled" : true,
"redirectUris": [
"/photoz-restful-api/*"
],
"secret": "secret"
"webOrigins" : ["*"]
}
]
}

View file

@ -152,7 +152,7 @@
}
},
{
"name": "Delete Album Policy",
"name": "Delete Album Permission",
"description": "A policy that only allows the owner to delete his albums.",
"type": "scope",
"logic": "POSITIVE",

View file

@ -25,6 +25,7 @@
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>

View file

@ -1,11 +1,14 @@
package org.keycloak.example.photoz.album;
import org.keycloak.authorization.client.AuthzClient;
import org.keycloak.authorization.client.Configuration;
import org.keycloak.authorization.client.representation.ResourceRepresentation;
import org.keycloak.authorization.client.representation.ScopeRepresentation;
import org.keycloak.authorization.client.resource.ProtectionResource;
import org.keycloak.example.photoz.ErrorResponse;
import org.keycloak.example.photoz.entity.Album;
import org.keycloak.representations.adapters.config.AdapterConfig;
import org.keycloak.util.JsonSerialization;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
@ -38,9 +41,18 @@ public class AlbumService {
@PersistenceContext
private EntityManager entityManager;
@Context
private HttpServletRequest request;
private AuthzClient authzClient;
public AlbumService() {
}
@POST
@Consumes("application/json")
public Response create(@Context HttpServletRequest request, Album newAlbum) {
public Response create(Album newAlbum) {
Principal userPrincipal = request.getUserPrincipal();
newAlbum.setUserId(userPrincipal.getName());
@ -78,7 +90,7 @@ public class AlbumService {
@GET
@Produces("application/json")
public Response findAll(@Context HttpServletRequest request) {
public Response findAll() {
return Response.ok(this.entityManager.createQuery("from Album where userId = '" + request.getUserPrincipal().getName() + "'").getResultList()).build();
}
@ -107,7 +119,7 @@ public class AlbumService {
albumResource.setOwner(album.getUserId());
AuthzClient.create().protection().resource().create(albumResource);
getAuthzClient().protection().resource().create(albumResource);
} catch (Exception e) {
throw new RuntimeException("Could not register protected resource.", e);
}
@ -117,7 +129,7 @@ public class AlbumService {
String uri = "/album/" + album.getId();
try {
ProtectionResource protection = AuthzClient.create().protection();
ProtectionResource protection = getAuthzClient().protection();
Set<String> search = protection.resource().findByFilter("uri=" + uri);
if (search.isEmpty()) {
@ -129,4 +141,19 @@ public class AlbumService {
throw new RuntimeException("Could not search protected resource.", e);
}
}
private AuthzClient getAuthzClient() {
if (this.authzClient == null) {
try {
AdapterConfig adapterConfig = JsonSerialization.readValue(this.request.getServletContext().getResourceAsStream("/WEB-INF/keycloak.json"), AdapterConfig.class);
Configuration configuration = new Configuration(adapterConfig.getAuthServerUrl(), adapterConfig.getRealm(), adapterConfig.getResource(), adapterConfig.getCredentials(), null);
this.authzClient = AuthzClient.create(configuration);
} catch (Exception e) {
throw new RuntimeException("Could not create authorization client.", e);
}
}
return this.authzClient;
}
}

View file

@ -1,8 +0,0 @@
{
"realm": "photoz",
"auth-server-url": "http://localhost:8080/auth",
"resource": "photoz-restful-api",
"credentials": {
"secret": "secret"
}
}

View file

@ -1,6 +1,6 @@
<%@page import="org.keycloak.AuthorizationContext" %>
<%@ page import="org.keycloak.KeycloakSecurityContext" %>
<%@ page import="org.keycloak.representations.authorization.Permission" %>
<%@ page import="org.keycloak.representations.idm.authorization.Permission" %>
<%
KeycloakSecurityContext keycloakSecurityContext = (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName());

View file

@ -6,7 +6,7 @@ module.controller('ResourceServerCtrl', function($scope, realm, ResourceServer)
});
});
module.controller('ResourceServerDetailCtrl', function($scope, $http, $route, $location, $upload, realm, ResourceServer, client, AuthzDialog, Notifications) {
module.controller('ResourceServerDetailCtrl', function($scope, $http, $route, $location, $upload, $modal, realm, ResourceServer, client, AuthzDialog, Notifications) {
$scope.realm = realm;
$scope.client = client;
@ -31,8 +31,7 @@ module.controller('ResourceServerDetailCtrl', function($scope, $http, $route, $l
}
$scope.reset = function() {
$scope.server = angular.copy(data);
$scope.changed = false;
$route.reload();
}
$scope.export = function() {
@ -54,38 +53,29 @@ module.controller('ResourceServerDetailCtrl', function($scope, $http, $route, $l
delete $scope.settings
}
$scope.onFileSelect = function($files) {
$scope.files = $files;
$scope.onFileSelect = function($fileContent) {
$scope.server = angular.copy(JSON.parse($fileContent));
$scope.importing = true;
};
$scope.clearFileSelect = function() {
$scope.files = null;
$scope.viewImportDetails = function() {
$modal.open({
templateUrl: resourceUrl + '/partials/modal/view-object.html',
controller: 'ObjectModalCtrl',
resolve: {
object: function () {
return $scope.server;
}
}
})
};
$scope.import = function () {
ResourceServer.import({realm : realm.realm, client : client.id}, $scope.server, function() {
$route.reload();
Notifications.success("The resource server has been updated.");
});
}
$scope.uploadFile = function() {
//$files: an array of files selected, each file has name, size, and type.
for (var i = 0; i < $scope.files.length; i++) {
var $file = $scope.files[i];
$scope.upload = $upload.upload({
url: authUrl + '/admin/realms/' + $route.current.params.realm + '/clients/' + client.id + '/authz/resource-server', //upload.php script, node.js route, or servlet url
// method: POST or PUT,
// headers: {'headerKey': 'headerValue'}, withCredential: true,
data: {myObj: ""},
file: $file
/* set file formData name for 'Content-Desposition' header. Default: 'file' */
//fileFormDataName: myFile,
/* customize how data is added to formData. See #40#issuecomment-28612000 for example */
//formDataAppender: function(formData, key, val){}
}).progress(function(evt) {
console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
}).success(function(data, status, headers) {
$route.reload();
Notifications.success("The resource server has been updated.");
}).error(function() {
Notifications.error("The resource server can not be uploaded. Please verify the file.");
});
}
};
});
});

View file

@ -4,6 +4,7 @@ module.factory('ResourceServer', function($resource) {
client: '@client'
}, {
'update' : {method : 'PUT'},
'import' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/import', method : 'POST'},
'settings' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/settings', method : 'GET'}
});
});

View file

@ -7,22 +7,24 @@
<div class="form-group">
<label for="import-file" class="col-sm-2 control-label">Import</label>
<div class="col-md-6">
<div class="controls kc-button-input-file" data-ng-show="!files || files.length == 0">
<div class="controls kc-button-input-file" data-ng-show="!importing">
<label for="import-file" class="btn btn-default">Select file <i class="pficon pficon-import"></i></label>
<input id="import-file" type="file" class="hidden" ng-file-select="onFileSelect($files)">
<input id="import-file" type="file" class="hidden" kc-on-read-file="onFileSelect($fileContent)">
</div>
<div class="col-md-6" data-ng-show="importing">
<input type="button" class="btn btn-default" data-ng-click="viewImportDetails()" value="{{:: 'view-details' | translate}}"/>
</div>
<span class="kc-uploaded-file" data-ng-show="files.length > 0">{{files[0].name}}</span>
</div>
<kc-tooltip>Import a JSON file containing all settings for this resource server.</kc-tooltip>
</div>
<div class="form-group">
<div class="col-md-10 col-md-offset-2">
<button type="submit" data-ng-disabled="files.length == 0" data-ng-click="uploadFile()" class="btn btn-primary">Upload</button>
<button type="submit" data-ng-disabled="files.length == 0" data-ng-click="clearFileSelect()" class="btn btn-default" data-ng-show="files.length > 0">Cancel</button>
<div class="col-md-10 col-md-offset-2" data-ng-show="importing">
<button class="btn btn-default" data-ng-click="import()" data-ng-disabled="!changed">Import</button>
<button kc-cancel data-ng-click="reset()">Cancel</button>
</div>
</div>
</fieldset>
<fieldset class="border-top" data-ng-hide="files.length > 0">
<fieldset class="border-top" data-ng-hide="importing">
<div class="form-group">
<label class="col-md-2 control-label" for="server.policyEnforcementMode">Policy Enforcement Mode</label>
<div class="col-md-2">
@ -54,7 +56,7 @@
</div>
</fieldset>
<fieldset class="border-top" data-ng-show="!files || files.length == 0">
<fieldset class="border-top" data-ng-show="server.id">
<legend><span class="text">Export Settings</span>
<kc-tooltip>Here you can export all settings for this resource server.</kc-tooltip>
</legend>