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 e244f55fa92329838f6743f52cea7a15f6203745..86b05930717ea72780858b9772166cee1c00fc6d 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
@@ -10,6 +10,7 @@ export interface SearchTableController {
   newRow(): SearchTableEntity;
   newDialog(config?: MatDialogConfig): MatDialogRef<any>;
   dataSaved();
+  isRecordChanged(oldModel, newModel): boolean;
 
   /**
    * Returns whether the row expander should be shown as disabled even when the actual row is not fully disabled.
@@ -17,4 +18,5 @@ export interface SearchTableController {
    * @param row the row for which the row expander should be disabled or not
    */
   isRowExpanderDisabled(row: SearchTableEntity): boolean;
+
 }
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 05597247b02e50a66dac4bd7314bf324b9a2085c..5f7997ad007b42668cc7234d747c7478afebcf17 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
@@ -167,8 +167,7 @@ export class SearchTableComponent implements OnInit {
       // try again
       if (result.count < 1 && offset > 0) {
         this.pageInternal(offset--, pageSize, orderBy, asc)
-      }
-      else {
+      } else {
         this.offset = offset;
         this.rowLimiter.pageSize = pageSize;
         this.orderBy = orderBy;
@@ -233,7 +232,7 @@ export class SearchTableComponent implements OnInit {
         this.rows = [...this.rows, {...formRef.componentInstance.getCurrent()}];
         //this.rows = this.rows.concat(formRef.componentInstance.current);
         this.count++;
-        // this.searchtable.refresh();
+        // this.searchable.refresh();
       } else {
         this.unselectRows();
       }
@@ -254,10 +253,6 @@ export class SearchTableComponent implements OnInit {
 
   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();
@@ -333,11 +328,14 @@ export class SearchTableComponent implements OnInit {
     });
     formRef.afterClosed().subscribe(result => {
       if (result) {
-        const status = row.status === SearchTableEntityStatus.PERSISTED
-          ? SearchTableEntityStatus.UPDATED
-          : row.status;
-        this.rows[rowNumber] = {...formRef.componentInstance.getCurrent(), status};
-        this.rows = [...this.rows];
+        const changed = this.searchTableController.isRecordChanged(row, formRef.componentInstance.getCurrent());
+        if (changed) {
+          const status = row.status === SearchTableEntityStatus.PERSISTED
+            ? SearchTableEntityStatus.UPDATED
+            : row.status;
+          this.rows[rowNumber] = {...formRef.componentInstance.getCurrent(), status};
+          this.rows = [...this.rows];
+        }
       }
     });
   }
@@ -358,7 +356,7 @@ export class SearchTableComponent implements OnInit {
 
   private deleteSearchTableEntities(rows: Array<SearchTableEntity>) {
 
-    this.searchTableController.validateDeleteOperation(rows).subscribe( (res: SearchTableValidationResult) => {
+    this.searchTableController.validateDeleteOperation(rows).subscribe((res: SearchTableValidationResult) => {
       if (!res.validOperation) {
         this.alertService.exception("Delete validation error", res.stringMessage, false);
       } else {
diff --git a/smp-angular/src/app/domain/domain-controller.ts b/smp-angular/src/app/domain/domain-controller.ts
index b0b0f945e0fd5ba65af523948bcb9d35ad4ff74b..b075c22ae66a5f494b755ea4033b713bc29e8a92 100644
--- a/smp-angular/src/app/domain/domain-controller.ts
+++ b/smp-angular/src/app/domain/domain-controller.ts
@@ -67,4 +67,23 @@ export class DomainController implements SearchTableController {
   isRowExpanderDisabled(row: SearchTableEntity): boolean {
     return false;
   }
+
+  isRecordChanged(oldModel, newModel): boolean {
+    for (var property in oldModel) {
+      const isEqual = this.isEqual(newModel[property],oldModel[property]);
+      if (!isEqual) {
+        return true; // Property has changed
+      }
+    }
+    return false;
+  }
+
+  isEqual(val1, val2): boolean {
+    return (this.isEmpty(val1) && this.isEmpty(val2)
+      || val1 === val2);
+  }
+
+  isEmpty(str): boolean {
+    return (!str || 0 === str.length);
+  }
 }
diff --git a/smp-angular/src/app/service-group-edit/service-group-details-dialog/service-group-details-dialog.component.ts b/smp-angular/src/app/service-group-edit/service-group-details-dialog/service-group-details-dialog.component.ts
index 2d0142673cf4b9abb8a7a59167a82b716077b9e9..1b31878492318ca9af1d7f592ee37aaabf74c724 100644
--- a/smp-angular/src/app/service-group-edit/service-group-details-dialog/service-group-details-dialog.component.ts
+++ b/smp-angular/src/app/service-group-edit/service-group-details-dialog/service-group-details-dialog.component.ts
@@ -130,6 +130,7 @@ export class ServiceGroupDetailsDialogComponent implements OnInit {
       this.extensionObserver = this.http.get<ServiceGroupValidationRo>(SmpConstants.REST_SERVICE_GROUP_EXTENSION + '/' + this.current.id);
       this.extensionObserver.subscribe((res: ServiceGroupValidationRo) => {
         this.dialogForm.get('extension').setValue(res.extension);
+        this.current.extension = res.extension;
       });
     }
 
@@ -217,7 +218,9 @@ export class ServiceGroupDetailsDialogComponent implements OnInit {
       this.current.participantIdentifier = this.dialogForm.value['participantIdentifier'];
       this.current.participantScheme = this.dialogForm.value['participantScheme'];
     } else {
-      this.current.extensionStatus = SearchTableEntityStatus.UPDATED;
+
+      this.current.extensionStatus = this.extensionChanged()?
+        SearchTableEntityStatus.UPDATED:SearchTableEntityStatus.PERSISTED;
     }
     this.current.users = this.dialogForm.value['users'];
     this.current.extension = this.dialogForm.value['extension'];
@@ -261,7 +264,10 @@ export class ServiceGroupDetailsDialogComponent implements OnInit {
       return true;
     }
     return this.current.users !== this.dialogForm.value['users'];
-    this.current.extension !== this.dialogForm.value['extension'];
+  }
+
+  extensionChanged():boolean {
+    return  this.current.extension !== this.dialogForm.value['extension'].toString();
   }
 
   onExtensionDelete() {
diff --git a/smp-angular/src/app/service-group-edit/service-group-edit-controller.ts b/smp-angular/src/app/service-group-edit/service-group-edit-controller.ts
index 5249d57e8ac0b053c91c3a5460cdd19c898783d9..96ac25442867b9a89b022070e80ed735bd55d609 100644
--- a/smp-angular/src/app/service-group-edit/service-group-edit-controller.ts
+++ b/smp-angular/src/app/service-group-edit/service-group-edit-controller.ts
@@ -8,7 +8,6 @@ import {ServiceGroupMetadataDialogComponent} from "./service-group-metadata-dial
 import {of} from "rxjs/internal/observable/of";
 import {SearchTableValidationResult} from "../common/search-table/search-table-validation-result.model";
 import {SearchTableEntity} from "../common/search-table/search-table-entity.model";
-import {ServiceGroupSearchRo} from "../service-group-search/service-group-search-ro.model";
 
 export class ServiceGroupEditController implements SearchTableController {
 
@@ -89,4 +88,34 @@ export class ServiceGroupEditController implements SearchTableController {
     const serviceGroup = <ServiceGroupEditRo>row;
     return !(serviceGroup.serviceMetadata && serviceGroup.serviceMetadata.length);
   }
+
+  isRecordChanged(oldModel, newModel): boolean {
+    // check if the extension was changed
+    if (newModel["extensionStatus"]!== SearchTableEntityStatus.PERSISTED){
+      return true;
+    }
+    // check if other properties were changed
+    for (var property in oldModel) {
+      if (property === 'extensionStatus'||
+          property === 'extension') {
+        // ignore
+        continue;
+      } else {
+        const isEqual = this.isEqual(newModel[property], oldModel[property]);
+        if (!isEqual) {
+          return true; // Property has changed
+        }
+      }
+    }
+    return false;
+  }
+
+  isEqual(val1, val2): boolean {
+    return (this.isEmpty(val1) && this.isEmpty(val2)
+      || val1 === val2);
+  }
+
+  isEmpty(str): boolean {
+    return (!str || 0 === str.length);
+  }
 }
diff --git a/smp-angular/src/app/service-group-edit/service-group-edit.component.ts b/smp-angular/src/app/service-group-edit/service-group-edit.component.ts
index 77aa6b6f3655260e7caebfa4974971ffa9d7ff68..9be8017fdccac9d9585495c09d457f75d2bbfefe 100644
--- a/smp-angular/src/app/service-group-edit/service-group-edit.component.ts
+++ b/smp-angular/src/app/service-group-edit/service-group-edit.component.ts
@@ -132,6 +132,12 @@ export class ServiceGroupEditComponent implements OnInit {
     formRef.afterClosed().subscribe(result => {
       if (result) {
 
+        let isXMLChanged=formRef.componentInstance.isMetaDataXMLChanged();
+        if (!isXMLChanged){
+          // nothing to save
+          return;
+        }
+
         let statusMetadata =metaDataRow.status === SearchTableEntityStatus.PERSISTED
           ? SearchTableEntityStatus.UPDATED
           : metaDataRow;
@@ -176,6 +182,16 @@ export class ServiceGroupEditComponent implements OnInit {
     }
   }
 
+  isRecordChanged (oldModel, newModel): boolean {
+    for (var property in oldModel) {
+      var changed = false;
+      if (newModel[property] !== oldModel[property]) {
+        return true; // Property has changed
+      }
+    }
+    return false;
+  }
+
   // for dirty guard...
   isDirty (): boolean {
     return this.searchTable.isDirty();
diff --git a/smp-angular/src/app/service-group-edit/service-group-metadata-dialog/service-group-metadata-dialog.component.ts b/smp-angular/src/app/service-group-edit/service-group-metadata-dialog/service-group-metadata-dialog.component.ts
index 3f54f92e8197d40bc4fef7658e06880908c5cef7..3717028f6924b52192123ea67cb49f30e8754372 100644
--- a/smp-angular/src/app/service-group-edit/service-group-metadata-dialog/service-group-metadata-dialog.component.ts
+++ b/smp-angular/src/app/service-group-edit/service-group-metadata-dialog/service-group-metadata-dialog.component.ts
@@ -15,7 +15,7 @@ import {ServiceMetadataValidationEditRo} from "./service-metadata-validation-edi
 import {ServiceMetadataWizardRo} from "../service-metadata-wizard-dialog/service-metadata-wizard-edit-ro.model";
 
 @Component({
-  selector: 'app-messagelog-dialog',
+  selector: 'service-group-metadata-dialog',
   templateUrl: './service-group-metadata-dialog.component.html',
   styleUrls: ['./service-group-metadata-dialog.component.css']
 })
@@ -90,6 +90,8 @@ export class ServiceGroupMetadataDialogComponent implements OnInit {
       this.xmlServiceMetadataObserver = this.http.get<ServiceMetadataEditRo>(SmpConstants.REST_METADATA + '/' + this.current.id);
       this.xmlServiceMetadataObserver.subscribe((res: ServiceMetadataEditRo) => {
         this.dialogForm.get('xmlContent').setValue(res.xmlContent);
+        // store init xml to current value for change validation
+        this.current.xmlContent=res.xmlContent;
       });
     }
 
@@ -258,6 +260,10 @@ export class ServiceGroupMetadataDialogComponent implements OnInit {
     return this.current;
   }
 
+  public isMetaDataXMLChanged():boolean{
+     return  this.dialogForm.value['xmlContent'] !== this.current.xmlContent;
+  }
+
   compareDomainCode(sgDomain: ServiceGroupDomainEditRo, domainCode: String): boolean {
     return sgDomain.domainCode === domainCode;
   }
diff --git a/smp-angular/src/app/service-group-edit/service-metadata-wizard-dialog/service-metadata-wizard-dialog.component.ts b/smp-angular/src/app/service-group-edit/service-metadata-wizard-dialog/service-metadata-wizard-dialog.component.ts
index 6dbac57e9f921757d4cc21ec35367e48bc328efe..54c95f8159d80d8e51c39fdd4245a731566d9b44 100644
--- a/smp-angular/src/app/service-group-edit/service-metadata-wizard-dialog/service-metadata-wizard-dialog.component.ts
+++ b/smp-angular/src/app/service-group-edit/service-metadata-wizard-dialog/service-metadata-wizard-dialog.component.ts
@@ -6,8 +6,6 @@ import {CertificateService} from "../../user/certificate.service";
 import {CertificateRo} from "../../user/certificate-ro.model";
 import {AlertService} from "../../alert/alert.service";
 import {ServiceMetadataWizardRo} from "./service-metadata-wizard-edit-ro.model";
-import {ServiceMetadataEditRo} from "../service-metadata-edit-ro.model";
-
 
 @Component({
   selector: 'service-metadata-wizard-dialog',
diff --git a/smp-angular/src/app/service-group-search/service-group-search-controller.ts b/smp-angular/src/app/service-group-search/service-group-search-controller.ts
index beb8fbab0dc3bb4908f9f67726775b89b148cd12..08a02037ac8388756c269f4a585a4c90b52b326e 100644
--- a/smp-angular/src/app/service-group-search/service-group-search-controller.ts
+++ b/smp-angular/src/app/service-group-search/service-group-search-controller.ts
@@ -4,7 +4,6 @@ import {ServiceGroupSearchRo} from './service-group-search-ro.model';
 import {of} from "rxjs/internal/observable/of";
 import {SearchTableValidationResult} from "../common/search-table/search-table-validation-result.model";
 import {SearchTableEntity} from "../common/search-table/search-table-entity.model";
-import {ServiceGroupEditRo} from "../service-group-edit/service-group-edit-ro.model";
 
 export class ServiceGroupSearchController implements SearchTableController {
 
@@ -45,4 +44,23 @@ export class ServiceGroupSearchController implements SearchTableController {
     const serviceGroup = <ServiceGroupSearchRo>row;
     return !(serviceGroup.serviceMetadata && serviceGroup.serviceMetadata.length);
   }
+
+  isRecordChanged(oldModel, newModel): boolean {
+    for (var property in oldModel) {
+      const isEqual = this.isEqual(newModel[property],oldModel[property]);
+      if (!isEqual) {
+        return true; // Property has changed
+      }
+    }
+    return false;
+  }
+
+  isEqual(val1, val2): boolean {
+    return (this.isEmpty(val1) && this.isEmpty(val2)
+      || val1 === val2);
+  }
+
+  isEmpty(str): boolean {
+    return (!str || 0 === str.length);
+  }
 }
diff --git a/smp-angular/src/app/user/user-controller.ts b/smp-angular/src/app/user/user-controller.ts
index 76c18573f835437a2e5536fe662d6501f60be278..48e7ef542756ee63971ff87e59fb0b0fe04c7258 100644
--- a/smp-angular/src/app/user/user-controller.ts
+++ b/smp-angular/src/app/user/user-controller.ts
@@ -8,9 +8,14 @@ import {SearchTableEntity} from "../common/search-table/search-table-entity.mode
 import {SearchTableValidationResult} from "../common/search-table/search-table-validation-result.model";
 import {SmpConstants} from "../smp.constants";
 import {HttpClient} from "@angular/common/http";
+import {CertificateRo} from "./certificate-ro.model";
 
 export class UserController implements SearchTableController {
-  constructor(protected http: HttpClient, protected lookups: GlobalLookups, public dialog: MatDialog) { }
+
+  nullCert = this.newCertificateRo();
+
+  constructor(protected http: HttpClient, protected lookups: GlobalLookups, public dialog: MatDialog) {
+  }
 
   public showDetails(row: any) {
     let dialogRef: MatDialogRef<UserDetailsDialogComponent> = this.dialog.open(UserDetailsDialogComponent);
@@ -19,9 +24,11 @@ export class UserController implements SearchTableController {
     });
   }
 
-  public edit(row: any) { }
+  public edit(row: any) {
+  }
 
-  public delete(row: any) { }
+  public delete(row: any) {
+  }
 
   public newDialog(config?: MatDialogConfig): MatDialogRef<UserDetailsDialogComponent> {
     return this.dialog.open(UserDetailsDialogComponent, this.convertWithMode(config));
@@ -29,11 +36,13 @@ export class UserController implements SearchTableController {
 
   private convertWithMode(config) {
     return (config && config.data)
-      ? {...config,
-          data: {...config.data,
-            mode: config.data.mode || (config.data.edit ? UserDetailsDialogMode.EDIT_MODE : UserDetailsDialogMode.NEW_MODE)
-          }
+      ? {
+        ...config,
+        data: {
+          ...config.data,
+          mode: config.data.mode || (config.data.edit ? UserDetailsDialogMode.EDIT_MODE : UserDetailsDialogMode.NEW_MODE)
         }
+      }
       : config;
   }
 
@@ -54,16 +63,16 @@ export class UserController implements SearchTableController {
     this.lookups.refreshUserLookup();
   }
 
-  validateDeleteOperation(rows: Array<SearchTableEntity>){
+  validateDeleteOperation(rows: Array<SearchTableEntity>) {
     var deleteRowIds = rows.map(rows => rows.id);
-    return  this.http.post<SearchTableValidationResult>(SmpConstants.REST_USER_VALIDATE_DELETE, deleteRowIds);
+    return this.http.post<SearchTableValidationResult>(SmpConstants.REST_USER_VALIDATE_DELETE, deleteRowIds);
   }
 
   public newValidationResult(lst: Array<number>): SearchTableValidationResult {
     return {
       validOperation: false,
       stringMessage: null,
-      listId:lst,
+      listId: lst,
     }
   }
 
@@ -71,4 +80,64 @@ export class UserController implements SearchTableController {
     return false;
   }
 
+  isCertificateChanged(oldCert, newCert): boolean {
+    if (!this.isNotNull(oldCert) && !this.isNotNull(newCert)) {
+      return false;
+    }
+
+    if (!this.isNotNull(oldCert)) {
+      oldCert = this.nullCert;
+    }
+
+    if (!this.isNotNull(newCert)) {
+      newCert = this.nullCert;
+    }
+    return this.isRecordChanged(oldCert, newCert);
+  }
+
+
+  isRecordChanged(oldModel, newModel): boolean {
+
+    for (var property in oldModel) {
+      if (property === 'certificate') {
+        if (this.isCertificateChanged(newModel[property], oldModel[property])) {
+          return true; // Property has changed
+        }
+      } else {
+        const isEqual = this.isEqual(newModel[property], oldModel[property]);
+        if (!isEqual) {
+          return true; // Property has changed
+        }
+      }
+    }
+    return false;
+  }
+
+  isEqual(val1, val2): boolean {
+    return (this.isEmpty(val1) && this.isEmpty(val2)
+      || val1 === val2);
+  }
+
+  isEmpty(str): boolean {
+    return (!str || 0 === str.length);
+  }
+
+  isNotNull(obj): boolean {
+    return typeof obj != 'undefined' && obj
+  }
+
+
+  private newCertificateRo(): CertificateRo {
+    return {
+      subject: '',
+      validFrom: null,
+      validTo: null,
+      issuer: '',
+      serialNumber: '',
+      certificateId: '',
+      fingerprints: '',
+    };
+  }
+
+
 }
diff --git a/smp-angular/src/app/user/user.component.ts b/smp-angular/src/app/user/user.component.ts
index 8cc3d337fc859669c06c325ff24998ba7967c670..d8ee84c7501c20271e79e34e0daad7e7cb237d4b 100644
--- a/smp-angular/src/app/user/user.component.ts
+++ b/smp-angular/src/app/user/user.component.ts
@@ -7,7 +7,6 @@ import {HttpClient} from '@angular/common/http';
 import {SearchTableComponent} from "../common/search-table/search-table.component";
 import {SecurityService} from "../security/security.service";
 import {GlobalLookups} from "../common/global-lookups";
-import {KeystoreEditDialogComponent} from "../domain/keystore-edit-dialog/keystore-edit-dialog.component";
 import {TruststoreEditDialogComponent} from "./truststore-edit-dialog/truststore-edit-dialog.component";
 
 @Component({
@@ -72,7 +71,6 @@ export class UserComponent implements OnInit {
     return this.searchTable.isDirty();
   }
 
-
   openEditTruststoreDialog() {
     const formRef: MatDialogRef<any> = this.dialog.open(TruststoreEditDialogComponent);
     formRef.afterClosed().subscribe(result => {