keycloak-memoizeit

Details

diff --git a/examples/as7-eap-demo/server/src/main/webapp/saas/admin/js/controllers.js b/examples/as7-eap-demo/server/src/main/webapp/saas/admin/js/controllers.js
index 62bfc70..cbe254a 100755
--- a/examples/as7-eap-demo/server/src/main/webapp/saas/admin/js/controllers.js
+++ b/examples/as7-eap-demo/server/src/main/webapp/saas/admin/js/controllers.js
@@ -184,30 +184,14 @@ module.controller('RealmDetailCtrl', function($scope, Current, Realm, realm, $ht
 module.controller('UserListCtrl', function($scope, realm, User) {
 	$scope.realm = realm;
     $scope.users = [];
-    $scope.query = "*";
-    $scope.attribute = {};
-    var params = {};
-
-    $scope.addAttribute = function() {
-        console.log('queryAttribute');
-        params[$scope.attribute.name] = $scope.attribute.value;
-        for (var key in params) {
-            $scope.query = " " + key + "=" +params[key];
-        }
-    };
+    //$scope.search = "Search...";
 
-    $scope.executeQuery = function() {
-        console.log('executeQuery');
-        var parameters = angular.copy(params);
+    $scope.searchQuery = function() {
+        console.log('search: ' + $scope.search);
+        var parameters = { search : $scope.search };
         parameters["realm"] = realm.id;
         $scope.users = User.query(parameters);
     };
-
-    $scope.clearQuery = function() {
-        params = {};
-        $scopre.query = "*";
-        $scope.users = [];
-    };
 });
 
 module.controller('UserDetailCtrl', function($scope, realm, user, User, $location, Dialog, Notifications) {
@@ -224,37 +208,32 @@ module.controller('UserDetailCtrl', function($scope, realm, user, User, $locatio
 	}, true);
 
 	$scope.save = function() {
-		if ($scope.userForm.$valid) {
-            if ($scope.create) {
-                User.save({
-                    realm: realm.id
-                }, $scope.user, function () {
-                    $scope.changed = false;
-                    user = angular.copy($scope.user);
-
-                    $location.url("/realms/" + realm.id + "/users/" + $scope.user.username);
-                    Notifications.success("Created user");
-                });
-            } else {
-                User.update({
-                    realm: realm.id,
-                    userId: $scope.user.username
-                }, $scope.user, function () {
-                    $scope.changed = false;
-                    user = angular.copy($scope.user);
-                    Notifications.success("Saved changes to user");
-                });
+        if ($scope.create) {
+            User.save({
+                realm: realm.id
+            }, $scope.user, function () {
+                $scope.changed = false;
+                user = angular.copy($scope.user);
 
-            }
+                $location.url("/realms/" + realm.id + "/users/" + $scope.user.username);
+                Notifications.success("Created user");
+            });
         } else {
-			$scope.userForm.showErrors = true;
-		}
+            User.update({
+                realm: realm.id,
+                userId: $scope.user.username
+            }, $scope.user, function () {
+                $scope.changed = false;
+                user = angular.copy($scope.user);
+                Notifications.success("Saved changes to user");
+            });
+
+        }
 	};
 
 	$scope.reset = function() {
 		$scope.user = angular.copy(user);
 		$scope.changed = false;
-		$scope.userForm.showErrors = false;
 	};
 
 	$scope.cancel = function() {
diff --git a/examples/as7-eap-demo/server/src/main/webapp/saas/admin/partials/application-list.html b/examples/as7-eap-demo/server/src/main/webapp/saas/admin/partials/application-list.html
index 390e932..4128751 100755
--- a/examples/as7-eap-demo/server/src/main/webapp/saas/admin/partials/application-list.html
+++ b/examples/as7-eap-demo/server/src/main/webapp/saas/admin/partials/application-list.html
@@ -14,7 +14,7 @@
                     <caption>Table of realm applications</caption>
                     <thead>
                     <tr>
-                        <th class="rcue-table-actions" colspan="4">
+                        <th class="rcue-table-actions" colspan="3">
                             <div class="search-comp clearfix">
                                 <input type="text" placeholder="Search..." class="search">
                                 <button class="icon-search tooltipRightTrigger"
diff --git a/examples/as7-eap-demo/server/src/main/webapp/saas/admin/partials/realm-menu.html b/examples/as7-eap-demo/server/src/main/webapp/saas/admin/partials/realm-menu.html
index 1f109c6..cee7ac9 100755
--- a/examples/as7-eap-demo/server/src/main/webapp/saas/admin/partials/realm-menu.html
+++ b/examples/as7-eap-demo/server/src/main/webapp/saas/admin/partials/realm-menu.html
@@ -1,6 +1,6 @@
 <ul data-ng-hide="createRealm">
     <li data-ng-class="!path[2] && 'active'"><a href="#/realms/{{realm.id}}">Realm Settings</a></li>
-    <li data-ng-class="path[2] == 'users' && 'active'"><a href="#/realms/{{realm.id}}/users">Users</a>
+    <li data-ng-class="(path[2] == 'users' || path[1] == 'user') && 'active'"><a href="#/realms/{{realm.id}}/users">Users</a>
     </li>
     <li data-ng-class="(path[2] == 'applications' || path[1] == 'application') && 'active'"><a href="#/realms/{{realm.id}}/applications">Applications</a></li>
 </ul>
diff --git a/examples/as7-eap-demo/server/src/main/webapp/saas/admin/partials/user-detail.html b/examples/as7-eap-demo/server/src/main/webapp/saas/admin/partials/user-detail.html
index bcd7d20..0a5f7b2 100755
--- a/examples/as7-eap-demo/server/src/main/webapp/saas/admin/partials/user-detail.html
+++ b/examples/as7-eap-demo/server/src/main/webapp/saas/admin/partials/user-detail.html
@@ -1,106 +1,83 @@
 <div id="wrapper" class="container">
     <div class="row">
-        <aside class="span3" data-ng-include data-src="'partials/realm-menu.html'"></aside>
-        <div id="actions-bg"></div>
-
-        <div id="container-right" class="span9">
-            <h1 data-ng-show="create"><span class="gray">New User</span></h1>
-
-            <h1 data-ng-hide="create">
-                <span class="gray">User {{user.username}}</span>
-            </h1>
-
-            <div data-ng-show="userForm.showErrors && userForm.$error.required" class="alert alert-error">Please fill in
-                all required fields
+        <div class="bs-sidebar col-md-3 clearfix" data-ng-include data-src="'partials/realm-menu.html'"></div>
+        <div id="content-area" class="col-md-9" role="main">
+            <div class="top-nav" data-ng-show="create">
+                <ul class="rcue-tabs" >
+                    <li class="active"><a href="#">New User</a></li>
+                    <li><a href="#">Users</a></li>
+                </ul>
             </div>
-            <p class="subtitle subtitle-right" data-ng-show="create"><span class="required">*</span> Required fields</p>
-
-            <form class="form-horizontal" name="userForm" novalidate>
-                <fieldset>
-                    <legend>Details</legend>
-                    <div class="control-group">
-                        <label class="control-label" for="name">Username <span class="required" data-ng-show="create">*</span></label>
-
-                        <div class="controls">
-                            <input type="text" class="input-xlarge" id="name" name="name" data-ng-model="user.username"
-                                   autofocus required data-ng-readonly="!create">
+            <div class="top-nav" data-ng-show="!create">
+                <ul class="rcue-tabs" >
+                    <li class="active"><a href="#">Attributes</a></li>
+                    <li><a href="#">Credentials</a></li>
+                    <li><a href="#">Role Mappings</a></li>
+                </ul>
+            </div>
+            <div id="content">
+                <h2 class="pull-left" data-ng-show="create">New User</h2>
+                <h2 class="pull-left" data-ng-hide="create">User <span>{{user.username}}</span></h2>
+                <p class="subtitle"><span class="required">*</span> Required fields</p>
+                <form name="applicationForm" novalidate>
+                    <fieldset>
+                        <legend uncollapsed><span class="text">Attributes</span></legend>
+                        <div class="form-group">
+                            <label for="username">Username </label><span class="required" data-ng-show="create">*</span>
+                            <div class="controls">
+                                <input type="text" id="username" name="username" data-ng-model="user.username" autofocus
+                                       required data-ng-readonly="!create">
+                            </div>
                         </div>
-                    </div>
 
-                    <div class="control-group">
-                        <label class="control-label">Enabled</label>
+                        <div class="form-group">
+                            <label for="email" class="control-label">Email</label>
 
-                        <div class="controls">
-                            <input class="input-xlarge" type="checkbox" name="enabled"
-                                   data-ng-model="realm.enabled">
+                            <div class="controls">
+                                <input class="input-small" type="text" name="email" id="email"
+                                       data-ng-model="user.email">
+                            </div>
                         </div>
-                    </div>
-
-                    <div class="control-group">
-                        <label class="control-label" for="email">Email </label>
+                        <div class="form-group">
+                            <label for="firstName" class="control-label">First Name</label>
 
-                        <div class="controls">
-                            <input type="email" class="input-xlarge" id="email" name="email" data-ng-model="user.email">
-                            <span class="help-inline error"
-                                  data-ng-show="userForm.showErrors && userForm.email.$invalid">Invalid email</span>
+                            <div class="controls">
+                                <input class="input-small" type="text" name="firstName" id="firstName"
+                                       data-ng-model="user.firstName">
+                            </div>
                         </div>
-                    </div>
-
-                    <div class="control-group">
-                        <label class="control-label" for="firstName">Firstname </label>
+                        <div class="form-group">
+                            <label for="lastName" class="control-label">Last Name</label>
 
-                        <div class="controls">
-                            <input type="text" class="input-xlarge" id="firstName" data-ng-model="user.firstName">
+                            <div class="controls">
+                                <input class="input-small" type="text" name="lastName" id="lastName"
+                                       data-ng-model="user.lastName">
+                            </div>
                         </div>
+                    </fieldset>
+                    <div class="form-actions" data-ng-show="create">
+                        <button type="submit" data-ng-click="save()" class="primary">Save
+                        </button>
+                        <button type="submit" data-ng-click="cancel()" data-ng-click="cancel()"
+                                data-ng-show="changed">Cancel
+                        </button>
                     </div>
 
-                    <div class="control-group">
-                        <label class="control-label" for="lastName">Lastname </label>
-
-                        <div class="controls">
-                            <input type="text" class="input-xlarge" id="lastName" data-ng-model="user.lastName">
-                        </div>
+                    <div class="form-actions" data-ng-show="!create">
+                        <button type="submit" data-ng-click="save()" class="primary" data-ng-show="changed">Save
+                            changes
+                        </button>
+                        <button type="submit" data-ng-click="reset()" data-ng-show="changed">Clear changes
+                        </button>
+                        <button type="submit" data-ng-click="remove()" class="danger">
+                            Delete
+                        </button>
                     </div>
-                </fieldset>
-
-                <fieldset data-ng-show="user.attributes.length > 0">
-                    <legend>Attributes</legend>
 
-                    <table class="table table-striped table-bordered margin-top">
-                        <thead>
-                        <tr>
-                            <th>Name</th>
-                            <th>Value</th>
-                        </tr>
-                        </thead>
-                        <tr data-ng-repeat="attribute in user.attributes">
-                            <td><input type="text" placeholder="Name" value="{{attribute.name}}" readonly></td>
-                            <td><input type="text" placeholder="Value" value="{{attribute.value}}" readonly></td>
-                        </tr>
-                    </table>
-                </fieldset>
-
-                <div class="form-actions" data-ng-show="create">
-                    <button type="submit" data-ng-click="save()" class="btn btn-primary" data-ng-show="changed">Save
-                    </button>
-                    <button type="submit" data-ng-click="cancel()" class="btn" data-ng-click="cancel()"
-                            data-ng-show="changed">Cancel
-                    </button>
-                </div>
-
-                <div class="form-actions" data-ng-show="!create">
-                    <button type="submit" data-ng-click="save()" class="btn btn-primary" data-ng-show="changed">Save
-                        changes
-                    </button>
-                    <button type="submit" data-ng-click="reset()" class="btn" data-ng-show="changed">Clear changes
-                    </button>
-                    <button type="submit" data-ng-click="remove()" class="btn btn-danger" data-ng-hide="changed">
-                        Delete
-                    </button>
-                </div>
-
-            </form>
+                </form>
+            </div>
         </div>
         <div id="container-right-bg"></div>
     </div>
+</div>
 </div>
\ No newline at end of file
diff --git a/examples/as7-eap-demo/server/src/main/webapp/saas/admin/partials/user-list.html b/examples/as7-eap-demo/server/src/main/webapp/saas/admin/partials/user-list.html
index 59dd555..3e1e104 100755
--- a/examples/as7-eap-demo/server/src/main/webapp/saas/admin/partials/user-list.html
+++ b/examples/as7-eap-demo/server/src/main/webapp/saas/admin/partials/user-list.html
@@ -1,60 +1,61 @@
 <div id="wrapper" class="container">
     <div class="row">
-        <aside class="span3" data-ng-include data-src="'partials/realm-menu.html'"></aside>
-        <div id="actions-bg"></div>
-
-
-        <div id="container-right" class="span9">
-            <form class="form-horizontal" name="queryForm" novalidate>
-                <div class="control-group">
-                    <label class="control-label">Query </label>
-
-                    <div class="controls">
-                        <input type="text" class="input-xlarge" id="query" name="query" data-ng-model="query"
-                               autofocus required readonly>
-                        <button type="submit" data-ng-click="executeQuery()" class="btn btn-primary">Execute Query
-                        </button>
-                    </div>
-                </div>
-            </form>
-
-            <form class="form-horizontal" name="queryAttribute" novalidate>
-                <fieldset>
-                    <div class="control-group">
-                        <label class="control-label">Predefined Attribute</label>
-
-                        <div class="controls">
-                            <select style="width: auto;" name="name"
-                                    data-ng-model="attribute.name">
-                                <option value="loginName">Login name</option>
-                                <option value="lastName">Last name</option>
-                                <option value="firstName">First name</option>
-                                <option value="email">Email</option>
-                            </select>
-                            <input class="input-small" type="text" name="value"
-                                   data-ng-model="attribute.value">
-                            <button type="submit" data-ng-click="addAttribute()" class="btn btn-primary">Add Attribute
-                            </button>
-                        </div>
-                    </div>
-                </fieldset>
-            </form>
-            <table class="table table-striped table-bordered">
-                <thead>
-                <tr>
-                    <th>Username</th>
-                    <th>Firstname</th>
-                    <th>Lastname</th>
-                    <th>Email</th>
-                </tr>
-                </thead>
-                <tr data-ng-repeat="user in users">
-                    <td><a href="#/realms/{{realm.id}}/users/{{user.username}}">{{user.username}}</a></td>
-                    <td>{{user.firstName}}</td>
-                    <td>{{user.lastName}}</td>
-                    <td>{{user.email}}</td>
-                </tr>
-            </table>
+        <div class="bs-sidebar col-md-3 clearfix" data-ng-include data-src="'partials/realm-menu.html'"></div>
+        <div id="content-area" class="col-md-9" role="main">
+            <div class="top-nav" data-ng-hide="createRealm">
+                <ul class="rcue-tabs">
+                    <li class="active"><a href="#">Users</a></li>
+                    <li><a href="#/create/user/{{realm.id}}">New User</a></li>
+                </ul>
+            </div>
+            <div id="content">
+                <h2 class="pull-left">Query Users</h2>
+                <table>
+                    <caption>Table of realm users</caption>
+                    <thead>
+                    <tr>
+                        <th class="rcue-table-actions" colspan="4">
+                            <div class="search-comp clearfix">
+                                <input type="text" placeholder="Search..." data-ng-model="search" class="search">
+                                <button data-ng-click="searchQuery()"
+                                        class="icon-search tooltipRightTrigger"
+                                        data-original-title="Search by full name, last name, email, or username.">
+                                    Icon: search
+                                </button>
+                            </div>
+                        </th>
+                    </tr>
+                    <tr>
+                    <tr>
+                        <th>Username</th>
+                        <th>Last Name</th>
+                        <th>First Name</th>
+                        <th>Email</th>
+                    </tr>
+                    </tr>
+                    </thead>
+                    <tfoot>
+                    <tr>
+                        <td colspan="4">
+                            <div class="table-nav">
+                                <a href="#" class="first disabled">First page</a><a href="#" class="prev disabled">Previous
+                                page</a><span><strong>1-8</strong> of <strong>10</strong></span><a href="#"
+                                                                                                   class="next">Next
+                                page</a><a href="#" class="last">Last page</a>
+                            </div>
+                        </td>
+                    </tr>
+                    </tfoot>
+                    <tbody class="selectable-rows">
+                    <tr ng-repeat="user in users">
+                        <td><a href="#/realms/{{realm.id}}/users/{{user.username}}">{{user.username}}</a></td>
+                        <td>{{user.lastName</td>
+                        <td>{{user.firstName</td>
+                        <td>{{user.email</td>
+                    </tr>
+                    </tbody>
+                </table>
+            </div>
         </div>
         <div id="container-right-bg"></div>
     </div>
diff --git a/services/src/main/java/org/keycloak/services/managers/RealmManager.java b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
index 6fd1ee5..f23b917 100755
--- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
@@ -8,10 +8,7 @@ import org.keycloak.services.models.UserModel.RequiredAction;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
 import java.security.NoSuchAlgorithmException;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.concurrent.atomic.AtomicLong;
 
 /**
@@ -234,6 +231,66 @@ public class RealmManager {
         return user;
     }
 
+    /**
+     * Query users based on a search string:
+     *
+     * "Bill Burke" first and last name
+     * "bburke@redhat.com" email
+     * "Burke" lastname or username
+     *
+     * @param searchString
+     * @param realmModel
+     * @return
+     */
+    public List<UserModel> searchUsers(String searchString, RealmModel realmModel) {
+        if (searchString == null) {
+            return Collections.emptyList();
+        }
+
+        String search = searchString.trim();
+        if (search.contains(" ")) { //first and last name
+            String[] split = search.split(" ");
+            if (split.length != 2) {
+                return Collections.emptyList();
+            }
+            Map<String, String> attributes = new HashMap<String, String>();
+            attributes.put(UserModel.FIRST_NAME, split[0]);
+            attributes.put(UserModel.LAST_NAME, split[1]);
+            return realmModel.searchForUserByAttributes(attributes);
+        } else if (search.contains("@")) { // email
+            Map<String, String> attributes = new HashMap<String, String>();
+            attributes.put(UserModel.EMAIL, search);
+            return realmModel.searchForUserByAttributes(attributes);
+        } else { // username and lastname
+            Map<String, String> attributes = new HashMap<String, String>();
+            attributes.put(UserModel.LOGIN_NAME, search);
+            List<UserModel> usernameQuery = realmModel.searchForUserByAttributes(attributes);
+            attributes.clear();
+            attributes.put(UserModel.LAST_NAME, search);
+            List<UserModel> lastnameQuery = realmModel.searchForUserByAttributes(attributes);
+            if (usernameQuery.size() == 0) {
+                return lastnameQuery;
+            } else if (lastnameQuery.size() == 0) {
+                return usernameQuery;
+            }
+            List<UserModel> results = new ArrayList<UserModel>();
+            results.addAll(usernameQuery);
+            for (UserModel lastnameUser : lastnameQuery) {
+               boolean found = false;
+                for (UserModel usernameUser : usernameQuery) {
+                    if (usernameUser.getLoginName().equals(lastnameUser.getLoginName())) {
+                        found = true;
+                        break;
+                    }
+                }
+                if (!found) {
+                    results.add(lastnameUser);
+                }
+            }
+            return results;
+        }
+    }
+
     public void addRequiredCredential(RealmModel newRealm, String requiredCred) {
         newRealm.addRequiredCredential(requiredCred);
     }
@@ -254,6 +311,18 @@ public class RealmManager {
         }
     }
 
+    public UserRepresentation toRepresentation(UserModel user) {
+        UserRepresentation rep = new UserRepresentation();
+        rep.setUsername(user.getLoginName());
+        rep.setLastName(user.getLastName());
+        rep.setFirstName(user.getFirstName());
+        rep.setEmail(user.getEmail());
+        Map<String, String> attrs = new HashMap<String, String>();
+        attrs.putAll(user.getAttributes());
+        rep.setAttributes(attrs);
+        return rep;
+    }
+
     public RoleRepresentation toRepresentation(RoleModel role) {
         RoleRepresentation rep = new RoleRepresentation();
         rep.setId(role.getId());
diff --git a/services/src/main/java/org/keycloak/services/models/picketlink/RealmAdapter.java b/services/src/main/java/org/keycloak/services/models/picketlink/RealmAdapter.java
index 718efe4..4fd3aa8 100755
--- a/services/src/main/java/org/keycloak/services/models/picketlink/RealmAdapter.java
+++ b/services/src/main/java/org/keycloak/services/models/picketlink/RealmAdapter.java
@@ -777,4 +777,28 @@ public class RealmAdapter implements RealmModel {
 
         getRelationshipManager().remove(relationship);
     }
+
+    @Override
+    public List<UserModel> searchForUserByAttributes(Map<String, String> attributes) {
+        IdentityQuery<User> query = getIdm().createIdentityQuery(User.class);
+        for (Map.Entry<String, String> entry : attributes.entrySet()) {
+            if (entry.getKey().equals(UserModel.LOGIN_NAME)) {
+                query.setParameter(User.LOGIN_NAME, entry.getValue());
+            } else if (entry.getKey().equalsIgnoreCase(UserModel.FIRST_NAME)) {
+                query.setParameter(User.FIRST_NAME, entry.getValue());
+
+            } else if (entry.getKey().equalsIgnoreCase(UserModel.LAST_NAME)) {
+                query.setParameter(User.LAST_NAME, entry.getValue());
+
+            } else if (entry.getKey().equalsIgnoreCase(UserModel.EMAIL)) {
+                query.setParameter(User.EMAIL, entry.getValue());
+            }
+        }
+        List<User> users = query.getResultList();
+        List<UserModel> userModels = new ArrayList<UserModel>();
+        for (User user : users) {
+            userModels.add(new UserAdapter(user, idm));
+        }
+        return userModels;
+    }
 }
diff --git a/services/src/main/java/org/keycloak/services/models/RealmModel.java b/services/src/main/java/org/keycloak/services/models/RealmModel.java
index 9eaf1f5..5deed17 100755
--- a/services/src/main/java/org/keycloak/services/models/RealmModel.java
+++ b/services/src/main/java/org/keycloak/services/models/RealmModel.java
@@ -150,4 +150,6 @@ public interface RealmModel {
     public boolean isAutomaticRegistrationAfterSocialLogin();
 
     public void setAutomaticRegistrationAfterSocialLogin(boolean automaticRegistrationAfterSocialLogin);
+
+    List<UserModel> searchForUserByAttributes(Map<String, String> attributes);
 }
diff --git a/services/src/main/java/org/keycloak/services/models/UserModel.java b/services/src/main/java/org/keycloak/services/models/UserModel.java
index e0813b7..bab26e0 100755
--- a/services/src/main/java/org/keycloak/services/models/UserModel.java
+++ b/services/src/main/java/org/keycloak/services/models/UserModel.java
@@ -8,6 +8,11 @@ import java.util.Map;
  * @version $Revision: 1 $
  */
 public interface UserModel {
+    public static final String LOGIN_NAME = "username";
+    public static final String LAST_NAME = "lastName";
+    public static final String FIRST_NAME = "firstName";
+    public static final String EMAIL = "email";
+
     String getLoginName();
 
     boolean isEnabled();
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
index 5ed6520..524b9f8 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
@@ -17,7 +17,9 @@ import javax.ws.rs.core.Context;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -117,16 +119,94 @@ public class RealmAdminResource {
         return Response.created(uriInfo.getAbsolutePathBuilder().path(role.getId()).build()).build();
     }
 
+    @Path("users/{username}")
+    @PUT
+    @Consumes("application/json")
+    public void updateUser(final @PathParam("username") String username, final UserRepresentation rep) {
+        UserModel user = realm.getUser(username);
+        if (user == null) {
+            throw new NotFoundException();
+        }
+        user.setEmail(rep.getEmail());
+        user.setFirstName(rep.getFirstName());
+        user.setLastName(rep.getLastName());
+        for (Map.Entry<String, String> attr : rep.getAttributes().entrySet()) {
+            user.setAttribute(attr.getKey(), attr.getValue());
+        }
+    }
 
     @Path("users")
+    @POST
+    @Consumes("application/json")
+    public Response createUser(final @Context UriInfo uriInfo, final UserRepresentation rep) {
+        if (realm.getUser(rep.getUsername()) != null) {
+            throw new InternalServerErrorException(); // todo appropriate status here.
+        }
+        UserModel user = realm.addUser(rep.getUsername());
+        if (user == null) {
+            throw new NotFoundException();
+        }
+        user.setEmail(rep.getEmail());
+        user.setFirstName(rep.getFirstName());
+        user.setLastName(rep.getLastName());
+        if (rep.getAttributes() != null) {
+            for (Map.Entry<String, String> attr : rep.getAttributes().entrySet()) {
+                user.setAttribute(attr.getKey(), attr.getValue());
+            }
+        }
+        return Response.created(uriInfo.getAbsolutePathBuilder().path(user.getLoginName()).build()).build();
+    }
+
+    @Path("users/{username}")
     @GET
     @NoCache
     @Produces("application/json")
-    public List<UserRepresentation> getUsers() {
-        return null;
+    public UserRepresentation getUser(final @PathParam("username") String username) {
+        UserModel user = realm.getUser(username);
+        if (user == null) {
+            throw new NotFoundException();
+        }
+        return new RealmManager(session).toRepresentation(user);
     }
 
+    @Path("users")
+    @GET
+    @NoCache
+    @Produces("application/json")
+    public List<UserRepresentation> getUsers(@QueryParam("search") String search,
+                                             @QueryParam("lastName") String last,
+                                             @QueryParam("firstName") String first,
+                                             @QueryParam("email") String email,
+                                             @QueryParam("username") String username) {
+        RealmManager manager = new RealmManager(session);
+        List<UserRepresentation> results = new ArrayList<UserRepresentation>();
+        if (search != null) {
+            List<UserModel> userModels = manager.searchUsers(search, realm);
+            for (UserModel user : userModels) {
+                results.add(manager.toRepresentation(user));
+            }
+        } else {
+            Map<String, String> attributes = new HashMap<String, String>();
+            if (last != null) {
+                attributes.put(UserModel.LAST_NAME, last);
+            }
+            if (first != null) {
+                attributes.put(UserModel.FIRST_NAME, first);
+            }
+            if (email != null) {
+                attributes.put(UserModel.EMAIL, email);
+            }
+            if (username != null) {
+                attributes.put(UserModel.LOGIN_NAME, username);
+            }
+            List<UserModel> userModels = realm.searchForUserByAttributes(attributes);
+            for (UserModel user : userModels) {
+                results.add(manager.toRepresentation(user));
+            }
 
+        }
+        return results;
+    }
 
 
 }
diff --git a/services/src/test/java/org/keycloak/test/AdapterTest.java b/services/src/test/java/org/keycloak/test/AdapterTest.java
index c37abc5..015caac 100755
--- a/services/src/test/java/org/keycloak/test/AdapterTest.java
+++ b/services/src/test/java/org/keycloak/test/AdapterTest.java
@@ -115,8 +115,7 @@ public class AdapterTest {
             if (cred.getType().equals(CredentialRepresentation.PASSWORD)) {
                 password = true;
                 Assert.assertTrue(cred.isSecret());
-            }
-            else if (cred.getType().equals(CredentialRepresentation.TOTP)) {
+            } else if (cred.getType().equals(CredentialRepresentation.TOTP)) {
                 totp = true;
                 Assert.assertFalse(cred.isSecret());
             }
@@ -137,6 +136,118 @@ public class AdapterTest {
     }
 
     @Test
+    public void testUserSearch() throws Exception {
+        test1CreateRealm();
+        {
+            UserModel user = realmModel.addUser("bburke");
+            user.setLastName("Burke");
+            user.setFirstName("Bill");
+            user.setEmail("bburke@redhat.com");
+        }
+
+        {
+            List<UserModel> userModels = adapter.searchUsers("total junk query", realmModel);
+            Assert.assertEquals(userModels.size(), 0);
+        }
+
+        {
+            List<UserModel> userModels = adapter.searchUsers("Bill Burke", realmModel);
+            Assert.assertEquals(userModels.size(), 1);
+            UserModel bburke = userModels.get(0);
+            Assert.assertEquals(bburke.getFirstName(), "Bill");
+            Assert.assertEquals(bburke.getLastName(), "Burke");
+            Assert.assertEquals(bburke.getEmail(), "bburke@redhat.com");
+        }
+
+
+        {
+            List<UserModel> userModels = adapter.searchUsers("bburke@redhat.com", realmModel);
+            Assert.assertEquals(userModels.size(), 1);
+            UserModel bburke = userModels.get(0);
+            Assert.assertEquals(bburke.getFirstName(), "Bill");
+            Assert.assertEquals(bburke.getLastName(), "Burke");
+            Assert.assertEquals(bburke.getEmail(), "bburke@redhat.com");
+        }
+
+        {
+            List<UserModel> userModels = adapter.searchUsers("bburke", realmModel);
+            Assert.assertEquals(userModels.size(), 1);
+            UserModel bburke = userModels.get(0);
+            Assert.assertEquals(bburke.getFirstName(), "Bill");
+            Assert.assertEquals(bburke.getLastName(), "Burke");
+            Assert.assertEquals(bburke.getEmail(), "bburke@redhat.com");
+        }
+
+        {
+            List<UserModel> userModels = adapter.searchUsers("Burke", realmModel);
+            Assert.assertEquals(userModels.size(), 1);
+            UserModel bburke = userModels.get(0);
+            Assert.assertEquals(bburke.getFirstName(), "Bill");
+            Assert.assertEquals(bburke.getLastName(), "Burke");
+            Assert.assertEquals(bburke.getEmail(), "bburke@redhat.com");
+        }
+
+        {
+            UserModel user = realmModel.addUser("mburke");
+            user.setLastName("Burke");
+            user.setFirstName("Monica");
+            user.setEmail("mburke@redhat.com");
+        }
+
+        {
+            UserModel user = realmModel.addUser("thor");
+            user.setLastName("Thorgersen");
+            user.setFirstName("Stian");
+            user.setEmail("thor@redhat.com");
+        }
+
+        {
+            List<UserModel> userModels = adapter.searchUsers("Monica Burke", realmModel);
+            Assert.assertEquals(userModels.size(), 1);
+            UserModel bburke = userModels.get(0);
+            Assert.assertEquals(bburke.getFirstName(), "Monica");
+            Assert.assertEquals(bburke.getLastName(), "Burke");
+            Assert.assertEquals(bburke.getEmail(), "mburke@redhat.com");
+        }
+
+
+        {
+            List<UserModel> userModels = adapter.searchUsers("mburke@redhat.com", realmModel);
+            Assert.assertEquals(userModels.size(), 1);
+            UserModel bburke = userModels.get(0);
+            Assert.assertEquals(bburke.getFirstName(), "Monica");
+            Assert.assertEquals(bburke.getLastName(), "Burke");
+            Assert.assertEquals(bburke.getEmail(), "mburke@redhat.com");
+        }
+
+        {
+            List<UserModel> userModels = adapter.searchUsers("mburke", realmModel);
+            Assert.assertEquals(userModels.size(), 1);
+            UserModel bburke = userModels.get(0);
+            Assert.assertEquals(bburke.getFirstName(), "Monica");
+            Assert.assertEquals(bburke.getLastName(), "Burke");
+            Assert.assertEquals(bburke.getEmail(), "mburke@redhat.com");
+        }
+
+        {
+            List<UserModel> userModels = adapter.searchUsers("Burke", realmModel);
+            Assert.assertEquals(userModels.size(), 2);
+            UserModel first = userModels.get(0);
+            UserModel second = userModels.get(1);
+            if (!first.getEmail().equals("bburke@redhat.com") && !second.getEmail().equals("bburke@redhat.com")) {
+                Assert.fail();
+            }
+            if (!first.getEmail().equals("mburke@redhat.com") && !second.getEmail().equals("mburke@redhat.com")) {
+                Assert.fail();
+            }
+        }
+
+
+
+    }
+
+
+    @Test
     public void testRoles() throws Exception {
         test1CreateRealm();
         realmModel.addRole("admin");