diff options
author | Matthias <ilovemilk@wusa.io> | 2020-04-10 13:25:49 +0300 |
---|---|---|
committer | Matthias <ilovemilk@wusa.io> | 2020-04-10 13:25:49 +0300 |
commit | 133a820b88a78a4281e017d88d11bed169b5fdbf (patch) | |
tree | 0bd785eb085699b18a8283891248d77b194f5c12 /src | |
parent | 0a92726e3f3d082ad87101dfb9819e736bcd9df7 (diff) |
add first vuejs ui version
Diffstat (limited to 'src')
-rw-r--r-- | src/App.vue | 56 | ||||
-rw-r--r-- | src/components/Action.vue | 62 | ||||
-rw-r--r-- | src/components/FileOperationsTable.vue | 86 | ||||
-rw-r--r-- | src/components/Header.vue | 43 | ||||
-rw-r--r-- | src/components/ProtectionStatus.vue | 117 | ||||
-rw-r--r-- | src/components/RecoverAction.vue | 34 | ||||
-rw-r--r-- | src/components/ServiceStatus.vue | 92 | ||||
-rw-r--r-- | src/css/global.css | 19 | ||||
-rw-r--r-- | src/main.js | 40 | ||||
-rw-r--r-- | src/router.js | 31 | ||||
-rw-r--r-- | src/views/History.vue | 123 | ||||
-rw-r--r-- | src/views/Protection.vue | 85 | ||||
-rw-r--r-- | src/views/Recover.vue | 153 | ||||
-rw-r--r-- | src/webcomponents/ransomware-icons.js | 34 |
14 files changed, 975 insertions, 0 deletions
diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 0000000..9456891 --- /dev/null +++ b/src/App.vue @@ -0,0 +1,56 @@ +<template> + <Content app-name="ransomware_detection"> + <AppNavigation> + <ul> + <AppNavigationItem v-for="item in menu" :key="item.key" :item="item" /> + </ul> + </AppNavigation> + <router-view /> + </Content> +</template> + +<script> +import Content from 'nextcloud-vue/dist/Components/Content' +import AppNavigation from 'nextcloud-vue/dist/Components/AppNavigation' +import AppNavigationItem from 'nextcloud-vue/dist/Components/AppNavigationItem' +import AppContent from 'nextcloud-vue/dist/Components/AppContent' + +export default { + name: 'App', + components: { + Content, + AppNavigation, + AppNavigationItem, + AppContent, + }, + computed: { + menu() { + return [ + { + id: 'app-category-protection', + classes: [], + router: {name: 'protection'}, + icon: 'icon-dashboard', + text: t('ransomware_detection', 'Protection'), + }, + { + id: 'app-category-recover', + classes: [], + icon: 'icon-trash', + router: {name: 'recover'}, + text: t('ransomware_detection', 'Recover'), + }, { + id: 'app-category-history', + classes: [], + icon: 'icon-hourglass', + router: {name: 'history'}, + text: t('ransomware_detection', 'History'), + } + ]; + } + } +} +</script> + +<style scoped> +</style>
\ No newline at end of file diff --git a/src/components/Action.vue b/src/components/Action.vue new file mode 100644 index 0000000..2cf8766 --- /dev/null +++ b/src/components/Action.vue @@ -0,0 +1,62 @@ +<template> + <button class="action-button pull-right" + :data-type="type" :data-href="link" @click="onClickActionButton"> + <iron-icon icon="undo"></iron-icon> {{ label }} + </button> +</template> + +<script> +import axios from 'nextcloud-axios' +import '@polymer/iron-icon/iron-icon.js'; +import '@polymer/iron-icons/iron-icons.js'; + +export default { + name: 'Action', + + props: { + label: { + type: String, + default: '', + required: true + }, + link: { + type: String, + default: '', + required: true + }, + type: { + type: String, + default: '', + required: true + }, + data: { + type: Object, + default: {}, + required: false + } + }, + + methods: { + onClickActionButton: function() { + axios({ + method: this.type || 'GET', + url: this.link + }) + .then(() => { + this.$parent._$el.fadeOut(OC.menuSpeed) + this.$parent.$emit('remove') + $('body').trigger(new $.Event('OCA.Notification.Action', { + notification: this.$parent, + action: { + url: this.link, + type: this.type || 'GET' + } + })) + }) + .catch(() => { + OC.Notification.showTemporary(t('notifications', 'Failed to perform action')) + }); + } + } +} +</script>
\ No newline at end of file diff --git a/src/components/FileOperationsTable.vue b/src/components/FileOperationsTable.vue new file mode 100644 index 0000000..c5cc5b4 --- /dev/null +++ b/src/components/FileOperationsTable.vue @@ -0,0 +1,86 @@ +<template> + <vaadin-grid theme="row-dividers" column-reordering-allowed multi-sort :items.prop="fileOperations"> + <vaadin-grid-selection-column auto-select frozen></vaadin-grid-selection-column> + <vaadin-grid-column width="5em" flex-grow="0" id="status" header="Status"></vaadin-grid-column> + <vaadin-grid-sort-column width="9em" path="originalName" header="Name"></vaadin-grid-sort-column> + <vaadin-grid-sort-column width="9em" path="timestamp" id="time" header="GeƤndert"></vaadin-grid-sort-column> + </vaadin-grid> +</template> + +<script> +import '@vaadin/vaadin-grid/vaadin-grid.js'; +import '@vaadin/vaadin-grid/vaadin-grid-selection-column.js'; +import '@vaadin/vaadin-grid/vaadin-grid-sort-column.js'; +import '@vaadin/vaadin-grid/vaadin-grid-column.js'; +import '@polymer/iron-icon/iron-icon.js'; +import '@polymer/iron-icons/iron-icons.js'; +import '../webcomponents/ransomware-icons' +import 'time-elements/dist/time-elements'; + +export default { + name: 'FileOperationsTable', + data() { + return { + fileOperations: this.items + } + }, + props: { + data: { + type: Array, + required: true + } + }, + watch: { + data: { + immediate: true, + handler (newVal, oldVal) { + this.fileOperations = newVal; + this.$emit('table-state-changed'); + if (oldVal !== undefined) { + document.querySelector('vaadin-grid').clearCache(); + document.querySelector('vaadin-grid vaadin-grid-selection-column').selectAll = false; + } + } + } + }, + mounted () { + document.querySelector('#status').renderer = (root, grid, rowData) => { + const icon = document.createElement('iron-icon'); + switch (rowData.item.status) { + case 0: + icon.setAttribute('icon', 'ransomware:timelapse'); + icon.style = "color: blue;"; + break; + case 1: + icon.setAttribute('icon', 'verified-user'); + icon.style = "color: green;"; + break; + case 2: + icon.setAttribute('icon', 'error'); + icon.style = "color: red;"; + break; + default: + icon.setAttribute('icon', 'ransomware:timelapse'); + icon.style = "color: blue;"; + break; + } + root.innerHTML = ''; + root.appendChild(icon); + } + + document.querySelector('#time').renderer = (root, grid, rowData) => { + const localTime = document.createElement('local-time'); + localTime.setAttribute('datetime', moment.unix(rowData.item.timestamp).format("YYYY-MM-DDTHH:mm:ss.SSS")); + localTime.textContent = moment.unix(rowData.item.timestamp).format('dddd, MMMM Do YYYY, HH:mm:ss'); + root.innerHTML = ''; + root.appendChild(localTime); + } + } +} +</script> + +<style scoped> + vaadin-grid { + border: none; + } +</style>
\ No newline at end of file diff --git a/src/components/Header.vue b/src/components/Header.vue new file mode 100644 index 0000000..456d065 --- /dev/null +++ b/src/components/Header.vue @@ -0,0 +1,43 @@ +<template> + <div class="header"> + <h2> + {{this.header}} + </h2> + <div class="actions"> + <slot/> + </div> + </div> +</template> + +<script> +export default { + name: 'Header', + props: { + header: { + type: String, + default: '', + required: true + } + } +} +</script> + +<style scoped> + h2 { + height: 100%; + margin: 0px; + padding: 10px; + } + .actions { + height: 100%; + display: flex; + align-items: center; + padding: 0px 10px 0px 10px; + } + .header { + height: 50px; + display: flex; + justify-content: space-between; + align-items: stretch; + } +</style>
\ No newline at end of file diff --git a/src/components/ProtectionStatus.vue b/src/components/ProtectionStatus.vue new file mode 100644 index 0000000..62e4805 --- /dev/null +++ b/src/components/ProtectionStatus.vue @@ -0,0 +1,117 @@ +<template> + <div class="container" v-bind:class="[protection && !detection? 'good' : 'bad']"> + <h1> + <span v-if="protection && !detection"><iron-icon icon="ransomware:shield"></iron-icon> Your files are protected against destruction by ransomware.</span> + <span v-if="!protection"><iron-icon icon="error"></iron-icon> Your files are not protected. One service is not working properly.</span> + <span v-if="protection && detection"><iron-icon icon="ransomware:locked"></iron-icon> Ransomware attack detected.</span> + </h1> + <paper-button class="recover-button" @click="$router.push('recover')" v-if="protection && detection"><iron-icon icon="undo"></iron-icon>Recover</paper-button> + </div> +</template> + +<script> +import '@polymer/paper-button/paper-button.js'; +import '@polymer/iron-icon/iron-icon.js'; +import '@polymer/iron-icons/iron-icons.js'; +import '../webcomponents/ransomware-icons' +import axios from 'nextcloud-axios' + +export default { + name: 'ProtectionStatus', + props: { + protectionLink: { + type: String, + default: '', + required: true + }, + detectionLink: { + type: String, + default: '', + required: true + } + }, + created() { + this.fetchServicesStatus(); + this.fetchDetectionStatus(); + }, + data() { + return { + detection: 0, + protection: 0 + }; + }, + methods: { + fetchServicesStatus() { + axios({ + method: 'GET', + url: this.protectionLink + }) + .then(json => { + this.protection = 1; + for (i = 0; i < json.data.length; i++) { + if (json.data[i].status == 0) { + this.protection = 0; + } + } + this.$emit('protection-state-changed'); + }) + .catch( error => { console.error(error); }); + }, + fetchDetectionStatus() { + axios({ + method: 'GET', + url: this.detectionLink + }) + .then(json => { + this.detection = 0; + if (json.data.length > 0) { + this.detection = 1; + } + }) + .catch( error => { console.error(error); }); + } + } +} +</script> + +<style lang="scss" scoped> + .container { + h1 { + height: calc(100% - 52px); + color: #fff; + line-height: 48px; + display: flex; + align-items: center; + font-size: 32px; + iron-icon { + width: 48px; + height: 48px; + } + span { + vertical-align: middle; + } + } + + width: 100%; + height: 100%; + box-shadow: none; + color: #fff; + padding: 0px 10px 0px 10px; + &.good { + background-color: #18b977; + } + &.bad { + background-color: #e2523d; + } + } + + .recover-button { + display: flex; + border: 1px solid #fff; + } + + .recover-button:hover { + background-color: #fff; + color: #c00; + } +</style>
\ No newline at end of file diff --git a/src/components/RecoverAction.vue b/src/components/RecoverAction.vue new file mode 100644 index 0000000..515b83b --- /dev/null +++ b/src/components/RecoverAction.vue @@ -0,0 +1,34 @@ +<template> + <button class="action-button pull-right" @click="onClickActionButton"> + <iron-icon icon="ransomware:trash"></iron-icon> {{ label }} + </button> +</template> + +<script> +import '@polymer/iron-icon/iron-icon.js'; +import '@polymer/iron-icons/iron-icons.js'; +import '../webcomponents/ransomware-icons' + +export default { + name: 'RecoverAction', + + props: { + label: { + type: String, + default: '', + required: true + } + }, + methods: { + onClickActionButton() { + this.$emit("recover"); + } + } +} +</script> + +<style lang="scss" scoped> + button { + --border-radius-pill: 0px; + } +</style>
\ No newline at end of file diff --git a/src/components/ServiceStatus.vue b/src/components/ServiceStatus.vue new file mode 100644 index 0000000..9c005b5 --- /dev/null +++ b/src/components/ServiceStatus.vue @@ -0,0 +1,92 @@ +<template> + <div> + <h2 class="container"> + <div class="item name">{{ serviceName }}</div> + <div v-if="serviceStatus" class="item status active">Active</div> + <div v-if="!serviceStatus" class="item status offline">Offline</div> + </h2> + <div v-if="!serviceStatus" class="description">{{description}}</div> + </div> +</template> + +<script> +import '@polymer/paper-card/paper-card.js'; +import '@polymer/paper-button/paper-button.js'; +import '@polymer/iron-icon/iron-icon.js'; +import '@polymer/iron-icons/iron-icons.js'; +import axios from 'nextcloud-axios' + +export default { + name: 'ServiceStatus', + props: { + link: { + type: String, + default: '', + required: true + }, + description: { + type: String, + default: '', + required: true + } + }, + data() { + return { + serviceName: "Not available.", + serviceStatus: 0 + }; + }, + created () { + this.fetchServiceName(); + this.fetchServiceStatus(); + }, + methods: { + fetchServiceName: function() { + axios({ + method: 'GET', + url: this.link + }) + .then(json => { + this.serviceName = json.data.name; + }) + .catch( error => { console.error(error); }); + }, + fetchServiceStatus() { + axios({ + method: 'GET', + url: this.link + }) + .then(json => { + this.serviceStatus = json.data.status; + this.$emit('service-state-changed'); + }) + .catch( error => { console.error(error); }); + } + } +} +</script> + +<style lang="scss" scoped> + .container { + display: flex; + width: 100%; + justify-content: space-between; + } + h2 { + margin: 0px; + } + .item { + padding: 5px 10px 5px 10px; + } + .description { + color: #9b9b9b; + padding: 0px 10px 0px 10px; + } + .active { + color: #18b977; + } + .offline { + color: #e2523d; + } + +</style>
\ No newline at end of file diff --git a/src/css/global.css b/src/css/global.css new file mode 100644 index 0000000..8ef68df --- /dev/null +++ b/src/css/global.css @@ -0,0 +1,19 @@ +.icon-shield { + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgd2lkdGg9IjI0IgogICBoZWlnaHQ9IjI0IgogICB2aWV3Qm94PSIwIDAgMjQgMjQiCiAgIHZlcnNpb249IjEuMSIKICAgaWQ9InN2ZzYiCiAgIHNvZGlwb2RpOmRvY25hbWU9ImxvY2stc2hpZWxkLnN2ZyIKICAgaW5rc2NhcGU6dmVyc2lvbj0iMC45Mi4zICgyNDA1NTQ2LCAyMDE4LTAzLTExKSI+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhMTIiPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgICAgPGRjOnRpdGxlIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxkZWZzCiAgICAgaWQ9ImRlZnMxMCIgLz4KICA8c29kaXBvZGk6bmFtZWR2aWV3CiAgICAgcGFnZWNvbG9yPSIjZmZmZmZmIgogICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IgogICAgIGJvcmRlcm9wYWNpdHk9IjEiCiAgICAgb2JqZWN0dG9sZXJhbmNlPSIxMCIKICAgICBncmlkdG9sZXJhbmNlPSIxMCIKICAgICBndWlkZXRvbGVyYW5jZT0iMTAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjY0MCIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI0ODAiCiAgICAgaWQ9Im5hbWVkdmlldzgiCiAgICAgc2hvd2dyaWQ9ImZhbHNlIgogICAgIGlua3NjYXBlOnpvb209IjE1LjczMzEyNiIKICAgICBpbmtzY2FwZTpjeD0iMjguNDM2MzgyIgogICAgIGlua3NjYXBlOmN5PSIzNS4wNzIzNzIiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ic3ZnNiIgLz4KICA8cGF0aAogICAgIGQ9Ik0wIDBoMjR2MjRIMHoiCiAgICAgZmlsbD0ibm9uZSIKICAgICBpZD0icGF0aDIiIC8+CiAgPHBhdGgKICAgICBkPSJNIDEyIDEgTCAzIDUgTCAzIDExIEMgMyAxNi41NSA2Ljg0IDIxLjc0IDEyIDIzIEMgMTcuMTYgMjEuNzQgMjEgMTYuNTUgMjEgMTEgTCAyMSA1IEwgMTIgMSB6IE0gMTIgMy45NjA5Mzc1IEMgMTMuMTY1NDk1IDMuOTYwOTM3NSAxNC4xNjk1NTcgNC4zNzYwMjU4IDE0Ljc4NTE1NiA1LjE2MDE1NjIgQyAxNS40MDA3NTUgNS45NDQyODY2IDE1LjYxNzE4OCA2Ljk3NjE2NzkgMTUuNjE3MTg4IDguMTQyNTc4MSBMIDE1LjYxNzE4OCA5LjU1NDY4NzUgTCAxNy4zNTE1NjIgOS41NTQ2ODc1IEMgMTcuNTE0MDk2IDkuNTU0Njg3NSAxNy42NDQ1MzEgOS42ODUxMjMgMTcuNjQ0NTMxIDkuODQ3NjU2MiBMIDE3LjY0NDUzMSAxNi4zMTY0MDYgQyAxNy42NDQ1MzEgMTYuNDc4OTM5IDE3LjUxNDA5NiAxNi42MTEzMjggMTcuMzUxNTYyIDE2LjYxMTMyOCBMIDYuNjQ4NDM3NSAxNi42MTEzMjggQyA2LjQ4NTkwNDMgMTYuNjExMzI4IDYuMzU1NDY4OCAxNi40Nzg5MzkgNi4zNTU0Njg4IDE2LjMxNjQwNiBMIDYuMzU1NDY4OCA5Ljg0NzY1NjIgQyA2LjM1NTQ2ODcgOS42ODUxMjMgNi40ODU5MDQzIDkuNTU0Njg3NSA2LjY0ODQzNzUgOS41NTQ2ODc1IEwgOC4zODI4MTI1IDkuNTU0Njg3NSBMIDguMzgyODEyNSA4LjE0MjU3ODEgQyA4LjM4MjgxMjUgNi45NzYxNjc5IDguNTk5MjQ1IDUuOTQ0Mjg2NiA5LjIxNDg0MzggNS4xNjAxNTYyIEMgOS44MzA0NDI1IDQuMzc2MDI1OCAxMC44MzQ1MDYgMy45NjA5Mzc1IDEyIDMuOTYwOTM3NSB6IE0gMTIgNS41NDg4MjgxIEMgMTEuMTY4MzUxIDUuNTQ4ODI4MSAxMC43NjEzIDUuNzYzMDA4OCAxMC40NjQ4NDQgNi4xNDA2MjUgQyAxMC4xNjgzODcgNi41MTgyNDIzIDkuOTcwNzAzMSA3LjE4MTU0MDMgOS45NzA3MDMxIDguMTQyNTc4MSBMIDkuOTcwNzAzMSA5LjU1NDY4NzUgTCAxNC4wMjkyOTcgOS41NTQ2ODc1IEwgMTQuMDI5Mjk3IDguMTQyNTc4MSBDIDE0LjAyOTI5NyA3LjE4MTU0MDMgMTMuODMxNjEzIDYuNTE4MjQyNyAxMy41MzUxNTYgNi4xNDA2MjUgQyAxMy4yMzg3IDUuNzYzMDA5MSAxMi44MzE2NSA1LjU0ODgyODEgMTIgNS41NDg4MjgxIHogTSAxMS42MTcxODggMTAuMTYwMTU2IEwgMTEuNjE3MTg4IDEwLjg3MzA0NyBDIDExLjQyMzQyNCAxMC45MDgyNzYgMTEuMjU2MTUzIDEwLjk2NTY1NSAxMS4xMTUyMzQgMTEuMDQ0OTIyIEMgMTAuOTc0MzE1IDExLjExOTc4NSAxMC44NTk1NTkgMTEuMjEyMTczIDEwLjc3MTQ4NCAxMS4zMjIyNjYgQyAxMC42ODM0MSAxMS40MzIzNTkgMTAuNjE2MzAzIDExLjU1NDg5MSAxMC41NzIyNjYgMTEuNjkxNDA2IEMgMTAuNTMyNjMyIDExLjgyMzUxOCAxMC41MTM2NzIgMTEuOTYyMDk5IDEwLjUxMzY3MiAxMi4xMDc0MjIgQyAxMC41MTM2NzIgMTIuMjk2NzgyIDEwLjU0NzIyNiAxMi40NjExNDEgMTAuNjEzMjgxIDEyLjU5NzY1NiBDIDEwLjY4Mzc0MSAxMi43Mjk3NjggMTAuNzcyNzE5IDEyLjg0MTExNiAxMC44ODI4MTIgMTIuOTMzNTk0IEMgMTAuOTk3MzA5IDEzLjAyNjA3MiAxMS4xMjMyNSAxMy4xMDU4MTkgMTEuMjU5NzY2IDEzLjE3MTg3NSBDIDExLjQwMDY4NSAxMy4yMzM1MjcgMTEuNTQ0MTMgMTMuMjkwOTA1IDExLjY4OTQ1MyAxMy4zNDM3NSBDIDExLjgzOTE3OSAxMy4zOTY1OTQgMTEuOTcxNDQxIDEzLjQ0NTcgMTIuMDg1OTM4IDEzLjQ5NDE0MSBDIDEyLjIwMDQzNCAxMy41MzgxNzggMTIuMjk0Mjc3IDEzLjU4NjMyNSAxMi4zNjkxNDEgMTMuNjM0NzY2IEMgMTIuNDQ0MDA0IDEzLjY4MzIwNiAxMi41MDEzODIgMTMuNzM3MTc1IDEyLjU0MTAxNiAxMy43OTg4MjggQyAxMi41ODA2NDkgMTMuODU2MDc3IDEyLjYwMTU2MiAxMy45MjYwOTUgMTIuNjAxNTYyIDE0LjAwOTc2NiBDIDEyLjYwMTU2MiAxNC4xNDE4NzcgMTIuNTUwNTA0IDE0LjI0NTQ0OSAxMi40NDkyMTkgMTQuMzIwMzEyIEMgMTIuMzUyMzM3IDE0LjM5MDc3MiAxMi4xNjkwMTggMTQuNDI1NzgxIDExLjkwMDM5MSAxNC40MjU3ODEgQyAxMS42NjI1OSAxNC40MjU3ODEgMTEuNDM2NDg2IDE0LjM5NTYzNyAxMS4yMjA3MDMgMTQuMzMzOTg0IEMgMTEuMDA5MzI0IDE0LjI3MjMzMiAxMC44MjYwMDUgMTQuMjAzNzY5IDEwLjY3MTg3NSAxNC4xMjg5MDYgTCAxMC4zODg2NzIgMTQuODg4NjcyIEMgMTAuNDg1NTU0IDE0LjkzNzExNCAxMC42MzE5MTEgMTQuOTk5MzU2IDEwLjgzMDA3OCAxNS4wNzQyMTkgQyAxMS4wMzI2NDkgMTUuMTQ0Njc4IDExLjI5NTcxNiAxNS4xOTIzMjggMTEuNjE3MTg4IDE1LjIxODc1IEwgMTEuNjE3MTg4IDE2LjAwNTg1OSBMIDEyLjQ0OTIxOSAxNi4wMDU4NTkgTCAxMi40NDkyMTkgMTUuMTk5MjE5IEMgMTIuNjY1MDAxIDE1LjE2Mzk4OSAxMi44NDQ5MTEgMTUuMTA2NjExIDEyLjk5MDIzNCAxNS4wMjczNDQgQyAxMy4xMzk5NjEgMTQuOTQ4MDc2IDEzLjI2MTAzOCAxNC44NTQyMzMgMTMuMzUzNTE2IDE0Ljc0NDE0MSBDIDEzLjQ0NTk5NCAxNC42MzQwNDggMTMuNTEzMTAxIDE0LjUxNDQyNyAxMy41NTI3MzQgMTQuMzg2NzE5IEMgMTMuNTkyMzY4IDE0LjI1NDYwNyAxMy42MTEzMjggMTQuMTIyMzQ2IDEzLjYxMTMyOCAxMy45OTAyMzQgQyAxMy42MTEzMjggMTMuNzk2NDcgMTMuNTc2MzE5IDEzLjYyNzc0NCAxMy41MDU4NTkgMTMuNDgyNDIyIEMgMTMuNDM5ODA0IDEzLjMzNzA5OSAxMy4zNDU5NjEgMTMuMjExMTU4IDEzLjIyMjY1NiAxMy4xMDU0NjkgQyAxMy4xMDM3NTYgMTIuOTk1Mzc1IDEyLjk2MDMxIDEyLjg5ODEyNCAxMi43OTI5NjkgMTIuODE0NDUzIEMgMTIuNjMwMDMxIDEyLjczMDc4MyAxMi40NTE1NzYgMTIuNjU0NDQ0IDEyLjI1NzgxMiAxMi41ODM5ODQgQyAxMi4xMzAxMDQgMTIuNTM5OTQ3IDEyLjAyMDIxMiAxMi41MDAwNzMgMTEuOTI3NzM0IDEyLjQ2NDg0NCBDIDExLjgzOTY2IDEyLjQyNTIxIDExLjc2NDc3NyAxMi4zODUzMzcgMTEuNzAzMTI1IDEyLjM0NTcwMyBDIDExLjY0MTQ3MyAxMi4zMDYwNyAxMS41OTUyNzkgMTIuMjYxMzMxIDExLjU2NDQ1MyAxMi4yMTI4OTEgQyAxMS41MzgwMzEgMTIuMTYwMDQ2IDExLjUyNTM5MSAxMi4wOTk3NTYgMTEuNTI1MzkxIDEyLjAyOTI5NyBDIDExLjUyNTM5MSAxMS45MTQ4IDExLjU3MTU4NCAxMS44MjI0MTMgMTEuNjY0MDYyIDExLjc1MTk1MyBDIDExLjc1NjU0MSAxMS42ODE0OTQgMTEuOTA3NzYyIDExLjY0NjQ4NCAxMi4xMTkxNDEgMTEuNjQ2NDg0IEMgMTIuMzMwNTE5IDExLjY0NjQ4NCAxMi41MzEzNDMgMTEuNjY2OSAxMi43MjA3MDMgMTEuNzEwOTM4IEMgMTIuOTE0NDY3IDExLjc1NDk3NSAxMy4wODMxOTMgMTEuODA5NDQyIDEzLjIyODUxNiAxMS44NzEwOTQgTCAxMy40MzM1OTQgMTEuMDc4MTI1IEMgMTMuMzM2NzEyIDExLjAzNDA4OSAxMy4yMDQ0NTEgMTAuOTg5MzUgMTMuMDM3MTA5IDEwLjk0NTMxMiBDIDEyLjg3NDE3MiAxMC45MDEyNzUgMTIuNjc4MjEyIDEwLjg2NzcyMiAxMi40NDkyMTkgMTAuODQ1NzAzIEwgMTIuNDQ5MjE5IDEwLjE2MDE1NiBMIDExLjYxNzE4OCAxMC4xNjAxNTYgeiAiCiAgICAgaWQ9InBhdGg0IiAvPgogIDxnCiAgICAgaWQ9Imc2IgogICAgIHRyYW5zZm9ybT0ibWF0cml4KDAuMTMzMzU1MDYsMCwwLDAuMTMzMzU1MDYsMC40MzE0NTQ3LC04Ljk2ODAwNDEpIj4KICAgIDxnCiAgICAgICBpZD0idGV4dDgyOCIKICAgICAgIHN0eWxlPSJmb250LXN0eWxlOm5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zaXplOjQ5LjUzMzc5NDRweDtsaW5lLWhlaWdodDoxMjUlO2ZvbnQtZmFtaWx5OlNhbnM7dGV4dC1hbGlnbjplbmQ7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7dGV4dC1hbmNob3I6ZW5kO2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MTEuMzIyMDA5MDlweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgYXJpYS1sYWJlbD0iJCIgLz4KICA8L2c+Cjwvc3ZnPgo=) +} + +.icon-undo { + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+DQo8c3ZnPg0KPGcgaWQ9InVuZG8iPjxwYXRoIGQ9Ik0xMi41IDhjLTIuNjUgMC01LjA1Ljk5LTYuOSAyLjZMMiA3djloOWwtMy42Mi0zLjYyYzEuMzktMS4xNiAzLjE2LTEuODggNS4xMi0xLjg4IDMuNTQgMCA2LjU1IDIuMzEgNy42IDUuNWwyLjM3LS43OEMyMS4wOCAxMS4wMyAxNy4xNSA4IDEyLjUgOHoiPjwvcGF0aD48L2c+DQo8L3N2Zz4=) +} + +.icon-hourglass { + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB3aWR0aD0iMTc5MiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNMTUzNiAxMjhxMCAyNjEtMTA2LjUgNDYxLjV0LTI2Ni41IDMwNi41cTE2MCAxMDYgMjY2LjUgMzA2LjV0MTA2LjUgNDYxLjVoOTZxMTQgMCAyMyA5dDkgMjN2NjRxMCAxNC05IDIzdC0yMyA5aC0xNDcycS0xNCAwLTIzLTl0LTktMjN2LTY0cTAtMTQgOS0yM3QyMy05aDk2cTAtMjYxIDEwNi41LTQ2MS41dDI2Ni41LTMwNi41cS0xNjAtMTA2LTI2Ni41LTMwNi41dC0xMDYuNS00NjEuNWgtOTZxLTE0IDAtMjMtOXQtOS0yM3YtNjRxMC0xNCA5LTIzdDIzLTloMTQ3MnExNCAwIDIzIDl0OSAyM3Y2NHEwIDE0LTkgMjN0LTIzIDloLTk2em0tMTI4IDBoLTEwMjRxMCA2NiA5IDEyOGgxMDA2cTktNjEgOS0xMjh6bTAgMTUzNnEwLTEzMC0zNC0yNDkuNXQtOTAuNS0yMDgtMTI2LjUtMTUyLTE0Ni05NC41aC0yMzBxLTc2IDMxLTE0NiA5NC41dC0xMjYuNSAxNTItOTAuNSAyMDgtMzQgMjQ5LjVoMTAyNHoiLz48L3N2Zz4=) +} + +.icon-dashboard { + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDEyOCAxMjgiIGhlaWdodD0iMTI4cHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCAxMjggMTI4IiB3aWR0aD0iMTI4cHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxnPjxnPjxwYXRoIGQ9Ik0yNCw4MEg4LjV2OEgyNGMyLjIxMSwwLDQtMS43ODksNC00UzI2LjIxMSw4MCwyNCw4MHogTTI3LjIzLDQxLjU3NGwtNi42MTcsMy45MSAgICBjMC4xOCwwLjYzNywwLjQ2MSwxLjI0NiwwLjk2MSwxLjc0NmwxMS4zMTMsMTEuMzEzYzEuNTYzLDEuNTY2LDQuMDk0LDEuNTY2LDUuNjU2LDBjMS41NjYtMS41NjMsMS41NjYtNC4wOTQsMC01LjY1NiAgICBMMjcuMjMsNDEuNTc0eiBNNjAsMjguNTMxVjQ0YzAsMi4yMTEsMS43ODksNCw0LDRzNC0xLjc4OSw0LTRWMjhoLTRMNjAsMjguNTMxeiBNMTAwLjc3LDQxLjU3NEw4OS40NTcsNTIuODg3ICAgIGMtMS41NjYsMS41NjMtMS41NjYsNC4wOTQsMCw1LjY1NmMxLjU2MywxLjU2Niw0LjA5NCwxLjU2Niw1LjY1NiwwbDEwLjY5OS0xMC42OTlMMTAwLjc3LDQxLjU3NHogTTEwNCw4MGMtMi4yMTEsMC00LDEuNzg5LTQsNCAgICBzMS43ODksNCw0LDRoMTMuNXYtOEgxMDR6Ii8+PC9nPjwvZz48cGF0aCBkPSJNNjQsMjRDMzAuODYzLDI0LDQsNTAuODYzLDQsODRjMCw3LjAyMywxLjI3LDEzLjczNCwzLjQ4OCwyMGgxMTMuMDIzQzEyMi43Myw5Ny43MzQsMTI0LDkxLjAyMywxMjQsODQgIEMxMjQsNTAuODYzLDk3LjEzNywyNCw2NCwyNHogTTUyLjkzLDk2YzEuNjU2LTQuNjIxLDUuODc1LTgsMTEuMDctOHM5LjQxNCwzLjM3OSwxMS4wNyw4SDUyLjkzeiBNODIuNTEyLDkyLjVsMjMuNDMtMzMuNzE1ICBMNzIuNTcsODIuMDA4YzAuMDk4LDAuMDQ3LDAuMTgsMC4xMTcsMC4yNzMsMC4xNjRDNzAuMTcyLDgwLjgyOCw2Ny4xOTUsODAsNjQsODBjLTkuNjQxLDAtMTcuNTk0LDYuODk4LTE5LjQ1MywxNkgxMy40NDUgIEMxMi40ODQsOTIuMDMxLDEyLDg4LjAxNiwxMiw4NGMwLTI4LjY3MiwyMy4zMjgtNTIsNTItNTJzNTIsMjMuMzI4LDUyLDUyYzAsNC4wMTYtMC40ODQsOC4wMzEtMS40NDUsMTJIODMuNDUzIi8+PC9zdmc+); +} + +.icon-trash { + background-image: url(data:img/png;base64,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) +}
\ No newline at end of file diff --git a/src/main.js b/src/main.js new file mode 100644 index 0000000..e2242b0 --- /dev/null +++ b/src/main.js @@ -0,0 +1,40 @@ +import "core-js/stable"; +import "regenerator-runtime/runtime"; + +import Vue from 'vue' +import App from './App' +import router from './router' +import VueMoment from 'vue-moment' +import AsyncComputed from 'vue-async-computed' +import {sync} from 'vuex-router-sync' +import axios from "axios"; + + +// CSP config for webpack dynamic chunk loading +// eslint-disable-next-line +__webpack_nonce__ = btoa(OC.requestToken) + +// Correct the root of the app for chunk loading +// OC.linkTo matches the apps folders +// eslint-disable-next-line +__webpack_public_path__ = OC.linkTo('ransomware_detection', 'js/') + +import "./css/global.css" + +Vue.prototype.t = t +Vue.prototype.n = n +Vue.prototype.OC = OC +Vue.prototype.OCA = OCA +Vue.prototype.$axios = axios + +Vue.use(VueMoment); +Vue.use(AsyncComputed); + +Vue.config.devtools = true + +/* eslint-disable-next-line no-new */ +new Vue({ + el: '#content', + router, + render: h => h(App) +})
\ No newline at end of file diff --git a/src/router.js b/src/router.js new file mode 100644 index 0000000..c93277b --- /dev/null +++ b/src/router.js @@ -0,0 +1,31 @@ +import Vue from 'vue' +import Router from 'vue-router' +import {generateUrl} from 'nextcloud-server/dist/router' + +const Protection = () => import('./views/Protection') +const Recover = () => import('./views/Recover') +const History = () => import('./views/History') + +Vue.use(Router) + +export default new Router({ + base: generateUrl('/apps/ransowmare_detection/'), + linkActiveClass: 'active', + routes: [ + { + path: '/', + name: 'protection', + component: Protection, + }, + { + path: '/recover', + name: 'recover', + component: Recover, + }, + { + path: '/history', + name: 'history', + component: History, + }, + ], +})
\ No newline at end of file diff --git a/src/views/History.vue b/src/views/History.vue new file mode 100644 index 0000000..18bd197 --- /dev/null +++ b/src/views/History.vue @@ -0,0 +1,123 @@ +<template> + <AppContent> + <iron-pages :selected="page"> + <div id="loading" class="page"> + <paper-spinner active></paper-spinner> + </div> + <div class="page"> + <Header header="History"> + <RecoverAction id="recover" label="Recover selected files" v-on:recover="onRecover" primary></RecoverAction> + </Header> + <FileOperationsTable id="ransomware-table" :data="fileOperations" v-on:table-state-changed="tableStateChanged"></FileOperationsTable> + </div> + </iron-pages> + </AppContent> +</template> + +<script> +import '@polymer/paper-spinner/paper-spinner.js'; +import '@polymer/iron-pages/iron-pages.js'; +import FileOperationsTable from '../components/FileOperationsTable' +import Header from '../components/Header' +import RecoverAction from '../components/RecoverAction' +import AppContent from 'nextcloud-vue/dist/Components/AppContent' + +export default { + name: 'History', + components: { + AppContent, + FileOperationsTable, + Header, + RecoverAction + }, + data() { + return { + fileOperations: [], + page: 0 + }; + }, + mounted() { + this.page = 0; + this.fetchData(); + setInterval(() => this.fetchData(), 3000); + }, + computed: { + recoverUrl() { + return OC.generateUrl('/apps/ransomware_detection/api/v1/file-operation') + }, + fileOperationsUrl() { + return OC.generateUrl('/apps/ransomware_detection/api/v1/file-operation') + } + }, + methods: { + tableStateChanged() { + this.page = 1; + }, + fetchData() { + this.$axios({ + method: 'GET', + url: this.fileOperationsUrl + }) + .then(json => { + this.fileOperations = json.data; + }) + .catch( error => { console.error(error); }); + }, + onRecover() { + const items = document.querySelector('#ransomware-table').items; + const selected = document.querySelector('#ransomware-table').selectedItems; + for (var i = 0; i < selected.length; i++) { + this.recover(selected[i].id); + } + }, + remove(id) { + for (var i = 0; i < this.fileOperations.length; i++) { + if (this.fileOperations[i].id === id) { + this.fileOperations.splice(i, 1); + } + } + }, + async recover(id) { + await this.$axios({ + method: 'PUT', + url: this.recoverUrl + '/' + id + '/recover' + }) + .then(response => { + switch(response.status) { + case 204: + this.remove(id); + break; + default: + console.log(response); + break; + } + }) + .catch(error => { + console.error(error); + }); + } + } +} +</script> + +<style scoped> + #ransomware-table { + height: calc(100% - 50px); + } + #recover { + background-color: grey; + color: #fff; + } + iron-pages { + height: 100%; + } + .page { + height: 100%; + } + #loading { + display: flex; + align-items: center; + height: 90vh; + justify-content: center; + } +</style>
\ No newline at end of file diff --git a/src/views/Protection.vue b/src/views/Protection.vue new file mode 100644 index 0000000..3ce0848 --- /dev/null +++ b/src/views/Protection.vue @@ -0,0 +1,85 @@ +<template> + <AppContent> + <iron-pages selected="0"> + <div id="loading"> + <paper-spinner active></paper-spinner> + </div> + <div> + <ProtectionStatus :detection-link="detectionUrl" :protection-link="servicesUrl" id="protection-status" v-on:protection-state-changed="protectionStateChanged"></ProtectionStatus> + <div id="services"> + <ServiceStatus :link="detectionServiceUrl" description="Your files currently cannot be analyzed for ransomware. To enable ransomware detection, contact your system administator." v-on:service-state-changed="detectionStateChanged" class="service"></ServiceStatus> + <ServiceStatus :link="monitorServiceUrl" description="There may be a problem with your Nextcloud installation. Please contact your system administator." v-on:service-state-changed="monitorStateChanged" class="service"></ServiceStatus> + </div> + </div> + </iron-pages> + </AppContent> +</template> + +<script> +import '@polymer/paper-spinner/paper-spinner.js'; +import '@polymer/iron-pages/iron-pages.js'; +import AppContent from 'nextcloud-vue/dist/Components/AppContent' +import ProtectionStatus from '../components/ProtectionStatus' +import ServiceStatus from '../components/ServiceStatus' + +export default { + name: 'Protection', + components: { + AppContent, + ProtectionStatus, + ServiceStatus + }, + data() { + return { + protectionReady: false, + detectionReady: false, + monitorReady: false + }; + }, + computed: { + detectionUrl() { + return OC.generateUrl('/apps/ransomware_detection/api/v1/detection'); + }, + servicesUrl() { + return OC.generateUrl('/apps/ransomware_detection/api/v1/service'); + }, + detectionServiceUrl() { + return OC.generateUrl('/apps/ransomware_detection/api/v1/service/0'); + }, + monitorServiceUrl() { + return OC.generateUrl('/apps/ransomware_detection/api/v1/service/1'); + } + }, + methods: { + protectionStateChanged() { + this.protectionReady = true; + this.hideSpinner(); + }, + monitorStateChanged() { + this.monitorReady = true; + this.hideSpinner(); + }, + detectionStateChanged() { + this.detectionReady = true; + this.hideSpinner(); + }, + hideSpinner() { + if (this.protectionReady && this.monitorReady && this.detectionReady) { + document.querySelector('iron-pages').selectIndex(1); + } + } + } +} +</script> + +<style scoped> + #protection-status { + height: 40vh; + } + #loading { + display: flex; + align-items: center; + height: 90vh; + justify-content: center; + } +</style>
\ No newline at end of file diff --git a/src/views/Recover.vue b/src/views/Recover.vue new file mode 100644 index 0000000..51aa6ca --- /dev/null +++ b/src/views/Recover.vue @@ -0,0 +1,153 @@ +<template> + <AppContent> + <iron-pages :selected="page"> + <div id="loading" class="page"> + <paper-spinner active></paper-spinner> + </div> + <div class="page"> + <Header header="Recover"> + <RecoverAction v-if="detected" id="recover" label="Recover" v-on:recover="onRecover" primary></RecoverAction> + </Header> + <FileOperationsTable v-if="detected" id="ransomware-table" :data="fileOperations" v-on:table-state-changed="tableStateChanged"></FileOperationsTable> + <span id="message" v-if="!detected"> + <iron-icon icon="verified-user"></iron-icon> + Nothing found. You are safe. + </span> + </div> + </iron-pages> + </AppContent> +</template> + +<script> +import '@polymer/paper-spinner/paper-spinner.js'; +import '@polymer/iron-pages/iron-pages.js'; +import '@polymer/iron-icon/iron-icon.js'; +import '@polymer/iron-icons/iron-icons.js'; +import FileOperationsTable from '../components/FileOperationsTable' +import Header from '../components/Header' +import RecoverAction from '../components/RecoverAction' +import AppContent from 'nextcloud-vue/dist/Components/AppContent' + +export default { + name: 'Recover', + components: { + AppContent, + FileOperationsTable, + Header, + RecoverAction + }, + data() { + return { + detected: 0, + fileOperations: [], + page: 0 + }; + }, + mounted() { + this.page = 0; + this.fetchDetectionStatus(); + this.fetchData(); + }, + methods: { + fetchDetectionStatus() { + this.$axios({ + method: 'GET', + url: this.detectionsUrl + }) + .then(json => { + if (json.data.length > 0) { + this.detected = 1; + } else { + this.page = 1; + } + }) + .catch( error => { console.error(error); }); + }, + tableStateChanged() { + this.page = 1; + }, + fetchData() { + this.$axios({ + method: 'GET', + url: this.fileOperationsUrl + }) + .then(json => { + this.fileOperations = json.data; + }) + .catch( error => { console.error(error); }); + }, + onRecover() { + const items = document.querySelector('#ransomware-table').items; + const selected = document.querySelector('#ransomware-table').selectedItems; + for (var i = 0; i < selected.length; i++) { + this.recover(selected[i].id); + } + }, + remove(id) { + for (var i = 0; i < this.fileOperations.length; i++) { + if (this.fileOperations[i].id === id) { + this.fileOperations.splice(i, 1); + } + } + }, + async recover(id) { + await this.$axios({ + method: 'PUT', + url: this.recoverUrl + '/' + id + '/recover' + }) + .then(response => { + switch(response.status) { + case 204: + this.remove(id); + break; + default: + console.log(response); + break; + } + }) + .catch(error => { + console.error(error); + }); + } + }, + computed: { + detectionsUrl() { + return OC.generateUrl('/apps/ransomware_detection/api/v1/detection'); + }, + recoverUrl() { + return OC.generateUrl('/apps/ransomware_detection/api/v1/file-operation') + }, + fileOperationsUrl() { + return OC.generateUrl('/apps/ransomware_detection/api/v1/file-operation') + } + } +} +</script> + +<style scoped> + #ransomware-table { + height: calc(100% - 50px); + } + #recover { + background-color: green; + color: #fff; + } + #message { + display: flex; + justify-content: center; + font-size: 1.5em; + font-weight: bold; + } + iron-pages { + height: 100%; + } + .page { + height: 100%; + } + #loading { + display: flex; + align-items: center; + height: 90vh; + justify-content: center; + } +</style>
\ No newline at end of file diff --git a/src/webcomponents/ransomware-icons.js b/src/webcomponents/ransomware-icons.js new file mode 100644 index 0000000..9d23d52 --- /dev/null +++ b/src/webcomponents/ransomware-icons.js @@ -0,0 +1,34 @@ +/** +@license +Copyright (c) 2014 The Polymer Project Authors. All rights reserved. +This code may only be used under the BSD style license found at +http://polymer.github.io/LICENSE.txt The complete set of authors may be found at +http://polymer.github.io/AUTHORS.txt The complete set of contributors may be +found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as +part of the polymer project is also subject to an additional IP rights grant +found at http://polymer.github.io/PATENTS.txt +*/ +import '@polymer/iron-icon/iron-icon.js'; +import '@polymer/iron-iconset-svg/iron-iconset-svg.js'; + +import {html} from '@polymer/polymer/lib/utils/html-tag.js'; + +const template = html`<iron-iconset-svg name="ransomware" size="24"> +<svg><defs> +<g id="timelapse"><path d="M16.24 7.76C15.07 6.59 13.54 6 12 6v6l-4.24 4.24c2.34 2.34 6.14 2.34 8.49 0 2.34-2.34 2.34-6.14-.01-8.48zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"></path></g> +<g id="shield"><path d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm0 2.96c1.165 0 2.17.416 2.785 1.2.616.784.832 1.816.832 2.983v1.412h1.735c.162 0 .293.13.293.293v6.468c0 .163-.13.295-.293.295H6.648a.294.294 0 0 1-.293-.295V9.848c0-.163.13-.293.293-.293h1.735V8.143c0-1.167.216-2.199.832-2.983.615-.784 1.62-1.2 2.785-1.2zm0 1.589c-.832 0-1.239.214-1.535.592-.297.377-.494 1.04-.494 2.002v1.412h4.058V8.143c0-.961-.197-1.625-.494-2.002-.296-.378-.703-.592-1.535-.592zm-.383 4.611v.713a1.603 1.603 0 0 0-.502.172 1.083 1.083 0 0 0-.543.646 1.44 1.44 0 0 0-.058.416c0 .19.033.354.1.49.07.133.159.244.269.337.114.092.24.172.377.238.14.062.284.119.43.172.15.053.281.102.396.15.114.044.208.092.283.14a.56.56 0 0 1 .172.165c.04.057.06.127.06.21a.365.365 0 0 1-.152.311c-.097.07-.28.106-.549.106a2.46 2.46 0 0 1-.68-.092 3.522 3.522 0 0 1-.548-.205l-.283.76a5 5 0 0 0 .441.185c.203.07.466.118.787.145v.787h.832v-.807a1.7 1.7 0 0 0 .541-.172c.15-.079.271-.173.364-.283.092-.11.16-.23.199-.357.04-.132.058-.265.058-.397a1.15 1.15 0 0 0-.105-.508 1.078 1.078 0 0 0-.283-.377 1.837 1.837 0 0 0-.43-.29 4.653 4.653 0 0 0-.535-.231 11.1 11.1 0 0 1-.33-.12 1.734 1.734 0 0 1-.225-.118.447.447 0 0 1-.139-.133.403.403 0 0 1-.039-.184c0-.114.047-.207.14-.277.092-.07.243-.106.454-.106.212 0 .412.02.602.065.193.044.362.098.508.16l.205-.793a2.571 2.571 0 0 0-.397-.133 3.604 3.604 0 0 0-.588-.1v-.685h-.832z"/></g> +<g id="locked"><path d="M12 1a5 5 0 0 0-5 5v2H6a2 2 0 0 0-2 2v10c0 1.1.9 2 2 2h12a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2h-1V6a5 5 0 0 0-5-5zm0 1.9A3.1 3.1 0 0 1 15.1 6v2H8.9V6A3.1 3.1 0 0 1 12 2.9zm-.83 6.83h1.76V11a2.33 2.33 0 0 1 1.88 2.23h-1.3c-.03-.73-.41-1.23-1.46-1.23-.99 0-1.58.45-1.58 1.09 0 .55.43.91 1.76 1.26 1.33.34 2.75.91 2.75 2.57 0 1.2-.9 1.86-2.05 2.08v1.26h-1.76V19c-1.12-.24-2.08-.96-2.15-2.24h1.29c.06.69.54 1.23 1.74 1.23 1.3 0 1.58-.65 1.58-1.05 0-.55-.29-1.06-1.75-1.41-1.64-.4-2.76-1.07-2.76-2.42 0-1.13.92-1.87 2.05-2.12V9.73z"/></g> +<g id="hourglass"><path d="M1536 128q0 261-106.5 461.5t-266.5 306.5q160 106 266.5 306.5t106.5 461.5h96q14 0 23 9t9 23v64q0 14-9 23t-23 9h-1472q-14 0-23-9t-9-23v-64q0-14 9-23t23-9h96q0-261 106.5-461.5t266.5-306.5q-160-106-266.5-306.5t-106.5-461.5h-96q-14 0-23-9t-9-23v-64q0-14 9-23t23-9h1472q14 0 23 9t9 23v64q0 14-9 23t-23 9h-96zm-128 0h-1024q0 66 9 128h1006q9-61 9-128zm0 1536q0-130-34-249.5t-90.5-208-126.5-152-146-94.5h-230q-76 31-146 94.5t-126.5 152-90.5 208-34 249.5h1024z"/></g> +<g id="dashboard"><path d="M64,24C30.863,24,4,50.863,4,84c0,7.023,1.27,13.734,3.488,20h113.023C122.73,97.734,124,91.023,124,84 C124,50.863,97.137,24,64,24z M52.93,96c1.656-4.621,5.875-8,11.07-8s9.414,3.379,11.07,8H52.93z M82.512,92.5l23.43-33.715 L72.57,82.008c0.098,0.047,0.18,0.117,0.273,0.164C70.172,80.828,67.195,80,64,80c-9.641,0-17.594,6.898-19.453,16H13.445 C12.484,92.031,12,88.016,12,84c0-28.672,23.328-52,52-52s52,23.328,52,52c0,4.016-0.484,8.031-1.445,12H83.453"/></g> +<g id="file"><path d="M1152 512v-472q22 14 36 28l408 408q14 14 28 36h-472zm-128 32q0 40 28 68t68 28h544v1056q0 40-28 68t-68 28h-1344q-40 0-68-28t-28-68v-1600q0-40 28-68t68-28h800v544z"/></g> +<g id="trash" > + <path d="M 9.066406 1.101562 L 7.507812 2.199219 L 6.957031 1.667969 C 6.585938 1.316406 6.320312 1.183594 6.167969 1.265625 C 5.347656 1.765625 2.816406 3.734375 2.730469 3.933594 C 2.679688 4.082031 2.800781 4.398438 3 4.667969 L 3.351562 5.148438 L 1.710938 6.449219 C 0.636719 7.316406 0.0664062 7.898438 0.0351562 8.148438 C -0.0351562 8.535156 0.167969 8.851562 1.726562 10.964844 L 2.378906 11.867188 L 2.78125 11.484375 L 3.183594 11.117188 L 3.183594 18.984375 L 4.074219 19.25 C 5.398438 19.648438 7.960938 20 9.554688 20 C 11.09375 20 13.476562 19.667969 14.882812 19.25 L 15.753906 18.984375 L 15.753906 8.332031 L 11.632812 8.300781 L 7.507812 8.25 L 10.878906 5.867188 L 14.261719 3.484375 L 12.957031 1.785156 C 11.261719 -0.417969 11.210938 -0.417969 9.066406 1.101562 Z M 11.800781 2.234375 L 12.570312 3.148438 L 8.179688 6.265625 C 5.75 7.984375 3.535156 9.566406 3.234375 9.800781 L 2.699219 10.199219 L 2.09375 9.351562 C 1.761719 8.898438 1.476562 8.449219 1.476562 8.382812 C 1.457031 8.300781 10.425781 1.683594 10.996094 1.351562 C 11.011719 1.332031 11.363281 1.734375 11.800781 2.234375 Z M 14.414062 13.898438 L 14.414062 18.300781 L 13.542969 18.5 C 11.546875 18.933594 7.003906 18.867188 5.078125 18.382812 L 4.691406 18.300781 L 4.691406 14.285156 C 4.691406 10.398438 4.710938 10.265625 5.0625 9.882812 C 5.414062 9.5 5.429688 9.5 9.921875 9.5 L 14.414062 9.5 Z M 14.414062 13.898438 "/> + <path d="M 6.199219 14.25 L 6.199219 17.167969 L 7.375 17.167969 L 7.375 11.332031 L 6.199219 11.332031 Z M 6.199219 14.25 "/> + <path d="M 8.882812 14.25 L 8.882812 17.167969 L 10.054688 17.167969 L 10.054688 11.332031 L 8.882812 11.332031 Z M 8.882812 14.25 "/> + <path d="M 11.730469 14.25 L 11.730469 17.167969 L 12.90625 17.167969 L 12.90625 11.332031 L 11.730469 11.332031 Z M 11.730469 14.25 "/> + <path d="M 14.496094 5.382812 C 14.128906 5.515625 13.609375 5.714844 13.339844 5.867188 C 12.871094 6.117188 12.90625 6.117188 14.09375 6.050781 C 15.082031 5.984375 15.519531 6.035156 16.289062 6.316406 C 17.847656 6.917969 19.054688 8.367188 19.359375 10.035156 L 19.476562 10.667969 L 17.597656 10.667969 L 19.121094 12.582031 C 19.960938 13.632812 20.699219 14.5 20.78125 14.5 C 20.867188 14.5 21.601562 13.632812 22.441406 12.582031 L 23.964844 10.667969 L 22.492188 10.667969 L 22.289062 9.867188 C 21.839844 8.035156 20.546875 6.5 18.804688 5.699219 C 17.664062 5.183594 15.585938 5.035156 14.496094 5.382812 Z M 14.496094 5.382812 "/> +</g> +</defs></svg> +</iron-iconset-svg>`; + +document.head.appendChild(template.content); |