Skip to content

Commit

Permalink
Impersonate Feature (#139)
Browse files Browse the repository at this point in the history
* Added impersonate feature

* Fixed losing selected values after page refresh

* Changed impersonation method to CallerObjectId

* Add badge for impersonation

* Exclude current user

* Fixed badge rendering to be live

* 1. Automatically refresh tab when impersonation is checked/unchecked.
2. Bump version

* Added check if user has permission to impersonate

Co-authored-by: Natraj Yegnaraman <natraj@dreamingincrm.com>
  • Loading branch information
DynamicsNinja and rajyraman committed Jan 31, 2021
1 parent ae00b55 commit 756ff61
Show file tree
Hide file tree
Showing 10 changed files with 537 additions and 456 deletions.
33 changes: 25 additions & 8 deletions app/manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
{
"background": {
"scripts": ["/scripts/background.js"],
"persistant": false
"scripts": [
"/scripts/background.js"
],
"persistant": true
},
"browser_action": {
"default_icon": {
Expand All @@ -15,17 +17,32 @@
"description": "Quickly perform advanced/hidden actions in Dynamics 365/Power Apps, without bookmarklets.",
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"],
"js": ["/scripts/app.js"]
"matches": [
"http://*/*",
"https://*/*"
],
"js": [
"/scripts/app.js"
]
}
],
"icons": {
"16": "icon-16.png",
"48": "icon-48.png",
"128": "icon-128.png"
},
"permissions": ["activeTab", "tabs"],
"web_accessible_resources": ["/scripts/Sdk.Soap.min.js", "/scripts/levelup.extension.js"],
"version": "3.5.5",
"permissions": [
"storage",
"activeTab",
"tabs",
"webRequest",
"webRequestBlocking",
"*://*.dynamics.com/api/*"
],
"web_accessible_resources": [
"/scripts/Sdk.Soap.min.js",
"/scripts/levelup.extension.js"
],
"version": "3.5.6",
"manifest_version": 2
}
}
33 changes: 33 additions & 0 deletions app/pages/options.html
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,20 @@
.mdl-layout__content {
padding-bottom: 15px;
}

#impersonate-tab{
display: none;
}

.impersonization-rows{
display: flex;
align-items: center;
justify-content: center;
}

label.mdl-switch{
width: auto;
}
</style>
</head>
<body>
Expand All @@ -71,6 +85,7 @@
<a href="#fixed-tab-1" class="mdl-layout__tab is-active">Forms</a>
<a href="#fixed-tab-2" class="mdl-layout__tab">Navigation</a>
<a href="#fixed-tab-3" class="mdl-layout__tab">View</a>
<a id="impersonate-tab" href="#fixed-tab-4" class="mdl-layout__tab">Impersonate</a>
</div>
</header>
<div class="mdl-layout__drawer">
Expand Down Expand Up @@ -448,6 +463,24 @@
</div>
</div>
</section>
<section class="mdl-layout__tab-panel" id="fixed-tab-4">
<div class="page-content">
<div class="mdl-grid">
<div class="mdl-cell mdl-cell--8-col impersonization-rows">
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<select class="mdl-textfield__input" id="users-dropdown" name="users-dropdown"></select>
<label id="users-dropdown-label" class="mdl-textfield__label" for="users-dropdown">User to impersonate</label>
</div>
</div>
<div class="mdl-cell mdl-cell--8-col impersonization-rows">
<label class="mdl-switch mdl-js-switch mdl-js-ripple-effect" for="impersonate-toggle">
<input type="checkbox" id="impersonate-toggle" class="mdl-switch__input">
<span id="impersonate-cbx-label" class="mdl-switch__label">OFF</span>
</label>
</div>
</div>
</div>
</section>
</main>
</div>
</div>
Expand Down
16 changes: 15 additions & 1 deletion app/scripts/app.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/// <reference path="inject/levelup.common.utility.ts" />

import { Utility } from './inject/levelup.common.utility';
import { ICustomMessage, IExtensionMessage, AreaType } from './interfaces/types';
import { ICustomMessage, IExtensionMessage, AreaType, LocalStorage, IExtensionLocalStorage } from './interfaces/types';

