directory-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From kayyag...@apache.org
Subject [1/3] directory-fortress-enmasse git commit: first drop of new Fortress UI
Date Sun, 18 Nov 2018 15:40:44 GMT
Repository: directory-fortress-enmasse
Updated Branches:
  refs/heads/FC-247 [created] 5aae8a26f


http://git-wip-us.apache.org/repos/asf/directory-fortress-enmasse/blob/5aae8a26/embrasure/package.json
----------------------------------------------------------------------
diff --git a/embrasure/package.json b/embrasure/package.json
new file mode 100644
index 0000000..540874e
--- /dev/null
+++ b/embrasure/package.json
@@ -0,0 +1,50 @@
+{
+  "name": "embrasure",
+  "version": "0.1.0",
+  "description": "Fortress Dashboard",
+  "author": "Fortress Dev <dev@directory.apache.org>",
+  "private": true,
+  "scripts": {
+    "serve": "vue-cli-service serve",
+    "build": "vue-cli-service build",
+    "lint": "vue-cli-service lint"
+  },
+  "dependencies": {
+    "axios": "^0.18.0",
+    "element-ui": "^2.4.8",
+    "rfc6902": "^3.0.1",
+    "vue": "^2.5.17",
+    "vue-data-tables": "^3.4.4",
+    "vue-router": "^3.0.1",
+    "vue-split-pane": "0.0.8"
+  },
+  "devDependencies": {
+    "@vue/cli-plugin-babel": "^3.0.5",
+    "@vue/cli-plugin-eslint": "^3.0.5",
+    "@vue/cli-service": "^3.0.5",
+    "vue-template-compiler": "^2.5.17"
+  },
+  "eslintConfig": {
+    "root": true,
+    "env": {
+      "node": true
+    },
+    "extends": [
+      "plugin:vue/essential",
+      "eslint:recommended"
+    ],
+    "parserOptions": {
+      "parser": "babel-eslint"
+    }
+  },
+  "postcss": {
+    "plugins": {
+      "autoprefixer": {}
+    }
+  },
+  "browserslist": [
+    "> 1%",
+    "last 2 versions",
+    "not ie <= 8"
+  ]
+}

http://git-wip-us.apache.org/repos/asf/directory-fortress-enmasse/blob/5aae8a26/embrasure/public/embrasure-favicon.png
----------------------------------------------------------------------
diff --git a/embrasure/public/embrasure-favicon.png b/embrasure/public/embrasure-favicon.png
new file mode 100644
index 0000000..3dcf65f
Binary files /dev/null and b/embrasure/public/embrasure-favicon.png differ

http://git-wip-us.apache.org/repos/asf/directory-fortress-enmasse/blob/5aae8a26/embrasure/public/index.html
----------------------------------------------------------------------
diff --git a/embrasure/public/index.html b/embrasure/public/index.html
new file mode 100644
index 0000000..86f9174
--- /dev/null
+++ b/embrasure/public/index.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width,initial-scale=1.0">
+    <link rel="icon" href="<%= BASE_URL %>embrasure-favicon.png">
+    <title>Fortress Administrator</title>
+  </head>
+  <body>
+    <noscript>
+      <strong>Please enable Javascript to continue</strong>
+    </noscript>
+    <div id="app"></div>
+    <!-- built files will be auto injected -->
+  </body>
+</html>

http://git-wip-us.apache.org/repos/asf/directory-fortress-enmasse/blob/5aae8a26/embrasure/src/App.vue
----------------------------------------------------------------------
diff --git a/embrasure/src/App.vue b/embrasure/src/App.vue
new file mode 100644
index 0000000..e27ff98
--- /dev/null
+++ b/embrasure/src/App.vue
@@ -0,0 +1,92 @@
+<template>
+  <div id="app">
+    <div>
+      <el-container style="border: 1px solid #eee">
+        <el-header style="text-align: right; font-size: 12px; padding: 0px;">
+            <el-menu router :default-active="activeIndex" class="el-menu-demo" mode="horizontal" background-color="#545c64" text-color="#fff" active-text-color="#ffd04b">
+              <el-menu-item index="/" >Users</el-menu-item>
+              <el-menu-item index="/groups">Groups</el-menu-item>
+              <el-submenu index="rbac">
+                <template slot="title">RBAC</template>
+                <el-menu-item index="/roles">Roles</el-menu-item>
+                <el-menu-item index="/perms">Perms</el-menu-item>
+                <el-menu-item index="/ssds">SSDs</el-menu-item>
+                <el-menu-item index="/dsds">DSDs</el-menu-item>
+              </el-submenu>
+              <el-submenu index="arbac">
+                <template slot="title">ARBAC</template>
+                <el-menu-item index="/policies">Policies</el-menu-item>
+              </el-submenu>
+              <el-menu-item index="/pobjs">PObjs</el-menu-item>
+              <el-menu-item index="/ousers">OUsers</el-menu-item>
+              <el-menu-item index="/ouprms">OUPrms</el-menu-item>
+              <el-menu-item index="/admrles">ADMRLEs</el-menu-item>
+              <el-menu-item index="/admobjs">ADMObjs</el-menu-item>
+              <el-menu-item index="/admperms">ADMPerms</el-menu-item>
+              <el-menu-item index="/logout">Logout</el-menu-item>
+            </el-menu>
+          </el-header>
+      </el-container>
+    </div>
+    <div>
+        <router-view/>
+    </div>
+  </div>
+</template>
+
+<script>
+import axios from "axios"
+
+export default {
+  name: 'app',
+  data() {
+    return {
+      activeIndex: '/'
+    }
+  }
+}
+</script>
+
+<style>
+#app {
+  font-family: 'Avenir', Helvetica, Arial, sans-serif;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+  text-align: center;
+  color: #2200cc;
+}
+.split-pane {
+  display: flex;
+  flex-direction: row;
+  height: 80%;
+}
+
+.split-pane-item,
+.split-pane-gutter {
+  height: 80%;
+}
+
+.split-pane-gutter {
+  background: #000;
+  opacity: 0.2;
+  z-index: 1;
+  box-sizing: border-box;
+  background-clip: padding-box;
+  width: 11px;
+  margin: 0 -5px;
+  border-left: 5px solid rgba(255, 255, 255, 0);
+  border-right: 5px solid rgba(255, 255, 255, 0);
+  cursor: col-resize;
+}
+
+.split-pane-gutter:hover,
+.split-pane-gutter:focus {
+  border-left: 5px solid rgba(0, 0, 0, 0.5);
+  border-right: 5px solid rgba(0, 0, 0, 0.5);
+  transition: all 2s ease;
+}
+
+.is-dragging {
+  cursor: col-resize;
+}
+</style>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/directory-fortress-enmasse/blob/5aae8a26/embrasure/src/assets/nophoto.jpg
----------------------------------------------------------------------
diff --git a/embrasure/src/assets/nophoto.jpg b/embrasure/src/assets/nophoto.jpg
new file mode 100644
index 0000000..b4e10b0
Binary files /dev/null and b/embrasure/src/assets/nophoto.jpg differ

