diff --git a/smp-angular/e2e/app.e2e-spec.ts b/smp-angular/e2e/app.e2e-spec.ts
index c19f4bb57d493a22833f1649afeda698efe5d8fe..08f1001f7aa25d6c45be027ef13d7e4b118c8c80 100644
--- a/smp-angular/e2e/app.e2e-spec.ts
+++ b/smp-angular/e2e/app.e2e-spec.ts
@@ -1,6 +1,6 @@
 import { SmpAngular2WebPage } from './app.po';
 
-describe('domibus-MSH-web App', function() {
+describe('smp-MSH-web App', function() {
   let page: SmpAngular2WebPage;
 
   beforeEach(() => {
diff --git a/smp-angular/src/app/app.module.ts b/smp-angular/src/app/app.module.ts
index c020feac1a525e279c611200ccedc026267400d4..a84d410edd314a159c2993144f6a21d4649d8ad0 100644
--- a/smp-angular/src/app/app.module.ts
+++ b/smp-angular/src/app/app.module.ts
@@ -69,6 +69,8 @@ import {DomainDetailsDialogComponent} from "./domain/domain-details-dialog/domai
 import {UserDetailsDialogComponent} from "./user/user-details-dialog/user-details-dialog.component";
 import {DownloadService} from "./download/download.service";
 import {TrustStoreService} from "./trust-store/trust-store.service";
+import {UserService} from "./user/user.service";
+import {RoleService} from "./security/role.service";
 
 export function extendedHttpClientFactory(xhrBackend: XHRBackend, requestOptions: RequestOptions, httpEventService: HttpEventService) {
   return new ExtendedHttpClient(xhrBackend, requestOptions, httpEventService);
@@ -158,6 +160,8 @@ export function extendedHttpClientFactory(xhrBackend: XHRBackend, requestOptions
     AlertService,
     DownloadService,
     TrustStoreService,
+    UserService,
+    RoleService,
     {
       provide: Http,
       useFactory: extendedHttpClientFactory,
diff --git a/smp-angular/src/app/common/save-dialog/save-dialog.component.ts b/smp-angular/src/app/common/save-dialog/save-dialog.component.ts
index 1c098b49146b8f450a1828d1b75dc7184653ed55..46c47800980b1215ed227b784bc19c8405a0c042 100644
--- a/smp-angular/src/app/common/save-dialog/save-dialog.component.ts
+++ b/smp-angular/src/app/common/save-dialog/save-dialog.component.ts
@@ -3,7 +3,8 @@ import {MdDialogRef} from "@angular/material";
 
 @Component({
   selector: 'app-messagefilter-dialog',
-  templateUrl: './save-dialog.component.html'
+  templateUrl: './save-dialog.component.html',
+  styleUrls: ['./save-dialog.component.css']
 })
 export class SaveDialogComponent {
 
diff --git a/smp-angular/src/app/common/search-table/search-table-controller.ts b/smp-angular/src/app/common/search-table/search-table-controller.ts
index 4bce65d93f7828ef707393e616d5496e47455a4a..534c99864167a585f00af1c639606f12a10e53d3 100644
--- a/smp-angular/src/app/common/search-table/search-table-controller.ts
+++ b/smp-angular/src/app/common/search-table/search-table-controller.ts
@@ -1,5 +1,10 @@
+import {MdDialogConfig, MdDialogRef} from "@angular/material";
+import {SearchTableEntity} from "./search-table-entity.model";
+
 export interface SearchTableController {
   showDetails(row);
   edit(row);
   delete(row);
+  newRow(): SearchTableEntity;
+  newDialog(config?: MdDialogConfig): MdDialogRef<any>;
 }
diff --git a/smp-angular/src/app/common/search-table/search-table-entity-status.model.ts b/smp-angular/src/app/common/search-table/search-table-entity-status.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9a4a22120c5d02dbce4a07c2b291888d541b4da2
--- /dev/null
+++ b/smp-angular/src/app/common/search-table/search-table-entity-status.model.ts
@@ -0,0 +1,6 @@
+export enum SearchTableEntityStatus {
+  PERSISTED,
+  UPDATED,
+  NEW,
+  REMOVED
+}
diff --git a/smp-angular/src/app/common/search-table/search-table-entity.model.ts b/smp-angular/src/app/common/search-table/search-table-entity.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..572e888d8a51db84942da8b26403ab7031e0f69c
--- /dev/null
+++ b/smp-angular/src/app/common/search-table/search-table-entity.model.ts
@@ -0,0 +1,6 @@
+import {SearchTableEntityStatus} from "./search-table-entity-status.model";
+
+export interface SearchTableEntity {
+  status: SearchTableEntityStatus;
+  deleted?: boolean;
+}
diff --git a/smp-angular/src/app/common/search-table/search-table-result.model.ts b/smp-angular/src/app/common/search-table/search-table-result.model.ts
index 9c6c6ad72156b1f4a1906e5adc2f0310deb99b1f..2588f29c6c91a58b33e63d1f6f58961f5d223252 100644
--- a/smp-angular/src/app/common/search-table/search-table-result.model.ts
+++ b/smp-angular/src/app/common/search-table/search-table-result.model.ts
@@ -1,5 +1,7 @@
+import {SearchTableEntity} from "./search-table-entity.model";
+
 export interface SearchTableResult {
-  serviceEntities: Array<any>;
+  serviceEntities: Array<SearchTableEntity>;
   pageSize: number;
   count: number;
   filter: any;
diff --git a/smp-angular/src/app/common/search-table/search-table.component.css b/smp-angular/src/app/common/search-table/search-table.component.css
index d1a612141160e70f00bfe5aee114e010052ee4a5..163929e63bb1ffe88f047c87a83d0045c52a8263 100644
--- a/smp-angular/src/app/common/search-table/search-table.component.css
+++ b/smp-angular/src/app/common/search-table/search-table.component.css
@@ -12,7 +12,16 @@
   position: fixed;
 }
 
-
-.datatable-body{
+.datatable-body {
   overflow-y: scroll;
 }
+
+/deep/ .deleted span[title] {
+  text-decoration: line-through !important;
+}
+
+.group-action-button {
+  position: absolute;
+  left: 8px;
+  bottom: 8px;
+}
diff --git a/smp-angular/src/app/common/search-table/search-table.component.html b/smp-angular/src/app/common/search-table/search-table.component.html
index 3135c062c9ac7c07366d12d102ad89044829b9b0..a2f392c20a1273ba517af2c6ac75764215f5849d 100644
--- a/smp-angular/src/app/common/search-table/search-table.component.html
+++ b/smp-angular/src/app/common/search-table/search-table.component.html
@@ -3,9 +3,7 @@
 
   <div class="selectionCriteria">
     <md-card>
-
       <md-card-content>
-
         <div class="panel">
           <form name="filterForm" #filterForm="ngForm" (ngSubmit)="search()">
             <ng-container *ngTemplateOutlet="searchPanel"></ng-container>
@@ -24,22 +22,22 @@
 
   <div class="panel" style="position: absolute; top: 270px; bottom: 5px; left: 5px; right: 5px;">
     <div class="group-filter-button">
-    <span class="row-button">
-      <app-row-limiter [pageSizes]="rowLimiter.pageSizes"
-                       (onPageSizeChanged)="changePageSize($event.value)"></app-row-limiter>
-    </span>
+      <span class="row-button">
+        <app-row-limiter [pageSizes]="rowLimiter.pageSizes"
+                         (onPageSizeChanged)="changePageSize($event.value)"></app-row-limiter>
+      </span>
       <span class="column-filter-button">
-      <app-column-picker [allColumns]="columnPicker.allColumns" [selectedColumns]="columnPicker.selectedColumns"
-                         (onSelectedColumnsChanged)="columnPicker.changeSelectedColumns($event)"></app-column-picker>
-    </span>
+        <app-column-picker [allColumns]="columnPicker.allColumns" [selectedColumns]="columnPicker.selectedColumns"
+                           (onSelectedColumnsChanged)="columnPicker.changeSelectedColumns($event)"></app-column-picker>
+      </span>
     </div>
     <!-- temporal solution <div - absolut - wrapping> for stretch table height to fit screen size: scrollbarV does not work - virtual scrolling has
     row bugs.-->
-    <div class="panel" style="position: absolute; overflow-y: scroll;top: 100px; bottom: 40px; left: 0px; right: 0px;">
+    <div class="panel">
       <ngx-datatable
-        id="serviceGroupTable"
+        id="searchTable"
         class="material striped"
-        style=""
+        [rowClass]="getRowClass"
         [rows]="rows"
         [columns]="columnPicker.selectedColumns"
         [columnMode]="'force'"
@@ -50,62 +48,55 @@
         [externalPaging]="true"
         [externalSorting]="true"
         [loadingIndicator]="loading"
-        [count]="count"
+        [count]="rows.length"
         [offset]="offset"
         [limit]="rowLimiter.pageSize"
-        [sorts]="[{prop: 'received', dir: 'desc'}]"
-        (page)='onPage($event)'
+        (page)="onPage($event)"
         (sort)="onSort($event)"
         [selected]="selected"
         [selectionType]="'multi'"
         (activate)="onActivate($event)"
         (select)="onSelect($event)">
       </ngx-datatable>
-    </div>
-
-    <ng-template #rowActions let-row="row" let-value="value" ngx-datatable-cell-template>
-
-      <button md-icon-button color="primary"
-              (click)="editRowButtonAction(row)" id="editButtonRow{{row.$$index}}_id" tooltip="Edit">
-        <md-icon>edit</md-icon>
-      </button>
-
-
-      <button md-icon-button color="primary" (click)="deleteRowButtonAction(row)"
-              id="deleteButtonRow{{row.$$index}}_id" tooltip="Delete">
-        <md-icon>delete</md-icon>
-      </button>
-    </ng-template>
-
-
-    <div class="group-action-button" style="position: absolute;left: 8px; bottom: 8px;">
-      <button md-raised-button color="primary"  (click)="newButtonAction()"
-              id="add_id">
-        <md-icon>add</md-icon>
-        <span>New</span>
-      </button>
-
-      <button md-raised-button color="primary" [disabled]="!isRowSelected()" (click)="editButtonAction()"
-              id="edit_id">
-        <md-icon>edit</md-icon>
-        <span>Edit</span>
-      </button>
-
-      <button md-raised-button color="primary" [disabled]="!isRowSelected()" (click)="deleteButtonAction()"
-              id="resendbutton_id">
-        <md-icon>delete</md-icon>
-        <span>Delete</span>
-      </button>
-
-      <ng-container *ngTemplateOutlet="additionalToolButtons"></ng-container>
 
+      <div class="group-action-button">
+        <button id="cancelButton" md-raised-button (click)="onCancelButtonClicked()" color="primary" [disabled]="!submitButtonsEnabled">
+          <md-icon>cancel</md-icon>
+          <span>Cancel</span>
+        </button>
+        <button id="saveButton" md-raised-button (click)="onSaveButtonClicked(false)" color="primary" [disabled]="!submitButtonsEnabled">
+          <md-icon>save</md-icon>
+          <span>Save</span>
+        </button>
+        <button id="newButton" md-raised-button (click)="onNewButtonClicked()" [disabled]="loading" color="primary">
+          <md-icon>add</md-icon>
+          <span>New</span>
+        </button>
+        <button id="editButton" md-raised-button (click)="onEditButtonClicked()" [disabled]="!editButtonEnabled || loading" color="primary">
+          <md-icon>edit</md-icon>
+          <span>Edit</span>
+        </button>
+        <button id="deleteButton" md-raised-button (click)="onDeleteButtonClicked()" [disabled]="!deleteButtonEnabled || loading" color="primary">
+          <md-icon>delete</md-icon>
+          <span>Delete</span>
+        </button>
+
+        <ng-container *ngTemplateOutlet="additionalToolButtons"></ng-container>
+
+      </div>
+
+      <ng-template #rowActions let-row="row" ngx-datatable-cell-template>
+        <div>
+          <button md-icon-button color="primary" [disabled]="row.deleted || loading"
+                  (click)="onEditRowActionClicked(row.$$index)" tooltip="Edit">
+            <md-icon>edit</md-icon>
+          </button>
+          <button md-icon-button color="primary" [disabled]="row.deleted || loading"
+                  (click)="onDeleteRowActionClicked(row)" tooltip="Delete">
+            <md-icon>delete</md-icon>
+          </button>
+        </div>
+      </ng-template>
     </div>
   </div>
-
-
 </div>
-
-<!--
-
-
--->
diff --git a/smp-angular/src/app/common/search-table/search-table.component.ts b/smp-angular/src/app/common/search-table/search-table.component.ts
index 294f8b26b9533d949c002e3717d6b3d2d8c7a7dd..3fc0522697aebd76a92d9cd4c0f34e7224f0ae83 100644
--- a/smp-angular/src/app/common/search-table/search-table.component.ts
+++ b/smp-angular/src/app/common/search-table/search-table.component.ts
@@ -1,5 +1,5 @@
-import {Component, EventEmitter, Input, OnInit, TemplateRef, ViewChild} from "@angular/core";
-import {Http, URLSearchParams, Response} from "@angular/http";
+import {ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit, TemplateRef, ViewChild} from "@angular/core";
+import {Http, Response, URLSearchParams} from "@angular/http";
 import {SearchTableResult} from "./search-table-result.model";
 import {Observable} from "rxjs";
 import {AlertService} from "../../alert/alert.service";
@@ -8,13 +8,18 @@ import {ColumnPicker} from "../column-picker/column-picker.model";
 import {RowLimiter} from "../row-limiter/row-limiter.model";
 import {AlertComponent} from "../../alert/alert.component";
 import {SearchTableController} from "./search-table-controller";
+import {finalize, map} from "rxjs/operators";
+import {SearchTableEntity} from "./search-table-entity.model";
+import {SearchTableEntityStatus} from "./search-table-entity-status.model";
+import {CancelDialogComponent} from "../cancel-dialog/cancel-dialog.component";
+import {SaveDialogComponent} from "../save-dialog/save-dialog.component";
+import {DownloadService} from "../../download/download.service";
 
 @Component({
   selector: 'smp-search-table',
   templateUrl: './search-table.component.html',
   styleUrls: ['./search-table.component.css']
 })
-
 export class SearchTableComponent implements OnInit {
   @ViewChild('rowActions') rowActions: TemplateRef<any>;
 
@@ -28,26 +33,26 @@ export class SearchTableComponent implements OnInit {
   @Input() searchTableController: SearchTableController;
   @Input() filter: any = {};
 
-  columnActions:any;
+  loading = false;
+
+  columnActions: any;
 
   rowLimiter: RowLimiter = new RowLimiter();
 
-  selected = [];
+  rowNumber: number;
+
+  rows: Array<SearchTableEntity> = [];
+  selected: Array<SearchTableEntity> = [];
 
-  loading: boolean = false;
-  rows = [];
   count: number = 0;
   offset: number = 0;
-  //default value
   orderBy: string = null;
-  //default value
-  asc: boolean = false;
+  asc = false;
 
-  msgStatus: Array<String>;
-
-  messageResent = new EventEmitter(false);
-
-  constructor(protected http: Http, protected alertService: AlertService, public dialog: MdDialog) {
+  constructor(protected http: Http,
+              protected alertService: AlertService,
+              private downloadService: DownloadService,
+              public dialog: MdDialog) {
   }
 
   ngOnInit() {
@@ -57,24 +62,30 @@ export class SearchTableComponent implements OnInit {
       width: 80,
       sortable: false
     };
-    /**
-     * Add actions to last column
-     */
+
+    // Add actions to last column
     if (this.columnPicker) {
       this.columnPicker.allColumns.push(this.columnActions);
-
       this.columnPicker.selectedColumns.push(this.columnActions);
     }
     this.page(this.offset, this.rowLimiter.pageSize, this.orderBy, this.asc);
   }
 
-  getTableDataEntries(offset: number, pageSize: number, orderBy: string, asc: boolean): Observable< SearchTableResult > {
+  getRowClass(row): string {
+    return row.deleted ? 'deleted' : '';
+  }
+
+  getTableDataEntries$(offset: number, pageSize: number, orderBy: string, asc: boolean): Observable<SearchTableResult> {
     let searchParams: URLSearchParams = new URLSearchParams();
     searchParams.set('page', offset.toString());
     searchParams.set('pageSize', pageSize.toString());
     searchParams.set('orderBy', orderBy);
 
     //filters
+    if (this.filter.userName) {
+      searchParams.set('userName', this.filter.userName);
+    }
+
     if (this.filter.participantId) {
       searchParams.set('participantId', this.filter.participantId);
     }
@@ -83,7 +94,6 @@ export class SearchTableComponent implements OnInit {
       searchParams.set('participantSchema', this.filter.participantSchema);
     }
 
-
     if(this.filter.domain) {
       searchParams.set('domain', this.filter.domain )
     }
@@ -92,114 +102,212 @@ export class SearchTableComponent implements OnInit {
       searchParams.set('asc', asc.toString());
     }
 
+    // TODO move to the HTTP service
+    this.loading = true;
     return this.http.get(this.url, {
       search: searchParams
-    }).map((response: Response) =>
-      response.json()
+    }).pipe(
+      map((response: Response) => response.json()),
+      finalize(() => {
+        this.loading = false;
+      })
     );
   }
 
-  page(offset, pageSize, orderBy, asc) {
-    this.loading = true;
-
-    this.getTableDataEntries(offset, pageSize, orderBy, asc).subscribe((result: SearchTableResult ) => {
-      console.log("service group response:" + result);
+  page(offset: number, pageSize: number, orderBy: string, asc: boolean) {
+    this.getTableDataEntries$(offset, pageSize, orderBy, asc).subscribe((result: SearchTableResult ) => {
       this.offset = offset;
       this.rowLimiter.pageSize = pageSize;
       this.orderBy = orderBy;
       this.asc = asc;
-      this.count = result.count;
-      this.selected = [];
 
+      this.unselectRows();
+      const count = result.count;
       const start = offset * pageSize;
-      const end = start + pageSize;
+      const end = Math.min(start + pageSize, count);
       const newRows = [...result.serviceEntities];
 
       let index = 0;
       for (let i = start; i < end; i++) {
-        newRows[i] = result.serviceEntities[index++];
+        newRows[i] = {...result.serviceEntities[index++],
+          status: SearchTableEntityStatus.PERSISTED,
+          deleted: false
+        };
       }
-
       this.rows = newRows;
 
-      this.loading = false;
-
-      if(this.count > AlertComponent.MAX_COUNT_CSV) {
+      if(count > AlertComponent.MAX_COUNT_CSV) {
         this.alertService.error("Maximum number of rows reached for downloading CSV");
       }
     }, (error: any) => {
-      console.log("error getting the message log:" + error);
-      this.loading = false;
-      this.alertService.error("Error occured:" + error);
+      this.alertService.error("Error occurred:" + error);
     });
   }
 
   onPage(event) {
-    console.log('Page Event', event);
     this.page(event.offset, event.pageSize, this.orderBy, this.asc);
   }
 
   onSort(event) {
-    console.log('Sort Event', event);
-    let ascending = true;
-    if (event.newValue === 'desc') {
-      ascending = false;
-    }
+    let ascending = event.newValue !== 'desc';
     this.page(this.offset, this.rowLimiter.pageSize, event.column.prop, ascending);
   }
 
   onSelect({selected}) {
-    // console.log('Select Event', selected, this.selected);
+    this.selected = [...selected];
+    if(this.editButtonEnabled) {
+      this.rowNumber = this.selected[0]["$$index"];
+    }
   }
 
   onActivate(event) {
-    // console.log('Activate Event', event);
-
     if ("dblclick" === event.type) {
       this.details(event.row);
     }
   }
 
   changePageSize(newPageLimit: number) {
-    console.log('New page limit:', newPageLimit);
     this.page(0, newPageLimit, this.orderBy, this.asc);
   }
 
   search() {
-    console.log("Searching using filter:" + this.filter);
     this.page(0, this.rowLimiter.pageSize, this.orderBy, this.asc);
   }
 
-  isRowSelected() {
-    if (this.selected)
-      return true;
+  details(selectedRow: any) {
+    this.searchTableController.showDetails(selectedRow);
+  }
+
+  onEditRowActionClicked(rowNumber: number) {
+    this.editSearchTableEntity(rowNumber);
+  }
 
-    return false;
+  onDeleteRowActionClicked(row: SearchTableEntity) {
+    this.deleteSearchTableEntities([row]);
   }
 
+  onNewButtonClicked() {
+    const formRef: MdDialogRef<any> = this.searchTableController.newDialog({
+      data: { edit: false }
+    });
+    formRef.afterClosed().subscribe(result => {
+      if (result) {
+        this.rows = [...this.rows, {...formRef.componentInstance.current}];
+      } else {
+        this.unselectRows();
+      }
+    });
+  }
 
-  details(selectedRow: any) {
-    this.searchTableController.showDetails(selectedRow);
+  onDeleteButtonClicked() {
+    this.deleteSearchTableEntities(this.selected);
+  }
+
+  onEditButtonClicked() {
+    if (this.rowNumber >= 0 && this.rows[this.rowNumber] && this.rows[this.rowNumber].deleted) {
+      this.alertService.error('You cannot edit a deleted entry.', false);
+      return;
+    }
+    this.editSearchTableEntity(this.rowNumber);
+  }
+
+  onSaveButtonClicked(withDownloadCSV: boolean) {
+    try {
+      // TODO: add validation support to existing controllers
+      // const isValid = this.userValidatorService.validateUsers(this.users);
+      // if (!isValid) return;
+
+      this.dialog.open(SaveDialogComponent).afterClosed().subscribe(result => {
+        if (result) {
+          // this.unselectRows();
+          const modifiedUsers = this.rows.filter(el => el.status !== SearchTableEntityStatus.PERSISTED);
+          // this.isBusy = true;
+          this.http.put(/*UserComponent.USER_USERS_URL TODO: use PUT url*/'', modifiedUsers).subscribe(res => {
+            // this.isBusy = false;
+            // this.getUsers();
+            this.alertService.success('The operation \'update\' completed successfully.', false);
+            if (withDownloadCSV) {
+              this.downloadService.downloadNative(/*UserComponent.USER_CSV_URL TODO: use CSV url*/ '');
+            }
+          }, err => {
+            // this.isBusy = false;
+            // this.getUsers();
+            this.alertService.exception('The operation \'update\' not completed successfully.', err, false);
+          });
+        } else {
+          if (withDownloadCSV) {
+            this.downloadService.downloadNative(/*UserComponent.USER_CSV_URL TODO: use CSV url*/ '');
+          }
+        }
+      });
+    } catch (err) {
+      // this.isBusy = false;
+      this.alertService.exception('The operation \'update\' completed with errors.', err);
+    }
+  }
+
+  onCancelButtonClicked() {
+    this.dialog.open(CancelDialogComponent).afterClosed().subscribe(result => {
+      if (result) {
+        this.page(this.offset, this.rowLimiter.pageSize, this.orderBy, this.asc);
+      }
+    });
   }
 
-  newButtonAction(){
+  getRowsAsString(): number {
+    return this.rows.length;
+  }
 
+  get editButtonEnabled(): boolean {
+    return this.selected && this.selected.length == 1 && !this.selected[0].deleted;
   }
 
-  editButtonAction(){
-    this.editRowButtonAction( this.selected[0]);
+  get deleteButtonEnabled(): boolean {
+    return this.selected && this.selected.length > 0 && !this.selected.every(el => el.deleted);
   }
 
-  deleteButtonAction(){
-      // delete all seleted rows
+  get submitButtonsEnabled(): boolean {
+    const rowsDeleted = !!this.rows.find(row => row.deleted);
+    const dirty = rowsDeleted || !!this.rows.find(el => el.status !== SearchTableEntityStatus.PERSISTED);
+    return dirty;
   }
 
-  editRowButtonAction(row: any){
-    this.details(row);
+  private editSearchTableEntity(rowNumber: number) {
+    const row = this.rows[rowNumber];
+    const formRef: MdDialogRef<any> = this.searchTableController.newDialog({
+      data: {edit: true, row}
+    });
+    formRef.afterClosed().subscribe(result => {
+      if (result) {
+        const status = row.status === SearchTableEntityStatus.PERSISTED
+          ? SearchTableEntityStatus.UPDATED
+          : row.status;
+        this.rows[rowNumber] = {...formRef.componentInstance.current, status};
+        this.rows = [...this.rows];
+      }
+    });
   }
 
-  deleteRowButtonAction(row: any){
+  private deleteSearchTableEntities(rows: Array<SearchTableEntity>) {
+    // TODO: add validation support to existing controllers
+    // if (this.searchTableController.validateDeleteOperation(rows)) {
+    //   this.alertService.error('You cannot delete the logged in user: ' + this.securityService.getCurrentUser().username);
+    //   return;
+    // }
+
+    for (const row of rows) {
+      if (row.status === SearchTableEntityStatus.NEW) {
+        this.rows.splice(this.rows.indexOf(row), 1);
+      } else {
+        row.status = SearchTableEntityStatus.REMOVED;
+        row.deleted = true;
+      }
+    }
 
+    this.unselectRows()
   }
 
+  private unselectRows() {
+    this.selected = [];
+  }
 }
diff --git a/smp-angular/src/app/domain/domain-controller.ts b/smp-angular/src/app/domain/domain-controller.ts
index b5b62d4a96ff99225b1cb9758fed4eb1522de3aa..3748b9156e6b27fc9459d8330b9b6d2243c9444e 100644
--- a/smp-angular/src/app/domain/domain-controller.ts
+++ b/smp-angular/src/app/domain/domain-controller.ts
@@ -1,6 +1,8 @@
 import {SearchTableController} from "../common/search-table/search-table-controller";
-import {MdDialog, MdDialogRef} from "@angular/material";
+import {MdDialog, MdDialogConfig, MdDialogRef} from "@angular/material";
 import {DomainDetailsDialogComponent} from "./domain-details-dialog/domain-details-dialog.component";
+import {DomainRo} from "./domain-ro.model";
+import {SearchTableEntityStatus} from "../common/search-table/search-table-entity-status.model";
 
 export class DomainController implements SearchTableController {
 
@@ -17,4 +19,19 @@ export class DomainController implements SearchTableController {
   public edit(row: any) { }
 
   public  delete(row: any) { }
+
+  public newDialog(config?: MdDialogConfig): MdDialogRef<DomainDetailsDialogComponent> {
+    return this.dialog.open(DomainDetailsDialogComponent, config);
+  }
+
+  public newRow(): DomainRo {
+    return {
+      domainId: '',
+      bdmslClientCertHeader: '',
+      bdmslClientCertAlias: '',
+      bdmslSmpId: '',
+      signatureCertAlias: '',
+      status: SearchTableEntityStatus.NEW
+    }
+  }
 }
diff --git a/smp-angular/src/app/domain/domain-ro.model.ts b/smp-angular/src/app/domain/domain-ro.model.ts
index 7b90e0359c670d95d64139ff4b93fc60ab99a37e..b6c52bb9730122cc578a03133c1aaf6faedfe99f 100644
--- a/smp-angular/src/app/domain/domain-ro.model.ts
+++ b/smp-angular/src/app/domain/domain-ro.model.ts
@@ -1,4 +1,6 @@
-export interface DomainRo {
+import {SearchTableEntity} from "../common/search-table/search-table-entity.model";
+
+export interface DomainRo extends SearchTableEntity {
   domainId: string;
   bdmslClientCertHeader: string;
   bdmslClientCertAlias: string;
diff --git a/smp-angular/src/app/guards/authorized-admin.guard.ts b/smp-angular/src/app/guards/authorized-admin.guard.ts
index 67cf658d82a2f14cbab7b9f09d911ef0f417d757..85536d95085637dde74362d3dedb890232524172 100644
--- a/smp-angular/src/app/guards/authorized-admin.guard.ts
+++ b/smp-angular/src/app/guards/authorized-admin.guard.ts
@@ -2,6 +2,7 @@
 import {ActivatedRouteSnapshot, RouterStateSnapshot} from "@angular/router";
 import {SecurityService} from "../security/security.service";
 import {AuthorizedGuard} from "./authorized.guard";
+import {Role} from "../security/role.model";
 
 @Injectable()
 export class AuthorizedAdminGuard extends AuthorizedGuard {
@@ -15,7 +16,8 @@ export class AuthorizedAdminGuard extends AuthorizedGuard {
   }
 
 
-  getAllowedRoles(route: ActivatedRouteSnapshot): Array<string> {
-    return [SecurityService.ROLE_DOMAIN_ADMIN, SecurityService.ROLE_AP_ADMIN];
+  getAllowedRoles(route: ActivatedRouteSnapshot): Array<Role> {
+    // TODO check if we need the SMP admin in here
+    return [Role.SYSTEM_ADMINISTRATOR/*, Role.SMP_ADMINISTRATOR*/];
   }
 }
diff --git a/smp-angular/src/app/guards/authorized.guard.ts b/smp-angular/src/app/guards/authorized.guard.ts
index 915ff55eb682982b3502f0de983cd242804fd11e..7d95f26e53939ddf68f461b381925ffeffee5ad7 100644
--- a/smp-angular/src/app/guards/authorized.guard.ts
+++ b/smp-angular/src/app/guards/authorized.guard.ts
@@ -2,6 +2,7 @@
 import {CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot} from "@angular/router";
 import {SecurityService} from "../security/security.service";
 import {ReplaySubject} from "rxjs";
+import {Role} from "../security/role.model";
 
 @Injectable()
 export class AuthorizedGuard implements CanActivate {
@@ -23,6 +24,6 @@ export class AuthorizedGuard implements CanActivate {
   }
 
   getAllowedRoles(route: ActivatedRouteSnapshot) {
-    return route.data["allowedRoles"] as Array<string>;
+    return route.data["allowedRoles"] as Array<Role>;
   }
 }
diff --git a/smp-angular/src/app/login/login.component.html b/smp-angular/src/app/login/login.component.html
index aae172e41b1d0ec6fbe4c0ec4af3ae3607254052..c01af67c6828283f52a25d6f9c806341d28d4b2b 100644
--- a/smp-angular/src/app/login/login.component.html
+++ b/smp-angular/src/app/login/login.component.html
@@ -5,7 +5,7 @@
         <tr>
           <td>
             <md-input-container>
-              <input mdInput placeholder="Username" name="username" [(ngModel)]="model.username" #username="ngModel"
+              <input mdInput placeholder="Username" name="userName" [(ngModel)]="model.userName" #userName="ngModel"
                      required id="username_id">
             </md-input-container>
           </td>
diff --git a/smp-angular/src/app/security/is-authorized.directive.ts b/smp-angular/src/app/security/is-authorized.directive.ts
index 1fbe813d9480415f87a3c97f3be19909297f7d59..cdc15ec41c090d82688fa4764af6b1d0c855e3ce 100644
--- a/smp-angular/src/app/security/is-authorized.directive.ts
+++ b/smp-angular/src/app/security/is-authorized.directive.ts
@@ -1,25 +1,27 @@
 import {Directive, ElementRef, Input, OnInit} from '@angular/core';
 import {SecurityService} from './security.service';
+import {Role} from "./role.model";
 
 @Directive({
     selector:'[isAuthorized]'
 })
 export class IsAuthorized implements OnInit {
-    @Input('isAuthorized') role:string;
+    @Input('isAuthorized') role: Role;
+
     constructor(private _elementRef:ElementRef, private securityService:SecurityService) {
     }
 
-    ngOnInit():void {
-      if(this.role && this.role.trim() !== '') {
+    ngOnInit() {
+      if(this.role) {
         this.securityService.isAuthorized([this.role]).subscribe((isAuthorized:boolean) => {
           if(!isAuthorized) {
             let el : HTMLElement = this._elementRef.nativeElement;
             el.parentNode.removeChild(el);
           }
         },
-          (error:any) => {
-            console.log("Error in IsAuthorized directive [" + error + "]");
-          });
+        (error:any) => {
+          console.log("Error in IsAuthorized directive [" + error + "]");
+        });
       }
     }
 }
diff --git a/smp-angular/src/app/security/role.model.ts b/smp-angular/src/app/security/role.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ac0c7a3c8e1537c60d96ca477c558d5cf9a7fb42
--- /dev/null
+++ b/smp-angular/src/app/security/role.model.ts
@@ -0,0 +1,14 @@
+export enum Role {
+  /**
+   * The system administrator (a.k.a. the "super admin") role
+   */
+  SYSTEM_ADMINISTRATOR,
+  /**
+   * The SMP Administrator role. It is assimilable to the {@link SERVICE_GROUP_ADMINISTRATOR} role for now.
+    */
+  SMP_ADMINISTRATOR,
+  /**
+   * The ServiceGroup administrator role
+   */
+  SERVICE_GROUP_ADMINISTRATOR,
+}
diff --git a/smp-angular/src/app/security/role.service.ts b/smp-angular/src/app/security/role.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ed50cf01d4ead2837efade7b527009c61bd051b8
--- /dev/null
+++ b/smp-angular/src/app/security/role.service.ts
@@ -0,0 +1,25 @@
+import {Injectable} from "@angular/core";
+import {Role} from "./role.model";
+
+@Injectable()
+export class RoleService {
+
+  /**
+   * Returns a user representation of the role or an empty string if the role is unknown.
+   *
+   * @param role the role for which we need to display the user representation
+   */
+  public getLabel(role: Role): string {
+    switch (role) {
+      case Role.SMP_ADMINISTRATOR:
+        return 'SMP Administrator';
+      case Role.SERVICE_GROUP_ADMINISTRATOR:
+        return 'ServiceGroup Administrator';
+      case Role.SYSTEM_ADMINISTRATOR:
+        return 'System Administrator';
+      default:
+        return '';
+    }
+  }
+
+}
diff --git a/smp-angular/src/app/security/security.service.ts b/smp-angular/src/app/security/security.service.ts
index 5ef7058a3145375f3854eefd7d36dbfeb75c9384..d68fabfe6ec65649193d5864c3ac1ea31e725c60 100644
--- a/smp-angular/src/app/security/security.service.ts
+++ b/smp-angular/src/app/security/security.service.ts
@@ -6,16 +6,15 @@ import {User} from './user.model';
 import {ReplaySubject} from 'rxjs';
 import {SecurityEventService} from './security-event.service';
 import {DomainService} from './domain.service';
+import {Role} from "./role.model";
 
 @Injectable()
 export class SecurityService {
-  static ROLE_AP_ADMIN = 'ROLE_AP_ADMIN';
-  static ROLE_DOMAIN_ADMIN = 'ROLE_ADMIN';
 
   constructor (private http: Http, private securityEventService: SecurityEventService, private domainService: DomainService) {
   }
 
-  login (username: string, password: string) {
+  login(username: string, password: string) {
     this.domainService.resetDomain();
     let headers = new Headers({'Content-Type': 'application/json'});
     return this.http.post('rest/security/authentication',
@@ -35,7 +34,7 @@ export class SecurityService {
         });
   }
 
-  logout () {
+  logout() {
     console.log('Logging out');
     this.domainService.resetDomain();
     this.http.delete('rest/security/authentication').subscribe((res: Response) => {
@@ -48,11 +47,11 @@ export class SecurityService {
       });
   }
 
-  getCurrentUser (): User {
+  getCurrentUser(): User {
     return JSON.parse(localStorage.getItem('currentUser'));
   }
 
-  private getCurrentUsernameFromServer (): Observable<string> {
+  private getCurrentUsernameFromServer(): Observable<string> {
     let subject = new ReplaySubject();
     this.http.get('rest/security/user')
       .subscribe((res: Response) => {
@@ -64,7 +63,7 @@ export class SecurityService {
     return subject.asObservable();
   }
 
-  isAuthenticated (callServer: boolean = false): Observable<boolean> {
+  isAuthenticated(callServer: boolean = false): Observable<boolean> {
     let subject = new ReplaySubject();
     if (callServer) {
       //we get the username from the server to trigger the redirection to the login screen in case the user is not authenticated
@@ -84,19 +83,19 @@ export class SecurityService {
     return subject.asObservable();
   }
 
-  isCurrentUserSuperAdmin (): boolean {
-    return this.isCurrentUserInRole([SecurityService.ROLE_AP_ADMIN]);
+  isCurrentUserSuperAdmin(): boolean {
+    return this.isCurrentUserInRole([Role.SYSTEM_ADMINISTRATOR]);
   }
 
-  isCurrentUserAdmin (): boolean {
-    return this.isCurrentUserInRole([SecurityService.ROLE_DOMAIN_ADMIN, SecurityService.ROLE_AP_ADMIN]);
+  isCurrentUserAdmin(): boolean {
+    return this.isCurrentUserInRole([Role.SYSTEM_ADMINISTRATOR, Role.SMP_ADMINISTRATOR]);
   }
 
-  isCurrentUserInRole (roles: Array<string>): boolean {
+  isCurrentUserInRole(roles: Array<Role>): boolean {
     let hasRole = false;
     const currentUser = this.getCurrentUser();
     if (currentUser && currentUser.authorities) {
-      roles.forEach((role: string) => {
+      roles.forEach((role: Role) => {
         if (currentUser.authorities.indexOf(role) !== -1) {
           hasRole = true;
         }
@@ -105,7 +104,7 @@ export class SecurityService {
     return hasRole;
   }
 
-  isAuthorized (roles: Array<string>): Observable<boolean> {
+  isAuthorized(roles: Array<Role>): Observable<boolean> {
     let subject = new ReplaySubject();
 
     this.isAuthenticated(false).subscribe((isAuthenticated: boolean) => {
diff --git a/smp-angular/src/app/security/user.model.ts b/smp-angular/src/app/security/user.model.ts
index d0042d6c0b64c7da99b34a477634d8fd85754654..53fcc6cb70db870969f54279c890a4cfa77ab1f6 100644
--- a/smp-angular/src/app/security/user.model.ts
+++ b/smp-angular/src/app/security/user.model.ts
@@ -1,6 +1,8 @@
+import {Role} from "./role.model";
+
 export interface User {
   id: number;
   username: string;
-  authorities: Array<string>;
+  authorities: Array<Role>;
   defaultPasswordUsed: boolean;
 }
diff --git a/smp-angular/src/app/service-group/service-group-controller.ts b/smp-angular/src/app/service-group/service-group-controller.ts
index 8dc51671053b65f4dbd5c1d624d423584913e8ae..ab2408eb75bb3b11126e6793a283862f5009720d 100644
--- a/smp-angular/src/app/service-group/service-group-controller.ts
+++ b/smp-angular/src/app/service-group/service-group-controller.ts
@@ -1,17 +1,21 @@
 import {SearchTableController} from "../common/search-table/search-table-controller";
-import {MdDialog, MdDialogRef} from "@angular/material";
+import {MdDialog, MdDialogConfig, MdDialogRef} from "@angular/material";
 import {ServiceGroupDetailsDialogComponent} from "./service-group-details-dialog/service-group-details-dialog.component";
 import {Http} from "@angular/http";
 import {AlertService} from "../alert/alert.service";
 import {ServiceGroupExtensionDialogComponent} from "./service-group-extension-dialog/service-group-extension-dialog.component";
 import {ServiceGroupMetadataListDialogComponent} from "./service-group-metadata-list-dialog/service-group-metadata-list-dialog.component";
+import {UserDetailsDialogComponent} from "../user/user-details-dialog/user-details-dialog.component";
+import {SearchTableEntity} from "../common/search-table/search-table-entity.model";
+import {ServiceGroupRo} from "./service-group-ro.model";
+import {SearchTableEntityStatus} from "../common/search-table/search-table-entity-status.model";
 
 export class ServiceGroupController implements SearchTableController {
 
   constructor(public dialog: MdDialog) { }
 
   public showDetails(row: any) {
-    let dialogRef: MdDialogRef<ServiceGroupDetailsDialogComponent> = this.dialog.open(ServiceGroupDetailsDialogComponent);
+    let dialogRef: MdDialogRef<ServiceGroupDetailsDialogComponent> = this.newDialog();
     dialogRef.componentInstance.servicegroup = row;
     dialogRef.afterClosed().subscribe(result => {
       //Todo:
@@ -37,5 +41,21 @@ export class ServiceGroupController implements SearchTableController {
 
   public edit(row: any) { }
 
-  public  delete(row: any) { }
+  public delete(row: any) { }
+
+  public newDialog(config?: MdDialogConfig): MdDialogRef<ServiceGroupDetailsDialogComponent> {
+    return this.dialog.open(ServiceGroupDetailsDialogComponent, config);
+  }
+
+  public newRow(): ServiceGroupRo {
+    return {
+      domain: '',
+      serviceGroupROId: {
+        participantId: '',
+        participantSchema: ''
+      },
+      status: SearchTableEntityStatus.NEW
+    };
+  }
+
 }
diff --git a/smp-angular/src/app/service-group/service-group-ro.model.ts b/smp-angular/src/app/service-group/service-group-ro.model.ts
index 13955cf6ab0f8f9b12f152271fcec07bcedd0e62..4c4427e2e460bf4af89428df96fb706d7be3628b 100644
--- a/smp-angular/src/app/service-group/service-group-ro.model.ts
+++ b/smp-angular/src/app/service-group/service-group-ro.model.ts
@@ -1,6 +1,7 @@
 import { ServiceGroupROId } from './service-group-ro-id.model';
+import {SearchTableEntity} from "../common/search-table/search-table-entity.model";
 
-export interface ServiceGroupRo {
+export interface ServiceGroupRo extends SearchTableEntity {
   serviceGroupROId: ServiceGroupROId;
   domain: string;
 }
diff --git a/smp-angular/src/app/user/user-controller.ts b/smp-angular/src/app/user/user-controller.ts
index 9ada4224bfea3801cfb7ad9bc91b58cd5f2fc979..ac89843415e037ebfff790f419d5b947b4ae7bc6 100644
--- a/smp-angular/src/app/user/user-controller.ts
+++ b/smp-angular/src/app/user/user-controller.ts
@@ -1,6 +1,8 @@
 import {SearchTableController} from "../common/search-table/search-table-controller";
-import {MdDialog, MdDialogRef} from "@angular/material";
+import {MdDialog, MdDialogConfig, MdDialogRef} from "@angular/material";
 import {UserDetailsDialogComponent} from "./user-details-dialog/user-details-dialog.component";
+import {UserRo} from "./user-ro.model";
+import {SearchTableEntityStatus} from "../common/search-table/search-table-entity-status.model";
 
 export class UserController implements SearchTableController {
 
@@ -8,7 +10,6 @@ export class UserController implements SearchTableController {
 
   public showDetails(row: any) {
     let dialogRef: MdDialogRef<UserDetailsDialogComponent> = this.dialog.open(UserDetailsDialogComponent);
-    dialogRef.componentInstance.user = row;
     dialogRef.afterClosed().subscribe(result => {
       //Todo:
     });
@@ -16,5 +17,17 @@ export class UserController implements SearchTableController {
 
   public edit(row: any) { }
 
-  public  delete(row: any) { }
+  public delete(row: any) { }
+
+  public newDialog(config?: MdDialogConfig): MdDialogRef<UserDetailsDialogComponent> {
+    return this.dialog.open(UserDetailsDialogComponent, config);
+  }
+
+  public newRow(): UserRo {
+    return {
+      userName: '',
+      role: '',
+      status: SearchTableEntityStatus.NEW
+    }
+  }
 }
diff --git a/smp-angular/src/app/user/user-details-dialog/user-details-dialog.component.html b/smp-angular/src/app/user/user-details-dialog/user-details-dialog.component.html
index c31301815e36538e24b8803da20b32b213fa0b4a..fb361aee12206c4df4b74fc565a6e647c1b0ce7f 100644
--- a/smp-angular/src/app/user/user-details-dialog/user-details-dialog.component.html
+++ b/smp-angular/src/app/user/user-details-dialog/user-details-dialog.component.html
@@ -1,36 +1,64 @@
-<h2 md-dialog-title>User details</h2>
+<h2 md-dialog-title>{{formTitle}}</h2>
 <md-dialog-content style="height:260px;width:650px">
   <md-card>
     <md-card-content>
-      <md-input-container style="width:100%">
-        <input mdInput placeholder="Username id" value="{{user.username}}" readonly/>
-      </md-input-container>
+      <div style="margin-top:15px;">
+        <md-input-container style="width:100%">
+          <input mdInput placeholder="Username" name="userName" id="userName" [value]="current.userName" (blur)="updateUserName($event)" [formControl]="userForm.controls['userName']" maxlength="255" required>
+          <div *ngIf="userForm.controls['userName'].hasError('required') && userForm.controls['userName'].touched" style="color:red; font-size: 70%">You should type an username</div>
+        </md-input-container>
+      </div>
 
-      <md-input-container style="width:100%">
-        <input mdInput placeholder="isAdmin" value="{{user.idAdmin}}" readonly/>
-      </md-input-container>
+      <div style="margin-top:10px;">
+        <md2-select mdInput placeholder="Role" [style.width]="'100%'" [formControl]="userForm.controls['role']"
+                    [(ngModel)]="role" required>
+          <md2-option *ngFor="let item of existingRoles" [value]="item">{{getRoleLabel(item)}}</md2-option>
+        </md2-select>
+        <div *ngIf="userForm.controls['role'].hasError('required') && userForm.controls['role'].touched" style="color:red; font-size: 70%">You need to choose at least one role for this user</div>
+      </div>
 
+      <div style="margin-top:15px;">
+        <md-input-container [style.width]="'100%'">
+          <input mdInput placeholder="Password" name="password" type="password" [value]="current.password" (blur)="updatePassword($event)" [formControl]="userForm.controls['password']" [pattern]="passwordPattern" [required]="!editMode">
+          <div *ngIf="!editMode && userForm.controls['password'].hasError('required') && userForm.controls['password'].touched" style="color:red; font-size: 70%">You should type a password</div>
+          <div *ngIf="userForm.controls['password'].dirty && userForm.controls['password'].hasError('pattern') && userForm.controls['password'].touched" style="color:red; font-size: 70%">
+            Password should follow all of these rules:<br>
+            - Minimum length: 8 characters<br>
+            - Maximum length: 32 characters<br>
+            - At least one letter in lowercase<br>
+            - At least one letter in uppercase<br>
+            - At least one digit<br>
+            - At least one special character
+          </div>
+        </md-input-container>
+      </div>
 
+      <div>
+        <md-input-container [style.width]="'100%'">
+          <input mdInput placeholder="Confirmation" name="confirmation" type="password" [value]="current.confirmation" [formControl]="userForm.controls['confirmation']" [required]="!editMode">
+          <div *ngIf="!editMode && userForm.controls['confirmation'].hasError('required') && userForm.controls['confirmation'].touched" style="color:red; font-size: 70%">You should type a password</div>
+          <div *ngIf="userForm.errors?.confirmation && userForm.controls['confirmation'].touched" style="color:red; font-size: 70%">Passwords do not match</div>
+        </md-input-container>
+      </div>
 
     </md-card-content>
   </md-card>
 
 </md-dialog-content>
 
-<md-dialog-actions>
-  <div class="group-action-button" >
-  <button id="ServiceGroupsSaveButton" md-raised-button color="primary" (click)="dialogRef.close({})"
-          style="margin-top:10px">
-    <md-icon>save</md-icon>
-    <span>Save</span>
-  </button>
-
-
-  <button id="ServiceGroupsCloseButton" md-raised-button color="primary" (click)="dialogRef.close({})"
-          style="margin-top:10px">
-    <md-icon>close</md-icon>
-    <span>Close</span>
-  </button>
-  </div>
-</md-dialog-actions>
+<table class="buttonsRow">
+  <tr>
+    <td>
+      <button md-raised-button color="primary" [md-dialog-close]="true" (click)="submitForm()" [disabled]="!userForm.valid">
+        <md-icon>check_circle</md-icon>
+        <span>OK</span>
+      </button>
+      <button md-raised-button color="primary" md-dialog-close>
+        <md-icon>cancel</md-icon>
+        <span>Cancel</span>
+      </button>
+    </td>
+  </tr>
+</table>
+<div  style="text-align: right; font-size: 70%">* required fields</div>
 
diff --git a/smp-angular/src/app/user/user-details-dialog/user-details-dialog.component.ts b/smp-angular/src/app/user/user-details-dialog/user-details-dialog.component.ts
index 6921a83d8b3ee21b1bc940cfc13dca72ee9fdf20..f88bdb22784f9af13324a0a499cd943386050f8f 100644
--- a/smp-angular/src/app/user/user-details-dialog/user-details-dialog.component.ts
+++ b/smp-angular/src/app/user/user-details-dialog/user-details-dialog.component.ts
@@ -1,5 +1,11 @@
-import {Component} from '@angular/core';
-import {MdDialogRef} from "@angular/material";
+import {Component, Inject} from '@angular/core';
+import {MD_DIALOG_DATA, MdDialogRef} from "@angular/material";
+import {AbstractControl, FormBuilder, FormControl, FormGroup, ValidationErrors, ValidatorFn, Validators} from "@angular/forms";
+import {UserService} from "../user.service";
+import {Role} from "../../security/role.model";
+import {RoleService} from "../../security/role.service";
+import {UserRo} from "../user-ro.model";
+import {SearchTableEntityStatus} from "../../common/search-table/search-table-entity-status.model";
 
 @Component({
   selector: 'user-details-dialog',
@@ -7,10 +13,86 @@ import {MdDialogRef} from "@angular/material";
 })
 export class UserDetailsDialogComponent {
 
-  user;
-  dateFormat: String = 'yyyy-MM-dd HH:mm:ssZ';
+  static readonly NEW_MODE = 'New User';
+  static readonly EDIT_MODE = 'User Edit';
 
-  constructor(public dialogRef: MdDialogRef<UserDetailsDialogComponent>) {
+  // readonly emailPattern = '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}';
+  readonly passwordPattern = '^(?=.*[A-Z])(?=.*[ !#$%&\'()*+,-./:;<=>?@\\[^_`{|}~\\\]"])(?=.*[0-9])(?=.*[a-z]).{8,32}$';
+
+  editMode: boolean;
+  formTitle: string;
+  userRoles = [];
+  existingRoles = [];
+  confirmation = '';
+  current: UserRo & { confirmation?: string };
+  userForm: FormGroup;
+
+  private passwordConfirmationValidator: ValidatorFn = (control: FormGroup): ValidationErrors | null => {
+    const password = control.get('password');
+    const confirmation = control.get('confirmation');
+    return password && confirmation && password.value !== confirmation.value ? { confirmation: true } : null;
+  };
+
+  constructor(private dialogRef: MdDialogRef<UserDetailsDialogComponent>,
+              private userService: UserService,
+              private roleService: RoleService,
+              @Inject(MD_DIALOG_DATA) public data: any,
+              private fb: FormBuilder) {
+    this.editMode = data.edit;
+    this.formTitle = this.editMode ?  UserDetailsDialogComponent.EDIT_MODE: UserDetailsDialogComponent.NEW_MODE;
+
+    this.current = this.editMode
+      ? {
+        ...data.row,
+        confirmation: data.row.password
+      }
+      : {
+        userName: '',
+        password: '',
+        confirmation: '',
+        role: '',
+        status: SearchTableEntityStatus.NEW
+      };
+
+    this.userForm = fb.group({
+      'userName': new FormControl({value: this.current.userName, disabled: this.editMode}, this.editMode ? Validators.nullValidator : null),
+      'role': new FormControl(this.current.role, Validators.required),
+      'password': new FormControl(this.current.password, [Validators.required, Validators.pattern(this.passwordPattern)]),
+      'confirmation': new FormControl(this.current.password, Validators.pattern(this.passwordPattern))
+    }, {
+      validator: this.passwordConfirmationValidator
+    });
+
+    this.userService.getUserRoles$().subscribe(userRoles => {
+      this.userRoles = userRoles.json();
+      this.existingRoles = this.editMode
+        ? this.getAllowedRoles(this.userRoles, this.current.role)
+        : this.userRoles;
+    });
+  }
+
+  submitForm() {
+    this.dialogRef.close(true);
   }
 
+  updateUserName(event) {
+    this.current.userName = event.target.value;
+  }
+
+  updatePassword(event) {
+    this.current.password = event.target.value;
+  }
+
+  getRoleLabel(role: Role): string {
+    return this.roleService.getLabel(role);
+  }
+
+  // filters out roles so that the user cannot change from system administrator to the other roles or vice-versa
+  private getAllowedRoles(allRoles, userRole) {
+    if (userRole === Role.SYSTEM_ADMINISTRATOR) {
+      return [Role.SYSTEM_ADMINISTRATOR];
+    } else {
+      return allRoles.filter(role => role !== Role.SYSTEM_ADMINISTRATOR);
+    }
+  }
 }
diff --git a/smp-angular/src/app/user/user-ro.model.ts b/smp-angular/src/app/user/user-ro.model.ts
index 5642811adb043cc5a40f6eaee4ed124abba0a576..fc604cea8d38cf58212a444f893f2c8b715882c1 100644
--- a/smp-angular/src/app/user/user-ro.model.ts
+++ b/smp-angular/src/app/user/user-ro.model.ts
@@ -1,4 +1,8 @@
-export interface UserRo {
-  username: string;
-  admin: string;
+import {SearchTableEntity} from "../common/search-table/search-table-entity.model";
+
+export interface UserRo extends SearchTableEntity {
+  userName: string;
+  password?: string;
+  role: string;
+  suspended?: boolean;
 }
diff --git a/smp-angular/src/app/user/user.component.css b/smp-angular/src/app/user/user.component.css
index 94bdbcf05b52992fba8266a500a289e9ed65f3b8..1e2c4567531ce54920b3da930aeb44ed2c39f44e 100644
--- a/smp-angular/src/app/user/user.component.css
+++ b/smp-angular/src/app/user/user.component.css
@@ -11,5 +11,3 @@
 #hiddenButtonId {
   position: fixed;
 }
-
-
diff --git a/smp-angular/src/app/user/user.component.html b/smp-angular/src/app/user/user.component.html
index d14b4cd377464035304dadc7b72aea03f231f775..cf3b4eef12ef2ba50900b5278a90e59eb6adf0d3 100644
--- a/smp-angular/src/app/user/user.component.html
+++ b/smp-angular/src/app/user/user.component.html
@@ -1,31 +1,18 @@
-
 <smp-search-table
   page_id= 'user_id'
   title= 'Users'
   [columnPicker] = "columnPicker"
-  url="ui/user"
+  [url]="'ui/user'"
   [additionalToolButtons]="additionalToolButtons"
   [searchTableController]="userController"
   [searchPanel]="searchPanel"
-  [filter]="filter"
-
->
-
+  [filter]="filter">
 
-  <ng-template #additionalToolButtons >
-
-  </ng-template>
+  <ng-template #additionalToolButtons></ng-template>
 
   <ng-template #searchPanel>
     <md-input-container>
-      <input mdInput placeholder="Username" name="Username" [(ngModel)]="filter.messageId"
-             #messageId="ngModel" id="messageid_id">
+      <input mdInput placeholder="Username" name="Username" [(ngModel)]="filter.userName" #messageId="ngModel">
     </md-input-container>
-    <md-input-container>
-      <input mdInput placeholder="isAdmin" name="isAdmin" [(ngModel)]="filter.messageId"
-             #messageId="ngModel" id="participanschema_id">
-    </md-input-container>
-
   </ng-template>
-
 </smp-search-table>
diff --git a/smp-angular/src/app/user/user.component.ts b/smp-angular/src/app/user/user.component.ts
index bcac77b2ba171b483a21743a333a4e46fe231a8d..ebb9a4b37ebcf0c5d6d57fc44804343a3b2cf24e 100644
--- a/smp-angular/src/app/user/user.component.ts
+++ b/smp-angular/src/app/user/user.component.ts
@@ -7,7 +7,6 @@ import {AlertService} from "../alert/alert.service";
 import {UserController} from "./user-controller";
 
 @Component({
-  moduleId: module.id,
   templateUrl:'./user.component.html',
   styleUrls: ['./user.component.css']
 })
@@ -30,23 +29,29 @@ export class UserComponent implements OnInit {
     this.columnPicker.allColumns = [
       {
         name: 'Username',
-        prop: 'username',
-        width: 275
+        prop: 'userName',
+        canAutoResize: true
       },
       {
-        name: 'isAdmin',
-        prop: 'isadmin',
-        width: 40
+        name: 'Role',
+        prop: 'role',
+        canAutoResize: true
+      },
+      {
+        name: 'Password',
+        prop: 'password',
+        canAutoResize: true,
+        sortable: false,
+        width: 25
       }
     ];
 
     this.columnPicker.selectedColumns = this.columnPicker.allColumns.filter(col => {
-      return ["Username", "isAdmin"].indexOf(col.name) != -1
+      return ['Username', 'Role'].indexOf(col.name) != -1
     });
   }
 
   details(row: any) {
     this.userController.showDetails(row);
-
   }
 }
diff --git a/smp-angular/src/app/user/user.service.ts b/smp-angular/src/app/user/user.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e74ef377a0b20d2dc65212a4d50db3aabb09f417
--- /dev/null
+++ b/smp-angular/src/app/user/user.service.ts
@@ -0,0 +1,16 @@
+import {Injectable} from "@angular/core";
+import {Observable, Subject} from "rxjs";
+import {Http} from "@angular/http";
+import {Role} from "../security/role.model";
+
+@Injectable()
+export class UserService {
+
+  constructor(private http: Http) {}
+
+  getUserRoles$() {
+    // return this.http.get('rest/user/userroles');
+    // TODO create the endpoint
+    return Observable.of({json: () => [Role.SYSTEM_ADMINISTRATOR, Role.SMP_ADMINISTRATOR, Role.SERVICE_GROUP_ADMINISTRATOR]});
+  }
+}
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/UserRO.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/UserRO.java
index c81c50ebcac948c44523decd55ea798cdb42aff8..d1704548e426df4593124d6eab5bfaea43b9d59b 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/UserRO.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/UserRO.java
@@ -16,7 +16,7 @@ public class UserRO implements Serializable {
 
 
     private static final long serialVersionUID = -4971552086560325302L;
-    private String username;
+    private String userName;
     private String password;
     private String email;
     LocalDateTime passwordChanged;
@@ -28,12 +28,12 @@ public class UserRO implements Serializable {
     }
 
 
-    public String getUsername() {
-        return username;
+    public String getUserName() {
+        return userName;
     }
 
-    public void setUsername(String username) {
-        this.username = username;
+    public void setUserName(String userName) {
+        this.userName = userName;
     }
 
     public String getPassword() {
diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/UserResource.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/UserResource.java
index 3b258ee45e3fc3fdd201f314929a289f5bab8b2f..1d8f8a4956b988e909743dd45e22dc0bc87242b9 100644
--- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/UserResource.java
+++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/UserResource.java
@@ -33,7 +33,7 @@ public class UserResource {
     @PutMapping(produces = {"application/json"})
     @ResponseBody
     @RequestMapping(method = RequestMethod.GET)
-    public ServiceResult<UserRO> getUserist(
+    public ServiceResult<UserRO> getUsers(
             @RequestParam(value = "page", defaultValue = "0") int page,
             @RequestParam(value = "pageSize", defaultValue = "10") int pageSize,
             @RequestParam(value = "orderBy", required = false) String orderBy,