class App {
isCRMPage: boolean;
Expand All @@ -17,6 +17,8 @@ class App {
}

start() {
this.initializeStorage();

this.hookupEventListeners();
if (this.isCRMPage) {
Utility.injectScript(chrome.extension.getURL('scripts/Sdk.Soap.min.js'));
Expand All @@ -27,6 +29,18 @@ class App {
}
}

private initializeStorage() {
chrome.storage.local.get([LocalStorage.lastUrl], function (result: IExtensionLocalStorage) {
let lastUrl = result.lastUrl;
let currentUrl = document.location.origin;

if (lastUrl != currentUrl) {
chrome.storage.local.clear();
chrome.storage.local.set({ [LocalStorage.lastUrl]: currentUrl });
}
});
}

private hookupEventListeners() {
document.addEventListener('levelup', (data: ICustomMessage) => {
if (data.detail && data.detail.type === 'Page') {
Expand Down
123 changes: 119 additions & 4 deletions app/scripts/background.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,39 @@
import { IResultRow, IResultRowKeyValues, IExtensionMessage } from './interfaces/types';
import {
IResultRow,
IResultRowKeyValues,
IExtensionMessage,
IImpersonateMessage,
LocalStorage,
IExtensionLocalStorage,
} from './interfaces/types';

let content: IResultRow[] | IResultRowKeyValues[][] | IImpersonateMessage | string;
let userId: string;

chrome.storage.local.clear();

let content: IResultRow[] | IResultRowKeyValues[][] | string;
chrome.runtime.onMessage.addListener(function (message: IExtensionMessage, sender, sendResponse) {
if (message.type === 'Page') {
let c = message.category.toString();
switch (c) {
case 'canImpersonate':
chrome.tabs.query({ active: true }, function (tabs) {
chrome.tabs.sendMessage(tabs[0].id, {
category: 'canImpersonate',
type: 'Background',
content: message.content,
});
});
break;
case 'allUsers':
chrome.tabs.query({ active: true }, function (tabs) {
chrome.tabs.sendMessage(tabs[0].id, {
category: 'allUsers',
type: 'Background',
content: message.content,
});
});
break;
case 'Settings':
content = message.content;
chrome.tabs.create({
Expand All @@ -28,8 +57,10 @@ chrome.runtime.onMessage.addListener(function (message: IExtensionMessage, sende
});
break;
case 'Extension':
if (message.content === 'On') chrome.browserAction.enable(sender.tab.id);
else if (message.content === 'Off') chrome.browserAction.disable(sender.tab.id);
renderBadge();
if (message.content === 'On') {
chrome.browserAction.enable(sender.tab.id);
} else if (message.content === 'Off') chrome.browserAction.disable(sender.tab.id);
break;
case 'Load':
sendResponse(content);
Expand All @@ -49,6 +80,60 @@ chrome.runtime.onMessage.addListener(function (message: IExtensionMessage, sende
default:
break;
}
} else if (message.type === 'Impersonate') {
let category = message.category;
let impersonizationMessage = <IImpersonateMessage>message.content;

renderBadge();

switch (category) {
case 'activation':
userId = impersonizationMessage.UserId;

chrome.webRequest.onBeforeSendHeaders.removeListener(headerListener);

if (impersonizationMessage.IsActive) {
chrome.webRequest.onBeforeSendHeaders.addListener(
headerListener,
{
urls: [impersonizationMessage.Url + 'api/*'],
},
['blocking', 'requestHeaders', 'extraHeaders']
);
}
break;
case 'changeUser':
userId = impersonizationMessage.UserId;
break;
}
} else if (message.type === 'API') {
let c = message.category.toString();
switch (c) {
case 'allUsers':
chrome.tabs.query(
{
active: true,
},
function (tabs) {
chrome.tabs.executeScript(tabs[0].id, {
code: `window.postMessage({ type: '${c}', category: '${message.type}' }, '*');`,
});
}
);
break;
case 'canImpersonate':
chrome.tabs.query(
{
active: true,
},
function (tabs) {
chrome.tabs.executeScript(tabs[0].id, {
code: `window.postMessage({ type: '${c}', category: '${message.type}' }, '*');`,
});
}
);
break;
}
} else {
chrome.tabs.query(
{
Expand All @@ -64,3 +149,33 @@ chrome.runtime.onMessage.addListener(function (message: IExtensionMessage, sende
);
}
});

function headerListener(details: chrome.webRequest.WebRequestHeadersDetails) {
details.requestHeaders.push({
name: 'CallerObjectId',
value: userId,
});

return { requestHeaders: details.requestHeaders };
}

function renderBadge(){
chrome.storage.local.get([LocalStorage.isImpersonating, LocalStorage.userName], function (
result: IExtensionLocalStorage
) {
if (result.isImpersonating) {
chrome.browserAction.setBadgeBackgroundColor({ color: [255, 0, 0, 255] });
chrome.browserAction.setTitle({ title: `Impersonating ${result.userName}` });
chrome.browserAction.setBadgeText({
text: result.userName
.split(' ')
.map((x) => x[0])
.join(''),
});
} else {
chrome.browserAction.setBadgeBackgroundColor({ color: [0, 0, 0, 0] });
chrome.browserAction.setBadgeText({ text: null });
chrome.browserAction.setTitle({ title: '' });
}
});
}
2 changes: 1 addition & 1 deletion app/scripts/inject/levelup.common.utility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export class Utility {
});
}

messageExtension(message: any[], category: Category): void {
messageExtension(message: any[] | boolean, category: Category): void {
let extensionMessage = {
type: 'Page',
category: category,
Expand Down
Loading

0 comments on commit 756ff61

Please sign in to comment.