http://git-wip-us.apache.org/repos/asf/directory-fortress-enmasse/blob/5aae8a26/embrasure/src/components/ContactInformation.vue
----------------------------------------------------------------------
diff --git a/embrasure/src/components/ContactInformation.vue b/embrasure/src/components/ContactInformation.vue
new file mode 100644
index 0000000..e7e9cd1
--- /dev/null
+++ b/embrasure/src/components/ContactInformation.vue
@@ -0,0 +1,94 @@
+<template :entity="entity">
+    <div>
+        <el-row justify="start" type="flex">
+            <el-form-item label="Emails:">
+                <List :holder.sync="entity" fieldName="emails"/>
+            </el-form-item>
+            <el-form-item label="Phones:">
+                <List :holder.sync="entity" fieldName="phones"/>
+            </el-form-item>
+        </el-row>
+        <el-row justify="start" type="flex">
+            <el-form-item label="Mobiles:">
+                <List :holder.sync="entity" fieldName="mobiles"/>
+            </el-form-item>
+        </el-row>
+        <el-row justify="start" type="flex">
+            <el-form-item label="City:">
+                <el-input label="City" placeholder="" v-model="entity.address.city" size="small"></el-input>
+            </el-form-item>
+            <el-form-item label="State:">
+                <el-input label="State" placeholder="" v-model="entity.address.state" size="small"></el-input>
+            </el-form-item>
+        </el-row>
+        <el-row justify="start" type="flex">
+            <el-form-item label="Country:">
+                <el-input label="Country" placeholder="" v-model="entity.address.country" size="small"></el-input>
+            </el-form-item>
+            <el-form-item label="Postal Code:">
+                <el-input label="Postal Code" placeholder="" v-model="entity.address.postalCode" size="small"></el-input>
+            </el-form-item>
+        </el-row>
+        <el-row justify="start" type="flex">
+            <el-form-item label="Post Office Box:">
+                <el-input label="Post Office Box" placeholder="" v-model="entity.address.postOfficeBox" size="small"></el-input>
+            </el-form-item>
+            <el-form-item label="Building:">
+                <el-input label="Building" placeholder="" v-model="entity.address.building" size="small"></el-input>
+            </el-form-item>
+        </el-row>
+        <el-row justify="start" type="flex">
+            <el-form-item label="Department Number:">
+                <el-input label="Department Number" placeholder="" v-model="entity.address.departmentNumber" size="small"></el-input>
+            </el-form-item>
+            <el-form-item label="Room Number:">
+                <el-input label="Room Number" placeholder="" v-model="entity.address.roomNumber" size="small"></el-input>
+            </el-form-item>
+        </el-row>
+        <el-row justify="start" type="flex">
+            <el-form-item label="Addresses:">
+                <List :holder.sync="entity.address" fieldName="addresses"/>
+            </el-form-item>
+        </el-row>
+    </div>
+</template>
+
+<script>
+/* eslint-disable */
+import List from './List.vue'
+
+export default {
+  name: 'ContactInformation',
+props: {
+  entity: Object,
+},
+data() {
+    return {
+    }
+},
+created() {
+let address = {
+                addresses: null,
+                city: null,
+                state: null,
+                country: null,
+                postalCode: null,
+                postOfficeBox: null,
+                building: null,
+                departmentNumber: null,
+                roomNumber: null
+            }
+    if(this.entity.address == undefined || this.entity.address == null) {
+        this.$set(this.entity, 'address', address)
+    }
+},
+computed: {
+
+},
+methods: {
+},
+components: {
+    List
+}
+};
+</script>

http://git-wip-us.apache.org/repos/asf/directory-fortress-enmasse/blob/5aae8a26/embrasure/src/components/List.vue
----------------------------------------------------------------------
diff --git a/embrasure/src/components/List.vue b/embrasure/src/components/List.vue
new file mode 100644
index 0000000..27dd45d
--- /dev/null
+++ b/embrasure/src/components/List.vue
@@ -0,0 +1,77 @@
+<template :holder="holder" :fieldName="fieldName">
+  <div style="min-width: 10px; min-height: 10px;">
+    <table>
+        <tr style="line-height: 25px;"
+          v-for="(n, index) in items"
+          :key="index"
+          :label="n"
+          :value="index">
+          <td>
+              <span class="option_span_item">{{index+1}}.</span>
+          </td>
+          <td>
+            <input type="text" :value="n" :ref="fieldName+index" @input="updateItem(index)"/><i @click="deleteItem(index)" style="color: red; padding-left: 1px;" class="el-icon-remove"></i>
+          </td>
+        </tr>
+      </table>
+      <div style="padding: 1px;"/>
+      <el-button type="success" round size="mini" @click="addItem">+</el-button>
+  </div>
+</template>
+
+<script>
+/* eslint-disable */
+export default {
+  name: 'List',
+props: {
+  holder: Object,
+  fieldName: String
+},
+data() {
+    return {
+    }
+},
+created() {
+
+},
+computed: {
+    items: {
+        get() {
+            let arr = this.holder[this.fieldName]
+            if(arr == null) {
+                arr = []
+            }
+
+            return arr
+        },
+        set(newVal) {
+            // ignore
+        }
+    }
+},
+methods: {
+    addItem() {
+        let arr = this.holder[this.fieldName]
+        if(arr == null) {
+            this.$set(this.holder, this.fieldName, [])
+            arr = this.holder[this.fieldName]
+        }
+        arr.push('')
+    },
+    deleteItem(index) {
+        let arr = this.holder[this.fieldName]
+        if(arr != null) {
+            arr.splice(index, 1)
+        }
+    },
+    updateItem(index) {
+        let inputRef = this.fieldName + index
+        let newVal = this.$refs[inputRef][0].value
+        let arr = this.holder[this.fieldName]
+        if(arr != null) {
+            arr[index] = newVal
+        }        
+    }
+}
+};
+</script>

