aboutsummaryrefslogtreecommitdiffstats
path: root/dashboard/src
diff options
context:
space:
mode:
Diffstat (limited to 'dashboard/src')
-rw-r--r--dashboard/src/App.vue41
-rw-r--r--dashboard/src/assets/logo.pngbin0 -> 6849 bytes
-rw-r--r--dashboard/src/components/FormHeader.vue27
-rw-r--r--dashboard/src/components/assignment/Category.vue103
-rw-r--r--dashboard/src/components/assignment/CreateData.vue75
-rw-r--r--dashboard/src/components/assignment/Policy.vue123
-rw-r--r--dashboard/src/components/model/AddCategory.vue126
-rw-r--r--dashboard/src/components/model/AddMetarule.vue111
-rw-r--r--dashboard/src/components/model/Category.vue64
-rw-r--r--dashboard/src/components/model/CreateModel.vue57
-rw-r--r--dashboard/src/components/model/Metarule.vue155
-rw-r--r--dashboard/src/components/model/Model.vue112
-rw-r--r--dashboard/src/components/model/OrphanCategory.vue65
-rw-r--r--dashboard/src/components/model/Orphans.vue79
-rw-r--r--dashboard/src/components/pdp/AddPolicy.vue64
-rw-r--r--dashboard/src/components/pdp/CreatePdp.vue83
-rw-r--r--dashboard/src/components/pdp/Pdp.vue173
-rw-r--r--dashboard/src/components/pdp/UpdatePolicy.vue64
-rw-r--r--dashboard/src/components/policy/AssignPerimeter.vue244
-rw-r--r--dashboard/src/components/policy/CreateData.vue75
-rw-r--r--dashboard/src/components/policy/CreatePerimeter.vue73
-rw-r--r--dashboard/src/components/policy/CreatePolicy.vue84
-rw-r--r--dashboard/src/components/policy/CreateRule.vue213
-rw-r--r--dashboard/src/components/policy/DataList.vue16
-rw-r--r--dashboard/src/components/policy/FilterRules.vue24
-rw-r--r--dashboard/src/components/policy/Policy.vue186
-rw-r--r--dashboard/src/components/policy/Rule.vue231
-rw-r--r--dashboard/src/components/policy/UnusedData.vue89
-rw-r--r--dashboard/src/config.js4
-rw-r--r--dashboard/src/helpstrings.js25
-rw-r--r--dashboard/src/main.js58
-rw-r--r--dashboard/src/router.js56
-rw-r--r--dashboard/src/services/Attribute.service.js46
-rw-r--r--dashboard/src/services/Import.service.js16
-rw-r--r--dashboard/src/services/Model.service.js351
-rw-r--r--dashboard/src/services/Pdp.service.js92
-rw-r--r--dashboard/src/services/Policy.service.js470
-rw-r--r--dashboard/src/services/Util.service.js153
-rw-r--r--dashboard/src/views/Admin.vue52
-rw-r--r--dashboard/src/views/Assignments.vue54
-rw-r--r--dashboard/src/views/Auth.vue65
-rw-r--r--dashboard/src/views/Error.vue8
-rw-r--r--dashboard/src/views/Models.vue80
-rw-r--r--dashboard/src/views/Pdps.vue53
-rw-r--r--dashboard/src/views/Rules.vue50
45 files changed, 4390 insertions, 0 deletions
diff --git a/dashboard/src/App.vue b/dashboard/src/App.vue
new file mode 100644
index 00000000..99c5cd0d
--- /dev/null
+++ b/dashboard/src/App.vue
@@ -0,0 +1,41 @@
+<template>
+ <div id="app">
+
+ <nav role="navigation" class="navbar navbar-expand-sm" v-if="$route.name != 'auth' && $route.name != 'error'">
+ <ul class="navbar-nav" >
+ <li class="nav-item">
+ <router-link to="/models" class="nav-link" active-class="o-active">Models</router-link>
+ </li>
+ <li class="nav-item">
+ <router-link to="/rules" class="nav-link" active-class="o-active">Rules</router-link>
+ </li>
+ <li class="nav-item">
+ <router-link to="/assignments" class="nav-link" active-class="o-active">Assignments</router-link>
+ </li>
+ <li class="nav-item">
+ <router-link to="/pdp" class="nav-link" active-class="o-active">PDP</router-link>
+ </li>
+ <li class="nav-item">
+ <router-link to="/admin" class="nav-link" active-class="o-active">Admin</router-link>
+ </li>
+ </ul>
+ <a href="#" class="btn btn-primary btn-sm active ml-auto" role="button" aria-pressed="true" @click="logout()">logout</a>
+ </nav>
+ <br>
+
+ <router-view/>
+ </div>
+</template>
+
+
+<script>
+export default {
+ methods: {
+ logout: function() {
+ localStorage.setItem("auth-key", null);
+ this.$router.push('auth');
+ }
+ }
+}
+</script>
+
diff --git a/dashboard/src/assets/logo.png b/dashboard/src/assets/logo.png
new file mode 100644
index 00000000..f3d2503f
--- /dev/null
+++ b/dashboard/src/assets/logo.png
Binary files differ
diff --git a/dashboard/src/components/FormHeader.vue b/dashboard/src/components/FormHeader.vue
new file mode 100644
index 00000000..05419179
--- /dev/null
+++ b/dashboard/src/components/FormHeader.vue
@@ -0,0 +1,27 @@
+<template>
+ <div class="form-inline row">
+ <input
+ type="search"
+ class="form-control filter col-9 mr-auto"
+ :placeholder="placeholder"
+ v-bind:value="value"
+ v-on:input="$emit('input', $event.target.value)"
+ >
+ <div class="col"></div>
+ <button v-if="needButton" type="button" class="btn btn-primary col-auto " @click="$emit('click')">
+ <span class="fa fa-plus"></span>
+ {{ buttonText }}
+ </button>
+ </div>
+</template>
+
+<script>
+export default {
+ props: {
+ value: String,
+ needButton: Boolean,
+ buttonText: String,
+ placeholder: String,
+ }
+}
+</script> \ No newline at end of file
diff --git a/dashboard/src/components/assignment/Category.vue b/dashboard/src/components/assignment/Category.vue
new file mode 100644
index 00000000..0f2f921b
--- /dev/null
+++ b/dashboard/src/components/assignment/Category.vue
@@ -0,0 +1,103 @@
+<template>
+ <details class="list-group" >
+ <summary @click="populatePolicy()">
+ <h4 class="inline-block width-200">
+ {{name}}
+ <i class="fa fa-question-circle" style="margin-left: 2%" v-if="categoryHelpStrings[name.toLowerCase()]" data-toggle="tooltip" :title="categoryHelpStrings[name.toLowerCase()]"></i>
+ </h4>
+ </summary>
+ <div class="assignments-details">
+ <FormHeader
+ :placeholder="placeholder"
+ v-model="filter"
+ needButton
+ :buttonText="buttonText"
+ @click="creatingData"
+ ></FormHeader>
+ <create-data
+ class="m-3"
+ @close="creatingDataOpen = false"
+ :policy="policy"
+ :type="name.toLowerCase()"
+ :categoryId="categoryId"
+ v-if="creatingDataOpen"
+ ></create-data>
+ <br/>
+ <details class="list-group" v-for="item in filteredData" :key="item.id">
+ <summary @click="assignData(name.toLowerCase(), item)">
+ <h4 class="inline-block width-200">{{item.name}}</h4>
+ </summary>
+ <AssignPerimeter :policy="policy" :dataToAssign="dataToAssign"></AssignPerimeter>
+ </details>
+ </div>
+ </details>
+</template>
+
+<script>
+ import AssignPerimeter from "../policy/AssignPerimeter";
+ import FormHeader from "../FormHeader";
+ import CreateData from "./CreateData";
+ import util from "../../services/Util.service";
+ import PolicyService from "../../services/Policy.service";
+ import helpstrings from "../../helpstrings";
+
+ export default {
+ props:{
+ policy: Object,
+ data: Array,
+ name: String
+ },
+ name: "Assignment",
+ components: {
+ AssignPerimeter,
+ FormHeader,
+ CreateData
+ },
+ data() {
+ return{
+ placeholder: "",
+ buttonText: "",
+ creatingDataOpen: false,
+ filter: "",
+ dataToAssign: {},
+ categoryId: "",
+ categoryHelpStrings: {}
+ }
+ },
+ created() {
+ this.categoryHelpStrings = helpstrings.metarule;
+ this.placeholder = "Filter by " + this.name;
+ this.buttonText = "Create " + this.name;
+ if (this.policy.model.meta_rules.length){
+ let category = this.name.toLowerCase()+ "_categories";
+ let metaRule = this.policy.model.meta_rules[0];
+ this.categoryId = metaRule[category][0].id;
+ }
+
+ },
+ methods: {
+ populatePolicy() {
+ PolicyService.populatePolicy(this.policy);
+ },
+ assignData(type, data) {
+ this.dataToAssign = {
+ selectedData: data,
+ selectedDataType: type,
+ };
+ },
+ creatingData(){
+ this.creatingDataOpen = true;
+
+ }
+ },
+ computed: {
+ filteredData() {
+ return util.filterAndSortByName(this.data, this.filter);
+ }
+ }
+ }
+</script>
+
+<style scoped>
+
+</style> \ No newline at end of file
diff --git a/dashboard/src/components/assignment/CreateData.vue b/dashboard/src/components/assignment/CreateData.vue
new file mode 100644
index 00000000..63c69650
--- /dev/null
+++ b/dashboard/src/components/assignment/CreateData.vue
@@ -0,0 +1,75 @@
+<template>
+ <div class="list-group-item row">
+ <form>
+ <div class="form-group">
+ <label for="dataName">Name</label>
+ <input
+ type="text"
+ name="name"
+ v-model="dataCreate.name"
+ v-validate.initial="'alpha_dash|required|min:3'"
+ class="form-control"
+ id="dataName"
+ />
+ </div>
+ <div class="form-group">
+ <label for="dataDescription">Description</label>
+ <textarea
+ name="description"
+ v-model="dataCreate.description"
+ v-validate.initial="'required|min:3'"
+ class="form-control"
+ ></textarea>
+ </div>
+ <ul>
+ <li v-for="error in errors.all()" :key="error.id">{{ error }}</li>
+ </ul>
+ <button type="button" class="btn btn-secondary" @click="close()">Cancel</button>
+ <span>&nbsp;</span>
+ <button
+ type="button"
+ :disabled="errors.any()"
+ class="btn btn-primary"
+ @click="createData()"
+ >Create</button>
+ </form>
+ </div>
+</template>
+
+<script>
+import PolicyService from "./../../services/Policy.service.js";
+
+export default {
+ name: "createData",
+ props: {
+ policy: Object,
+ type: String,
+ categoryId: String,
+ },
+ data: function() {
+ return {
+ dataCreate: {
+ name: "",
+ description: ""
+ }
+ };
+ },
+ methods: {
+ createData() {
+ var self = this;
+ PolicyService.createData(
+ this.type,
+ this.policy,
+ this.categoryId,
+ this.dataCreate
+ ).then(function(datas) {
+ self.$emit("dataCreated", datas[0]);
+ self.close();
+ });
+ },
+ close() {
+ this.$emit("close");
+ }
+ }
+};
+</script>
diff --git a/dashboard/src/components/assignment/Policy.vue b/dashboard/src/components/assignment/Policy.vue
new file mode 100644
index 00000000..6fd4afb4
--- /dev/null
+++ b/dashboard/src/components/assignment/Policy.vue
@@ -0,0 +1,123 @@
+<template>
+ <div>
+ <template v-if="edit">
+ <form>
+ <div class="form-group" >
+ <label for="policyName">Name</label>
+ <input
+ type="text"
+ name="name"
+ v-model="policyEdit.name"
+ v-validate="'alpha_dash|required|min:3'"
+ class="form-control"
+ id="policyName"
+ />
+ </div>
+ <div class="form-group">
+ <label for="policyDescription">Description</label>
+ <textarea
+ name="description"
+ v-model="policyEdit.description"
+ v-validate="'required|min:3'"
+ class="form-control"
+ ></textarea>
+ </div>
+ <div class="form-group">
+ <label for="policyGenre">Genre</label>
+ <select
+ v-model="policyEdit.genre"
+ class="form-control"
+ id="policyGenre"
+ v-validate.initial="'required'"
+ name="genre"
+ >
+ <option>admin</option>
+ <option>authz</option>
+ </select>
+ </div>
+ <ul>
+ <li v-for="error in errors.all()" :key="error.id">{{ error }}</li>
+ </ul>
+ <button type="button" class="btn btn-secondary" @click="edit = false">Cancel</button>
+ <span>&nbsp;</span>
+ <button
+ type="button"
+ :disabled="errors.any()"
+ class="btn btn-primary"
+ @click="updatePolicy()"
+ >Update</button>
+ </form>
+ </template>
+ <template v-else>
+ <h3 class="list-group-item-heading inline " data-toggle="tooltip" data-placement="top" title="Tooltip on top">{{ policy.name }}</h3>
+ <div class="pull-right">
+ <button
+ type="button"
+ class="fa fa-trash btn-dark btn-sm"
+ title="Remove Policy"
+ @click="removePolicy()"
+ ></button>
+ <button
+ type="button"
+ class="fa fa-edit btn-dark btn-sm"
+ title="Edit Policy"
+ @click="updatingPolicy()"
+ ></button>
+ </div>
+ <p class="list-group-item-text">{{ policy.description }}</p>
+
+ <Category :policy="policy" :data="policy.subjectData" name="Subject" ></Category>
+ <Category :policy="policy" :data="policy.objectData" name="Object" ></Category>
+ <Category :policy="policy" :data="policy.actionData" name="Action" ></Category>
+ </template>
+ <hr />
+ </div>
+</template>
+
+<script>
+import PolicyService from "./../../services/Policy.service.js";
+import util from "./../../services/Util.service.js";
+import Category from "./Category";
+
+export default {
+ props: {
+ policy: Object
+ },
+ data() {
+ return {
+ filter: "",
+ edit: false,
+ policyEdit: {},
+ assignments: []
+ };
+ },
+ computed: {
+
+ },
+ components: {
+ Category
+ },
+ methods: {
+
+ removePolicy() {
+ if (
+ confirm(
+ "Are you sure to delete this Policy? (Associated perimeter, data an PDP will be deleted too)"
+ )
+ )
+ PolicyService.removePolicy(this.policy);
+ },
+ updatingPolicy() {
+ this.policyEdit = util.clone(this.policy);
+ this.edit = true;
+ },
+ updatePolicy() {
+ this.edit = false;
+ PolicyService.updatePolicy(this.policyEdit);
+ },
+ showAssignments(data){
+ this.assignments = this.policy[data];
+ }
+ }
+};
+</script> \ No newline at end of file
diff --git a/dashboard/src/components/model/AddCategory.vue b/dashboard/src/components/model/AddCategory.vue
new file mode 100644
index 00000000..c22ab333
--- /dev/null
+++ b/dashboard/src/components/model/AddCategory.vue
@@ -0,0 +1,126 @@
+<template>
+ <div>
+ <template v-if="categories.length > 0">
+ <h4>Select category:</h4>
+ <form data-vv-scope="select">
+ <div class="form-group">
+ <select v-model="selectedCategoryId" v-validate.initial="'required'">
+ <option disabled value>Please select one</option>
+ <option
+ v-for="category in categories"
+ :value="category.id"
+ :key="category.id"
+ >{{category.name}}</option>
+ </select>
+ </div>
+ <button type="button" class="btn btn-secondary" @click="close()">Cancel</button>
+ <span>&nbsp;</span>
+ <button
+ type="button"
+ :disabled="errors.any('select')"
+ class="btn btn-primary"
+ @click="addCategory()"
+ >Add</button>
+ </form>
+ <br>
+ <br>
+ <h4>Or create a new one:</h4>
+ </template>
+ <h4 v-else>Create a category:</h4>
+ <form data-vv-scope="create">
+ <div class="form-group">
+ <label for="categoryName">Name</label>
+ <input
+ type="text"
+ name="name"
+ v-model="categoryCreate.name"
+ v-validate.initial="'alpha_dash|required|min:3'"
+ class="form-control"
+ id="categoryName"
+ >
+ </div>
+ <div class="form-group">
+ <label for="modelDescription">Description</label>
+ <textarea
+ name="description"
+ v-model="categoryCreate.description"
+ v-validate="'required|min:3'"
+ class="form-control"
+ ></textarea>
+ </div>
+ <ul>
+ <li v-for="error in errors.all('create')" :key="error.id">{{ error }}</li>
+ </ul>
+ <button type="button" class="btn btn-secondary" @click="close()">Cancel</button>
+ <span>&nbsp;</span>
+ <button
+ type="button"
+ :disabled="errors.any('create')"
+ class="btn btn-primary"
+ @click="createCategory()"
+ >Create</button>
+ </form>
+ </div>
+</template>
+
+<script>
+import ModelService from "./../../services/Model.service.js";
+import util from "./../../services/Util.service.js";
+
+var categoryMap = {
+ subject: {
+ listName: "subject_categories",
+ serviceListName: "subjectCategories"
+ },
+ object: {
+ listName: "object_categories",
+ serviceListName: "objectCategories"
+ },
+ action: {
+ listName: "action_categories",
+ serviceListName: "actionCategories"
+ }
+};
+
+export default {
+ name: "addCategory",
+ data: function() {
+ return {
+ selectedCategoryId: null,
+ categoryCreate: {
+ name: "",
+ description: ""
+ }
+ };
+ },
+ props: {
+ metarule: Object,
+ type: String
+ },
+ methods: {
+ createCategory() {
+ ModelService.createCategory(this.type, this.categoryCreate).then(category => {
+ this.selectedCategoryId = category.id;
+ this.addCategory();
+ });
+ },
+ addCategory() {
+ var category = ModelService.getCategory(this.type, this.selectedCategoryId);
+ var metaRuleCopy = util.clone(this.metarule);
+ metaRuleCopy[categoryMap[this.type].listName].push(category);
+ ModelService.updateMetaRule(metaRuleCopy);
+ this.close();
+ },
+ close() {
+ this.$emit("close");
+ }
+ },
+ computed: {
+ categories() {
+ return ModelService[categoryMap[this.type].serviceListName].filter(
+ el => !this.metarule[categoryMap[this.type].listName].includes(el)
+ );
+ }
+ }
+};
+</script>
diff --git a/dashboard/src/components/model/AddMetarule.vue b/dashboard/src/components/model/AddMetarule.vue
new file mode 100644
index 00000000..ef6e8503
--- /dev/null
+++ b/dashboard/src/components/model/AddMetarule.vue
@@ -0,0 +1,111 @@
+<template>
+ <div>
+ <hr>
+ <template v-if="metarules.length > 0">
+ <h4>Select metarule:</h4>
+ <form data-vv-scope="select">
+ <div class="form-group">
+ <select v-model="selectedMetaruleId" v-validate.initial="'required'">
+ <option disabled value>Please select one</option>
+ <option
+ v-for="metarule in metarules"
+ :value="metarule.id"
+ :key="metarule.id"
+ >{{metarule.name}}</option>
+ </select>
+ </div>
+ <button type="button" class="btn btn-secondary" @click="close()">Cancel</button>
+ <span>&nbsp;</span>
+ <button
+ type="button"
+ :disabled="errors.any('select')"
+ class="btn btn-primary"
+ @click="addMetarule()"
+ >Add</button>
+ </form>
+ <br>
+ <br>
+ <h4>Or create a new one:</h4>
+ </template>
+ <h4 v-else>Create a metarule:</h4>
+ <form data-vv-scope="create">
+ <div class="form-group">
+ <label for="metaruleName">Name</label>
+ <input
+ type="text"
+ name="name"
+ v-model="metaruleCreate.name"
+ v-validate.initial="'alpha_dash|required|min:3'"
+ class="form-control"
+ id="metaruleName"
+ >
+ </div>
+ <div class="form-group">
+ <label for="modelDescription">Description</label>
+ <textarea
+ name="description"
+ v-model="metaruleCreate.description"
+ v-validate="'required|min:3'"
+ class="form-control"
+ ></textarea>
+ </div>
+ <ul>
+ <li v-for="error in errors.all('create')" :key="error.id">{{ error }}</li>
+ </ul>
+ <button type="button" class="btn btn-secondary" @click="close()">Cancel</button>
+ <span>&nbsp;</span>
+ <button
+ type="button"
+ :disabled="errors.any('create')"
+ class="btn btn-primary"
+ @click="createMetarule()"
+ >Create and add</button>
+ </form>
+ </div>
+</template>
+
+<script>
+import ModelService from "./../../services/Model.service.js";
+import util from "./../../services/Util.service.js";
+
+export default {
+ name: "addMetarule",
+ data: function() {
+ return {
+ selectedMetaruleId: null,
+ metaruleCreate: {
+ name: "",
+ description: ""
+ }
+ };
+ },
+ props: {
+ model: Object
+ },
+ methods: {
+ createMetarule() {
+ ModelService.createMetaRule(this.metaruleCreate).then(metarule => {
+ this.selectedMetaruleId = metarule.id;
+ this.addMetarule();
+ });
+ },
+ addMetarule() {
+ var metaRule = ModelService.getMetaRule(this.selectedMetaruleId);
+ var modelCopy = util.clone(this.model);
+ modelCopy.meta_rules.push(metaRule);
+ ModelService.updateModel(modelCopy);
+ this.close();
+ },
+ close() {
+ this.$emit("close");
+ }
+ },
+ computed: {
+ metarules() {
+ return ModelService.metaRules.filter(
+ el => !this.model.meta_rules.includes(el)
+ );
+ }
+ }
+};
+</script>
diff --git a/dashboard/src/components/model/Category.vue b/dashboard/src/components/model/Category.vue
new file mode 100644
index 00000000..81efb1ed
--- /dev/null
+++ b/dashboard/src/components/model/Category.vue
@@ -0,0 +1,64 @@
+<template>
+ <div>
+ <span :title="category.description">{{ category.name }}</span>
+ <button type="button" class="fa fa-trash pull-right btn btn-dark btn-sm" @click="removeCategory()" title="Remove"></button>
+ <div v-for="attribute in attributes" :key="attribute.id">
+ <b>attributes: </b> {{attribute.id}}
+ </div>
+ </div>
+</template>
+
+<script>
+import ModelService from "./../../services/Model.service.js";
+import util from "./../../services/Util.service.js";
+
+var categoryMap = {
+ subject: {
+ addTitle: "Add Subject Category",
+ removeTitleFromMetaRule:
+ "Are you sure to remove from meta rule this Subject Category?",
+ removeTitle: "Are you sure to remove this Subject Category?",
+ listName: "subject_categories",
+ serviceListName: "subjectCategories"
+ },
+ object: {
+ addTitle: "Add Object Category",
+ removeTitleFromMetaRule:
+ "Are you sure to remove from meta rule this Object Category?",
+ removeTitle: "Are you sure to remove this Object Category?",
+ listName: "object_categories",
+ serviceListName: "objectCategories"
+ },
+ action: {
+ addTitle: "Add Action Category",
+ removeTitleFromMetaRule:
+ "Are you sure to remove from meta rule this Action Category?",
+ removeTitle: "Are you sure to remove this Action Category?",
+ listName: "action_categories",
+ serviceListName: "actionCategories"
+ }
+};
+
+export default {
+ name: "category",
+ props: {
+ metarule: Object,
+ category: Object,
+ attributes: Array,
+ type: String
+ },
+ methods: {
+ removeCategory() {
+ var typeValue = categoryMap[this.type];
+ if (confirm(typeValue.removeTitleFromMetaRule)) {
+ var metaruleCopy = util.clone(this.metarule);
+ metaruleCopy[typeValue.listName].splice(
+ metaruleCopy[typeValue.listName].indexOf(this.category),
+ 1
+ );
+ ModelService.updateMetaRule(metaruleCopy);
+ }
+ }
+ }
+};
+</script>
diff --git a/dashboard/src/components/model/CreateModel.vue b/dashboard/src/components/model/CreateModel.vue
new file mode 100644
index 00000000..d5403e7b
--- /dev/null
+++ b/dashboard/src/components/model/CreateModel.vue
@@ -0,0 +1,57 @@
+<template>
+ <div class="list-group-item row">
+ <form>
+ <div class="form-group">
+ <label for="modelName">Name</label>
+ <input
+ type="text"
+ name="name"
+ v-model="modelCreate.name"
+ v-validate.initial="'alpha_dash|required|min:3'"
+ class="form-control"
+ id="modelName"
+ >
+ </div>
+ <div class="form-group">
+ <label for="modelDescription">Description</label>
+ <textarea
+ name="description"
+ v-model="modelCreate.description"
+ v-validate.initial="'required|min:3'"
+ class="form-control"
+ ></textarea>
+ </div>
+ <ul>
+ <li v-for="error in errors.all()" :key="error.id">{{ error }}</li>
+ </ul>
+ <button type="button" class="btn btn-secondary" @click="close()">Cancel</button>
+ <span>&nbsp;</span>
+ <button type="button" :disabled="errors.any()" class="btn btn-primary" @click="createModel()">Create</button>
+ </form>
+ </div>
+</template>
+
+<script>
+import ModelService from "./../../services/Model.service.js";
+
+export default {
+ name: "createModel",
+ data: function() {
+ return {
+ modelCreate: {
+ name: "",
+ description: ""
+ }
+ };
+ },
+ methods: {
+ createModel() {
+ ModelService.createModel(this.modelCreate);
+ this.close();
+ },
+ close() {
+ this.$emit("close")
+ }
+ }
+};
+</script>
diff --git a/dashboard/src/components/model/Metarule.vue b/dashboard/src/components/model/Metarule.vue
new file mode 100644
index 00000000..1cb266bd
--- /dev/null
+++ b/dashboard/src/components/model/Metarule.vue
@@ -0,0 +1,155 @@
+<template>
+ <div class="">
+ <template v-if="edit">
+ <form>
+ <div class="form-group">
+ <label for="metaruleName">Name</label>
+ <input
+ type="text"
+ name="name"
+ v-model="metaruleEdit.name"
+ v-validate="'alpha_dash|required|min:3'"
+ class="form-control"
+ id="metaruleName"
+ >
+ </div>
+ <div class="form-group">
+ <label for="modelDescription">Description</label>
+ <textarea
+ name="description"
+ v-model="metaruleEdit.description"
+ v-validate="'required|min:3'"
+ class="form-control"
+ ></textarea>
+ </div>
+ <ul>
+ <li v-for="error in errors.all()" :key="error.id">{{ error }}</li>
+ </ul>
+ <button type="button" class="btn btn-secondary" @click="edit = false">Cancel</button>
+ <span>&nbsp;</span>
+ <button type="button" :disabled="errors.any()" class="btn btn-primary" @click="updateMetarule()">Update</button>
+ </form>
+ </template>
+ <template v-else>
+ <h3 class="list-group-item-heading inline">{{ metarule.name }}</h3>
+ <div class="pull-right">
+ <button
+ type="button"
+ class="fa fa-trash btn btn-dark btn-sm"
+ @click="removeMetarule()"
+ title="Remove Meta Rule"
+ ></button>
+ <button
+ type="button"
+ class="fa fa-edit btn btn-dark btn-sm"
+ @click="updatingMetarule()"
+ title="Edit Meta Rule"
+ ></button>
+ </div>
+ <p class="list-group-item-text">{{ metarule.description }}</p>
+ <p class="list-group-item-text"></p>
+ <table class="table categories">
+ <thead>
+ <tr>
+ <th>
+ <span>Subjects</span>
+ <i class="fa fa-question-circle" style="margin-left: 2%" v-if="metaruleHelpStrings.subject" data-toggle="tooltip" :title="metaruleHelpStrings.subject"></i>
+ <button
+ type="button"
+ class="fa fa-plus pull-right btn btn-dark btn-sm"
+ @click="addSubjectCategory = true"
+ title="Add Subject"
+ ></button>
+ </th>
+ <th>
+ <span>Objects</span>
+ <i class="fa fa-question-circle" style="margin-left: 2%" v-if="metaruleHelpStrings.object" data-toggle="tooltip" :title="metaruleHelpStrings.object"></i>
+ <button
+ type="button"
+ class="fa fa-plus pull-right btn btn-dark btn-sm"
+ @click="addObjectCategory = true"
+ title="Add Object"
+ ></button>
+ </th>
+ <th>
+ <span>Actions</span>
+ <i class="fa fa-question-circle" style="margin-left: 2%" v-if="metaruleHelpStrings.action" data-toggle="tooltip" :title="metaruleHelpStrings.action"></i>
+ <button
+ type="button"
+ class="fa fa-plus pull-right btn btn-dark btn-sm"
+ @click="addActionCategory = true"
+ title="Add Action"
+ ></button>
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>
+ <AddCategory v-if="addSubjectCategory" :metarule="metarule" type="subject" @close="addSubjectCategory = false"></AddCategory>
+ <Category v-else v-for="category in metarule.subject_categories" :key="category.id" :category="category" :metarule="metarule" :attributes="metarule.subjectAttributes" type="subject"></Category>
+ </td>
+ <td>
+ <AddCategory v-if="addObjectCategory" :metarule="metarule" type="object" @close="addObjectCategory = false"></AddCategory>
+ <Category v-else v-for="category in metarule.object_categories" :key="category.id" :category="category" :metarule="metarule" :attributes="metarule.objectAttributes" type="object"></Category>
+ </td>
+ <td>
+ <AddCategory v-if="addActionCategory" :metarule="metarule" type="action" @close="addActionCategory = false"></AddCategory>
+ <Category v-else v-for="category in metarule.action_categories" :key="category.id" :category="category" :metarule="metarule" :attributes="metarule.actionAttributes" type="action"></Category>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </template>
+ </div>
+</template>
+
+<script>
+import Category from './Category.vue'
+import AddCategory from './AddCategory.vue'
+import ModelService from "./../../services/Model.service.js";
+import util from "./../../services/Util.service.js";
+import helpstrings from "../../helpstrings";
+
+export default {
+ name: "metarule",
+ data: function() {
+ return {
+ edit: false,
+ metaruleEdit: {},
+ addSubjectCategory: false,
+ addObjectCategory: false,
+ addActionCategory: false,
+ metaruleHelpStrings: {}
+ };
+ },
+ components: {
+ Category,
+ AddCategory
+ },
+ props: {
+ metarule: Object,
+ model: Object,
+ },
+ mounted() {
+ this.metaruleHelpStrings = helpstrings.metarule;
+ },
+ methods: {
+ updatingMetarule() {
+ this.metaruleEdit = util.clone(this.metarule);
+ this.edit = true;
+ },
+ updateMetarule() {
+ this.edit = false;
+ ModelService.updateMetaRule(this.metaruleEdit);
+ },
+ removeMetarule() {
+ if (confirm('Are you sure to remove this Meta Rule from model?')) {
+ var modelCopy = util.clone(this.model);
+ modelCopy.meta_rules.splice(modelCopy.meta_rules.indexOf(this.metarule), 1);
+ ModelService.updateModel(modelCopy);
+ }
+ }
+ }
+};
+</script>
diff --git a/dashboard/src/components/model/Model.vue b/dashboard/src/components/model/Model.vue
new file mode 100644
index 00000000..82ad992d
--- /dev/null
+++ b/dashboard/src/components/model/Model.vue
@@ -0,0 +1,112 @@
+<template>
+ <div>
+ <template v-if="edit">
+ <form>
+ <div class="form-group">
+ <label for="modelName">Name</label>
+ <input
+ type="text"
+ name="name"
+ v-model="modelEdit.name"
+ v-validate="'alpha_dash|required|min:3'"
+ class="form-control"
+ id="modelName"
+ >
+ </div>
+ <div class="form-group">
+ <label for="modelDescription">Description</label>
+ <textarea
+ name="description"
+ v-model="modelEdit.description"
+ v-validate="'required|min:3'"
+ class="form-control"
+ ></textarea>
+ </div>
+ <ul>
+ <li v-for="error in errors.all()" :key="error.id">{{ error }}</li>
+ </ul>
+ <button type="button" class="btn btn-secondary" @click="edit = false">Cancel</button>
+ <span>&nbsp;</span>
+ <button type="button" :disabled="errors.any()" class="btn btn-primary" @click="updateModel()">Update</button>
+ </form>
+ </template>
+ <template v-else>
+ <h3 class="list-group-item-heading inline">{{ model.name }}</h3>
+ <div class="pull-right">
+ <button type="button" class="fa fa-trash btn btn-dark btn-sm" @click="removeModel()" title="Remove Model"></button>
+ <button type="button" class="fa fa-edit btn btn-dark btn-sm" @click="updatingModel()" title="Edit Model"></button>
+ </div>
+ <p class="list-group-item-text">{{ model.description }}</p>
+
+ <AddMetarule v-if="addMetarule" :model="model" @close="addMetarule = false"></AddMetarule>
+ <details class="list-group-item-text" v-else>
+ <summary>
+ <h4 class="inline-block width-200">
+ {{ model.meta_rules.length + ' meta rule' + (model.meta_rules.length > 1 ? 's' : '&nbsp;') }}
+ <i class="fa fa-question-circle" v-if="modelHelpStrings.metarule" data-toggle="tooltip" :title="modelHelpStrings.metarule"></i>
+ </h4>
+ <button
+ type="button"
+ class="fa fa-plus btn btn-dark btn-sm"
+ @click="addMetarule = true"
+ title="Add Meta Rule"
+ ></button>
+ </summary>
+ <div class="list-group">
+ <Metarule
+ v-for="metarule in model.meta_rules"
+ :key="metarule.id"
+ :metarule="metarule"
+ :model="model"
+ ></Metarule>
+ </div>
+ </details>
+ </template>
+ <hr>
+ </div>
+</template>
+
+<script>
+import Metarule from "./Metarule.vue";
+import ModelService from "./../../services/Model.service.js";
+import AddMetarule from "./AddMetarule.vue";
+import util from "./../../services/Util.service.js";
+import helpstrings from "../../helpstrings";
+
+export default {
+ name: "model",
+ data: function() {
+ return {
+ edit: false,
+ addMetarule: false,
+ modelEdit: {},
+ modelHelpStrings: {}
+ };
+ },
+ components: {
+ Metarule,
+ AddMetarule
+ },
+ props: {
+ model: Object
+ },
+ mounted() {
+ this.modelHelpStrings = helpstrings.model;
+ },
+ methods: {
+ updatingModel() {
+ this.modelEdit = util.clone(this.model);
+ this.edit = true;
+ },
+ updateModel() {
+ this.edit = false;
+ ModelService.updateModel(this.modelEdit);
+ },
+ removeModel() {
+ if (confirm('Are you sure to delete this Model?')) {
+ ModelService.removeModel(this.model);
+ }
+ }
+ }
+};
+</script>
diff --git a/dashboard/src/components/model/OrphanCategory.vue b/dashboard/src/components/model/OrphanCategory.vue
new file mode 100644
index 00000000..316b8095
--- /dev/null
+++ b/dashboard/src/components/model/OrphanCategory.vue
@@ -0,0 +1,65 @@
+<template>
+ <div class="list-group col-lg-3">
+ <h3 class="list-group-item active">{{title}}</h3>
+ <div v-for="category in categories" class="list-group-item" :key="category.id">
+ <h4 class="list-group-item-heading inline">{{ category.name }}</h4>
+ <button
+ type="button"
+ class="fa fa-trash pull-right btn btn-dark btn-sm"
+ @click="removeCategory(category)"
+ :title="buttonTitle"
+ ></button>
+ <p class="list-group-item-text">{{ category.description }}</p>
+ </div>
+ </div>
+</template>
+
+<script>
+import ModelService from "./../../services/Model.service.js";
+
+var categoryMap = {
+ subject: {
+ title: "Orphan Subject categories",
+ removeButtonTitle: "Remove Subject category",
+ removeTitle: "Are you sure to remove this Subject Category?",
+ listName: "subject_categories",
+ serviceListName: "subjectCategories"
+ },
+ object: {
+ title: "Orphan Object categories",
+ removeButtonTitle: "Remove Object category",
+ removeTitle: "Are you sure to remove this Object Category?",
+ listName: "object_categories",
+ serviceListName: "objectCategories"
+ },
+ action: {
+ title: "Orphan Action categories",
+ removeButtonTitle: "Remove Action category",
+ removeTitle: "Are you sure to remove this Action Category?",
+ listName: "action_categories",
+ serviceListName: "actionCategories"
+ }
+};
+
+export default {
+ props: {
+ categories: Array,
+ type: String
+ },
+ computed: {
+ title() {
+ return categoryMap[this.type].title;
+ },
+ buttonTitle() {
+ return categoryMap[this.type].removeButtonTitle;
+ }
+ },
+ methods: {
+ removeCategory(category) {
+ if (confirm(categoryMap[this.type].removeTitle)) {
+ ModelService.removeCategory(this.type, category);
+ }
+ }
+ }
+};
+</script>
diff --git a/dashboard/src/components/model/Orphans.vue b/dashboard/src/components/model/Orphans.vue
new file mode 100644
index 00000000..b3c1c524
--- /dev/null
+++ b/dashboard/src/components/model/Orphans.vue
@@ -0,0 +1,79 @@
+<template>
+ <div>
+ <div class="alert alert-dismissable alert-warning">
+ <button type="button" class="close" data-dismiss="alert" @click="showOrphan = false; $emit('close')">×</button>
+ <h4>Warning!</h4>
+ <p>
+ Some metarules or categories are orphan, please check them and delete them if necessary.
+ <a
+ href
+ @click.prevent="showOrphan = true"
+ v-show="!showOrphan"
+ >Show orphans</a>
+ <a href @click.prevent="showOrphan = false" v-show="showOrphan">Hide orphans</a>
+ </p>
+ </div>
+
+ <div class="row" v-show="showOrphan">
+ <div class="list-group col-lg-3" v-if="orphanMetaRules.length">
+ <h3 class="list-group-item active">Orphan Meta rules</h3>
+ <div v-for="metaRule in orphanMetaRules" class="list-group-item" :key="metaRule.id">
+ <h4 class="list-group-item-heading inline">{{ metaRule.name }}</h4>
+ <button
+ type="button"
+ class="fa fa-trash pull-right btn btn-dark btn-sm"
+ @click="removeMetarule(metaRule)"
+ title="Remove Meta rule"
+ ></button>
+ <p class="list-group-item-text">{{ metaRule.description }}</p>
+ </div>
+ </div>
+
+ <OrphanCategory
+ v-if="orphanSubjectCategories.length"
+ type="subject"
+ :categories="orphanSubjectCategories"
+ ></OrphanCategory>
+ <OrphanCategory
+ v-if="orphanObjectCategories.length"
+ type="object"
+ :categories="orphanObjectCategories"
+ ></OrphanCategory>
+ <OrphanCategory
+ v-if="orphanActionCategories.length"
+ type="action"
+ :categories="orphanActionCategories"
+ ></OrphanCategory>
+ </div>
+ </div>
+</template>
+
+<script>
+import ModelService from "./../../services/Model.service.js";
+import OrphanCategory from "./OrphanCategory.vue";
+
+export default {
+ props: {
+ orphanMetaRules: Array,
+ orphanSubjectCategories: Array,
+ orphanObjectCategories: Array,
+ orphanActionCategories: Array
+ },
+ components: {
+ OrphanCategory,
+ },
+ data() {
+ return {
+ showOrphan: false,
+ allowAlert: true
+ };
+ },
+ methods: {
+ removeMetarule(metarule) {
+ if (confirm("Are you sure to remove this Meta Rule?")) {
+ ModelService.removeMetaRule(metarule);
+ }
+ }
+ }
+};
+</script> \ No newline at end of file
diff --git a/dashboard/src/components/pdp/AddPolicy.vue b/dashboard/src/components/pdp/AddPolicy.vue
new file mode 100644
index 00000000..82ad07e2
--- /dev/null
+++ b/dashboard/src/components/pdp/AddPolicy.vue
@@ -0,0 +1,64 @@
+<template>
+ <div>
+ <hr>
+ <h4>Select policy:</h4>
+ <form data-vv-scope="select">
+ <div class="form-group">
+ <select v-model="selectedPolicyId" v-validate.initial="'required'">
+ <option disabled value>Please select one</option>
+ <option
+ v-for="policy in policies"
+ :value="policy.id"
+ :key="policy.id"
+ >{{policy.name}}</option>
+ </select>
+ </div>
+ <button type="button" class="btn btn-secondary" @click="close()">Cancel</button>
+ <span>&nbsp;</span>
+ <button
+ type="button"
+ :disabled="errors.any('select')"
+ class="btn btn-primary"
+ @click="addPolicy()"
+ >Add</button>
+ </form>
+ <br>
+ <br>
+ </div>
+</template>
+
+<script>
+import PdpService from "./../../services/Pdp.service.js";
+import util from "./../../services/Util.service.js";
+
+export default {
+ name: "addPolicy",
+ data: function() {
+ return {
+ selectedPolicyId: null,
+ };
+ },
+ props: {
+ pdp: Object
+ },
+ methods: {
+ addPolicy() {
+ var policy = PdpService.getPolicy(this.selectedPolicyId);
+ var pdpCopy = util.clone(this.pdp);
+ pdpCopy.security_pipeline.push(policy);
+ PdpService.updatePdp(pdpCopy);
+ this.close();
+ },
+ close() {
+ this.$emit("close");
+ }
+ },
+ computed: {
+ policies() {
+ return PdpService.policies.filter(
+ el => !this.pdp.security_pipeline.includes(el)
+ );
+ }
+ }
+};
+</script>
diff --git a/dashboard/src/components/pdp/CreatePdp.vue b/dashboard/src/components/pdp/CreatePdp.vue
new file mode 100644
index 00000000..aca46413
--- /dev/null
+++ b/dashboard/src/components/pdp/CreatePdp.vue
@@ -0,0 +1,83 @@
+<template>
+ <div class="list-group-item row">
+ <form>
+ <div class="form-group">
+ <label for="modelName">Name</label>
+ <input
+ type="text"
+ name="name"
+ v-model="pdpCreate.name"
+ v-validate.initial="'alpha_dash|required|min:3'"
+ class="form-control"
+ id="modelName"
+ >
+ </div>
+ <div class="form-group">
+ <label for="modelDescription">Description</label>
+ <textarea
+ name="description"
+ v-model="pdpCreate.description"
+ v-validate.initial="'required|min:3'"
+ class="form-control"
+ ></textarea>
+ </div>
+ <div class="form-group">
+ <label for="pdpVpi">Vim project id</label>
+ <input
+ type="text"
+ name="vim_project_id"
+ v-model="pdpCreate.vim_project_id"
+ class="form-control"
+ id="pdpVpi"
+ >
+ </div>
+ <div class="form-group">
+ <label for="pdpPolicy">Policy</label>
+ <select v-model="selectedPolicy" class="form-control" id="pdpPolicy" v-validate.initial="'required'" name="policy">
+ <option v-for="policy in policies" :key="policy.id" :value="policy.id">{{ policy.name }}</option>
+ </select>
+ </div>
+ <ul>
+ <li v-for="error in errors.all()" :key="error.id">{{ error }}</li>
+ </ul>
+ <button type="button" class="btn btn-secondary" @click="close()">Cancel</button>
+ <span>&nbsp;</span>
+ <button type="button" :disabled="errors.any()" class="btn btn-primary" @click="createModel()">Create</button>
+ </form>
+ </div>
+</template>
+
+<script>
+import PdpService from "./../../services/Pdp.service.js";
+import PolicyService from "../../services/Policy.service";
+import util from "../../services/Util.service";
+
+export default {
+ data: function() {
+ return {
+ selectedPolicy: null,
+ pdpCreate: {
+ name: "",
+ description: "",
+ security_pipeline: [],
+ vim_project_id: ""
+ }
+ };
+ },
+ computed:{
+ policies() {
+ return util.sortByName(PolicyService.policies);
+ }
+ },
+ methods: {
+ createModel() {
+ this.pdpCreate.security_pipeline.push(this.selectedPolicy);
+ PdpService.createPdp(this.pdpCreate);
+ this.close();
+ },
+ close() {
+ this.$emit("close")
+ }
+ }
+};
+</script>
diff --git a/dashboard/src/components/pdp/Pdp.vue b/dashboard/src/components/pdp/Pdp.vue
new file mode 100644
index 00000000..3aba3fed
--- /dev/null
+++ b/dashboard/src/components/pdp/Pdp.vue
@@ -0,0 +1,173 @@
+<template>
+ <div class="">
+ <template v-if="edit">
+ <form>
+ <div class="form-group">
+ <label for="pdpName">Name</label>
+ <input
+ type="text"
+ name="name"
+ v-model="pdpEdit.name"
+ v-validate="'alpha_dash|required|min:3'"
+ class="form-control"
+ id="pdpName"
+ >
+ </div>
+ <div class="form-group">
+ <label for="pdpDescription">Description</label>
+ <textarea
+ name="description"
+ v-model="pdpEdit.description"
+ v-validate="'required|min:3'"
+ class="form-control"
+ id="pdpDescription"
+ ></textarea>
+ </div>
+ <ul>
+ <li v-for="error in errors.all()" :key="error.id">{{ error }}</li>
+ </ul>
+ <button type="button" class="btn btn-secondary" @click="edit = false">Cancel</button>
+ <span>&nbsp;</span>
+ <button type="button" :disabled="errors.any()" class="btn btn-primary" @click="updatePdp()">Update</button>
+ </form>
+ </template>
+ <template v-else>
+ <div>
+ <h3 class="list-group-item-heading inline">{{ pdp.name }}</h3>
+ <div class="pull-right">
+ <button
+ type="button"
+ class="fa fa-trash btn btn-dark btn-sm"
+ @click="removePdp(pdp)"
+ title="Remove PDP"
+ ></button>
+ <button
+ type="button"
+ class="fa fa-edit btn btn-dark btn-sm"
+ @click="updatingPdp(pdp)"
+ title="Edit PDP"
+ ></button>
+ </div>
+ <p class="list-group-item-text">{{ pdp.description }}</p>
+ <h4 class="list-group-item-text">
+ <div v-if="!changeProject">
+ Project: {{ pdp.project ? pdp.project : 'none' }}
+ <button
+ type="button"
+ class="fa fa-edit btn btn-dark btn-sm"
+ @click="changingProject()"
+ title="Change project"
+ ></button>
+ </div>
+ <form class="form-inline" v-else>
+ <label for="projectId">Project ID: </label>
+ &nbsp;
+ <input
+ type="text"
+ name="id"
+ v-model="project"
+ class="form-control"
+ id="projectId"
+ >
+ &nbsp;
+ <button type="button" class="btn btn-secondary" @click="changeProject = false">Cancel</button>
+ <span>&nbsp;</span>
+ <button
+ type="button"
+ class="btn btn-primary"
+ @click="setProject()"
+ >OK</button>
+ </form>
+ </h4>
+
+ <UpdatePolicy v-if="updatePolicy" :pdp="pdp" @close="updatePolicy = false"></UpdatePolicy>
+ <details class="list-group-item-text" v-else>
+ <summary>
+ <h4 class="inline">
+ {{ pdp.security_pipeline.length }} {{ (pdp.security_pipeline.length > 1) ? "policies" : "policy"}}
+ </h4>
+ <button
+ type="button"
+ class="fa fa-edit btn btn-dark btn-sm"
+ @click="updatePolicy = true"
+ title="Change Policy"
+ ></button>
+ </summary>
+ <div class="list-group">
+ <div
+ v-for="policy in pdp.security_pipeline" :key="policy.id"
+ >
+ <h3 class="list-group-item-heading inline">{{ policy.name }}</h3>
+ <!--<button
+ type="button"
+ class="fa fa-trash pull-right btn btn-dark btn-sm"
+ @click="removePolicyFromPdp(policy)"
+ title="Remove Policy"
+ ></button>-->
+ <p class="list-group-item-text">{{ policy.description }}</p>
+ </div>
+ </div>
+ </details>
+ </div>
+ </template>
+ <hr>
+ </div>
+</template>
+
+<script>
+import PdpService from './../../services/Pdp.service.js';
+//import AddPolicy from "./AddPolicy.vue";
+import UpdatePolicy from "./UpdatePolicy";
+import util from "./../../services/Util.service.js";
+
+export default {
+ name: "pdp",
+ data: function() {
+ return {
+ edit: false,
+ updatePolicy: false,
+ changeProject: false,
+ project: "",
+ pdpEdit: {}
+ };
+ },
+ props: {
+ pdp: Object
+ },
+ components: {
+ //AddPolicy
+ UpdatePolicy
+ },
+ methods: {
+ changingProject() {
+ this.project = this.pdp.project;
+ this.changeProject = true;
+ },
+ removePdp() {
+ if (confirm('Are you sure to delete this PDP?'))
+ PdpService.removePdp(this.pdp);
+ },
+ updatingPdp() {
+ this.pdpEdit = util.clone(this.pdp);
+ this.edit = true;
+ },
+ updatePdp() {
+ this.edit = false;
+ PdpService.updatePdp(this.pdpEdit);
+ },
+ removePolicyFromPdp(policy) {
+ if (confirm('Are you sure to remove this Policy from PDP?')) {
+ //var pdpCopy = util.clone(this.pdp);
+ this.pdp.security_pipeline.splice(this.pdp.security_pipeline.indexOf(policy), 1);
+ PdpService.updatePdp(this.pdp);
+ }
+ },
+ setProject() {
+ var pdpCopy = util.clone(this.pdp);
+ pdpCopy.project = this.project;
+ PdpService.updatePdp(pdpCopy);
+ this.changeProject = false;
+ }
+ }
+};
+</script>
diff --git a/dashboard/src/components/pdp/UpdatePolicy.vue b/dashboard/src/components/pdp/UpdatePolicy.vue
new file mode 100644
index 00000000..f84cf37e
--- /dev/null
+++ b/dashboard/src/components/pdp/UpdatePolicy.vue
@@ -0,0 +1,64 @@
+<template>
+ <div>
+ <hr>
+ <h4>Select policy:</h4>
+ <form data-vv-scope="select">
+ <div class="form-group">
+ <select v-model="selectedPolicyId" v-validate.initial="'required'">
+ <option disabled value>Please select one</option>
+ <option
+ v-for="policy in policies"
+ :value="policy.id"
+ :key="policy.id"
+ >{{policy.name}}</option>
+ </select>
+ </div>
+ <button type="button" class="btn btn-secondary" @click="close()">Cancel</button>
+ <span>&nbsp;</span>
+ <button
+ type="button"
+ :disabled="errors.any('select')"
+ class="btn btn-primary"
+ @click="updatePolicy()"
+ >Update</button>
+ </form>
+ <br>
+ <br>
+ </div>
+</template>
+
+<script>
+import PdpService from "./../../services/Pdp.service.js";
+import util from "./../../services/Util.service.js";
+
+export default {
+ name: "updatePolicy",
+ data: function() {
+ return {
+ selectedPolicyId: null,
+ };
+ },
+ props: {
+ pdp: Object
+ },
+ methods: {
+ updatePolicy() {
+ var policy = PdpService.getPolicy(this.selectedPolicyId);
+ var pdpCopy = util.clone(this.pdp);
+ pdpCopy.security_pipeline = [policy];
+ PdpService.updatePdp(pdpCopy);
+ this.close();
+ },
+ close() {
+ this.$emit("close");
+ }
+ },
+ computed: {
+ policies() {
+ return PdpService.policies.filter(
+ el => !this.pdp.security_pipeline.includes(el)
+ );
+ }
+ }
+};
+</script>
diff --git a/dashboard/src/components/policy/AssignPerimeter.vue b/dashboard/src/components/policy/AssignPerimeter.vue
new file mode 100644
index 00000000..07814075
--- /dev/null
+++ b/dashboard/src/components/policy/AssignPerimeter.vue
@@ -0,0 +1,244 @@
+<template>
+ <div >
+ <div v-if="selectedData && loading" class="row padding-10">
+ <h4>Loading...</h4>
+ </div>
+ <div v-if="selectedData && !loading">
+ <div class="p-2">
+ <h3>Assign perimeters to {{ selectedData.name }}</h3>
+ <form-header
+ placeholder="Filter"
+ buttonText="Create Perimeter"
+ @click="creatingPerimeter = true"
+ v-model="filterPerimeter"
+ need-button
+ ></form-header>
+ </div>
+ <create-perimeter
+ v-if="creatingPerimeter"
+ :policy="policy"
+ :type="selectedDataType"
+ @close="creatingPerimeter = false"
+ @perimeterCreated="createPerimeter"
+ ></create-perimeter>
+ <div class="row mt-3" v-else>
+ <div class="col-sm">
+ <h4>
+ All perimeters
+ <i class="fa fa-question-circle" style="margin-left: 2%" v-if="assignPerimeterHelpStrings.allPerimeters" data-toggle="tooltip" :title="assignPerimeterHelpStrings.allPerimeters"></i>
+ </h4>
+ <div class="w-100 height-200 scroll list-group border">
+ <button
+ class="list-group-item"
+ v-for="perimeter in allPerimetersFiltered"
+ :title="perimeter.description"
+ :key="perimeter.id"
+ @click="addPerimeter(perimeter)"
+ >{{ perimeter.name }}</button>
+ </div>
+ <p>Click to add</p>
+ </div>
+ <div class="col-sm">
+ <h4>
+ Policy perimeters
+ <i class="fa fa-question-circle" style="margin-left: 2%" v-if="assignPerimeterHelpStrings.policyPerimeters" data-toggle="tooltip" :title="assignPerimeterHelpStrings.policyPerimeters"></i>
+ </h4>
+ <div class="w-100 height-200 scroll list-group list-group-flush border">
+ <div
+ @click="assign(perimeter)"
+ class="list-group-item"
+ :key="perimeter.id"
+ v-for="perimeter in perimetersFiltered"
+ >
+ <span :title="perimeter.description">{{ perimeter.name }}</span>
+ <button
+ type="button"
+ class="fa fa-trash pull-right btn-dark btn-sm"
+ @click.stop="removePerimeterFromPolicy(perimeter)"
+ title="Remove Perimeter"
+ ></button>
+ </div>
+ </div>
+ <p>Click to assign</p>
+ </div>
+ <div class="col-sm">
+ <h4>
+ Assigned perimeters
+ <i class="fa fa-question-circle" style="margin-left: 2%" v-if="assignPerimeterHelpStrings.assignedPerimeters" data-toggle="tooltip" :title="assignPerimeterHelpStrings.assignedPerimeters"></i>
+ </h4>
+ <div class="w-100 list-group border height-200 scroll">
+ <button
+ class="list-group-item"
+ :key="perimeter.id"
+ v-for="perimeter in assignmentsFiltered"
+ :title="perimeter.description"
+ @click="unassign(perimeter)"
+ >{{ perimeter.name }}</button>
+ </div>
+ <p>Click to unassign</p>
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
+
+<script>
+import PolicyService from "./../../services/Policy.service.js";
+import util from "./../../services/Util.service.js";
+import FormHeader from "./../FormHeader.vue";
+import CreatePerimeter from "./CreatePerimeter.vue";
+import helpstrings from "../../helpstrings";
+
+var categoryMap = {
+ subject: {
+ perimeterId: "subject_id"
+ },
+ object: {
+ perimeterId: "object_id"
+ },
+ action: {
+ perimeterId: "action_id"
+ }
+};
+
+export default {
+ props: {
+ dataToAssign: Object,
+ policy: Object
+ },
+ components: {
+ FormHeader,
+ CreatePerimeter
+ },
+ data() {
+ return {
+ selectedData: null,
+ selectedDataType: "",
+ loading: false,
+ perimeters: [],
+ allPerimeters: [],
+ assignments: [],
+ filterPerimeter: "",
+ creatingPerimeter: false,
+ assignPerimeterHelpStrings: {}
+ };
+ },
+ mounted() {
+ this.assignPerimeterHelpStrings = helpstrings.assignPerimeter;
+ },
+ computed: {
+ allPerimetersFiltered() {
+ return util.filterAndSortByName(this.allPerimeters, this.filterPerimeter);
+ },
+ perimetersFiltered() {
+ return util.filterAndSortByName(this.perimeters, this.filterPerimeter);
+ },
+ assignmentsFiltered() {
+ return util.filterAndSortByName(this.assignments, this.filterPerimeter);
+ }
+ },
+ watch: {
+ dataToAssign() {
+ this.selectedData = this.dataToAssign.selectedData;
+ this.selectedDataType = this.dataToAssign.selectedDataType;
+ if (this.selectedData) {
+ this.loadPerimeter();
+ }
+ }
+ },
+ methods: {
+ createPerimeter(perimeters) {
+ util.pushAll(this.perimeters, perimeters);
+ },
+ addPerimeter(perimeter) {
+ var self = this;
+ PolicyService.addPerimeterToPolicy(
+ self.selectedDataType,
+ self.policy,
+ perimeter
+ ).then(function() {
+ self.allPerimeters.splice(self.allPerimeters.indexOf(perimeter), 1);
+ self.perimeters.push(perimeter);
+ });
+ },
+ assign(perimeter) {
+ var self = this;
+ PolicyService.createAssignment(
+ self.selectedDataType,
+ self.policy,
+ perimeter,
+ self.selectedData
+ ).then(function() {
+ self.assignments.push(perimeter);
+ self.perimeters.splice(self.perimeters.indexOf(perimeter), 1);
+ });
+ },
+ unassign(perimeter) {
+ var self = this;
+ PolicyService.removeAssignment(
+ self.selectedDataType,
+ self.policy,
+ perimeter,
+ self.selectedData
+ ).then(function() {
+ self.perimeters.push(perimeter);
+ self.assignments.splice(self.assignments.indexOf(perimeter), 1);
+ });
+ },
+ removePerimeterFromPolicy(perimeter) {
+ if (
+ confirm(
+ "Are you sure to delete this Perimeter? (Associated assignments will be deleted too)"
+ )
+ ) {
+ var self = this;
+ PolicyService.removePerimeterFromPolicy(
+ self.selectedDataType,
+ self.policy,
+ perimeter
+ ).then(function() {
+ self.perimeters.splice(self.perimeters.indexOf(perimeter), 1);
+ perimeter.policy_list.splice(
+ perimeter.policy_list.indexOf(self.policy.id),
+ 1
+ );
+ if (perimeter.policy_list.length > 0) {
+ self.allPerimeters.push(perimeter);
+ }
+ });
+ }
+ },
+ loadPerimeter() {
+ var self = this;
+ self.loading = true;
+ self.perimeters = [];
+ self.allPerimeters = [];
+ self.assignments = [];
+
+ PolicyService.loadPerimetersAndAssignments(
+ self.selectedDataType,
+ self.policy
+ ).then(function(values) {
+ var category = categoryMap[self.selectedDataType];
+ self.loading = false;
+ self.perimeters = values.perimeters;
+ var index, perimeter;
+ for (index = 0; index < values.allPerimeters.length; index++) {
+ perimeter = values.allPerimeters[index];
+ if (perimeter.policy_list.indexOf(self.policy.id) < 0) {
+ self.allPerimeters.push(perimeter);
+ }
+ }
+ for (index = 0; index < values.assignments.length; index++) {
+ var assignment = values.assignments[index];
+ if (assignment.assignments.indexOf(self.selectedData.id) >= 0) {
+ perimeter = values.perimetersMap[assignment[category.perimeterId]];
+ self.assignments.push(perimeter);
+ self.perimeters.splice(self.perimeters.indexOf(perimeter), 1);
+ }
+ }
+ });
+ }
+ }
+};
+</script> \ No newline at end of file
diff --git a/dashboard/src/components/policy/CreateData.vue b/dashboard/src/components/policy/CreateData.vue
new file mode 100644
index 00000000..ca3c5183
--- /dev/null
+++ b/dashboard/src/components/policy/CreateData.vue
@@ -0,0 +1,75 @@
+<template>
+ <div class="list-group-item row">
+ <form>
+ <div class="form-group">
+ <label for="dataName">Name</label>
+ <input
+ type="text"
+ name="name"
+ v-model="dataCreate.name"
+ v-validate.initial="'alpha_dash|required|min:3'"
+ class="form-control"
+ id="dataName"
+ />
+ </div>
+ <div class="form-group">
+ <label for="dataDescription">Description</label>
+ <textarea
+ name="description"
+ v-model="dataCreate.description"
+ v-validate.initial="'required|min:3'"
+ class="form-control"
+ ></textarea>
+ </div>
+ <ul>
+ <li v-for="error in errors.all()" :key="error.id">{{ error }}</li>
+ </ul>
+ <button type="button" class="btn btn-secondary" @click="close()">Cancel</button>
+ <span>&nbsp;</span>
+ <button
+ type="button"
+ :disabled="errors.any()"
+ class="btn btn-primary"
+ @click="createData()"
+ >Create</button>
+ </form>
+ </div>
+</template>
+
+<script>
+import PolicyService from "./../../services/Policy.service.js";
+
+export default {
+ name: "createData",
+ props: {
+ policy: Object,
+ type: String,
+ category: Object,
+ },
+ data: function() {
+ return {
+ dataCreate: {
+ name: "",
+ description: ""
+ }
+ };
+ },
+ methods: {
+ createData() {
+ var self = this;
+ PolicyService.createData(
+ this.type,
+ this.policy,
+ this.category.id,
+ this.dataCreate
+ ).then(function(datas) {
+ self.$emit("dataCreated", datas[0]);
+ self.close();
+ });
+ },
+ close() {
+ this.$emit("close");
+ }
+ }
+};
+</script>
diff --git a/dashboard/src/components/policy/CreatePerimeter.vue b/dashboard/src/components/policy/CreatePerimeter.vue
new file mode 100644
index 00000000..b8a9d532
--- /dev/null
+++ b/dashboard/src/components/policy/CreatePerimeter.vue
@@ -0,0 +1,73 @@
+<template>
+ <div class="list-group-item row">
+ <form>
+ <div class="form-group">
+ <label for="perimeterName">Name</label>
+ <input
+ type="text"
+ name="name"
+ v-model="perimeterCreate.name"
+ v-validate.initial="'alpha_dash|required|min:3'"
+ class="form-control"
+ id="perimeterName"
+ />
+ </div>
+ <div class="form-group">
+ <label for="perimeterDescription">Description</label>
+ <textarea
+ name="description"
+ v-model="perimeterCreate.description"
+ v-validate.initial="'required|min:3'"
+ class="form-control"
+ ></textarea>
+ </div>
+ <ul>
+ <li v-for="error in errors.all()" :key="error.id">{{ error }}</li>
+ </ul>
+ <button type="button" class="btn btn-secondary" @click="close()">Cancel</button>
+ <span>&nbsp;</span>
+ <button
+ type="button"
+ :disabled="errors.any()"
+ class="btn btn-primary"
+ @click="createPerimeter()"
+ >Create</button>
+ </form>
+ </div>
+</template>
+
+<script>
+import PolicyService from "./../../services/Policy.service.js";
+
+export default {
+ name: "createPerimeter",
+ props: {
+ policy: Object,
+ type: String
+ },
+ data: function() {
+ return {
+ perimeterCreate: {
+ name: "",
+ description: ""
+ }
+ };
+ },
+ methods: {
+ createPerimeter() {
+ var self = this;
+ PolicyService.createPerimeter(
+ this.type,
+ this.policy,
+ this.perimeterCreate
+ ).then(function(perimeters) {
+ self.$emit("perimeterCreated", perimeters);
+ self.close();
+ });
+ },
+ close() {
+ this.$emit("close");
+ }
+ }
+};
+</script>
diff --git a/dashboard/src/components/policy/CreatePolicy.vue b/dashboard/src/components/policy/CreatePolicy.vue
new file mode 100644
index 00000000..b3e90c2d
--- /dev/null
+++ b/dashboard/src/components/policy/CreatePolicy.vue
@@ -0,0 +1,84 @@
+<template>
+ <div class="list-group-item row">
+ <form>
+ <div class="form-group">
+ <label for="policyName">Name</label>
+ <input
+ type="text"
+ name="name"
+ v-model="policyCreate.name"
+ v-validate.initial="'alpha_dash|required|min:3'"
+ class="form-control"
+ id="policyName"
+ />
+ </div>
+ <div class="form-group">
+ <label for="policyDescription">Description</label>
+ <textarea
+ name="description"
+ v-model="policyCreate.description"
+ v-validate.initial="'required|min:3'"
+ class="form-control"
+ ></textarea>
+ </div>
+ <div class="form-group">
+ <label for="policyGenre">Genre</label>
+ <select v-model="policyCreate.genre" class="form-control" id="policyGenre" v-validate.initial="'required'" name="genre">
+ <option>admin</option>
+ <option>authz</option>
+ </select>
+ </div>
+ <div class="form-group">
+ <label for="policyModel">Model</label>
+ <select v-model="policyCreate.model_id" class="form-control" id="policyModel" v-validate.initial="'required'" name="model">
+ <option v-for="model in models" :key="model.id" :value="model.id">{{ model.name }}</option>
+ </select>
+ </div>
+ <ul>
+ <li v-for="error in errors.all()" :key="error.id">{{ error }}</li>
+ </ul>
+ <button type="button" class="btn btn-secondary" @click="close()">Cancel</button>
+ <span>&nbsp;</span>
+ <button
+ type="button"
+ :disabled="errors.any()"
+ class="btn btn-primary"
+ @click="createPolicy()"
+ >Create</button>
+ </form>
+ </div>
+</template>
+
+<script>
+import PolicyService from "./../../services/Policy.service.js";
+import ModelService from "./../../services/Model.service.js";
+import util from "./../../services/Util.service.js";
+
+export default {
+ name: "createPolicy",
+ data: function() {
+ return {
+ policyCreate: {
+ name: "",
+ description: "",
+ genre: "",
+ model_id: ""
+ }
+ };
+ },
+ computed: {
+ models() {
+ return util.sortByName(ModelService.models);
+ }
+ },
+ methods: {
+ createPolicy() {
+ PolicyService.createPolicy(this.policyCreate);
+ this.close();
+ },
+ close() {
+ this.$emit("close");
+ }
+ }
+};
+</script>
diff --git a/dashboard/src/components/policy/CreateRule.vue b/dashboard/src/components/policy/CreateRule.vue
new file mode 100644
index 00000000..656fa39a
--- /dev/null
+++ b/dashboard/src/components/policy/CreateRule.vue
@@ -0,0 +1,213 @@
+<template>
+ <div class="list-group-item row">
+ <form v-if="!metaruleId">
+ <div class="form-group">
+ <label for="metarule">Select a Metarule:</label>
+ <select v-model="metaruleId" class="form-control" id="metarule" name="metarule">
+ <option
+ v-for="metarule in policy.model.meta_rules"
+ :key="metarule.id"
+ :value="metarule.id"
+ >{{ metarule.name }}</option>
+ </select>
+ </div>
+ <button type="button" class="btn btn-secondary" @click="close()">Cancel</button>
+ </form>
+ <form v-else>
+ <div
+ class="form-group"
+ v-for="(categoryWrapper, index) in ruleData"
+ :key="categoryWrapper.id"
+ >
+ <label
+ :for="categoryWrapper.category.name"
+ >{{ 'Select ' + categoryWrapper.type + ' data of ' + categoryWrapper.category.name + ' category' }}</label>
+ <select
+ v-model="ruleCreate.rule[index]"
+ class="form-control"
+ :id="categoryWrapper.category.name"
+ :name="categoryWrapper.category.name"
+ v-validate.initial="'required'"
+ >
+ <option
+ v-for="data in categoryWrapper.data"
+ :key="data.id"
+ :value="data.id"
+ >{{ data.name }}</option>
+ </select>
+ <create-data
+ class="m-3"
+ @close="creatingDataCategory = null"
+ @dataCreated="dataCreated(categoryWrapper, $event, index)"
+ :policy="policy"
+ :type="categoryWrapper.type"
+ :category="categoryWrapper.category"
+ v-if="creatingDataCategory == categoryWrapper.category"
+ ></create-data>
+ <button
+ v-else
+ type="button"
+ class="btn btn-primary mt-3"
+ @click="creatingDataCategory = categoryWrapper.category"
+ >Or Create one</button>
+ </div>
+ <div v-for='(attribute, index) in policy.attributes' :key="attribute.id" class="form-group">
+ <label for="ruleAttributes">Attribute {{attribute.id}}</label>
+ <select
+ v-model="attributes[index]"
+ class="form-control"
+ v-validate.initial="'required'"
+ >
+ <option
+ v-for="value in attribute.values"
+ :key="value"
+ :value="value"
+ >{{ value }}</option>
+ </select>
+ </div>
+ <div class="form-group">
+ <label for="ruleInstructions">Instructions</label>
+ <select
+ v-model="ruleCreate.instructions"
+ class="form-control"
+ v-validate.initial="'required'"
+ >
+ <option
+ v-for="data in instructions"
+ :key="data"
+ :value="data"
+ >{{ data }}</option>
+ </select>
+ </div>
+ <ul>
+ <li v-for="error in errors.all()" :key="error.id">{{ error }}</li>
+ </ul>
+ <button type="button" class="btn btn-secondary" @click="close()">Cancel</button>
+ <span>&nbsp;</span>
+ <button
+ type="button"
+ :disabled="errors.any()"
+ class="btn btn-primary"
+ @click="createRule()"
+ >Create</button>
+ </form>
+ </div>
+</template>
+
+<script>
+import PolicyService from "./../../services/Policy.service.js";
+import ModelService from "./../../services/Model.service.js";
+import CreateData from "./CreateData.vue";
+
+function addCategories(type, categories, data, mapArray, initArray) {
+ var dataFiltered = [];
+
+ for (var i = 0; i < categories.length; i++) {
+ var category = categories[i];
+ for (var j = 0; j < data.length; j++) {
+ var element = data[j];
+ if (element.category_id == category.id) {
+ dataFiltered.push(element);
+ }
+ }
+
+ mapArray.push({
+ id: category.id,
+ category: category,
+ data: dataFiltered,
+ type: type
+ });
+ initArray.push(dataFiltered.length > 0 ? dataFiltered[0].id : null);
+ }
+}
+
+
+export default {
+ name: "createRule",
+ props: {
+ policy: Object
+ },
+ components: {
+ CreateData
+ },
+ data: function() {
+ return {
+ metarule: null,
+ metaruleId: null,
+ creatingDataCategory: null,
+ ruleData: [],
+ ruleCreate: {
+ instructions: 'grant',
+ rule: [],
+ },
+ instructions: ['grant', 'deny'],
+ attributes: [],
+ attribute: '',
+ };
+ },
+ watch: {
+ policy() {
+ if (this.policy) {
+ this.metaruleId = null;
+ if (this.policy.model.meta_rules.length == 1) {
+ this.metaruleId = this.policy.model.meta_rules[0].id;
+ }
+ }
+ },
+ metaruleId() {
+ if (this.metaruleId) {
+ this.metarule = ModelService.getMetaRule(this.metaruleId);
+ if (this.metarule){
+ for (let i = 0; i < this.policy.attributes.length; i++){
+ let attrs = this.policy.attributes[i].default;
+ this.attributes.push(attrs);
+ }
+ }
+ addCategories(
+ "subject",
+ this.metarule.subject_categories,
+ this.policy.subjectData,
+ this.ruleData,
+ this.ruleCreate.rule
+ );
+ addCategories(
+ "object",
+ this.metarule.object_categories,
+ this.policy.objectData,
+ this.ruleData,
+ this.ruleCreate.rule
+ );
+ addCategories(
+ "action",
+ this.metarule.action_categories,
+ this.policy.actionData,
+ this.ruleData,
+ this.ruleCreate.rule
+ );
+ }
+ }
+ },
+ methods: {
+ createRule() {
+ this.ruleCreate.enabled = true;
+ const instruction = "[{\n\t\"decision\": " + "\"" + this.ruleCreate.instructions + "\"" + "\n}]";
+ this.ruleCreate.instructions = JSON.parse(instruction);
+ this.ruleCreate.meta_rule_id = this.metarule.id;
+ this.ruleCreate.policy_id = this.policy.id;
+ for (let i = 0; i < this.attributes.length; i++) {
+ this.ruleCreate.rule.push("attributes:" + this.attributes[i]);
+ }
+
+ PolicyService.addRuleToPolicy(this.policy, this.ruleCreate);
+ this.close();
+ },
+ dataCreated(wrapper, data, index) {
+ wrapper.data.push(data);
+ this.$set(this.ruleCreate.rule, index, data.id);
+ },
+ close() {
+ this.$emit("close");
+ }
+ }
+};
+</script>
diff --git a/dashboard/src/components/policy/DataList.vue b/dashboard/src/components/policy/DataList.vue
new file mode 100644
index 00000000..40f384f3
--- /dev/null
+++ b/dashboard/src/components/policy/DataList.vue
@@ -0,0 +1,16 @@
+<template>
+ <span>
+ <span v-for="(data, index) in list" :key="data.id">
+ <span v-if="index > 0">,</span>
+ <span>{{ data.name }}</span>
+ </span>
+ </span>
+</template>
+
+<script>
+export default {
+ props: {
+ list: Array
+ }
+}
+</script> \ No newline at end of file
diff --git a/dashboard/src/components/policy/FilterRules.vue b/dashboard/src/components/policy/FilterRules.vue
new file mode 100644
index 00000000..5d685952
--- /dev/null
+++ b/dashboard/src/components/policy/FilterRules.vue
@@ -0,0 +1,24 @@
+<template>
+ <div class="form-inline">
+ <input
+ type="search"
+ class="form-control filter col mr-auto"
+ placeholder="Filter rules"
+ v-bind:value="value"
+ v-on:input="$emit('input', $event.target.value)"
+ >
+ </div>
+</template>
+
+<script>
+ export default {
+ name: "FilterRules",
+ props: {
+ value: String
+ }
+ }
+</script>
+
+<style scoped>
+
+</style> \ No newline at end of file
diff --git a/dashboard/src/components/policy/Policy.vue b/dashboard/src/components/policy/Policy.vue
new file mode 100644
index 00000000..415f6b02
--- /dev/null
+++ b/dashboard/src/components/policy/Policy.vue
@@ -0,0 +1,186 @@
+<template>
+ <div>
+ <template v-if="edit">
+ <form>
+ <div class="form-group" >
+ <label for="policyName">Name</label>
+ <input
+ type="text"
+ name="name"
+ v-model="policyEdit.name"
+ v-validate="'alpha_dash|required|min:3'"
+ class="form-control"
+ id="policyName"
+ />
+ </div>
+ <div class="form-group">
+ <label for="policyDescription">Description</label>
+ <textarea
+ name="description"
+ v-model="policyEdit.description"
+ v-validate="'required|min:3'"
+ class="form-control"
+ ></textarea>
+ </div>
+ <div class="form-group">
+ <label for="policyGenre">Genre</label>
+ <select
+ v-model="policyEdit.genre"
+ class="form-control"
+ id="policyGenre"
+ v-validate.initial="'required'"
+ name="genre"
+ >
+ <option>admin</option>
+ <option>authz</option>
+ </select>
+ </div>
+ <ul>
+ <li v-for="error in errors.all()" :key="error.id">{{ error }}</li>
+ </ul>
+ <button type="button" class="btn btn-secondary" @click="edit = false">Cancel</button>
+ <span>&nbsp;</span>
+ <button
+ type="button"
+ :disabled="errors.any()"
+ class="btn btn-primary"
+ @click="updatePolicy()"
+ >Update</button>
+ </form>
+ </template>
+ <template v-else>
+ <h3 class="list-group-item-heading inline " >{{ policy.name }}</h3>
+ <div class="pull-right">
+ <button
+ type="button"
+ class="fa fa-trash btn-dark btn-sm"
+ title="Remove Policy"
+ @click="removePolicy()"
+ ></button>
+ <button
+ type="button"
+ class="fa fa-edit btn-dark btn-sm"
+ title="Edit Policy"
+ @click="updatingPolicy()"
+ ></button>
+ </div>
+ <p class="list-group-item-text">{{ policy.description }}</p>
+
+ <unused-data :policy="policy" v-if="showAlert" @close="allowAlert = false"></unused-data>
+
+ <details class="list-group-item-text">
+ <summary @click="populatePolicy()">
+ <h4 class="inline-block width-200">
+ Rules
+ <i class="fa fa-question-circle" style="margin-left: 2%" v-if="policyHelpStrings.rules" data-toggle="tooltip" :title="policyHelpStrings.rules"></i>
+ </h4>
+ <button
+ type="button"
+ class="fa fa-plus btn-dark btn-sm"
+ @click="creatingRule = true"
+ title="Add Rule"
+ ></button>
+ </summary>
+ <create-rule v-if="creatingRule" @close="creatingRule = false" :policy="policy"></create-rule>
+ <div class="list-group" v-else>
+ <filter-rules v-model="filter" ></filter-rules>
+ <br/>
+ <p v-if="!policy.rulesPopulated" class="list-group-item-text">Loading rules...</p>
+ <div v-else>
+ <rule
+ v-for="rule in filteredRules"
+ :key="rule.id"
+ :rule="rule"
+ :selected="selectedRule == rule"
+ :policy="policy"
+ @show="selectRule($event)"
+ ></rule>
+ </div>
+ </div>
+ </details>
+ </template>
+ <hr />
+ </div>
+</template>
+
+<script>
+import UnusedData from "./UnusedData.vue";
+import PolicyService from "./../../services/Policy.service.js";
+import util from "./../../services/Util.service.js";
+import Rule from "./Rule.vue";
+import CreateRule from "./CreateRule.vue";
+import FilterRules from "./FilterRules.vue";
+
+import Vue from "vue";
+import helpstrings from "../../helpstrings";
+
+var selectedRule = new Vue({data: {rule: null}});
+
+export default {
+ props: {
+ policy: Object
+ },
+ data() {
+ return {
+ filter: "",
+ edit: false,
+ creatingRule: false,
+ allowAlert: true,
+ policyEdit: {},
+ policyHelpStrings: {}
+ };
+ },
+ mounted() {
+ this.policyHelpStrings = helpstrings.policy;
+ },
+ computed: {
+ filteredRules() {
+ let filteredRules = PolicyService.filterByRules(this.policy.rules, this.filter);
+
+
+ return filteredRules;
+ },
+ selectedRule() {
+ return selectedRule.rule;
+ },
+ showAlert() {
+ return (
+ this.allowAlert &&
+ (this.policy.unusedSubjectData.length ||
+ this.policy.unusedObjectData.length ||
+ this.policy.unusedActionData.length)
+ );
+ },
+ },
+ components: {
+ UnusedData,
+ Rule,
+ CreateRule,
+ FilterRules
+ },
+ methods: {
+ populatePolicy() {
+ PolicyService.populatePolicy(this.policy);
+ },
+ removePolicy() {
+ if (
+ confirm(
+ "Are you sure to delete this Policy? (Associated perimeter, data an PDP will be deleted too)"
+ )
+ )
+ PolicyService.removePolicy(this.policy);
+ },
+ updatingPolicy() {
+ this.policyEdit = util.clone(this.policy);
+ this.edit = true;
+ },
+ updatePolicy() {
+ this.edit = false;
+ PolicyService.updatePolicy(this.policyEdit);
+ },
+ selectRule(rule) {
+ selectedRule.rule = rule;
+ }
+ }
+};
+</script> \ No newline at end of file
diff --git a/dashboard/src/components/policy/Rule.vue b/dashboard/src/components/policy/Rule.vue
new file mode 100644
index 00000000..c8889b86
--- /dev/null
+++ b/dashboard/src/components/policy/Rule.vue
@@ -0,0 +1,231 @@
+<template>
+ <div class="list-group-item">
+ <details >
+ <summary class="list-group-item-heading" :style="ruleIsGrant ? 'background-color: var(--green)' : 'background-color: Tomato'" >
+
+ <b>Rule: </b>
+ <data-list :list="rule.subjectData"></data-list> |
+ <data-list :list="rule.actionData"></data-list> |
+ <data-list :list="rule.objectData"></data-list>
+ <span v-if="rule.attributeData.length"> | <data-list :list="rule.attributeData"></data-list></span>
+ <div class="pull-right" style="background-color: white">
+ <button
+ type="button"
+ class="fa fa-trash pull-right btn-dark btn-sm"
+ @click="removeRuleFromPolicy()"
+ title="Remove Rule"
+ ></button>
+ <button type="button"
+ :class="ruleIsGrant ? buttonRuleIsGrant : buttonRuleIsDeny"
+ @click="changeRuleDecision()"
+ title="Change Decision"
+ ></button>
+ </div>
+ </summary>
+ <div >
+ <p class="list-group-item-text"></p>
+ <table class="table">
+ <thead>
+ <tr>
+ <th>
+ <span>
+ Subjects data
+ <i class="fa fa-question-circle" v-if="ruleHelpStrings.subjectsData" data-toggle="tooltip" :title="ruleHelpStrings.subjectsData"></i>
+ </span>
+ </th>
+ <th>
+ <span>
+ Objects data
+ <i class="fa fa-question-circle" v-if="ruleHelpStrings.objectsData" data-toggle="tooltip" :title="ruleHelpStrings.objectsData"></i>
+ </span>
+ </th>
+ <th>
+ <span>
+ Actions data
+ <i class="fa fa-question-circle" v-if="ruleHelpStrings.actionsData" data-toggle="tooltip" :title="ruleHelpStrings.actionsData"></i>
+ </span>
+ </th>
+ <th v-if="rule.attributeData.length">
+ <span>
+ Attributes
+ <i class="fa fa-question-circle" v-if="ruleHelpStrings.attributes" data-toggle="tooltip" :title="ruleHelpStrings.attributes"></i>
+ </span>
+ </th>
+ <th>
+ <span>
+ Instructions
+ <i class="fa fa-question-circle" v-if="ruleHelpStrings.instructions" data-toggle="tooltip" :title="ruleHelpStrings.instructions"></i>
+ </span>
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>
+ <p
+ v-for="data in rule.subjectData"
+ :key="data.id"
+ :class="{'selected-data': selectedData == data}"
+ >
+ <span :title="data.description">{{ data.name }}</span>
+ <button
+ v-if="selectedData != data"
+ type="button"
+ class="fa fa-exchange pull-right btn-dark btn-sm"
+ @click="assignData('subject', data)"
+ title="Assign to perimeters"
+ ></button>
+ <button
+ v-if="selectedData == data"
+ type="button"
+ class="fa fa-times pull-right btn-dark btn-sm"
+ @click="unassignData()"
+ title="Close"
+ ></button>
+ </p>
+ </td>
+ <td>
+ <p
+ v-for="data in rule.objectData"
+ :key="data.id"
+ :class="{'selected-data': selectedData == data}"
+ >
+ <span :title="data.description">{{ data.name }}</span>
+ <button
+ v-if="selectedData != data"
+ type="button"
+ class="fa fa-exchange pull-right btn-dark btn-sm"
+ @click="assignData('object', data)"
+ title="Assign to perimeters"
+ ></button>
+ <button
+ v-if="selectedData == data"
+ type="button"
+ class="fa fa-times pull-right btn-dark btn-sm"
+ @click="unassignData()"
+ title="Close"
+ ></button>
+ </p>
+ </td>
+ <td>
+ <p
+ v-for="data in rule.actionData"
+ :key="data.id"
+ :class="{'selected-data': selectedData == data}"
+ >
+ <span :title="data.description">{{ data.name }}</span>
+ <button
+ v-if="selectedData != data"
+ type="button"
+ class="fa fa-exchange pull-right btn-dark btn-sm"
+ @click="assignData('action', data)"
+ title="Assign to perimeters"
+ ></button>
+ <button
+ v-if="selectedData == data"
+ type="button"
+ class="fa fa-times pull-right btn-dark btn-sm"
+ @click="unassignData()"
+ title="Close"
+ ></button>
+ </p>
+ </td>
+ <td v-if="rule.attributeData.length">
+ <p
+ v-for="data in rule.attributeData"
+ :key="data.id"
+ :class="{'selected-data': selectedData == data}"
+ >
+ <span :title="data.description">{{data.id}} : {{ data.name }}</span>
+ </p>
+ </td>
+ <td>
+ <pre><code>{{rule.instructions}}</code></pre>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <assign-perimeter :policy="policy" :dataToAssign="dataToAssign"></assign-perimeter>
+
+ </div>
+ </details>
+ </div>
+</template>
+
+<script>
+ import AttributeService from "./../../services/Attribute.service"
+ import PolicyService from "./../../services/Policy.service.js";
+ import DataList from "./DataList.vue";
+ import AssignPerimeter from "./AssignPerimeter.vue";
+ import helpstrings from "../../helpstrings";
+
+ export default {
+ props: {
+ rule: Object,
+ selected: Boolean,
+ policy: Object
+ },
+ data() {
+ return {
+ dataToAssign: {
+ selectedData: null,
+ selectedDataType: "",
+ },
+ selectedData: null,
+ ruleIsGrant: false,
+ buttonRuleIsGrant: 'fa fa-toggle-on pull-right btn-dark btn-sm',
+ buttonRuleIsDeny: 'fa fa-toggle-off pull-right btn-dark btn-sm',
+ ruleHelpStrings: {}
+
+ }
+ },
+ components: {
+ DataList,
+ AssignPerimeter,
+ },
+ created() {
+ AttributeService.initialize();
+ },
+ mounted() {
+ this.ruleIsGrant = ("grant".localeCompare(this.rule.instructions[0].decision) == 0);
+ this.ruleHelpStrings = helpstrings.rule;
+ },
+ watch: {
+ selected() {
+ if (!this.selected)
+ this.unassignData();
+ }
+ },
+ methods: {
+ changeRuleDecision() {
+ var decision = ("grant".localeCompare(this.rule.instructions[0].decision) == 0) ? "deny" : "grant";
+ PolicyService.updateRule(this.policy, this.rule, decision).then(res => {
+ this.ruleIsGrant = !this.ruleIsGrant;
+ this.rule.instructions = res;
+ });
+
+ },
+ showRule() {
+ this.$emit("show", this.selected ? null : this.rule);
+ },
+ removeRuleFromPolicy() {
+ if (confirm("Are you sure to delete this Rule?"))
+ PolicyService.removeRuleFromPolicy(this.policy, this.rule);
+ },
+ assignData(type, data) {
+ this.dataToAssign = {
+ selectedData: data,
+ selectedDataType: type,
+ };
+ this.selectedData = data;
+ },
+ unassignData() {
+ this.assignData("", null);
+ }
+ }
+ };
+</script>
+
+<style scoped>
+
+</style> \ No newline at end of file
diff --git a/dashboard/src/components/policy/UnusedData.vue b/dashboard/src/components/policy/UnusedData.vue
new file mode 100644
index 00000000..48b8b642
--- /dev/null
+++ b/dashboard/src/components/policy/UnusedData.vue
@@ -0,0 +1,89 @@
+<template>
+ <div>
+ <div
+ v-if="policy.unusedSubjectData.length
+ || policy.unusedObjectData.length
+ || policy.unusedActionData.length"
+ class="alert alert-dismissable alert-warning"
+ >
+ <button type="button" class="close" data-dismiss="alert" @click="showUnused = false; $emit('close')">×</button>
+ <h4>Warning!</h4>
+ <p>
+ Some data are unused, please check them and delete them if necessary.
+ <a
+ href
+ @click.prevent="showUnused = true"
+ v-show="!showUnused"
+ >Show unused data</a>
+ <a href @click.prevent="showUnused = false" v-show="showUnused">Hide unused data</a>
+ </p>
+ </div>
+
+ <div v-if="showUnused" class="row overflow-hidden mb-3">
+ <div class="list-group col" v-if="policy.unusedSubjectData.length">
+ <h3 class="list-group-item active">Unused Subject data</h3>
+ <div v-for="subject in policy.unusedSubjectData" :key="subject.id" class="list-group-item">
+ <h4 class="list-group-item-heading inline" :title="subject.description">{{ subject.name }}</h4>
+ <button
+ type="button"
+ class="fa fa-trash pull-right btn-dark btn-sm"
+ @click="removeData('subject', policy, subject)"
+ title="Remove Subject data"
+ ></button>
+ </div>
+ </div>
+
+ <div class="list-group col" v-if="policy.unusedObjectData.length">
+ <h3 class="list-group-item active">Unused Object data</h3>
+ <div v-for="object in policy.unusedObjectData" :key="object.id" class="list-group-item">
+ <h4 class="list-group-item-heading inline" :title="object.description">{{ object.name }}</h4>
+ <button
+ type="button"
+ class="fa fa-trash pull-right btn-dark btn-sm"
+ @click="removeData('object', policy, object)"
+ title="Remove Object data"
+ ></button>
+ </div>
+ </div>
+
+ <div class="list-group col" v-if="policy.unusedActionData.length">
+ <h3 class="list-group-item active">Unused Action data</h3>
+ <div v-for="action in policy.unusedActionData" :key="action.id" class="list-group-item">
+ <h4 class="list-group-item-heading inline" :title="action.description">{{ action.name }}</h4>
+ <button
+ type="button"
+ class="fa fa-trash pull-right btn-dark btn-sm"
+ @click="removeData('action', policy, action)"
+ title="Remove Action data"
+ ></button>
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
+
+<script>
+import PolicyService from "./../../services/Policy.service.js";
+
+
+export default {
+ props: {
+ policy: Object
+ },
+ data() {
+ return {
+ showUnused: false
+ };
+ },
+ methods: {
+ removeData(type, policy, data) {
+ if (
+ confirm(
+ "Are you sure to delete this Data? (Associated assignments and rules will be deleted too)"
+ )
+ )
+ PolicyService.removeData(type, policy, data);
+ }
+ }
+};
+</script> \ No newline at end of file
diff --git a/dashboard/src/config.js b/dashboard/src/config.js
new file mode 100644
index 00000000..2849ec7d
--- /dev/null
+++ b/dashboard/src/config.js
@@ -0,0 +1,4 @@
+export default {
+ // Moon Manager URL
+ host: 'http://127.0.0.1:8000',
+} \ No newline at end of file
diff --git a/dashboard/src/helpstrings.js b/dashboard/src/helpstrings.js
new file mode 100644
index 00000000..63751e78
--- /dev/null
+++ b/dashboard/src/helpstrings.js
@@ -0,0 +1,25 @@
+export default {
+ model: {
+ metarule: 'model.metarule'
+ },
+ metarule: {
+ subject: 'metarule.subject',
+ object: 'meterule.object',
+ action: 'meterule.action'
+ },
+ policy:{
+ rules: 'policy.rules'
+ },
+ assignPerimeter:{
+ allPerimeters: 'assignPerimeter.allPerimeters',
+ policyPerimeters: 'assignPerimeter.policyPerimeters',
+ assignedPerimeters: 'assignPerimeter.assignedPerimeters'
+ },
+ rule:{
+ subjectsData: 'rule.subjectsData',
+ objectsData: 'rule.objectsData',
+ actionsData: 'rule.actionsData',
+ attributes: 'rule.attributes',
+ instructions: 'rule.instructions',
+ }
+} \ No newline at end of file
diff --git a/dashboard/src/main.js b/dashboard/src/main.js
new file mode 100644
index 00000000..e18e3ebf
--- /dev/null
+++ b/dashboard/src/main.js
@@ -0,0 +1,58 @@
+import Vue from 'vue'
+import App from './App.vue'
+import router from './router'
+import VueResource from 'vue-resource'
+import VeeValidate from 'vee-validate'
+import Toasted from 'vue-toasted'
+
+Vue.config.productionTip = false
+
+Vue.use(VueResource)
+Vue.use(VeeValidate)
+Vue.use(Toasted)
+
+Vue.http.interceptors.push(function () {
+ return function (response) {
+ if (response.status == 401) {
+ router.push('auth');
+ } else if (response.status == 0) {
+ router.push('error');
+ }
+ }
+});
+
+var authKey = localStorage.getItem("auth-key")
+if (authKey) {
+ Vue.http.headers.common['x-api-key'] = authKey;
+} else {
+ router.push('auth');
+}
+
+Vue.toasted.register('toast',
+ (payload) => {
+ return `
+ <div class="toast show" role="alert" aria-live="assertive" aria-atomic="true">
+ <div class="toast-header">
+ <span class="badge badge-${payload.type}">&nbsp;</span>
+ <strong class="ml-2">${payload.title}</strong>
+ </div>
+ <div class="toast-body">
+ ${payload.message}
+ </div>
+ </div>
+ `;
+ },
+
+ {
+ className: "toast-background",
+ position: 'top-center',
+ duration: 3000
+ })
+
+
+new Vue({
+ router,
+ render: h => h(App)
+}).$mount('#app')
+
+
diff --git a/dashboard/src/router.js b/dashboard/src/router.js
new file mode 100644
index 00000000..f05d9515
--- /dev/null
+++ b/dashboard/src/router.js
@@ -0,0 +1,56 @@
+import Vue from 'vue'
+import Router from 'vue-router'
+import Models from './views/Models.vue'
+import Auth from './views/Auth.vue'
+import Rules from './views/Rules.vue'
+import PDP from './views/Pdps.vue'
+import Admin from './views/Admin.vue'
+import ErrorPage from './views/Error.vue'
+import Assignments from "./views/Assignments";
+
+Vue.use(Router)
+
+export default new Router({
+ routes: [
+ {
+ path: '/auth',
+ name: 'auth',
+ component: Auth
+ },
+ {
+ path: '/',
+ redirect: { name: 'models' }
+ },
+ {
+ path: '/models',
+ name: 'models',
+ component: Models
+ },
+ {
+ path: '/rules',
+ name: 'rules',
+ component: Rules
+ },
+ {
+ path: '/assignments',
+ name: 'assignments',
+ component: Assignments
+ },
+ {
+ path: '/pdp',
+ name: 'pdp',
+ component: PDP
+ },
+ {
+ path: '/admin',
+ name: 'admin',
+ component: Admin
+ },
+ {
+ path: '/error',
+ name: 'error',
+ component: ErrorPage
+ },
+
+ ]
+})
diff --git a/dashboard/src/services/Attribute.service.js b/dashboard/src/services/Attribute.service.js
new file mode 100644
index 00000000..a00da8f1
--- /dev/null
+++ b/dashboard/src/services/Attribute.service.js
@@ -0,0 +1,46 @@
+import Vue from 'vue'
+import config from '../config.js'
+import util from './Util.service'
+
+var host = config.host;
+
+var attributeResource;
+
+var attributesMap = {};
+var attributes = [];
+
+function loadAttributes(){
+ attributeResource = Vue.resource(host + '/attributes{/id}', {});
+
+ attributeResource.query().then(res => {
+ createAttributes(res.body);
+ }, util.displayErrorFunction('Unable to load attributes'));
+}
+
+function createAttributes(attributesData){
+ attributes.splice(0, attributes.length);
+ util.cleanObject(attributesMap);
+ util.createInternal(attributesData.attributes, attributes, attributesMap);
+}
+
+function getAttribute(id){
+ return attributesMap[id];
+}
+
+function getAttributeId(name){
+ for (let i = 0; i < attributes.length; i++){
+ let attr = attributes[i];
+ for (let j = 0; j < attr.values.length; j++){
+ let value = attr.values[j];
+ if (value === name){
+ return attr.id;
+ }
+ }
+ }
+}
+
+export default {
+ initialize: loadAttributes,
+ getAttribute: getAttribute,
+ getAttributeId: getAttributeId
+} \ No newline at end of file
diff --git a/dashboard/src/services/Import.service.js b/dashboard/src/services/Import.service.js
new file mode 100644
index 00000000..f2cc77ff
--- /dev/null
+++ b/dashboard/src/services/Import.service.js
@@ -0,0 +1,16 @@
+import Vue from 'vue'
+import util from './Util.service.js'
+import config from '../config.js'
+
+var host = config.host;
+
+export default {
+ importData: async function importData(data) {
+ var importResource = Vue.resource(host + '/import/', {});
+
+ return importResource.save(null, data).then(success, util.displayErrorFunction('Unable to import data'));
+ function success() {
+ util.displaySuccess('Data imported');
+ }
+ }
+} \ No newline at end of file
diff --git a/dashboard/src/services/Model.service.js b/dashboard/src/services/Model.service.js
new file mode 100644
index 00000000..b9b96743
--- /dev/null
+++ b/dashboard/src/services/Model.service.js
@@ -0,0 +1,351 @@
+import Vue from 'vue'
+import util from './Util.service.js'
+import config from '../config.js'
+
+var host = config.host;
+
+var modelResource;
+var metaRuleResource;
+var subjectCategoryResource;
+var objectCategoryResource;
+var actionCategoryResource;
+var attributesResource;
+
+var modelsMap = {};
+var metaRulesMap = {};
+var subjectCategoriesMap = {};
+var objectCategoriesMap = {};
+var actionCategoriesMap = {};
+var attributesMap = {};
+var models = [];
+var metaRules = [];
+var orphanMetaRules = [];
+var subjectCategories = [];
+var objectCategories = [];
+var actionCategories = [];
+var attributes = [];
+var orphanSubjectCategories = [];
+var orphanObjectCategories = [];
+var orphanActionCategories = [];
+
+var categoryMap = {
+ 'subject': {
+ resource: subjectCategoryResource,
+ map: subjectCategoriesMap,
+ list: subjectCategories,
+ listName: 'subject_categories'
+ },
+ 'object': {
+ resource: objectCategoryResource,
+ map: objectCategoriesMap,
+ list: objectCategories,
+ listName: 'object_categories'
+ },
+ 'action': {
+ resource: actionCategoryResource,
+ map: actionCategoriesMap,
+ list: actionCategories,
+ listName: 'action_categories'
+ },
+ 'attribute' : {
+ resource: attributesResource,
+ map: attributesMap,
+ list: attributes,
+ listName: 'attributes'
+ }
+}
+
+
+function loadModels() {
+ modelResource = Vue.resource(host + '/models{/id}', {}, {patch: {method: 'PATCH'}});
+ metaRuleResource = Vue.resource(host + '/meta_rules{/id}', {}, {patch: {method: 'PATCH'}});
+ categoryMap['subject'].resource = subjectCategoryResource = Vue.resource(host + '/subject_categories{/id}');
+ categoryMap['object'].resource = objectCategoryResource = Vue.resource(host + '/object_categories{/id}');
+ categoryMap['action'].resource = actionCategoryResource = Vue.resource(host + '/action_categories{/id}');
+ categoryMap['attribute'].resource = attributesResource = Vue.resource(host + '/attributes{/id}');
+ var queries = [
+ modelResource.query(),
+ metaRuleResource.query(),
+ subjectCategoryResource.query(),
+ objectCategoryResource.query(),
+ actionCategoryResource.query(),
+ attributesResource.query()
+ ]
+
+ var result = Promise.all(queries).then(function (result) {
+ createModels(result[0].body, result[1].body, result[2].body, result[3].body, result[4].body, result[5].body)
+ })
+
+ return result;
+}
+
+function createModels(modelsData, metarulesData, subjectCategoriesData, objectCategoriesData, actionCategoriesData, attributesData) {
+ util.cleanObject(modelsMap);
+ util.cleanObject(metaRulesMap);
+ util.cleanObject(subjectCategoriesMap);
+ util.cleanObject(objectCategoriesMap);
+ util.cleanObject(actionCategoriesMap);
+ util.cleanObject(attributesMap);
+ models.splice(0, models.length);
+ metaRules.splice(0, metaRules.length);
+ subjectCategories.splice(0, subjectCategories.length);
+ objectCategories.splice(0, objectCategories.length);
+ actionCategories.splice(0, actionCategories.length);
+ attributes.splice(0, attributes.length);
+ if (subjectCategoriesData.subject_categories) createCategoryInternal('subject', subjectCategoriesData.subject_categories);
+ if (objectCategoriesData.object_categories) createCategoryInternal('object', objectCategoriesData.object_categories);
+ if (actionCategoriesData.action_categories) createCategoryInternal('action', actionCategoriesData.action_categories);
+ if (attributesData.attributes) createCategoryInternal('attribute', attributesData.attributes);
+ if (metarulesData.meta_rules) createMetaRuleInternal(metarulesData.meta_rules);
+ if (modelsData.models) createModelInternal(modelsData.models);
+
+ updateOrphan();
+}
+
+function mapModel(model) {
+ util.mapIdToItem(model.meta_rules, metaRulesMap);
+}
+
+function createModelInternal(data) {
+ return util.createInternal(data, models, modelsMap, mapModel);
+}
+
+function updateModelInternal(data) {
+ return util.updateInternal(data, modelsMap, mapModel);
+}
+
+function removeModelInternal(id) {
+ return util.removeInternal(id, models, modelsMap);
+}
+
+function mapIdToItemWithAttributes(categories, categoriesMap, attributes, attributesMap) {
+ let categoriesToRemove = []
+ if (categories) {
+ var index2 = 0;
+ for (var index = 0; index < categories.length; index++) {
+ var id = categories[index];
+ if (categoriesMap[id])
+ categories[index] = categoriesMap[id];
+ else {
+ if(id.includes('attributes:')){
+ const newId = id.split(':')[1];
+ if (attributesMap[newId]){
+ attributes[index2++] = attributesMap[newId];
+ }
+ categoriesToRemove.push(index)
+ }
+ }
+ }
+ for (let i = categoriesToRemove.length - 1; i >= 0; i--){
+ categories.splice(categoriesToRemove[i], 1);
+ }
+ }
+}
+
+function cleanCategoryByRemovingAttributes(categories, attributesMap){
+ let categoriesToRemove = []
+ if (categories) {
+ for (let index = 0; index < categories.length; index++){
+ var category = categories[index];
+ if (attributesMap[category.name]){
+ categoriesToRemove.push(index)
+ }
+ }
+ for (let i = categoriesToRemove.length - 1; i >= 0; i--){
+ categories.splice(categoriesToRemove[i], 1);
+ }
+ }
+}
+
+function mapMetaRule(metaRule) {
+ metaRule.subjectAttributes = [];
+ metaRule.objectAttributes = [];
+ metaRule.actionAttributes = [];
+ mapIdToItemWithAttributes(metaRule.subject_categories, subjectCategoriesMap, metaRule.subjectAttributes, attributesMap);
+ mapIdToItemWithAttributes(metaRule.object_categories, objectCategoriesMap, metaRule.objectAttributes,attributesMap);
+ mapIdToItemWithAttributes(metaRule.action_categories, actionCategoriesMap, metaRule.actionAttributes, attributesMap);
+ cleanCategoryByRemovingAttributes(subjectCategories, attributesMap);
+ cleanCategoryByRemovingAttributes(objectCategories, attributesMap);
+ cleanCategoryByRemovingAttributes(actionCategories, attributesMap);
+}
+
+function createMetaRuleInternal(data) {
+ return util.createInternal(data, metaRules, metaRulesMap, mapMetaRule);
+}
+
+function updateMetaRuleInternal(data) {
+ return util.updateInternal(data, metaRulesMap, mapMetaRule);
+}
+
+function removeMetaRuleInternal(id) {
+ return util.removeInternal(id, metaRules, metaRulesMap);
+}
+
+function createCategoryInternal(type, data) {
+ var categoryValue = categoryMap[type];
+
+ return util.createInternal(data, categoryValue.list, categoryValue.map)
+}
+
+function removeCategoryInternal(type, id) {
+ var categoryValue = categoryMap[type];
+ return util.removeInternal(id, categoryValue.list, categoryValue.map);
+}
+
+function updateOrphan() {
+ updateOrphanInternal(metaRules, orphanMetaRules, models, "meta_rules");
+ updateOrphanInternal(subjectCategories, orphanSubjectCategories, metaRules, "subject_categories");
+ updateOrphanInternal(objectCategories, orphanObjectCategories, metaRules, "object_categories");
+ updateOrphanInternal(actionCategories, orphanActionCategories, metaRules, "action_categories");
+}
+
+function updateOrphanInternal(list, orphanList, parentList, childListName) {
+ orphanList.splice(0, orphanList.length);
+ util.pushAll(orphanList, list);
+ for (var i = 0; i < parentList.length; i++) {
+ var parent = parentList[i];
+ var children = parent[childListName];
+ if (children) {
+ for (var j = 0; j < children.length; j++) {
+ var child = children[j];
+ if (child){
+ var notOrphanIndex = util.indexOf(orphanList, "id", child.id);
+ if (notOrphanIndex >= 0) {
+ orphanList.splice(notOrphanIndex, 1);
+ }
+ else{
+ for (var k = 0; k < attributes.length; k++){
+ const attr = attributes[k];
+ notOrphanIndex = util.indexOf(orphanList, "name", attr.id);
+ if (notOrphanIndex >= 0) {
+ orphanList.splice(notOrphanIndex, 1);
+ }
+ }
+ }
+
+ }
+ }
+ }
+ }
+}
+
+function getAttributesForModelId(modelId) {
+ let model = modelsMap[modelId];
+ let attrs = []
+
+ for (let i = 0; i < model.meta_rules.length; i++){
+ let metaRule = model.meta_rules[i];
+ let j;
+ for (j = 0; j < metaRule.subjectAttributes.length; j++){
+ attrs.push(metaRule.subjectAttributes[j])
+ }
+ for (j = 0; j < metaRule.objectAttributes.length; j++){
+ attrs.push(metaRule.objectAttributes[j])
+ }
+ for (j = 0; j < metaRule.actionAttributes.length; j++){
+ attrs.push(metaRule.actionAttributes[j])
+ }
+ }
+ return attrs;
+}
+
+export default {
+ initialize: loadModels,
+ createModels: createModels,
+ models: models,
+ metaRules: metaRules,
+ orphanMetaRules: orphanMetaRules,
+ orphanSubjectCategories: orphanSubjectCategories,
+ orphanObjectCategories: orphanObjectCategories,
+ orphanActionCategories: orphanActionCategories,
+ subjectCategories: subjectCategories,
+ objectCategories: objectCategories,
+ actionCategories: actionCategories,
+ getModel: function getModel(id) {
+ return modelsMap[id];
+ },
+ getAttributesForModelId: getAttributesForModelId,
+ createModel: function createModel(model) {
+ model.meta_rules = [];
+ modelResource.save(null, model).then(success, util.displayErrorFunction('Unable to create model'));
+
+ function success(data) {
+ createModelInternal(data.body.models);
+ util.displaySuccess('Model created');
+ }
+ },
+ removeModel: function removeModel(model) {
+ modelResource.remove({ id: model.id }).then(success, util.displayErrorFunction('Unable to remove model'));
+
+ function success() {
+ removeModelInternal(model.id);
+ updateOrphan();
+ util.displaySuccess('Model removed');
+ }
+ },
+ updateModel: function updateModel(model) {
+ util.mapItemToId(model.meta_rules)
+ modelResource.patch({ id: model.id }, model).then(success, util.displayErrorFunction('Unable to update model'));
+
+ function success(data) {
+ updateModelInternal(data.body.models);
+
+ updateOrphan();
+ util.displaySuccess('Model updated');
+ }
+ },
+ getMetaRule: function getMetaRule(id) {
+ return metaRulesMap[id];
+ },
+ createMetaRule: function createMetaRule(metaRule) {
+ metaRule.subject_categories = [];
+ metaRule.object_categories = [];
+ metaRule.action_categories = [];
+
+ return metaRuleResource.save(null, metaRule).then(function (data) {
+ util.displaySuccess('Meta Rule created');
+ return createMetaRuleInternal(data.body.meta_rules)[0];
+ }, util.displayErrorFunction('Unable to create meta rule'))
+ },
+ updateMetaRule: function updateMetaRule(metaRule) {
+ util.mapItemToId(metaRule.subject_categories);
+ util.mapItemToId(metaRule.object_categories);
+ util.mapItemToId(metaRule.action_categories);
+ metaRuleResource.patch({ id: metaRule.id }, metaRule).then(success, util.displayErrorFunction('Unable to update meta rule'));
+
+ function success(data) {
+ updateMetaRuleInternal(data.body.meta_rules);
+ updateOrphan();
+ util.displaySuccess('Meta Rule updated');
+ }
+ },
+ removeMetaRule: function removeMetaRule(metaRule) {
+ metaRuleResource.remove({ id: metaRule.id }).then(success, util.displayErrorFunction('Unable to remove meta rule'));
+
+ function success() {
+ removeMetaRuleInternal(metaRule.id);
+ updateOrphan();
+ util.displaySuccess('Meta Rule removed');
+ }
+ },
+ getCategory: function getCategory(type, id) {
+ return categoryMap[type].map[id];
+ },
+ createCategory: function createCategory(type, category) {
+ var categoryValue = categoryMap[type];
+ return categoryValue.resource.save(null, category).then(function (data) {
+ util.displaySuccess('Category created');
+ return createCategoryInternal(type, data.body[categoryValue.listName])[0];
+ }, util.displayErrorFunction('Unable to create category'))
+ },
+ removeCategory: function removeCategory(type, category) {
+ var categoryValue = categoryMap[type];
+ categoryValue.resource.remove({ id: category.id }).then(success, util.displayErrorFunction('Unable to remove category'));
+
+ function success() {
+ removeCategoryInternal(type, category.id);
+ updateOrphan();
+ util.displaySuccess('Category removed');
+ }
+ },
+} \ No newline at end of file
diff --git a/dashboard/src/services/Pdp.service.js b/dashboard/src/services/Pdp.service.js
new file mode 100644
index 00000000..e0c286a0
--- /dev/null
+++ b/dashboard/src/services/Pdp.service.js
@@ -0,0 +1,92 @@
+import Vue from 'vue'
+import util from './Util.service.js'
+import config from '../config.js'
+
+var host = config.host;
+
+var pdpResource;
+var policyResource;
+
+var pdpsMap = {};
+var pdps = [];
+var policiesMap = {};
+var policies = [];
+
+function loadPdps() {
+ pdpResource = Vue.resource(host + '/pdp{/id}', {}, {patch: {method: 'PATCH'}});
+ policyResource = Vue.resource(host + '/policies{/id}', {});
+
+ var queries = [
+ pdpResource.query(),
+ policyResource.query(),
+ ]
+ Promise.all(queries).then(function (result) {
+ createPdps(result[0].body, result[1].body)
+ })
+
+}
+
+function createPdps(pdpsData, policiesData) {
+ pdps.splice(0, pdps.length);
+ policies.splice(0, policies.length);
+ util.cleanObject(pdpsMap);
+ util.cleanObject(policiesMap);
+
+ util.createInternal(policiesData.policies, policies, policiesMap);
+ createPdpInternal(pdpsData.pdps);
+}
+
+function mapPdp(pdp) {
+ util.mapIdToItem(pdp.security_pipeline, policiesMap);
+ pdp.project = pdp.vim_project_id;
+}
+
+function createPdpInternal(data) {
+ return util.createInternal(data, pdps, pdpsMap, mapPdp);
+}
+
+function updatePdpInternal(data) {
+ return util.updateInternal(data, pdpsMap, mapPdp);
+}
+
+function removePdpInternal(id) {
+ return util.removeInternal(id, pdps, pdpsMap);
+}
+
+export default {
+ initialize: loadPdps,
+ createPdps: createPdps,
+ pdps: pdps,
+ policies: policies,
+ createPdp: function createPdp(pdp) {
+ pdpResource.save(null, pdp).then(success, util.displayErrorFunction('Unable to create PDP'));
+
+ function success(data) {
+ createPdpInternal(data.body.pdps);
+ util.displaySuccess('PDP created');
+ }
+ },
+ removePdp: function removePdp(pdp) {
+ pdpResource.remove({ id: pdp.id }).then(success, util.displayErrorFunction('Unable to remove PDP'));
+
+ function success() {
+ removePdpInternal(pdp.id);
+ util.displaySuccess('PDP removed');
+ }
+ },
+ updatePdp: function updatePdp(pdp) {
+ util.mapItemToId(pdp.security_pipeline);
+ pdp.vim_project_id = pdp.project;
+ pdpResource.patch({ id: pdp.id }, pdp).then(success, util.displayErrorFunction('Unable to update PDP'));
+
+ function success(data) {
+ updatePdpInternal(data.body.pdps)
+ util.displaySuccess('PDP updated');
+ }
+ },
+ getPolicy: function getPolicy(id) {
+ return policiesMap[id];
+ },
+}
+
+
diff --git a/dashboard/src/services/Policy.service.js b/dashboard/src/services/Policy.service.js
new file mode 100644
index 00000000..433680e9
--- /dev/null
+++ b/dashboard/src/services/Policy.service.js
@@ -0,0 +1,470 @@
+import Vue from 'vue'
+import util from './Util.service.js'
+import ModelService from './Model.service.js'
+import AttributeService from './Attribute.service'
+import config from '../config.js'
+
+var host = config.host;
+
+var policyResource;
+var policyRulesResource;
+
+var categoryMap = {
+ 'subject': {
+ arrayName: "subjectData",
+ mapName: "subjectDataMap",
+ responseName: "subject_data",
+ perimeterResponseName: "subjects",
+ assignmentResponseName: "subject_assignments",
+ unusedArrayName: "unusedSubjectData",
+ },
+ 'object': {
+ arrayName: "objectData",
+ mapName: "objectDataMap",
+ responseName: "object_data",
+ perimeterResponseName: "objects",
+ assignmentResponseName: "object_assignments",
+ unusedArrayName: "unusedObjectData",
+ },
+ 'action': {
+ arrayName: "actionData",
+ mapName: "actionDataMap",
+ responseName: "action_data",
+ perimeterResponseName: "actions",
+ assignmentResponseName: "action_assignments",
+ unusedArrayName: "unusedActionData",
+ }
+}
+
+var policiesMap = {};
+var policies = [];
+
+function loadPolicies() {
+ policyResource = Vue.resource(host + '/policies{/id}', {}, {patch: {method: 'PATCH'}});
+ categoryMap['subject'].policyPerimeterResource = Vue.resource(host + '/policies{/policy_id}/subjects{/perimeter_id}', {});
+ categoryMap['object'].policyPerimeterResource = Vue.resource(host + '/policies{/policy_id}/objects{/perimeter_id}', {}, );
+ categoryMap['action'].policyPerimeterResource = Vue.resource(host + '/policies{/policy_id}/actions{/perimeter_id}', {}, );
+ categoryMap['subject'].perimeterResource = Vue.resource(host + '/subjects{/perimeter_id}', {}, {patch: {method: 'PATCH'}});
+ categoryMap['object'].perimeterResource = Vue.resource(host + '/objects{/perimeter_id}', {}, {patch: {method: 'PATCH'}});
+ categoryMap['action'].perimeterResource = Vue.resource(host + '/actions{/perimeter_id}', {}, {patch: {method: 'PATCH'}});
+ categoryMap['subject'].assignmentResource = Vue.resource(host + '/policies{/policy_id}/subject_assignments{/perimeter_id}{/category_id}{/data_id}', {}, );
+ categoryMap['object'].assignmentResource = Vue.resource(host + '/policies{/policy_id}/object_assignments{/perimeter_id}{/category_id}{/data_id}', {}, );
+ categoryMap['action'].assignmentResource = Vue.resource(host + '/policies{/policy_id}/action_assignments{/perimeter_id}{/category_id}{/data_id}', {}, );
+ var queries = [
+ policyResource.query(),
+ ModelService.initialize(),
+ AttributeService.initialize()
+ ]
+
+ Promise.all(queries).then(function (result) {
+ createPolicies(result[0].body);
+ })
+}
+
+function createPolicies(policiesData) {
+ policies.splice(0, policies.length);
+ util.cleanObject(policiesMap);
+ createPolicyInternal(policiesData.policies);
+}
+
+function mapPolicy(policy) {
+ policy.rulesPopulated = false;
+ policy.rules = [];
+ policy.subjectData = [];
+ policy.objectData = [];
+ policy.actionData = [];
+ policy.unusedSubjectData = [];
+ policy.unusedObjectData = [];
+ policy.unusedActionData = [];
+ policy.attributes = [];
+ if (policy.model_id) {
+ policy.model = ModelService.getModel(policy.model_id);
+ policy.attributes = ModelService.getAttributesForModelId(policy.model_id);
+ }
+}
+
+function createPolicyInternal(data) {
+ return util.createInternal(data, policies, policiesMap, mapPolicy);
+}
+
+function removePolicyInternal(id) {
+ return util.removeInternal(id, policies, policiesMap);
+}
+
+function updatePolicyInternal(data) {
+ return util.updateInternal(data, policiesMap, mapPolicy);
+}
+
+function removeRuleInternal(policy, rule) {
+ policy.rules.splice(policy.rules.indexOf(rule), 1);
+ updateUnusedData(policy);
+}
+
+function loadPolicyRule(policy) {
+ if (!policy.rulesPopulated) {
+ policyRulesResource = Vue.resource(host + '/policies{/policy_id}/rules{/rule_id}', {}, {patch: {method: 'PATCH'}});
+ categoryMap['subject'].resource = Vue.resource(host + '/policies{/policy_id}/subject_data{/category_id}{/data_id}', {});
+ categoryMap['object'].resource = Vue.resource(host + '/policies{/policy_id}/object_data{/category_id}{/data_id}', {});
+ categoryMap['action'].resource = Vue.resource(host + '/policies{/policy_id}/action_data{/category_id}{/data_id}', {});
+ var queries = [
+ policyRulesResource.query({ policy_id: policy.id }),
+ categoryMap['subject'].resource.query({ policy_id: policy.id }),
+ categoryMap['object'].resource.query({ policy_id: policy.id }),
+ categoryMap['action'].resource.query({ policy_id: policy.id })
+ ]
+
+ Promise.all(queries).then(function (result) {
+ createRules(policy, result[0].body, result[1].body, result[2].body, result[3].body);
+ updateUnusedData(policy);
+ }, util.displayErrorFunction('Unable to load rules'))
+ }
+}
+
+function updateUnusedData(policy) {
+ policy.unusedSubjectData.splice(0, policy.unusedSubjectData.length);
+ util.pushAll(policy.unusedSubjectData, policy.subjectData);
+
+ policy.unusedObjectData.splice(0, policy.unusedObjectData.length);
+ util.pushAll(policy.unusedObjectData, policy.objectData);
+
+ policy.unusedActionData.splice(0, policy.unusedActionData.length);
+ util.pushAll(policy.unusedActionData, policy.actionData);
+
+ for (var i = 0; i < policy.rules.length; i++) {
+ var rule = policy.rules[i];
+ removeUsedData(rule.subjectData, policy.unusedSubjectData);
+ removeUsedData(rule.objectData, policy.unusedObjectData);
+ removeUsedData(rule.actionData, policy.unusedActionData);
+ }
+}
+
+function removeUsedData(list, orphanList) {
+ for (var j = 0; j < list.length; j++) {
+ var data = list[j];
+ if (data) {
+ var notOrphanIndex = util.indexOf(orphanList, "id", data.id);
+ if (notOrphanIndex >= 0) {
+ orphanList.splice(notOrphanIndex, 1);
+ }
+ }
+ }
+}
+
+function transformData(list) {
+ var result = {};
+ for (var index = 0; index < list.length; index++) {
+ var data = list[index].data;
+ for (var key in data) {
+ if (data.hasOwnProperty(key)) {
+ result[key] = data[key];
+ }
+ }
+ }
+ return result;
+}
+
+function createRules(policy, rulesData, subjectsData, objectsData, actionsData) {
+ policy.rules = rulesData ? rulesData.rules.rules : [];
+ policy.subjectDataMap = transformData(subjectsData.subject_data);
+ policy.subjectData = util.mapToArray(policy.subjectDataMap);
+ policy.objectDataMap = transformData(objectsData.object_data);
+ policy.objectData = util.mapToArray(policy.objectDataMap);
+ policy.actionDataMap = transformData(actionsData.action_data);
+ policy.actionData = util.mapToArray(policy.actionDataMap);
+ for (var i = 0; i < policy.rules.length; i++) {
+ var rule = policy.rules[i];
+ populateRule(policy, rule);
+ }
+ policy.rulesPopulated = true;
+}
+
+function populateRule(policy, rule) {
+ if (rule.meta_rule_id) {
+ rule.metaRule = ModelService.getMetaRule(rule.meta_rule_id);
+ }
+ if (rule.metaRule) {
+ var j = 0;
+ var k, id;
+ rule.subjectData = [];
+ rule.objectData = [];
+ rule.actionData = [];
+ rule.attributeData = [];
+
+ for (k = 0; k < rule.metaRule.subject_categories.length; k++) {
+ id = rule.rule[j + k];
+ if (policy.subjectDataMap[id])
+ rule.subjectData.push(policy.subjectDataMap[id]);
+ }
+ j += k;
+ for (k = 0; k < rule.metaRule.object_categories.length; k++) {
+ id = rule.rule[j + k];
+ if (policy.objectDataMap[id])
+ rule.objectData.push(policy.objectDataMap[id]);
+ }
+ j += k;
+ for (k = 0; k < rule.metaRule.action_categories.length; k++) {
+ id = rule.rule[j + k];
+ if (policy.actionDataMap[id]) {
+ rule.actionData.push(policy.actionDataMap[id]);
+ }
+ }
+
+ for (const value of rule.rule.values()){
+ if (value.includes("attributes:")){
+ let attrName = value.split(':')[1];
+ let attrId = AttributeService.getAttributeId(attrName);
+ rule.attributeData.push({id: attrId, name: attrName});
+
+ }
+ }
+ }
+ return rule;
+}
+
+function updateRule(policy, rule, decision){
+ return new Promise(resolveUpdateRule => {
+ var body = {
+ "instructions":[
+ {"decision": decision}
+ ]
+ };
+
+ policyRulesResource.patch({policy_id: policy.id, rule_id: rule.id}, body).then(success, util.displayErrorFunction('Unable to update Rule'));
+
+ function success(data){
+ resolveUpdateRule(data.body.rules[rule.id].instructions);
+ }
+ });
+}
+
+function filterRuleBySpecificItem(filteredRules, rule, items, filter){
+ items.forEach(item => {
+ if (item){
+ if (filter == null || item.name.indexOf(filter) >= 0){
+ if (!(filteredRules.includes(rule)))
+ filteredRules.push(rule)
+ }
+ }
+
+ });
+}
+
+function interArray(array1, array2){
+ let inter = [];
+
+
+ for (let i = 0; i < array1.length; i++){
+ for (let j = 0; j < array2.length; j++){
+ if (array1[i] === array2[j])
+ inter.push(array2[j]);
+ }
+ }
+ return inter;
+}
+
+
+function filterByRules(rules, filters){
+ let filteredRules = [];
+ let filteredByWords = [];
+
+
+ if (filters === ""){
+ filteredRules = rules;
+ } else {
+ filters = filters.split(' ');
+ filters.forEach( filter => {
+ if (filter !== "") {
+ let tmp = [];
+ rules.forEach((rule) => {
+
+ filterRuleBySpecificItem(tmp, rule, rule.subjectData, filter);
+ filterRuleBySpecificItem(tmp, rule, rule.objectData, filter);
+ filterRuleBySpecificItem(tmp, rule, rule.actionData, filter);
+ filterRuleBySpecificItem(tmp, rule, rule.attributeData, filter);
+
+ filteredByWords.push(tmp);
+ });
+
+ }
+ });
+
+ filteredRules = filteredByWords[0];
+ for (let i = 1; i < filteredByWords.length; i++){
+ filteredRules = interArray(filteredRules, filteredByWords[i]);
+ }
+ }
+
+ return filteredRules;
+}
+
+function policyRuleWithAttributes(policy){
+ const meta_rules = policy.model.meta_rules;
+
+ for (let i = 0; i < meta_rules.length; i++){
+ const meta_rule = meta_rules[i];
+ if (meta_rule.actionAttributes.length || meta_rule.subjectAttributes.length || meta_rule.objectAttributes.length)
+ return true;
+ }
+ return false;
+}
+
+export default {
+ policyRuleWithAttributes: policyRuleWithAttributes,
+ filterByRules: filterByRules,
+ initialize: loadPolicies,
+ createPolicies: createPolicies,
+ policies: policies,
+ getPolicy: function getPolicy(id) {
+ return policiesMap[id];
+ },
+ createPolicy: function createPolicy(policy) {
+ policyResource.save(null, policy).then(success, util.displayErrorFunction('Unable to create Policy'));
+
+ function success(data) {
+ createPolicyInternal(data.body.policies);
+ util.displaySuccess('Policy created');
+ }
+ },
+ removePolicy: function removePolicy(policy) {
+ policyResource.remove({ id: policy.id }, null).then(success, util.displayErrorFunction('Unable to remove Policy'));
+
+ function success() {
+ removePolicyInternal(policy.id);
+ util.displaySuccess('Policy removed');
+ }
+ },
+ updatePolicy: function updatePolicy(policy) {
+ policyResource.patch({ id: policy.id }, policy).then(success, util.displayErrorFunction('Unable to update Policy'));
+
+ function success(data) {
+ updatePolicyInternal(data.body.policies)
+ util.displaySuccess('Policy updated');
+ }
+ },
+ populatePolicy: loadPolicyRule,
+ createRules: createRules,
+ updateRule: updateRule,
+ addRuleToPolicy: function addRuleToPolicy(policy, rule) {
+ policyRulesResource.save({ policy_id: policy.id }, rule).then(success, util.displayErrorFunction('Unable to create Rule'));
+
+ function success(data) {
+ var rules = util.mapToArray(data.body.rules);
+ for (var i = 0; i < rules.length; i++) {
+ var rule = rules[i];
+ policy.rules.push(populateRule(policy, rule))
+ }
+ util.displaySuccess('Rule created');
+ updateUnusedData(policy);
+ }
+ },
+ removeRuleFromPolicy: function removeRuleFromPolicy(policy, rule) {
+ policyRulesResource.remove({ policy_id: policy.id, rule_id: rule.id }, null).then(success, util.displayErrorFunction('Unable to remove Rule'));
+
+ function success() {
+ removeRuleInternal(policy, rule);
+ util.displaySuccess('Rule removed');
+ }
+ },
+ createData: function createData(type, policy, categoryId, dataCategory) {
+ var categoryValue = categoryMap[type];
+ return categoryValue.resource.save({ policy_id: policy.id, category_id: categoryId }, dataCategory).then(
+ function (data) {
+ var result = util.createInternal(data.body[categoryValue.responseName].data, policy[categoryValue.arrayName], policy[categoryValue.mapName]);
+ util.displaySuccess('Data created');
+ util.pushAll(policy[categoryValue.unusedArrayName], result);
+ return result;
+ },
+ util.displayErrorFunction('Unable to create Data')
+ );
+ },
+ removeData: function removeData(type, policy, data) {
+ var categoryValue = categoryMap[type];
+ return categoryValue.resource.remove({ policy_id: policy.id, category_id: data.category_id, data_id: data.id }).then(
+ function () {
+ policy[categoryValue.arrayName].splice(policy.subjectData.indexOf(data), 1);
+ policy[categoryValue.unusedArrayName].splice(policy.unusedSubjectData.indexOf(data), 1);
+ delete policy[categoryValue.mapName][data.id];
+ util.displaySuccess('Data removed');
+ },
+ util.displayErrorFunction('Unable to remove Data')
+ );
+ },
+ createPerimeter: function createPerimeter(type, policy, perimeter) {
+ var categoryValue = categoryMap[type];
+ return categoryValue.policyPerimeterResource.save({ policy_id: policy.id }, perimeter).then(
+ function (data) {
+ util.displaySuccess('Perimeter created');
+ return util.mapToArray(data.body[categoryValue.perimeterResponseName]);
+ },
+ util.displayErrorFunction('Unable to create Perimeter')
+ );
+ },
+ removePerimeterFromPolicy: function removePerimeterFromPolicy(type, policy, perimeter) {
+ var categoryValue = categoryMap[type];
+
+ return categoryValue.policyPerimeterResource.remove({ policy_id: policy.id, perimeter_id: perimeter.id }, null).then(
+ function () {
+ util.displaySuccess('Perimeter removed');
+ return perimeter;
+ },
+ util.displayErrorFunction('Unable to remove Perimeter')
+ )
+ },
+ addPerimeterToPolicy: function addPerimeterToPolicy(type, policy, perimeter) {
+ var categoryValue = categoryMap[type];
+ perimeter.policy_list.push(policy.id);
+ var perimeterClone = util.clone(perimeter);
+ delete perimeterClone.policy_list;
+ return categoryValue.policyPerimeterResource.save({ policy_id: policy.id }, perimeterClone).then(
+ function () {
+ util.displaySuccess('Perimeter added');
+ },
+ util.displayErrorFunction('Unable to add Perimeter')
+ )
+ },
+ loadPerimetersAndAssignments: function loadPerimetersAndAssignments(type, policy) {
+ var categoryValue = categoryMap[type];
+ var queries = [
+ categoryValue.perimeterResource.query(),
+ categoryValue.policyPerimeterResource.query({ policy_id: policy.id }),
+ categoryValue.assignmentResource.query({ policy_id: policy.id }),
+ ]
+
+ return Promise.all(queries).then(function (data) {
+ var result = {};
+ result.assignments = util.mapToArray(data[2].body[categoryValue.assignmentResponseName]);
+ result.perimetersMap = data[1].body[categoryValue.perimeterResponseName];
+ result.perimeters = util.mapToArray(result.perimetersMap);
+ result.allPerimeters = util.mapToArray(data[0].body[categoryValue.perimeterResponseName]);
+ return result;
+ }, util.displayErrorFunction('Unable to load Perimeters'))
+
+ },
+ createAssignment: function createAssignment(type, policy, perimeter, data) {
+ var categoryValue = categoryMap[type];
+ var assignment = {
+ "id": perimeter.id,
+ "category_id": data.category_id,
+ "data_id": data.id,
+ "policy_id": policy.id
+ }
+ return categoryValue.assignmentResource.save({ policy_id: policy.id }, assignment).then(
+ function (data) {
+ util.displaySuccess('Assignment created');
+ return util.mapToArray(data.body[categoryValue.assignmentResponseName]);
+ },
+ util.displayErrorFunction('Unable to create Assignment')
+ )
+ },
+ removeAssignment: function removeAssignment(type, policy, perimeter, data) {
+ var categoryValue = categoryMap[type];
+
+ return categoryValue.assignmentResource.remove({ policy_id: policy.id, perimeter_id: perimeter.id, category_id: data.category_id, data_id: data.id }, null).then(
+ function () {
+ util.displaySuccess('Assignment removed');
+ },
+ util.displayErrorFunction('Unable to remove Assignment')
+ )
+ },
+}
+
diff --git a/dashboard/src/services/Util.service.js b/dashboard/src/services/Util.service.js
new file mode 100644
index 00000000..a6b53d6f
--- /dev/null
+++ b/dashboard/src/services/Util.service.js
@@ -0,0 +1,153 @@
+import Vue from 'vue'
+
+export default {
+ sortByName(items) {
+ return items
+ .sort((item1, item2) => {
+ return item1.name.localeCompare(item2.name);
+ });
+ },
+ filterAndSortByName(items, filter) {
+ return items
+ .filter(item => {
+ return filter == null || item.name.indexOf(filter) >= 0;
+ })
+ .sort((item1, item2) => {
+ return item1.name.localeCompare(item2.name);
+ });
+ },
+ clone: function clone(src) {
+ return JSON.parse(JSON.stringify(src));
+ },
+
+ mapToArray: function mapToArray(map, action) {
+ var result = []
+ for (var key in map) {
+ if (map.hasOwnProperty(key)) {
+ var item = map[key];
+ item.id = key;
+ if (action != null) {
+ action(item);
+ }
+ result.push(item);
+ }
+ }
+ return result;
+ },
+
+
+ mapIdToItem: function mapIdToItem(array, map) {
+ if (array) {
+ for (var index = 0; index < array.length; index++) {
+ var id = array[index];
+ if (map[id])
+ array[index] = map[id];
+ }
+ }
+ },
+
+ mapItemToId: function mapItemToId(array) {
+ if (array) {
+ for (var index = 0; index < array.length; index++) {
+ var item = array[index];
+ array[index] = item.id;
+ }
+ }
+ },
+
+ addToMap: function addToMap(array, map) {
+ if (array) {
+ for (var index = 0; index < array.length; index++) {
+ var item = array[index];
+ map[item.id] = item;
+ }
+ }
+ },
+
+ updateObject: function updateObject(object, newObject) {
+ for (var key in newObject) {
+ if (newObject.hasOwnProperty(key)) {
+ object[key] = newObject[key];
+ }
+ }
+ },
+
+ cleanObject: function cleanObject(object) {
+ for (var key in object) {
+ if (object.hasOwnProperty(key)) {
+ delete object[key];
+ }
+ }
+ },
+
+ pushAll: function pushAll(array, arrayToPush) {
+ for (var i = 0; i < arrayToPush.length; i += 1) {
+ array.push(arrayToPush[i]);
+ }
+ },
+
+ indexOf: function indexOf(array, property, value) {
+ for (var i = 0; i < array.length; i += 1) {
+ if (array[i][property] === value) {
+ return i;
+ }
+ }
+ return -1;
+ },
+
+ createInternal: function createInternal(data, array, map, action) {
+ var added = this.mapToArray(data, action)
+ this.addToMap(added, map);
+ this.pushAll(array, added);
+
+ return added;
+ },
+
+ updateInternal: function updateInternal(data, map, action) {
+ var updated = this.mapToArray(data, action)
+ var result = []
+ for (var index = 0; index < updated.length; index++) {
+ var item = updated[index];
+ this.updateObject(map[item.id], item)
+ result.push(map[item.id])
+ }
+ return result;
+ },
+
+ removeInternal: function removeInternal(id, array, map) {
+ var old = map[id];
+ delete map[old.id];
+ array.splice(array.indexOf(old), 1);
+ return old;
+ },
+
+ arrayToTitleMap: function arrayToTitleMap(array) {
+ return array.map(function (item) {
+ return { value: item.id, name: item.name }
+ }).sort(function (itemA, itemB) {
+ return itemA.name.localeCompare(itemB.name);
+ })
+ },
+
+ displayErrorFunction: function displayErrorFunction(message) {
+ return function (response) {
+ var text = message;
+ if (response && response.data && response.data.message) {
+ text += ' (' + response.data.message + ')'
+ }
+ console.log(text);
+ Vue.toasted.global.toast({message: text, type: 'danger', title: 'Error'});
+ }
+ },
+
+ displaySuccess: function displaySuccess(message) {
+ Vue.toasted.global.toast({message: message, type: 'success', title: 'Success'});
+ },
+
+ displayError: function displayError(message) {
+ Vue.toasted.global.toast({message: message, type: 'danger', title: 'Error'});
+ },
+
+
+
+} \ No newline at end of file
diff --git a/dashboard/src/views/Admin.vue b/dashboard/src/views/Admin.vue
new file mode 100644
index 00000000..414e68a0
--- /dev/null
+++ b/dashboard/src/views/Admin.vue
@@ -0,0 +1,52 @@
+<template>
+ <div>
+ <div v-if="isLoading" >
+ <div class="d-flex justify-content-center">
+ <div class="spinner-border" role="status">
+ <span class="sr-only">Loading...</span>
+ </div>
+ </div>
+ </div>
+ <div v-else class="row justify-content-center">
+ <label for="file" class="label-file btn btn-primary">
+ <span class="fa fa-upload"></span>
+ Import
+ </label>
+ <input
+ id="file"
+ class="input-file"
+ type="file"
+ @change="readFile"
+ accept="application/json, .json"
+ />
+
+
+ </div>
+ </div>
+</template>
+
+<script>
+import ImportService from "./../services/Import.service.js";
+
+export default {
+ data(){
+ return {
+ isLoading: false
+ }
+ },
+ methods: {
+ readFile(event) {
+ var reader = new FileReader();
+ reader.onload = async function() {
+ this.isLoading = true;
+ var fileContents = reader.result;
+ await ImportService.importData(JSON.parse(fileContents));
+ this.isLoading = false;
+ }.bind(this);
+ reader.readAsText(event.target.files[0]);
+ },
+ }
+};
+</script>
+
+
diff --git a/dashboard/src/views/Assignments.vue b/dashboard/src/views/Assignments.vue
new file mode 100644
index 00000000..d0e500e3
--- /dev/null
+++ b/dashboard/src/views/Assignments.vue
@@ -0,0 +1,54 @@
+<template>
+ <div>
+ <form-header
+ placeholder="Filter by Policy"
+ needButton
+ buttonText="Create Policy"
+ @click="creatingPolicy = true"
+ v-model="filter"
+ ></form-header>
+ <br/>
+
+ <CreatePolicy v-if="creatingPolicy" @close="creatingPolicy = false"></CreatePolicy>
+ <div class="list-group row" v-else>
+ <policy v-for="policy in filteredPolicies" :key="policy.id" :policy="policy"></policy>
+ </div>
+ </div>
+</template>
+
+<script>
+ import PolicyService from './../services/Policy.service.js'
+ //import util from './../services/Util.service.js'
+ import FormHeader from "./../components/FormHeader.vue"
+ import Policy from "./../components/assignment/Policy.vue"
+ import CreatePolicy from "./../components/policy/CreatePolicy.vue"
+ import util from "../services/Util.service";
+
+ export default {
+ data() {
+ return {
+ filter: "",
+ creatingPolicy: false,
+ policies: []
+ };
+ },
+ mounted() {
+ PolicyService.initialize();
+ this.policies = PolicyService.policies;
+ },
+ components: {
+ FormHeader,
+ Policy,
+ CreatePolicy,
+ },
+ computed: {
+ filteredPolicies() {
+ return util.filterAndSortByName(this.policies, this.filter);
+ }
+ }
+ }
+</script>
+
+<style scoped>
+
+</style> \ No newline at end of file
diff --git a/dashboard/src/views/Auth.vue b/dashboard/src/views/Auth.vue
new file mode 100644
index 00000000..c057e284
--- /dev/null
+++ b/dashboard/src/views/Auth.vue
@@ -0,0 +1,65 @@
+<template>
+ <div class="row justify-content-center">
+ <form>
+ <div class="form-group">
+ <label for="login">Login</label>
+ <input
+ type="text"
+ name="login"
+ v-model="name"
+ v-validate.initial="'required'"
+ class="form-control"
+ id="login"
+ />
+ </div>
+ <div class="form-group">
+ <label for="password">Password</label>
+ <input
+ type="password"
+ name="password"
+ v-model="password"
+ v-validate.initial="'required'"
+ class="form-control"
+ id="password"
+ />
+ </div>
+ <button type="button" class="btn btn-primary btn-xlg col-auto" @click="login()">Login</button>
+ </form>
+
+ </div>
+</template>
+
+<script>
+import Vue from "vue";
+import util from "./../services/Util.service.js";
+import config from '../config.js'
+
+var host = config.host;
+
+export default {
+ name: "auth",
+ data() {
+ return {
+ name: "",
+ password: ""
+ };
+ },
+ methods: {
+ login() {
+ Vue.http.headers.common["Authorization"] =
+ "Basic " + btoa(this.name + ":" + this.password);
+ Vue.http.get(host + "/auth").then(
+ response => {
+ Vue.http.headers.common["Authorization"] = "Basic ";
+ Vue.http.headers.common["x-api-key"] = response.data;
+ localStorage.setItem("auth-key", response.data);
+ this.$router.push("models");
+ },
+ response => {
+ util.displayError("Unable to log in " + response);
+ }
+ );
+ }
+ }
+};
+</script>
diff --git a/dashboard/src/views/Error.vue b/dashboard/src/views/Error.vue
new file mode 100644
index 00000000..528fb8aa
--- /dev/null
+++ b/dashboard/src/views/Error.vue
@@ -0,0 +1,8 @@
+<template>
+ <div class="row justify-content-center">
+ /!\ Error please configure CORS on server
+ </div>
+</template>
+
+
+
diff --git a/dashboard/src/views/Models.vue b/dashboard/src/views/Models.vue
new file mode 100644
index 00000000..bdd46483
--- /dev/null
+++ b/dashboard/src/views/Models.vue
@@ -0,0 +1,80 @@
+<template>
+ <div>
+ <orphans
+ @close="allowAlert = false"
+ v-if="showAlert"
+ :orphanMetaRules="orphanMetaRules"
+ :orphanSubjectCategories="orphanSubjectCategories"
+ :orphanObjectCategories="orphanObjectCategories"
+ :orphanActionCategories="orphanActionCategories"
+ ></orphans>
+
+ <br />
+ <form-header
+ placeholder="Filter"
+ buttonText="Create Model"
+ @click="creatingModel = true"
+ v-model="filter"
+ need-button
+ ></form-header>
+ <br />
+
+ <CreateModel v-if="creatingModel" @close="creatingModel = false"></CreateModel>
+ <div class="list-group row" v-else>
+ <Model v-for="model in filteredModels" :key="model.id" :model="model"></Model>
+ </div>
+ </div>
+</template>
+
+<script>
+import Model from "./../components/model/Model.vue";
+import CreateModel from "./../components/model/CreateModel.vue";
+import ModelService from "./../services/Model.service.js";
+import util from "./../services/Util.service.js";
+import Orphans from "./../components/model/Orphans.vue";
+import FormHeader from "./../components/FormHeader.vue";
+
+export default {
+ name: "models",
+ components: {
+ Model,
+ CreateModel,
+ FormHeader,
+ Orphans
+ },
+ mounted() {
+ ModelService.initialize();
+ this.models = ModelService.models;
+ this.orphanMetaRules = ModelService.orphanMetaRules;
+ this.orphanSubjectCategories = ModelService.orphanSubjectCategories;
+ this.orphanObjectCategories = ModelService.orphanObjectCategories;
+ this.orphanActionCategories = ModelService.orphanActionCategories;
+ },
+ data() {
+ return {
+ filter: "",
+ creatingModel: false,
+ allowAlert: true,
+ orphanMetaRules: [],
+ orphanSubjectCategories: [],
+ orphanActionCategories: [],
+ orphanObjectCategories: [],
+ models: []
+ };
+ },
+ computed: {
+ showAlert() {
+ return (
+ this.allowAlert &&
+ (this.orphanMetaRules.length ||
+ this.orphanSubjectCategories.length ||
+ this.orphanActionCategories.length ||
+ this.orphanObjectCategories.length)
+ );
+ },
+ filteredModels() {
+ return util.filterAndSortByName(this.models, this.filter);
+ }
+ }
+};
+</script>
diff --git a/dashboard/src/views/Pdps.vue b/dashboard/src/views/Pdps.vue
new file mode 100644
index 00000000..778f23fe
--- /dev/null
+++ b/dashboard/src/views/Pdps.vue
@@ -0,0 +1,53 @@
+<template>
+ <div>
+ <form-header
+ placeholder="Filter"
+ buttonText="Create PDP"
+ @click="creatingPdp = true"
+ v-model="filter"
+ need-button
+ ></form-header>
+ <br />
+
+ <CreatePdp v-if="creatingPdp" @close="creatingPdp = false"></CreatePdp>
+ <div class="list-group row" v-else>
+ <pdp v-for="pdp in filteredPdps" :key="pdp.id" :pdp="pdp"></pdp>
+ </div>
+ </div>
+</template>
+
+<script>
+import PdpService from "./../services/Pdp.service.js";
+import util from "./../services/Util.service.js";
+import Pdp from "./../components/pdp/Pdp.vue";
+import CreatePdp from "./../components/pdp/CreatePdp.vue";
+import FormHeader from "./../components/FormHeader.vue";
+import PolicyService from "../services/Policy.service";
+
+export default {
+ data() {
+ return {
+ filter: "",
+ creatingPdp: false,
+ pdps: []
+ };
+ },
+ mounted() {
+ PdpService.initialize();
+ PolicyService.initialize();
+ this.pdps = PdpService.pdps;
+ },
+ components: {
+ Pdp,
+ CreatePdp,
+ FormHeader
+ },
+ computed: {
+ filteredPdps() {
+ return util.filterAndSortByName(this.pdps, this.filter);
+ }
+ }
+};
+</script>
+
+
diff --git a/dashboard/src/views/Rules.vue b/dashboard/src/views/Rules.vue
new file mode 100644
index 00000000..042b00c0
--- /dev/null
+++ b/dashboard/src/views/Rules.vue
@@ -0,0 +1,50 @@
+<template>
+ <div>
+ <form-header
+ placeholder="Filter by Policy"
+ needButton
+ buttonText="Create Policy"
+ @click="creatingPolicy = true"
+ v-model="filter"
+ ></form-header>
+ <br/>
+
+ <CreatePolicy v-if="creatingPolicy" @close="creatingPolicy = false"></CreatePolicy>
+ <div class="list-group row" v-else>
+ <policy v-for="policy in filteredPolicies" :key="policy.id" :policy="policy"></policy>
+ </div>
+ </div>
+</template>
+
+<script>
+import PolicyService from './../services/Policy.service.js'
+//import util from './../services/Util.service.js'
+import FormHeader from "./../components/FormHeader.vue"
+import Policy from "./../components/policy/Policy.vue"
+import CreatePolicy from "./../components/policy/CreatePolicy.vue"
+import util from "../services/Util.service";
+
+export default {
+ data() {
+ return {
+ filter: "",
+ creatingPolicy: false,
+ policies: []
+ };
+ },
+ mounted() {
+ PolicyService.initialize();
+ this.policies = PolicyService.policies;
+ },
+ components: {
+ FormHeader,
+ Policy,
+ CreatePolicy,
+ },
+ computed: {
+ filteredPolicies() {
+ return util.filterAndSortByName(this.policies, this.filter);
+ }
+ }
+}
+</script>