diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 39a929d42d7..2b57067cbec 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -36,6 +36,7 @@ import { CollectionAddEditComponent as OrgCollectionAddEditComponent, } from './organizations/manage/collection-add-edit.component'; import { CollectionsComponent as OrgManageCollectionsComponent } from './organizations/manage/collections.component'; +import { EntityEventsComponent as OrgEntityEventsComponent } from './organizations/manage/entity-events.component'; import { EntityUsersComponent as OrgEntityUsersComponent } from './organizations/manage/entity-users.component'; import { EventsComponent as OrgEventsComponent } from './organizations/manage/events.component'; import { GroupAddEditComponent as OrgGroupAddEditComponent } from './organizations/manage/group-add-edit.component'; @@ -43,7 +44,6 @@ import { GroupsComponent as OrgGroupsComponent } from './organizations/manage/gr import { ManageComponent as OrgManageComponent } from './organizations/manage/manage.component'; import { PeopleComponent as OrgPeopleComponent } from './organizations/manage/people.component'; import { UserAddEditComponent as OrgUserAddEditComponent } from './organizations/manage/user-add-edit.component'; -import { UserEventsComponent as OrgUserEventsComponent } from './organizations/manage/user-events.component'; import { UserGroupsComponent as OrgUserGroupsComponent } from './organizations/manage/user-groups.component'; import { ExportComponent as OrgExportComponent } from './organizations/tools/export.component'; @@ -179,6 +179,7 @@ import { SearchPipe } from 'jslib/angular/pipes/search.pipe'; OrgCiphersComponent, OrgCollectionAddEditComponent, OrgCollectionsComponent, + OrgEntityEventsComponent, OrgEntityUsersComponent, OrgEventsComponent, OrgExportComponent, @@ -191,7 +192,6 @@ import { SearchPipe } from 'jslib/angular/pipes/search.pipe'; OrgPeopleComponent, OrgToolsComponent, OrgUserAddEditComponent, - OrgUserEventsComponent, OrgUserGroupsComponent, OrganizationsComponent, OrganizationLayoutComponent, @@ -241,10 +241,10 @@ import { SearchPipe } from 'jslib/angular/pipes/search.pipe'; OrgAttachmentsComponent, OrgCollectionAddEditComponent, OrgCollectionsComponent, + OrgEntityEventsComponent, OrgEntityUsersComponent, OrgGroupAddEditComponent, OrgUserAddEditComponent, - OrgUserEventsComponent, OrgUserGroupsComponent, PasswordGeneratorHistoryComponent, PurgeVaultComponent, diff --git a/src/app/organizations/manage/user-events.component.html b/src/app/organizations/manage/entity-events.component.html similarity index 93% rename from src/app/organizations/manage/user-events.component.html rename to src/app/organizations/manage/entity-events.component.html index 4e102768854..5dd9716301a 100644 --- a/src/app/organizations/manage/user-events.component.html +++ b/src/app/organizations/manage/entity-events.component.html @@ -41,6 +41,7 @@ {{'device' | i18n}} + {{'user' | i18n}} {{'event' | i18n}} @@ -50,6 +51,9 @@ + + {{e.userName}} + diff --git a/src/app/organizations/manage/user-events.component.ts b/src/app/organizations/manage/entity-events.component.ts similarity index 62% rename from src/app/organizations/manage/user-events.component.ts rename to src/app/organizations/manage/entity-events.component.ts index 743523ba034..6f9e72026ec 100644 --- a/src/app/organizations/manage/user-events.component.ts +++ b/src/app/organizations/manage/entity-events.component.ts @@ -15,13 +15,15 @@ import { EventResponse } from 'jslib/models/response/eventResponse'; import { ListResponse } from 'jslib/models/response/listResponse'; @Component({ - selector: 'app-user-events', - templateUrl: 'user-events.component.html', + selector: 'app-entity-events', + templateUrl: 'entity-events.component.html', }) -export class UserEventsComponent implements OnInit { +export class EntityEventsComponent implements OnInit { @Input() name: string; - @Input() organizationUserId: string; + @Input() entity: 'user' | 'cipher'; + @Input() entityId: string; @Input() organizationId: string; + @Input() showUser = false; loading = true; loaded = false; @@ -32,6 +34,9 @@ export class UserEventsComponent implements OnInit { refreshPromise: Promise; morePromise: Promise; + private orgUsersUserIdMap = new Map(); + private orgUsersIdMap = new Map(); + constructor(private apiService: ApiService, private i18nService: I18nService, private eventService: EventService, private toasterService: ToasterService) { } @@ -39,6 +44,18 @@ export class UserEventsComponent implements OnInit { const defaultDates = this.eventService.getDefaultDateFilters(); this.start = defaultDates[0]; this.end = defaultDates[1]; + await this.load(); + } + + async load() { + if (this.showUser) { + const response = await this.apiService.getOrganizationUsers(this.organizationId); + response.data.forEach((u) => { + const name = u.name == null || u.name.trim() === '' ? u.email : u.name; + this.orgUsersIdMap.set(u.id, { name: name, email: u.email }); + this.orgUsersUserIdMap.set(u.userId, { name: name, email: u.email }); + }); + } await this.loadEvents(true); this.loaded = true; } @@ -60,8 +77,14 @@ export class UserEventsComponent implements OnInit { this.loading = true; let response: ListResponse; try { - const promise = this.apiService.getEventsOrganizationUser(this.organizationId, this.organizationUserId, - dates[0], dates[1], clearExisting ? null : this.continuationToken); + let promise: Promise; + if (this.entity === 'user') { + promise = this.apiService.getEventsOrganizationUser(this.organizationId, this.entityId, + dates[0], dates[1], clearExisting ? null : this.continuationToken); + } else { + promise = this.apiService.getEventsCipher(this.entityId, + dates[0], dates[1], clearExisting ? null : this.continuationToken); + } if (clearExisting) { this.refreshPromise = promise; } else { @@ -74,11 +97,15 @@ export class UserEventsComponent implements OnInit { const events = response.data.map((r) => { const userId = r.actingUserId == null ? r.userId : r.actingUserId; const eventInfo = this.eventService.getEventInfo(r); + const user = this.showUser && userId != null && this.orgUsersUserIdMap.has(userId) ? + this.orgUsersUserIdMap.get(userId) : null; return { message: eventInfo.message, appIcon: eventInfo.appIcon, appName: eventInfo.appName, userId: userId, + userName: user != null ? user.name : this.showUser ? this.i18nService.t('unknown') : null, + userEmail: user != null ? user.email : this.showUser ? '' : null, date: r.date, ip: r.ipAddress, type: r.type, diff --git a/src/app/organizations/manage/people.component.ts b/src/app/organizations/manage/people.component.ts index 7f1ee2274d2..c4580afa015 100644 --- a/src/app/organizations/manage/people.component.ts +++ b/src/app/organizations/manage/people.component.ts @@ -26,8 +26,8 @@ import { OrganizationUserType } from 'jslib/enums/organizationUserType'; import { Utils } from 'jslib/misc/utils'; import { ModalComponent } from '../../modal.component'; +import { EntityEventsComponent } from './entity-events.component'; import { UserAddEditComponent } from './user-add-edit.component'; -import { UserEventsComponent } from './user-events.component'; import { UserGroupsComponent } from './user-groups.component'; @Component({ @@ -174,12 +174,14 @@ export class PeopleComponent implements OnInit { const factory = this.componentFactoryResolver.resolveComponentFactory(ModalComponent); this.modal = this.eventsModalRef.createComponent(factory).instance; - const childComponent = this.modal.show( - UserEventsComponent, this.eventsModalRef); + const childComponent = this.modal.show( + EntityEventsComponent, this.eventsModalRef); - childComponent.name = user != null ? user.name || user.email : null; + childComponent.name = user.name || user.email; childComponent.organizationId = this.organizationId; - childComponent.organizationUserId = user != null ? user.id : null; + childComponent.entityId = user.id; + childComponent.showUser = false; + childComponent.entity = 'user'; this.modal.onClosed.subscribe(() => { this.modal = null; diff --git a/src/app/organizations/vault/ciphers.component.ts b/src/app/organizations/vault/ciphers.component.ts index 30e6f12ddd3..8f3561518f3 100644 --- a/src/app/organizations/vault/ciphers.component.ts +++ b/src/app/organizations/vault/ciphers.component.ts @@ -1,4 +1,8 @@ -import { Component } from '@angular/core'; +import { + Component, + EventEmitter, + Output, +} from '@angular/core'; import { ToasterService } from 'angular2-toaster'; import { Angulartics2 } from 'angulartics2'; @@ -20,7 +24,10 @@ import { CiphersComponent as BaseCiphersComponent } from '../../vault/ciphers.co templateUrl: '../../vault/ciphers.component.html', }) export class CiphersComponent extends BaseCiphersComponent { + @Output() onEventsClicked = new EventEmitter(); + organization: Organization; + accessEvents = false; constructor(cipherService: CipherService, analytics: Angulartics2, toasterService: ToasterService, i18nService: I18nService, @@ -33,6 +40,7 @@ export class CiphersComponent extends BaseCiphersComponent { await super.load(); return; } + this.accessEvents = this.organization.useEvents; const ciphers = await this.apiService.getCiphersOrganization(this.organization.id); if (ciphers != null && ciphers.data != null && ciphers.data.length) { const decCiphers: CipherView[] = []; @@ -64,4 +72,8 @@ export class CiphersComponent extends BaseCiphersComponent { checkCipher(c: CipherView) { // do nothing } + + events(c: CipherView) { + this.onEventsClicked.emit(c); + } } diff --git a/src/app/organizations/vault/vault.component.html b/src/app/organizations/vault/vault.component.html index 7b56cb9923f..0673f9ab074 100644 --- a/src/app/organizations/vault/vault.component.html +++ b/src/app/organizations/vault/vault.component.html @@ -13,7 +13,7 @@ + (onCollectionsClicked)="editCipherCollections($event)" (onEventsClicked)="viewEvents($event)"> @@ -21,3 +21,4 @@ + diff --git a/src/app/organizations/vault/vault.component.ts b/src/app/organizations/vault/vault.component.ts index 3d4ec131ff6..3cb7d11413f 100644 --- a/src/app/organizations/vault/vault.component.ts +++ b/src/app/organizations/vault/vault.component.ts @@ -22,6 +22,7 @@ import { CipherType } from 'jslib/enums/cipherType'; import { ModalComponent } from '../../modal.component'; +import { EntityEventsComponent } from '../manage/entity-events.component'; import { AddEditComponent } from './add-edit.component'; import { AttachmentsComponent } from './attachments.component'; import { CiphersComponent } from './ciphers.component'; @@ -38,6 +39,7 @@ export class VaultComponent implements OnInit { @ViewChild('attachments', { read: ViewContainerRef }) attachmentsModalRef: ViewContainerRef; @ViewChild('cipherAddEdit', { read: ViewContainerRef }) cipherAddEditModalRef: ViewContainerRef; @ViewChild('collections', { read: ViewContainerRef }) collectionsModalRef: ViewContainerRef; + @ViewChild('eventsTemplate', { read: ViewContainerRef }) eventsModalRef: ViewContainerRef; organization: Organization; collectionId: string; @@ -210,6 +212,27 @@ export class VaultComponent implements OnInit { return childComponent; } + async viewEvents(cipher: CipherView) { + if (this.modal != null) { + this.modal.close(); + } + + const factory = this.componentFactoryResolver.resolveComponentFactory(ModalComponent); + this.modal = this.eventsModalRef.createComponent(factory).instance; + const childComponent = this.modal.show( + EntityEventsComponent, this.eventsModalRef); + + childComponent.name = cipher.name; + childComponent.organizationId = this.organization.id; + childComponent.entityId = cipher.id; + childComponent.showUser = true; + childComponent.entity = 'cipher'; + + this.modal.onClosed.subscribe(() => { + this.modal = null; + }); + } + private clearFilters() { this.collectionId = null; this.type = null; diff --git a/src/app/vault/ciphers.component.html b/src/app/vault/ciphers.component.html index d8566ed3ea9..67b092c687e 100644 --- a/src/app/vault/ciphers.component.html +++ b/src/app/vault/ciphers.component.html @@ -38,6 +38,10 @@ {{'collections' | i18n}} + + + {{'eventLogs' | i18n}} + {{'delete' | i18n}}