http://git-wip-us.apache.org/repos/asf/directory-fortress-enmasse/blob/5aae8a26/embrasure/src/components/RoleAssignment.vue
----------------------------------------------------------------------
diff --git a/embrasure/src/components/RoleAssignment.vue b/embrasure/src/components/RoleAssignment.vue
new file mode 100644
index 0000000..c728506
--- /dev/null
+++ b/embrasure/src/components/RoleAssignment.vue
@@ -0,0 +1,196 @@
+<template :holder="holder" :fieldName="fieldName" :isAdminRole="isAdminRole">
+<div>
+    <div style="float: left; display: inline-block; width:200px; padding-right: 20px;">
+        <select v-model="selectedResIndex" :size="5" style="width: 100%; min-height: 175px;" @change="selectCurRole">
+            <option class="el-select-dropdown__item"
+                v-for="(r, index) in roles"
+                :key="r.modId"
+                :label="r.name"
+                :value="index" :title="r.name">
+                <span class="option_span_item">{{r.name}}</span>
+            </option>
+        </select>
+        <div style="padding: 1px;"/>
+        <el-button type="success" round size="mini" @click="openRoleSearch">+</el-button>
+        <el-button type="warning" round size="mini" @click="deleteRole">-</el-button>
+    </div>
+    <TemporalConstraints v-if="curRole != null" :tcHolder="curRole"/>
+    <el-dialog title="Select Role" :visible.sync="roleDialogVisible" width="30%" center modal @open="setFocusRoleSelector">
+      <el-select ref="roleSelector" v-model="selectedRoles" multiple filterable remote reserve-keyword placeholder="Type first three letters of role" :remote-method="searchRoles" :loading="loading">
+        <el-option
+          v-for="(r, index) in foundRoles"
+          :key="r.modId"
+          :label="r.name"
+          :value="index" :title="r.name">
+        </el-option>
+      </el-select>
+      <el-button type="success" round size="mini" @click="addSelectedRoles">Ok</el-button>
+    </el-dialog>
+</div>
+</template>
+
+<script>
+/* eslint-disable */
+import TemporalConstraints from './TemporalConstraints.vue'
+import axios from "axios"
+import * as ft from "../lib/fortress"
+
+export default {
+  name: 'RoleAssignment',
+props: {
+  holder: Object,
+  fieldName: String,
+  isAdminRole: Boolean
+},
+data() {
+    return {
+        selectedResIndex: 0,
+        curRole: null,
+        selectedRoles: [],
+        roleDialogVisible: false,
+        foundRoles: [],
+        loading: false
+    }
+},
+beforeUpdate() {
+    this.selectCurRole()
+},
+beforeDestroy() {
+    console.log('beforeDestroy')
+},
+computed: {
+    roles: {
+        get() {
+            this.selectedResIndex = 0
+            console.log('computing roles get()')
+            let existing = this.holder[this.fieldName]
+            if(existing == null) {
+                existing = []
+            }
+
+            return existing
+        },
+        set(newVal) {
+            console.log('setting new values')
+            // ignore
+        }
+    }
+},
+methods: {
+    selectCurRole() {
+        let existing = this.holder[this.fieldName]
+        if(existing != null && existing.length > 0) {
+            console.log('updating cur role ' + this.selectedResIndex )
+            this.curRole = existing[this.selectedResIndex]
+        } else {
+            console.log('setting cur role to NULL')
+            this.curRole = null
+        }
+    },
+    openRoleSearch() {
+        this.selectedRoles = []
+        this.roleDialogVisible = true
+    },
+    deleteRole() {
+        let existing = this.holder[this.fieldName]
+        if(existing != null) {
+            existing.splice(this.selectedResIndex, 1)
+        }
+    },
+    updateItem(index) {
+        let inputRef = this.fieldName + index
+        let newVal = this.$refs[inputRef][0].value
+        let existing = this.holder[this.fieldName]
+        if(existing != null) {
+            existing[index] = newVal
+        }        
+    },
+    roleDialogClose(dropdownOpened) {
+        if(!dropdownOpened) {
+            this.roleDialogVisible = false
+        }
+    },
+    setFocusRoleSelector() {
+        this.$nextTick(function(){
+        this.$refs.roleSelector.focus()
+        })
+    },
+    addSelectedRoles() {
+        this.roleDialogVisible = false
+        console.log(this.selectedRoles)
+        let selected = this.selectedRoles
+        let existing = this.holder[this.fieldName]
+        if(existing == null) {
+            this.$set(this.holder, this.fieldName, [])
+            existing = this.holder[this.fieldName]
+        }
+        for(let i=0; i < selected.length; i++) {
+            let sr = this.foundRoles[selected[i]]
+            let add = true
+            for(let j=0; j < existing.length; j++) {
+                let er = existing[j]
+                if(er.modId == sr.modId) {
+                    add = false
+                    break
+                }
+            }
+            if(add) {
+                let ur = this.convertRoleToUserRole(sr)
+                console.log('adding role')
+                existing.push(ur)
+            }
+        }
+    },
+    convertRoleToUserRole(role) {
+        let userRole = {
+            fqcn: 'org.apache.directory.fortress.core.model.UserRole'
+        }
+        if(this.isAdminRole) {
+            userRole.fqcn = 'org.apache.directory.fortress.core.model.UserAdminRole'
+        }
+
+        userRole.userId = this.holder.userId
+        userRole.name = role.name
+        userRole.isGroupRole = false
+        userRole.timeout = role.timeout
+        userRole.beginTime = role.beginTime
+        userRole.endTime = role.endTime
+        userRole.beginDate = role.beginDate
+        userRole.endDate = role.endDate
+        userRole.beginLockDate = role.beginLockDate
+        userRole.endLockDate = role.endLockDate
+        userRole.dayMask = role.dayMask
+        userRole.parents = role.parents
+        userRole.roleConstraints = null
+
+        return userRole
+    },
+    searchRoles(query) {
+        this.selectedRoles = []
+        if (query.length > 2) {
+          this.loading = true
+          let ftReq = {
+                        "value": query,
+                        contextId: ft.CONTEXT_ID
+                      }
+          let url = ft.FT_BASE_URL + '/roleSearch'
+          if(this.isAdminRole) {
+              url = ft.FT_BASE_URL + '/arleSearch'
+          }
+          setTimeout(() => {
+            axios.post(url, ftReq, ft.AXIOS_FT_CONFIG).then(resp => {
+                this.foundRoles = resp.data.entities
+                this.loading = false
+            }).catch(e => {
+                this.foundRoles = []
+                ft.showErr(e, '')
+            })
+          }, 200);
+        }
+    }
+},
+components: {
+    TemporalConstraints
+}
+};
+</script>

http://git-wip-us.apache.org/repos/asf/directory-fortress-enmasse/blob/5aae8a26/embrasure/src/components/TemporalConstraints.vue
----------------------------------------------------------------------
diff --git a/embrasure/src/components/TemporalConstraints.vue b/embrasure/src/components/TemporalConstraints.vue
new file mode 100644
index 0000000..0efd331
--- /dev/null
+++ b/embrasure/src/components/TemporalConstraints.vue
@@ -0,0 +1,115 @@
+<template :tcHolder="tcHolder">
+  <div>
+    <el-row justify="start" type="flex">
+        <el-form-item label="Begin Time:">
+            <el-time-select :picker-options="timeSelectOps" v-model="beginTime" size="mini"></el-time-select>
+        </el-form-item>
+        <el-form-item label="End Time:">
+            <el-time-select :picker-options="timeSelectOps" v-model="endTime" size="mini"></el-time-select>
+        </el-form-item>
+    </el-row>
+    <el-row justify="start" type="flex">
+        <el-form-item label="Begin Date:">
+            <el-date-picker v-model="beginDate" value-format="yyyyMMdd" format="MM/dd/yyyy" type="date"></el-date-picker>
+        </el-form-item>
+        <el-form-item label="End Date:">
+            <el-date-picker v-model="endDate" value-format="yyyyMMdd" format="MM/dd/yyyy" type="date"></el-date-picker>
+        </el-form-item>
+    </el-row>
+    <el-row justify="start" type="flex">
+        <el-form-item label="Begin Lock Date:">
+            <el-date-picker v-model="beginLockDate" value-format="yyyyMMdd" format="MM/dd/yyyy" type="date"></el-date-picker>
+        </el-form-item>
+        <el-form-item label="End Lock Date:">
+            <el-date-picker v-model="endLockDate" value-format="yyyyMMdd" format="MM/dd/yyyy" type="date"></el-date-picker>
+        </el-form-item>
+    </el-row>
+  </div>
+</template>
+
+<script>
+/* eslint-disable */
+export default {
+  name: 'TemporalConstraints',
+props: {
+  tcHolder: Object,
+},
+data() {
+    return {
+        timeSelectOps: {
+            start: '00:00',
+            end: '23:30'
+        }
+    }
+},
+created() {
+
+},
+computed: {
+    // the below functions are used to convert date and time values in the format fortress stores
+    beginTime: {
+        get() {
+            return this.formatInput(this.tcHolder.beginTime)
+        },
+        set(newVal) {
+            this.tcHolder.beginTime = this.formatOutput(newVal)
+        }
+    },
+    endTime: {
+        get() {
+            return this.formatInput(this.tcHolder.endTime)
+        },
+        set(newVal) {
+            this.tcHolder.endTime = this.formatOutput(newVal)
+        }
+    },
+    beginDate: {
+        get() {
+            return this.tcHolder.beginDate
+        },
+        set(newVal) {
+            this.$set(this.tcHolder, 'beginDate', newVal)
+        }        
+    },
+    endDate: {
+        get() {
+            return this.tcHolder.endDate
+        },
+        set(newVal) {
+            this.$set(this.tcHolder, 'endDate', newVal)
+        }        
+    },
+    beginLockDate: {
+        get() {
+            return this.tcHolder.beginLockDate
+        },
+        set(newVal) {
+            this.$set(this.tcHolder, 'beginLockDate', newVal)
+        }        
+    },
+    endLockDate: {
+        get() {
+            return this.tcHolder.endLockDate
+        },
+        set(newVal) {
+            this.$set(this.tcHolder, 'endLockDate', newVal)
+        }        
+    }
+},
+methods: {
+    formatInput(t) {
+        if(t == null) {
+            return "00:00"
+        }
+        return t.substring(0,2) + ':' + t.substring(2)
+    },
+    formatOutput(t) {
+        if(t == null) {
+            return "0000"
+        }
+        return t.substring(0,2) + t.substring(3) // exclude : char
+    }
+
+}
+};
+</script>

http://git-wip-us.apache.org/repos/asf/directory-fortress-enmasse/blob/5aae8a26/embrasure/src/components/UserList.vue
----------------------------------------------------------------------
diff --git a/embrasure/src/components/UserList.vue b/embrasure/src/components/UserList.vue
new file mode 100644
index 0000000..b660a78
--- /dev/null
+++ b/embrasure/src/components/UserList.vue
@@ -0,0 +1,67 @@
+<template>
+<el-container>
+  <el-aside width="200px" style="background-color: #808471">
+    <el-menu :default-active="activeIndex" class="el-menu-demo" mode="vertical" background-color="#808471" text-color="#fff" active-text-color="#ffd04b">
+    </el-menu>
+  </el-aside>
+  <el-main>
+      <div style="float: right; margin-bottom: 1px">
+        <el-row>
+          <el-col>
+            <el-input v-model="filters[0].value" placeholder="search"></el-input>
+          </el-col>
+        </el-row>
+      </div>
+      <data-tables :data="resources" :table-props="tableProps" :page-size="10" :pagination-props="{ background: true, pageSizes: [10, 20, 50, 100] }" :filters="filters" highlight-current-row @row-click="fetchEntity" @selection-change="handleSelectionChange">
+       <el-table-column type="selection" width="55"></el-table-column>
+       <el-table-column v-for="col in columns" :prop="col.prop" :label="col.label" :key="col.label" sortable="custom" width="200" header-align="center">
+       </el-table-column>
+      </data-tables>
+  </el-main>
+  </el-container>
+</template>
+
+<script>
+/* eslint-disable */
+import axios from "axios"
+
+export default {
+  name: "UserList",
+  data() {
+    return {
+      resources: [],
+      activeIndex: "1",
+      multipleSelection: [],
+      columns: [{
+          prop: "username",
+          label: "Username"
+          }, {
+          prop: "displayname",
+          label: "Name"
+        }
+      ],
+     tableProps: {
+        border: false,
+        stripe: true,
+        defaultSort: {
+          prop: 'username',
+          order: 'ascending'
+        }
+     },
+      filters: [
+        {
+          prop: ['username', 'displayname'],
+          value: ''
+        }
+      ]
+    }
+    },
+    created() {
+    },
+    methods: {
+      fetchEntity(val) {
+        this.$router.push({name: "UserDetails", params: val});
+      }
+    }
+};
+</script>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/directory-fortress-enmasse/blob/5aae8a26/embrasure/src/components/UserParent.vue
----------------------------------------------------------------------
diff --git a/embrasure/src/components/UserParent.vue b/embrasure/src/components/UserParent.vue
new file mode 100644
index 0000000..2270d19
--- /dev/null
+++ b/embrasure/src/components/UserParent.vue
@@ -0,0 +1,37 @@
+<template>
+<el-container>
+  <el-aside width="200px" style="background-color: #808471">
+    <el-menu class="el-menu-demo" mode="vertical" background-color="#808471" text-color="#fff" active-text-color="#ffd04b">
+      <el-menu-item index="1" @click="save" v-if="enableSave">Save</el-menu-item>
+      <el-menu-item index="2" @click="back">&lt;- User List</el-menu-item>
+      <el-menu-item index="3" @click="addEntity">Add New</el-menu-item>
+      <!-- <el-menu-item index="4" @click="deleteEntity" v-if="multipleSelection.length > 0">Delete</el-menu-item> -->
+    </el-menu>
+  </el-aside>
+  <router-view class="child"></router-view>
+</el-container>
+</template>
+<script>
+/* eslint-disable */
+import axios from "axios"
+
+export default {
+  name: 'UserParent',
+  data() {
+    return {
+      enableSave: false
+    }
+  },
+  methods: {
+    addEntity() {
+
+    },
+    deleteEntity() {
+
+    },
+    back() {
+      
+    }
+  }
+};
+</script>

http://git-wip-us.apache.org/repos/asf/directory-fortress-enmasse/blob/5aae8a26/embrasure/src/components/Users.vue
----------------------------------------------------------------------
diff --git a/embrasure/src/components/Users.vue b/embrasure/src/components/Users.vue
new file mode 100644
index 0000000..f97757f
--- /dev/null
+++ b/embrasure/src/components/Users.vue
@@ -0,0 +1,488 @@
+<template>
+<el-container>
+  <el-main>
+    <el-row type="flex" justify="start" style="padding-bottom: 4px; text-align: left;">
+          <el-col :span="20">
+            <el-input style="width: 270px; padding-right: 20px;" v-model="filters[0].value" placeholder="search"></el-input>
+              <el-button type="primary" v-if="multipleSelection.length > 0" @click="deleteEntities">Delete</el-button>
+              <el-button type="primary" @click="newUser">New User</el-button>
+              <el-button type="primary" v-if="enableSave" @click="save">Save</el-button>
+          </el-col>
+    </el-row>
+    <el-row type="flex" justify="start">
+      <el-col :span="10" justify="center">
+      <data-tables ref="userTable" :data="entities" :table-props="tableProps" :page-size="10" :pagination-props="{ background: true, pageSizes: [10, 20, 50, 100] }" :filters="filters" :highlight-current-row="true" max-height="250" @row-click="showEntity" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55"></el-table-column>
+        <el-table-column v-for="col in columns" :prop="col.prop" :label="col.label" :key="col.label" sortable="custom" width="200" header-align="center">
+        </el-table-column>
+      </data-tables>
+      </el-col>
+
+      <el-col :span="25">
+        <el-form v-if="entity != null" v-model.lazy="entity" :inline="true" label-width="120px">
+        <el-tabs v-model="curTab" type="border-card">
+          <el-tab-pane name="User Details" label="User Details">
+            <el-row justify="start" type="flex">
+            <el-col :span="19">
+            <el-row justify="start" type="flex">
+              <el-form-item label="User ID:">
+                <el-input label="User ID" placeholder="User ID" v-model="entity.userId" size="small"></el-input>
+              </el-form-item>
+              <el-form-item label="Password:">
+                <el-input type="password" label="Password" v-model="entity.password" size="small"></el-input>
+              </el-form-item>
+            </el-row>
+            <el-row justify="start" type="flex">
+              <el-form-item label="Title:">
+                <el-input label="Title" v-model="entity.title" size="small"></el-input>
+              </el-form-item>
+              <el-form-item label="Display Name:">
+                <el-input label="Display Name" v-model="entity.displayName" size="small"></el-input>
+              </el-form-item>
+            </el-row>
+            <el-row justify="start" type="flex">
+              <el-form-item label="Organization:">
+                <el-input label="Organization" placeholder="Organization" v-model="entity.ou" size="small"></el-input>
+              </el-form-item>
+              <el-form-item label="Employee Type:">
+                <el-input label="Employee Type" v-model="entity.employeeType" size="small"></el-input>
+              </el-form-item>
+            </el-row>
+            <el-row justify="start" type="flex">
+              <el-form-item label="Description:">
+                <el-input label="Description" v-model="entity.description" size="small"></el-input>
+              </el-form-item>
+              <el-form-item label="Password Policy:">
+                <el-input label="Password Policy" placeholder="" v-model="entity.pwPolicy" size="small"></el-input>
+              </el-form-item>
+            </el-row>
+            <el-row justify="start" type="flex" v-if="entity.internalId != null">
+                <el-form-item v-if="!entity.reset" label="New Password:">
+                  <el-input type="password" label="New Password:" v-model="entity.newPassword" size="mini"></el-input>
+                </el-form-item>
+                <el-form-item label="">
+                  <el-button type="primary" size="mini" v-if="!entity.reset" @click="resetPassword">Reset Password</el-button>
+                  <el-button type="primary" size="mini" @click="lockOrUnlockUser">{{entity.locked? 'Unlock' : 'Lock'}}</el-button>
+                </el-form-item>
+            </el-row>
+            </el-col>
+            <el-col :span="4">
+                <label class="el-form-item__label" style="width: 33px;">Photo</label>
+                <img v-if="entity.jpegPhoto == null" ref="userPhoto" height="125" width="125" src="../assets/nophoto.jpg" @click="selectJpegPhoto"/>
+                <img v-else ref="userPhoto" height="125" width="125" :src="jpegPhoto" @click="selectJpegPhoto"/>
+                <input type="file" ref="photoFile" @change="changeJpegPhoto" style="opacity: 0; width: 0px; height: 0px">
+            </el-col>
+            </el-row>
+          </el-tab-pane>
+          <el-tab-pane name="Role Assignments" label="Role Assignments">
+            <!-- <el-form-item label="Roles:" label-width="50px"> -->
+              <RoleAssignment :holder.sync="entity" fieldName="roles"/>
+            <!-- </el-form-item> -->
+          </el-tab-pane>
+          <el-tab-pane name="Admin Role Assignments" label="Admin Role Assignments">
+            <!-- <el-form-item label="Roles:" label-width="50px"> -->
+              <RoleAssignment :holder.sync="entity" fieldName="adminRoles" isAdminRole/>
+            <!-- </el-form-item> -->
+          </el-tab-pane>
+          <el-tab-pane name="Contact Information" label="Contact Information">
+            <el-row justify="start" type="flex">
+              <ContactInformation :entity="entity"/>
+            </el-row>
+          </el-tab-pane>
+          <el-tab-pane name="Temporal Constraints" label="Temporal Constraints">
+            <TemporalConstraints :tcHolder="entity"/>
+          </el-tab-pane>
+          <el-tab-pane name="System Information" label="System Information">
+            <el-row justify="start" type="flex">
+              <el-form-item label="System:">
+                <el-checkbox v-model="entity.system" size="small"></el-checkbox>
+              </el-form-item>
+            </el-row>
+            <el-row justify="start" type="flex">
+              <el-form-item label="Internal ID:">
+                <span style="color: black;">{{entity.internalId}}</span>
+              </el-form-item>
+            </el-row>
+            <el-row justify="start" type="flex">
+              <el-form-item label="CN:">
+                <el-input label="CN" v-model="entity.cn" size="small"></el-input>
+              </el-form-item>
+            </el-row>
+            <el-row justify="start" type="flex">
+              <el-form-item label="DN:">
+                <span style="color: black;">{{entity.dn}}</span>
+              </el-form-item>
+            </el-row>
+            <el-row justify="start" type="flex">
+              <el-form-item label="SN:">
+                <el-input label="SN" v-model="entity.sn" size="small"></el-input>
+              </el-form-item>
+            </el-row>
+          </el-tab-pane>
+        </el-tabs>
+        </el-form>
+      </el-col>
+    </el-row>
+  </el-main>
+  </el-container>
+</template>
+
+<script>
+/* eslint-disable */
+import axios from "axios"
+import * as ft from "../lib/fortress"
+import * as jsondiff from "rfc6902"
+import SplitPane from 'vue-split-pane'
+import TemporalConstraints from './TemporalConstraints.vue'
+import List from './List.vue'
+import RoleAssignment from './RoleAssignment.vue'
+import ContactInformation from './ContactInformation.vue'
+import { Notification } from 'element-ui'
+
+export default {
+  name: "Users",
+  data() {
+    return {
+      entities: [],
+      entity: null,
+      rowIndex: -1,
+      curTab: 'User Details',
+      multipleSelection: [],
+      enableSave: false,
+      columns: [{
+          prop: "userId",
+          label: "User ID"
+          }, {
+          prop: "name",
+          label: "Name"
+        }
+      ],
+     tableProps: {
+        border: false,
+        stripe: true,
+        defaultSort: {
+          prop: 'userId',
+          order: 'ascending'
+        }
+     },
+      filters: [
+        {
+          prop: ['userId', 'name', 'displayName', 'roles', 'adminRoles'],
+          value: ''
+        }
+      ]
+    }
+    },
+    created() {
+      let ftReq = {
+                  	entity: {
+		                  fqcn: "org.apache.directory.fortress.core.model.User"
+	                  },
+                    contextId: ft.CONTEXT_ID
+                  }
+      ft.showWait()
+      axios.post(ft.FT_BASE_URL+ '/userSearch', ftReq, ft.AXIOS_FT_CONFIG).then(resp => {
+        this.entities = resp.data.entities
+        if(this.entities.length > 0) {
+          this.showEntity(this.entities[0])
+        }
+        ft.closeWait()
+      }).catch(e => {
+        ft.showErr(e, '')
+      })
+    },
+    computed: {
+      jpegPhoto: {
+        get() {
+          return 'data:image/jpeg;base64,' + this.entity.jpegPhoto
+        }
+      }
+    },
+    watch: {
+      entity: {
+        deep: true,
+      handler: function(newVal, oldVal) {
+          if(this.entity._justLoaded) {
+            delete this.entity._justLoaded
+          }
+          else {
+            this.enableSave = true
+          }
+        }
+      }
+    },
+    methods: {
+      newUser() {
+        this.entity = ft.newUser()
+        this.entity._justLoaded = true
+      },
+      showEntity(val) {
+        if(val !== undefined && val != null) {
+          this.enableSave = false
+          this.rowIndex = this.entities.indexOf(val)
+          // deep clone
+          this.entity = JSON.parse(JSON.stringify(val))
+          this.entity._justLoaded = true
+          //console.log(this.$refs.userTable)
+        }
+      },
+      handleSelectionChange(val) {
+        this.multipleSelection = val;
+      },
+      async save() {
+        ft.showWait()
+        let newUser = false
+        let url = ft.FT_BASE_URL+ '/userUpdate'
+        if(this.entity.internalId == null || this.entity.internalId == undefined) {
+          url = ft.FT_BASE_URL+ '/userAdd'
+          newUser = true
+          this.entity.fqcn = 'org.apache.directory.fortress.core.model.User'
+        }
+        let ftReq = {
+                  	entity: this.entity,
+                    contextId: ft.CONTEXT_ID
+                  }
+        axios.post(url, ftReq, ft.AXIOS_FT_CONFIG).then(resp => {
+          let rolesTobeAssigned = []
+          let rolesTobeDeAssigned = []
+          let admRolesTobeAssigned = []
+          let admRolesTobeDeAssigned = []
+          if(newUser) {
+            if(this.entity.roles != null) {
+              rolesTobeAssigned = this.entity.roles
+            }
+            if(this.entity.adminRoles != null) {
+              admRolesTobeAssigned = this.entity.adminRoles
+            }
+          }
+          else {
+            let origEntity = this.entities[this.rowIndex]
+            if(origEntity.roles == null) {
+                if(this.entity.roles != null) {
+                  rolesTobeAssigned = this.entity.roles
+                }
+            }
+            else {
+              let origRoles = origEntity.roles
+              let roles = this.entity.roles.slice() // if we don't slice here later the UI is failing to refresh properly
+              for(let i=0; i< origRoles.length; i++) {
+                 let or = origRoles[i]
+                 let found = false
+                 let j=0
+                for(; j< roles.length; j++) {
+                  let r = roles[j]
+                  if(or.modId == r.modId) {
+                      found = true
+                      let op = jsondiff.createPatch(or, r)
+                      if(op.length > 0) {
+                        rolesTobeAssigned.push(r)
+                      }
+                      break
+                  }
+                }
+                if(found) {
+                  roles.splice(j, 1)
+                }
+                else {
+                  rolesTobeDeAssigned.push(or)
+                }
+              }
+              rolesTobeAssigned.push(...roles) // remaining are the new roles
+            }
+
+            if(origEntity.adminRoles == null) {
+                if(this.entity.adminRoles != null) {
+                  admRolesTobeAssigned = this.entity.adminRoles
+                }
+            }
+            else {
+              let origAdmRoles = origEntity.adminRoles
+              let admRoles = this.entity.adminRoles.slice() // if we don't slice here later the UI is failing to refresh properly
+              for(let i=0; i< origAdmRoles.length; i++) {
+                 let or = origAdmRoles[i]
+                 let found = false
+                 let j=0
+                for(; j< admRoles.length; j++) {
+                  let r = admRoles[j]
+                  if(or.modId == r.modId) {
+                      found = true
+                      let op = jsondiff.createPatch(or, r)
+                      if(op.length > 0) {
+                        admRolesTobeAssigned.push(r)
+                      }
+                      break
+                  }
+                }
+                if(found) {
+                  admRoles.splice(j, 1)
+                }
+                else {
+                  admRolesTobeDeAssigned.push(or)
+                }
+              }
+              admRolesTobeAssigned.push(...admRoles) // remaining are the new roles
+            }
+          }
+          try {
+            this.updateRoleAssignments(rolesTobeDeAssigned, ft.FT_BASE_URL + '/roleDeasgn')
+            this.updateRoleAssignments(rolesTobeAssigned, ft.FT_BASE_URL + '/roleAsgn')
+            this.updateRoleAssignments(admRolesTobeDeAssigned, ft.FT_BASE_URL + '/arleDeasgn')
+            this.updateRoleAssignments(admRolesTobeAssigned, ft.FT_BASE_URL + '/arleAsgn')
+            let readPromise = this.fetchSingleUser(this.entity.userId)
+            readPromise.then( resp => {
+              console.log('fetching user')
+              if(newUser) {
+                this.entities.push(resp.data.entity)
+                this.showEntity(resp.data.entity)
+              }
+              else {
+                let origEntity = this.entities[this.rowIndex]
+                // Object.assign(origEntity, resp.data.entity)
+                origEntity = {...origEntity, ...resp.data.entity}
+                this.$set(this.entities, this.rowIndex, origEntity)
+              }
+              this.enableSave = false
+              ft.closeWait()
+            })
+          }
+          catch(e) {
+            ft.showErr(e, '')
+          }
+        }).catch(e => {
+          console.log(e)
+          ft.showErr(e, '')
+        })
+      },
+      changeJpegPhoto() {
+        let f = this.$refs.photoFile.files[0]
+        console.log(f.type)
+        if(!f.type.startsWith('image/jpeg')) {
+          ft.showErr('Invalid image')
+          return
+        }
+        if(f.size > 1048576) { // 1 MB
+          ft.showErr('Photo size cannot exceed 1MB, please select an image with smaller size')
+          return
+        }
+        let fileReader = new FileReader()
+        let imgRef = this.$refs.userPhoto
+        let self = this
+        fileReader.addEventListener('load', function(){
+          let img = fileReader.result
+          let commaPos = img.indexOf(',')
+          img = img.substring(commaPos+1)
+          console.log(img)
+          self.$set(self.entity, 'jpegPhoto', img)
+        }, false)
+        fileReader.readAsDataURL(f)
+      },
+      selectJpegPhoto() {
+        this.$refs.photoFile.click()
+      },
+      async updateRoleAssignments(roles, url) {
+        console.log('updating role assignment')
+        for(let i=0; i< roles.length; i++) {
+          let ftReq = {
+                      entity: roles[i],
+                      contextId: ft.CONTEXT_ID
+                    }
+          let respPromise = await axios.post(url, ftReq, ft.AXIOS_FT_CONFIG)
+        }
+      },
+      async fetchSingleUser(userId) {
+          let ftReq = {
+                      entity: {
+                        fqcn: 'org.apache.directory.fortress.core.model.User',
+                        userId: userId
+                      },
+                      contextId: ft.CONTEXT_ID
+                    }
+        let respPromise = await axios.post(ft.FT_BASE_URL + '/userRead', ftReq, ft.AXIOS_FT_CONFIG)
+        console.log(respPromise)
+        return respPromise
+      },
+      lockOrUnlockUser() {
+        let ftReq = {
+                      entity: {
+                        fqcn: 'org.apache.directory.fortress.core.model.User',
+                        userId: this.entity.userId
+                      },
+                      contextId: ft.CONTEXT_ID
+                    }
+        ft.showWait()
+        let origEntity = this.entities[this.rowIndex]
+        let url = ft.FT_BASE_URL+ '/userLock'
+        if(origEntity.locked) {
+          url = ft.FT_BASE_URL+ '/userUnlock'
+        }
+        let saveState = this.enableSave
+        axios.post(url, ftReq, ft.AXIOS_FT_CONFIG).then(resp => {
+          origEntity.locked = !origEntity.locked
+          this.entity.locked = origEntity.locked
+          if(!saveState) {
+            this.entity._justLoaded = true
+          }
+          ft.closeWait()
+        }).catch(e => {
+          ft.showErr(e, '')
+        })
+      },
+      resetPassword() {
+        if(this.entity.newPassword == null || this.entity.newPassword.trim().length == 0) {
+          ft.showErr('New password cannot be empty')
+          return
+        }
+        let ftReq = {
+                      entity: this.entity,
+                      contextId: ft.CONTEXT_ID
+                    }
+        ft.showWait()
+        let saveState = this.enableSave
+        axios.post(ft.FT_BASE_URL+ '/userReset', ftReq, ft.AXIOS_FT_CONFIG).then(resp => {
+          this.entity.reset = true
+          this.entities[this.rowIndex].reset = true
+          if(!saveState) {
+            this.entity._justLoaded = true
+          }
+          ft.closeWait()
+        }).catch(e => {
+          ft.showErr(e, '')
+        })
+      },
+      deleteEntities() {
+        ft.showWait()
+        for(let i=0; i< this.multipleSelection.length; i++) {
+          let e = this.multipleSelection[i]
+          let respPromise = this._deleteSingleEntity(e)
+          respPromise.then(resp => {
+            let row = this.entities.indexOf(e)
+            this.entities.splice(row, 1)
+          }).catch(e => {
+            let msg = 'Failed to delete user ' + e.userId
+            Notification.warning({message: msg, duration: 10000})
+          })
+        }
+
+        if(this.entities.length > 0) {
+          this.showEntity(this.entities[0])
+        }
+        ft.closeWait()
+      },
+      async _deleteSingleEntity(e) {
+          let ftReq = {
+                        entity: e,
+                        contextId: ft.CONTEXT_ID
+                      }
+          let respPromise = await axios.post(ft.FT_BASE_URL+ '/userDelete', ftReq, ft.AXIOS_FT_CONFIG)
+          return respPromise
+      }
+    },
+    components: {
+      SplitPane,
+      TemporalConstraints,
+      List,
+      RoleAssignment,
+      ContactInformation
+    }
+};
+</script>

http://git-wip-us.apache.org/repos/asf/directory-fortress-enmasse/blob/5aae8a26/embrasure/src/lib/fortress.js
----------------------------------------------------------------------
diff --git a/embrasure/src/lib/fortress.js b/embrasure/src/lib/fortress.js
new file mode 100644
index 0000000..e6f8e0c
--- /dev/null
+++ b/embrasure/src/lib/fortress.js
@@ -0,0 +1,99 @@
+import { Loading, Notification } from 'element-ui'
+
+export var FT_BASE_URL = '/fortress-rest'
+export var AXIOS_FT_CONFIG = {headers: {'Content-Type': 'application/json', 'Accept': 'application/json'}}
+export var CONTEXT_ID = 'HOME'
+
+var loadingWidget = null;
+export function showWait() {
+    loadingWidget = Loading.service({
+        lock: true,
+        text: 'Loading',
+        spinner: 'el-icon-loading',
+        background: 'rgba(0, 0, 0, 0)',
+        fullscreen: true
+      })
+ }
+
+export function closeWait() {
+     if(loadingWidget != null) {
+         loadingWidget.close()
+         loadingWidget = null
+     }
+ }
+
+ export function showSuccess(msg) {
+     Notification.success({message: msg})
+ }
+
+ export function showErr(msg, e) {
+     // close if the wait screen is still present
+     closeWait()
+     if(msg === undefined || msg === null) {
+         msg = ''
+     }
+     if(e !== null && e !== undefined && e.response !== undefined) {
+        if(e.response.data !== undefined) {
+            msg = msg + ' (' + e.response.data.detail + ')'
+        }
+     }
+     Notification.warning({message: msg, duration: 10000})
+ }
+
+ export function newUser() {
+    return {
+        address: {
+            addresses: null,
+            building: null,
+            city: null,
+            country: null,
+            departmentNumber: null,
+            postOfficeBox: null,
+            postalCode: null,
+            roomNumber: null,
+            state: null
+        },
+        adminRoles: null,
+        adminSession: null,
+        beginDate: null,
+        beginLockDate: null,
+        beginTime: null,
+        cn: null,
+        contextId: null,
+        dayMask: null,
+        description: null,
+        displayName: null,
+        dn: null,
+        emails: null,
+        employeeType: null,
+        endDate: null,
+        endLockDate: null,
+        endTime: null,
+        fqcn: null,
+        gecos: null,
+        gidNumber: null,
+        homeDirectory: null,
+        internalId: null,
+        jpegPhoto: null,
+        locked: false,
+        loginShell: null,
+        mobiles: null,
+        modCode: null,
+        modId: null,
+        name: null,
+        newPassword: null,
+        ou: null,
+        password: null,
+        phones: null,
+        pwPolicy: null,
+        reset: false,
+        roles: null,
+        sequenceId: 0,
+        sn: null,
+        system: false,
+        timeout: 60,
+        title: null,
+        uidNumber: null,
+        userId: null
+    }
+ }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/directory-fortress-enmasse/blob/5aae8a26/embrasure/src/lib/rest-examples.txt
----------------------------------------------------------------------
diff --git a/embrasure/src/lib/rest-examples.txt b/embrasure/src/lib/rest-examples.txt
new file mode 100644
index 0000000..171d2a1
--- /dev/null
+++ b/embrasure/src/lib/rest-examples.txt
@@ -0,0 +1,137 @@
+Contains curl commands of some basic fortress functions.
+
+a. Add Role:
+
+curl -X POST -u 'rbacuser' -H 'Content-type: text/xml' -k -d @test-add-role-user.xml https://HOSTNAME:8443/fortress-rest-2.0.2/roleAdd
+curl -X POST -u 'rbacuser' -H 'Content-type: text/xml' -k -d @test-add-role-member.xml https://HOSTNAME:8443/fortress-rest-2.0.2/roleAdd
+curl -X POST -u 'rbacuser' -H 'Content-type: text/xml' -k -d @test-add-role-custodian.xml https://HOSTNAME:8443/fortress-rest-2.0.2/roleAdd
+curl -X POST -u 'rbacuser' -H 'Content-type: text/xml' -k -d @test-add-role-owner.xml https://HOSTNAME:8443/fortress-rest-2.0.2/roleAdd
+curl -X POST -u 'rbacuser' -H 'Content-type: text/xml' -k -d @test-add-role-colgate.xml https://HOSTNAME:8443/fortress-rest-2.0.2/roleAdd
+curl -X POST -u 'rbacuser' -H 'Content-type: text/xml' -k -d @test-add-role-ibm.xml https://HOSTNAME:8443/fortress-rest-2.0.2/roleAdd
+curl -X POST -u 'rbacuser' -H 'Content-type: text/xml' -k -d @test-add-role-acme.xml https://HOSTNAME:8443/fortress-rest-2.0.2/roleAdd
+
+# Sample request:
+<FortRequest>
+  <contextId>HOME</contextId>
+      <entity xsi:type="role" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+         <name>acme1</name>
+         <description>access to acme group data</description>
+      </entity>
+</FortRequest>
+b. Search Role:
+
+curl -X POST -u 'rbacuser' -H 'Content-type: text/xml' -k -d @test-search-role.xml https://HOSTNAME:8443/fortress-rest-2.0.2/roleSearch
+
+<FortRequest>
+    <contextId>HOME</contextId>
+    <value>acme</value>
+</FortRequest>
+c. Add User:
+
+curl -X POST -u 'rbacuser' -H 'Content-type: text/xml' -k -d @test-add-user-curly.xml https://HOSTNAME:8443/fortress-rest-2.0.2/userAdd
+curl -X POST -u 'rbacuser' -H 'Content-type: text/xml' -k -d @test-add-user-moe.xml https://HOSTNAME:8443/fortress-rest-2.0.2/userAdd
+curl -X POST -u 'rbacuser' -H 'Content-type: text/xml' -k -d @test-add-user-larry.xml https://HOSTNAME:8443/fortress-rest-2.0.2/userAdd
+
+<FortRequest>
+      <contextId>HOME</contextId>
+      <entity xsi:type="user" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+         <userId>curly</userId>
+         <description>curly is a test user</description>
+         <ou>default</ou>
+         <sn>horowitz</sn>
+         <cn>curly horowitz</cn>
+         <locked>false</locked>
+      </entity>
+</FortRequest>
+d. Search User:
+
+curl -X POST -u 'rbacuser' -H 'Content-type: text/xml' -k -d @test-search-user.xml https://HOSTNAME:8443/fortress-rest-2.0.2/userSearch
+
+<FortRequest>
+    <entity xsi:type="user" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+        <userId>rbacuser</userId>
+    </entity>
+    <contextId>HOME</contextId>
+</FortRequest>
+e. Assign User:
+
+curl -X POST -u 'rbacuser' -H 'Content-type: text/xml' -k -d @test-assign-curly-acme.xml https://HOSTNAME:8443/fortress-rest-2.0.2/roleAsgn
+curl -X POST -u 'rbacuser' -H 'Content-type: text/xml' -k -d @test-assign-curly-colgate.xml https://HOSTNAME:8443/fortress-rest-2.0.2/roleAsgn
+curl -X POST -u 'rbacuser' -H 'Content-type: text/xml' -k -d @test-assign-curly-custodian.xml https://HOSTNAME:8443/fortress-rest-2.0.2/roleAsgn
+curl -X POST -u 'rbacuser' -H 'Content-type: text/xml' -k -d @test-assign-curly-ibm.xml https://HOSTNAME:8443/fortress-rest-2.0.2/roleAsgn
+curl -X POST -u 'rbacuser' -H 'Content-type: text/xml' -k -d @test-assign-curly-member.xml https://HOSTNAME:8443/fortress-rest-2.0.2/roleAsgn
+curl -X POST -u 'rbacuser' -H 'Content-type: text/xml' -k -d @test-assign-larry-acme.xml https://HOSTNAME:8443/fortress-rest-2.0.2/roleAsgn
+curl -X POST -u 'rbacuser' -H 'Content-type: text/xml' -k -d @test-assign-larry-colgate.xml https://HOSTNAME:8443/fortress-rest-2.0.2/roleAsgn
+curl -X POST -u 'rbacuser' -H 'Content-type: text/xml' -k -d @test-assign-larry-custodian.xml https://HOSTNAME:8443/fortress-rest-2.0.2/roleAsgn
+curl -X POST -u 'rbacuser' -H 'Content-type: text/xml' -k -d @test-assign-larry-member.xml https://HOSTNAME:8443/fortress-rest-2.0.2/roleAsgn
+curl -X POST -u 'rbacuser' -H 'Content-type: text/xml' -k -d @test-assign-moe-colgate.xml https://HOSTNAME:8443/fortress-rest-2.0.2/roleAsgn
+curl -X POST -u 'rbacuser' -H 'Content-type: text/xml' -k -d @test-assign-moe-ibm.xml https://HOSTNAME:8443/fortress-rest-2.0.2/roleAsgn
+curl -X POST -u 'rbacuser' -H 'Content-type: text/xml' -k -d @test-assign-moe-owner.xml https://HOSTNAME:8443/fortress-rest-2.0.2/roleAsgn
+
+<FortRequest>
+      <contextId>HOME</contextId>
+      <entity xsi:type="userRole" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+         <userId>curly</userId>
+         <name>acme</name>
+      </entity>
+</FortRequest>
+f. Create Session
+
+curl -X POST -u 'rbacuser' -H 'Content-type: text/xml' -k -d @test-create-session-curly.xml https://HOSTNAME:8443/fortress-rest-2.0.2/rbacCreateT
+curl -X POST -u 'rbacuser' -H 'Content-type: text/xml' -k -d @test-create-session-moe.xml https://HOSTNAME:8443/fortress-rest-2.0.2/rbacCreateT
+curl -X POST -u 'rbacuser' -H 'Content-type: text/xml' -k -d @test-create-session-larry.xml https://HOSTNAME:8443/fortress-rest-2.0.2/rbacCreateT
+h. Test Add Permission Object
+
+ curl -X POST -u 'rbacuser' -H 'Content-type: text/xml' -k -d @test-add-perm-object.xml https://HOSTNAME:8443/fortress-rest-2.0.1/objAdd
+i. Test Add Permission Operation
+
+ curl -X POST -u 'rbacuser' -H 'Content-type: text/xml' -k -d @test-add-perm-operation.xml https://HOSTNAME:8443/fortress-rest-2.0.2/permAdd
+j. Test Add Role Grant (to permission)
+
+ curl -X POST -u 'rbacuser' -H 'Content-type: text/xml' -k -d @test-add-role-grant.xml https://HOSTNAME:8443/fortress-rest-2.0.2/roleGrant
+k. Test Check Access
+
+ curl -X POST -u 'rbacuser' -H 'Content-type: text/xml' -k -d @test-check-access.xml https://HOSTNAME:8443/fortress-rest-2.0.2/rbacAuthZ
+l. Add User Role Constraint:
+
+ curl -X POST -u 'rbacuser' -H 'Content-type: text/xml' -k -d @test-assign-curly-role-member-constraint-colgate.xml https://HOSTNAME:8443/fortress-rest-2.0.2/addRoleConstraint
+
+ <FortRequest>
+    <contextId>HOME</contextId>
+    <entity xsi:type="userRole" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+        <name>member</name>
+        <userId>curly</userId>
+    </entity>
+    <entity2 xsi:type="roleConstraint" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+        <key>engagement</key>
+        <value>colgate</value>
+        <type>USER</type>
+    </entity2>
+ </FortRequest>
+m. Remove User Role Constraint:
+
+ curl -X POST -u 'rbacuser' -H 'Content-type: text/xml' -k -d @test-assign-curly-role-member-constraint-colgate.xml https://HOSTNAME:8443/fortress-rest-2.0.2/removeRoleConstraint
+
+ <FortRequest>
+    <contextId>HOME</contextId>
+    <entity xsi:type="userRole" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+        <name>member</name>
+        <userId>curly</userId>
+    </entity>
+    <entity2 xsi:type="roleConstraint" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+        <key>engagement</key>
+        <value>colgate</value>
+        <type>USER</type>
+    </entity2>
+ </FortRequest>
+n. Search Permissions:
+
+ curl -X POST -u 'rbacuser' -H 'Content-type: text/xml' -k -d @test-search-perms.xml https://HOSTNAME:8443/fortress-rest-2.0.2/permSearch
+
+ <FortRequest>
+    <contextId>HOME</contextId>
+    <entity xsi:type="permGrant" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+        <objName></objName>
+        <opName></opName>
+    </entity>
+ </FortRequest>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/directory-fortress-enmasse/blob/5aae8a26/embrasure/src/main.js
----------------------------------------------------------------------
diff --git a/embrasure/src/main.js b/embrasure/src/main.js
new file mode 100644
index 0000000..b7d6f7d
--- /dev/null
+++ b/embrasure/src/main.js
@@ -0,0 +1,22 @@
+import Vue from 'vue'
+import App from './App.vue'
+import router from './router'
+import ElementUI from 'element-ui'
+import 'element-ui/lib/theme-chalk/index.css'
+import locale from 'element-ui/lib/locale/lang/en'
+//import { Notification } from 'element-ui';
+import {DataTables} from 'vue-data-tables'
+
+Vue.config.productionTip = false
+Vue.use(ElementUI, { locale })
+Vue.use(DataTables)
+
+new Vue({
+  el: '#app',
+  router,
+  render: h => h(App),
+  components: { App },
+  template: '<App/>',
+  created: function() {
+  }
+})
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/directory-fortress-enmasse/blob/5aae8a26/embrasure/src/router/index.js
----------------------------------------------------------------------
diff --git a/embrasure/src/router/index.js b/embrasure/src/router/index.js
new file mode 100644
index 0000000..8bc8be3
--- /dev/null
+++ b/embrasure/src/router/index.js
@@ -0,0 +1,35 @@
+/* eslint-disable */
+import Vue from 'vue'
+import ViewRouter from 'vue-router'
+import UserList from '@/components/UserList'
+import UserParent from '@/components/UserParent'
+import Users from '@/components/Users'
+//import UserDetails from '@/components/UserDetails'
+
+Vue.use(ViewRouter)
+
+export default new ViewRouter({
+  routes: [
+    {
+      path: '/',
+      name: 'Users',
+      component: Users
+    },
+    {
+      path: '/users',
+      component: UserParent,
+      children: [
+        {
+          path: '',
+          name: 'UserList',
+          component: UserList
+        },
+        // {
+        //   path: ':id',
+        //   name: 'UserDetails',
+        //   component: UserDetails
+        // }
+      ]
+    }
+  ]
+})

http://git-wip-us.apache.org/repos/asf/directory-fortress-enmasse/blob/5aae8a26/embrasure/vue.config.js
----------------------------------------------------------------------
diff --git a/embrasure/vue.config.js b/embrasure/vue.config.js
new file mode 100644
index 0000000..91ae8e1
--- /dev/null
+++ b/embrasure/vue.config.js
@@ -0,0 +1,11 @@
+module.exports = {
+  lintOnSave: false,
+  devServer: {
+    proxy: {
+      '/fortress-rest': {
+        target: 'http://localhost:7070',
+        changeOrigin: true
+      }
+    }
+  }
+}
\ No newline at end of file


Mime
View raw message