Compare commits

..
34 Commits
Author SHA1 Message Date
marc 43d5279acf package.json 2021-12-23 13:30:37 +01:00
Dimitri Witkowski e674607fce Merge pull request #1945 from dolfinus/develop
Install NativeMessagingHost for Chromium and Chrome dev/canary
2021-12-20 22:01:39 +01:00
dolfinus 2e6239f637 Install NativeMessagingHost for Chromium and Chrome dev/canary 2021-12-06 00:53:23 +03:00
antelle bcb8ddc17b fixed msteams html file name 2021-07-19 23:21:43 +02:00
antelle cd619582ec fixed variable usage 2021-07-13 19:05:25 +02:00
antelle 3b49562f64 added MS Teams apps 2021-07-13 19:04:27 +02:00
antelle 674ed2cc72 renames some ms teams constants 2021-07-11 16:22:38 +02:00
Dimitri Witkowski 5621fd8a06 Merge pull request #1856 from saulobejo/develop
added teams storage; added tenant id configuration for microsot
2021-07-11 16:18:53 +02:00
Saulo Alves af27e0b111 renamed teams to microsoft teams; fixed grunt prettier error in onedrive 2021-07-11 10:52:40 +02:00
Saulo Alves 5a78e12f7d added teams storage; added tenant id configuration for onedrive and teams 2021-07-11 00:56:51 +02:00
antelle 47b19d1d74 Merge branch 'master' into develop 2021-05-19 10:42:24 +02:00
antelle 070573e68a Merge branch 'release-1.18'
Build / web (push) Canceled after 0s
Build / linux (push) Canceled after 0s
Build / darwin (push) Canceled after 0s
Build / win32 (push) Canceled after 0s
Build / publish (push) Canceled after 0s
2021-05-19 10:35:35 +02:00
antelle 946d7769f1 bump version 2021-05-19 10:33:41 +02:00
antelle f0426a3ec2 fix #1820: minimize on close on macOS 2021-05-19 10:33:17 +02:00
antelle b2d67818a8 fix #1818: extension connection error if browser cannot be identified 2021-05-19 10:16:40 +02:00
antelle c0b159d824 fix #1824: saving KDBX3 files with compression disabled 2021-05-19 10:07:59 +02:00
antelle d7e11e20c1 Merge branch 'master' into develop 2021-05-14 21:18:46 +02:00
antelle d590318153 Merge branch 'release-1.18'
Build / web (push) Canceled after 0s
Build / linux (push) Canceled after 0s
Build / darwin (push) Canceled after 0s
Build / win32 (push) Canceled after 0s
Build / publish (push) Canceled after 0s
2021-05-14 21:02:12 +02:00
antelle 5816ca9c4d bump version 2021-05-14 21:01:43 +02:00
antelle 6627e8c0ec fix #1817: crash on files with large attachments as KDBX3 2021-05-14 21:00:43 +02:00
antelle 379e28e8a4 about version in the the bug report template 2021-05-14 20:56:48 +02:00
antelle 4fb25451a2 fix #1816: old Chromium support, such as Android Edge 2021-05-13 10:45:48 +02:00
antelle 58a6a97130 Merge branch 'master' into develop 2021-05-12 21:43:27 +02:00
antelle f59b846643 Merge branch 'release-1.18'
Build / linux (push) Canceled after 0s
Build / web (push) Canceled after 0s
Build / darwin (push) Canceled after 0s
Build / win32 (push) Canceled after 0s
Build / publish (push) Canceled after 0s
2021-05-12 21:40:25 +02:00
antelle 28d7dfeb67 bump version 2021-05-12 21:39:47 +02:00
antelle 6cc39d2107 fix #1808: restore KeeWeb from system tray on extension request 2021-05-12 21:38:54 +02:00
antelle a92a01095b fix #1814: option to disable auto-type title filter by default 2021-05-12 21:00:51 +02:00
antelle 3f4cddd2a2 fix #1811: app doesn't quit during update 2021-05-12 20:50:24 +02:00
antelle aa734ea976 fix #1813: fixed custom icon selection 2021-05-11 18:50:37 +02:00
antelle 30628004fd https link in the readme 2021-05-10 20:40:38 +02:00
antelle a6e87b52fd fix #1810: fixed the extension on old macOS starting 10.11 2021-05-10 20:40:30 +02:00
antelle 6d06d825e5 fixed donate button border 2021-05-10 18:12:11 +02:00
antelle 0800d5f33a Merge branch 'master' into develop 2021-05-09 23:25:27 +02:00
antelle 6929931cae Merge branch 'master' into develop 2021-05-09 09:04:12 +02:00
27 changed files with 1007 additions and 21498 deletions
+2
View File
@@ -7,6 +7,8 @@
}],
"@babel/plugin-proposal-optional-catch-binding",
"@babel/plugin-proposal-optional-chaining",
"@babel/plugin-proposal-nullish-coalescing-operator",
"@babel/plugin-proposal-logical-assignment-operators",
"@babel/plugin-external-helpers"
]
}
+1 -1
View File
@@ -24,7 +24,7 @@ A clear and concise description of what you expected to happen.
If applicable, add screenshots to help explain your problem.
**Environment**
Your user-agent (from Settings/Help section)
Please copy all version information from Settings/Help
**Kdbx File**
Does it happen on Demo or New database?
+16
View File
@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>KeeWeb</title>
</head>
<body>
<script>
window.opener.postMessage(
{ storage: 'teams', search: location.search },
window.location.origin
);
window.close();
</script>
</body>
</html>
+1 -1
View File
@@ -258,7 +258,7 @@ const AutoType = {
if (entries.length === 0) {
if (evt.filter.useUrl) {
evt.filter.useUrl = false;
if (evt.filter.title) {
if (evt.filter.title && AppSettingsModel.autoTypeTitleFilterEnabled) {
evt.filter.useTitle = true;
}
}
+1 -1
View File
@@ -325,7 +325,7 @@ async function findEntry(request, returnIfOneMatch, filterOptions) {
if (!entries.length) {
if (AppSettingsModel.extensionFocusIfEmpty) {
filter.useUrl = false;
if (filter.title) {
if (filter.title && AppSettingsModel.autoTypeTitleFilterEnabled) {
filter.useTitle = true;
entries = filter.getEntries();
if (!entries.length) {
@@ -191,7 +191,7 @@ const Launcher = {
}
},
quitOnRealQuitEventIfMinimizeOnQuitIsEnabled() {
return !!(this.platform() === 'darwin' || this.pendingUpdateFile);
return !!this.pendingUpdateFile;
},
minimizeApp() {
this.remoteApp().minimizeApp({
+14 -1
View File
@@ -37,4 +37,17 @@ const OneDriveApps = {
}
};
export { DropboxApps, GDriveApps, OneDriveApps };
const MsTeamsApps = {
Local: {
id: '8fbe2245-13d5-446f-bedc-74c3b2e1f635'
},
Production: {
id: '8fbe2245-13d5-446f-bedc-74c3b2e1f635'
},
Desktop: {
id: '8fbe2245-13d5-446f-bedc-74c3b2e1f635',
secret: 'F02~HYaWs-~7MndJcVRtv9~h-50Brk_9ho'
}
};
export { DropboxApps, GDriveApps, OneDriveApps, MsTeamsApps };
+8 -1
View File
@@ -18,6 +18,7 @@ const DefaultAppSettings = {
colorfulIcons: false, // use colorful custom icons instead of grayscale
useMarkdown: true, // use Markdown in Notes field
directAutotype: true, // if only one matching entry is found, select that one automatically
autoTypeTitleFilterEnabled: true, // enable the title filtering in auto-type by default
titlebarStyle: 'default', // window titlebar style
lockOnMinimize: true, // lock the app when it's minimized
lockOnCopy: false, // lock the app after a password was copied
@@ -86,7 +87,13 @@ const DefaultAppSettings = {
onedrive: true, // enable OneDrive integration
onedriveClientId: null, // custom OneDrive client id
onedriveClientSecret: null // custom OneDrive client secret
onedriveClientSecret: null, // custom OneDrive client secret
onedriveTenantId: null, // custom OneDrive tenant id
msteams: false, // enable Microsoft Teams integration
msteamsClientId: null, // custom Microsoft Teams client id
msteamsClientSecret: null, // custom Microsoft Teams client secret
msteamsTenantId: null // custom Microsoft Teams tenant id
};
export { DefaultAppSettings };
+3 -1
View File
@@ -43,6 +43,7 @@
"dropbox": "Dropbox",
"gdrive": "Google Drive",
"onedrive": "OneDrive",
"msteams": "Microsoft Teams",
"menuAllItems": "All Items",
"menuColors": "Colors",
"menuTrash": "Trash",
@@ -432,7 +433,8 @@
"setGenColorfulIcons": "Colorful custom icons in the list",
"setGenUseMarkdown": "Markdown in notes",
"setGenUseGroupIconForEntries": "Automatically use group icon for new entries",
"setGenDirectAutotype": "If only one matching entry is found, select that one automatically for Autotype",
"setGenDirectAutotype": "If only one matching entry is found, select that one automatically for auto-type",
"setGenAutoTypeTitleFilterEnabled": "Filter entries by title in auto-type",
"setGenFunction": "Function",
"setGenAutoSyncOnClose": "Automatically save and sync on close",
"setGenAutoSyncTimer": "Automatically save and sync periodically",
+2 -2
View File
@@ -677,10 +677,10 @@ class FileModel extends Model {
addCustomIcon(iconData) {
const uuid = kdbxweb.KdbxUuid.random();
this.db.meta.customIcons[uuid] = {
this.db.meta.customIcons.set(uuid.id, {
data: kdbxweb.ByteUtils.arrayToBuffer(kdbxweb.ByteUtils.base64ToBytes(iconData)),
lastModified: new Date()
};
});
return uuid.toString();
}
+13 -5
View File
@@ -221,22 +221,30 @@ class StorageOneDrive extends StorageBase {
_getOAuthConfig() {
let clientId = this.appSettings.onedriveClientId;
let clientSecret = this.appSettings.onedriveClientSecret;
let tenant = this.appSettings.onedriveTenantId;
if (!clientId) {
if (Features.isDesktop) {
({ id: clientId, secret: clientSecret } = OneDriveApps.Desktop);
({ id: clientId, secret: clientSecret, tenantId: tenant } = OneDriveApps.Desktop);
} else if (Features.isLocal) {
({ id: clientId, secret: clientSecret } = OneDriveApps.Local);
({ id: clientId, secret: clientSecret, tenantId: tenant } = OneDriveApps.Local);
} else {
({ id: clientId, secret: clientSecret } = OneDriveApps.Production);
({
id: clientId,
secret: clientSecret,
tenantId: tenant
} = OneDriveApps.Production);
}
}
tenant = tenant || 'common';
let scope = 'files.readwrite';
if (!this.appSettings.shortLivedStorageToken) {
scope += ' offline_access';
}
return {
url: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
tokenUrl: 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
url: `https://login.microsoftonline.com/${tenant}/oauth2/v2.0/authorize`,
tokenUrl: `https://login.microsoftonline.com/${tenant}/oauth2/v2.0/token`,
scope,
clientId,
clientSecret,
+365
View File
@@ -0,0 +1,365 @@
import { StorageBase } from 'storage/storage-base';
import { MsTeamsApps } from 'const/cloud-storage-apps';
import { Features } from 'util/features';
// https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow
// https://graph.microsoft.com/v1.0/me/transitiveMemberOf/microsoft.graph.group?$count=true
// https://graph.microsoft.com/v1.0/groups?$filter=groupTypes/any(c:c+eq+'Unified')
// https://graph.microsoft.com/v1.0/groups?$filter=groupTypes/any(c:c+eq+'Unified')&$orderby=displayName
// https://graph.microsoft.com/v1.0/groups?$orderby=displayName
// /me/joinedTeams
// https://graph.microsoft.com/v1.0/groups/{group id}/drive/root/children
class StorageTeams extends StorageBase {
name = 'msteams';
enabled = true;
uipos = 50;
icon = 'user-friends';
_graphUrl = 'https://graph.microsoft.com/v1.0';
_groupsUrl = `${this._graphUrl}/me/joinedTeams`;
_baseUrl = `${this._graphUrl}/groups`;
getPathForName(fileName) {
return '/drive/root:/' + fileName + '.kdbx';
}
genUrlAddress(groupId, path) {
if (groupId) {
return this._baseUrl + '/' + groupId + (path ? '/' + path.replace(/^\/+/, '') : '');
} else {
return this._groupsUrl;
}
}
genUrl(path) {
if (!path) {
const groupId = null;
const dir = null;
const url = this.genUrlAddress(groupId, dir);
return [groupId, dir, url];
}
const parts = path.replace(/^\/+/, '').split('/');
if (parts.length === 0) {
const groupId = null;
const dir = null;
const url = this.genUrlAddress(groupId, dir);
return [groupId, dir, url];
} else if (parts.length === 1) {
const groupId = parts[0];
const dir = null;
const url = this.genUrlAddress(groupId, dir);
return [groupId, dir, url];
} else {
path = path.replace(/\/drive\/root\:/, '');
const groupId = parts[0];
const dir = ('/' + parts.slice(1).join('/')).replace(/^\/+/, '');
const url = this.genUrlAddress(groupId, dir);
return [groupId, dir, url];
}
}
load(path, opts, callback) {
this._oauthAuthorize((err) => {
if (err) {
return callback && callback(err);
}
this.logger.debug('Load', path);
const ts = this.logger.ts();
const urlParts = this.genUrl(path);
const groupId = urlParts[0];
path = urlParts[1];
const url = urlParts[2];
if (!groupId) {
const err = 'no group id defined';
return callback && callback(err);
}
this._xhr({
url,
responseType: 'json',
success: (response) => {
const downloadUrl = response['@microsoft.graph.downloadUrl'];
let rev = response.eTag;
if (!downloadUrl || !response.eTag) {
this.logger.debug(
'Load error',
path,
'no download url',
response,
this.logger.ts(ts)
);
return callback && callback('no download url');
}
this._xhr({
url: downloadUrl,
responseType: 'arraybuffer',
skipAuth: true,
success: (response, xhr) => {
rev = xhr.getResponseHeader('ETag') || rev;
this.logger.debug('Loaded', path, rev, this.logger.ts(ts));
return callback && callback(null, response, { rev });
},
error: (err) => {
this.logger.error('Load error', path, err, this.logger.ts(ts));
return callback && callback(err);
}
});
},
error: (err) => {
this.logger.error('Load error', path, err, this.logger.ts(ts));
return callback && callback(err);
}
});
});
}
stat(path, opts, callback) {
this._oauthAuthorize((err) => {
if (err) {
return callback && callback(err);
}
this.logger.debug('Stat', path);
const ts = this.logger.ts();
const urlParts = this.genUrl(path);
const groupId = urlParts[0];
path = urlParts[1];
const url = urlParts[2];
if (!groupId) {
const err = 'no group id defined';
return callback && callback(err);
}
this._xhr({
url,
responseType: 'json',
success: (response) => {
const rev = response.eTag;
if (!rev) {
this.logger.error('Stat error', path, 'no eTag', this.logger.ts(ts));
return callback && callback('no eTag');
}
this.logger.debug('Stated', path, rev, this.logger.ts(ts));
return callback && callback(null, { rev });
},
error: (err, xhr) => {
if (xhr.status === 404) {
this.logger.debug('Stated not found', path, this.logger.ts(ts));
return callback && callback({ notFound: true });
}
this.logger.error('Stat error', path, err, this.logger.ts(ts));
return callback && callback(err);
}
});
});
}
save(path, opts, data, callback, rev) {
this._oauthAuthorize((err) => {
if (err) {
return callback && callback(err);
}
this.logger.debug('Save', path, rev);
const ts = this.logger.ts();
const urlParts = this.genUrl(path);
const groupId = urlParts[0];
path = urlParts[1];
const url = urlParts[2] + ':/content';
if (!groupId) {
const err = 'no group id defined';
return callback && callback(err);
}
this._xhr({
url,
method: 'PUT',
responseType: 'json',
headers: rev ? { 'If-Match': rev } : null,
data,
statuses: [200, 201, 412],
success: (response, xhr) => {
rev = response.eTag;
if (!rev) {
this.logger.error('Save error', path, 'no eTag', this.logger.ts(ts));
return callback && callback('no eTag');
}
if (xhr.status === 412) {
this.logger.debug('Save conflict', path, rev, this.logger.ts(ts));
return callback && callback({ revConflict: true }, { rev });
}
this.logger.debug('Saved', path, rev, this.logger.ts(ts));
return callback && callback(null, { rev });
},
error: (err) => {
this.logger.error('Save error', path, err, this.logger.ts(ts));
return callback && callback(err);
}
});
});
}
list(dir, callback) {
this._oauthAuthorize((err) => {
if (err) {
return callback && callback(err);
}
this.logger.debug('List', dir);
const ts = this.logger.ts();
const urlParts = this.genUrl(dir);
const groupId = urlParts[0];
dir = urlParts[1];
const urlPath = groupId ? (dir ? ':/children' : '/drive/root/children') : '';
const url = urlParts[2] + urlPath;
const self = this;
self._groupId = groupId;
this._xhr({
url,
responseType: 'json',
success: (response) => {
if (!response || !response.value) {
this.logger.error('List error', this.logger.ts(ts), response);
return callback && callback('list error');
}
this.logger.debug('Listed', this.logger.ts(ts));
let fileList;
if (!self._groupId) {
fileList = response.value
.filter((f) => f.displayName)
.map((f) => ({
name: f.displayName,
path: '/' + f.id,
rev: f.id,
dir: true
}));
} else {
fileList = response.value
.filter((f) => f.name)
.map((f) => ({
name: f.name,
path: `/${self._groupId}${f.parentReference.path}/${f.name}`,
rev: f.eTag,
dir: !!f.folder
}));
}
return callback && callback(null, fileList);
},
error: (err) => {
this.logger.error('List error', this.logger.ts(ts), err);
return callback && callback(err);
}
});
});
}
remove(path, callback) {
this.logger.debug('Remove', path);
const ts = this.logger.ts();
const urlParts = this.genUrl(path);
const groupId = urlParts[0];
path = urlParts[1];
const url = urlParts[2];
if (!groupId) {
const err = 'no group id defined';
return callback && callback(err);
}
this._xhr({
url,
method: 'DELETE',
responseType: 'json',
statuses: [200, 204],
success: () => {
this.logger.debug('Removed', path, this.logger.ts(ts));
return callback && callback();
},
error: (err) => {
this.logger.error('Remove error', path, err, this.logger.ts(ts));
return callback && callback(err);
}
});
}
mkdir(path, callback) {
this._oauthAuthorize((err) => {
if (err) {
return callback && callback(err);
}
this.logger.debug('Make dir', path);
const ts = this.logger.ts();
const urlParts = this.genUrl(path);
const groupId = urlParts[0];
path = urlParts[1];
const url = urlParts[2] + '/drive/root/children';
if (!groupId) {
const err = 'no group id defined';
return callback && callback(err);
}
const data = JSON.stringify({ name: path.replace('/drive/root:/', ''), folder: {} });
this._xhr({
url,
method: 'POST',
responseType: 'json',
statuses: [200, 204],
data,
dataType: 'application/json',
success: () => {
this.logger.debug('Made dir', path, this.logger.ts(ts));
return callback && callback();
},
error: (err) => {
this.logger.error('Make dir error', path, err, this.logger.ts(ts));
return callback && callback(err);
}
});
});
}
logout(enabled) {
this._oauthRevokeToken();
}
_getOAuthConfig() {
let clientId = this.appSettings.msteamsClientId;
let clientSecret = this.appSettings.msteamsClientSecret;
let tenant = this.appSettings.msteamsTenantId;
if (!clientId) {
if (Features.isDesktop) {
({ id: clientId, secret: clientSecret, tenantId: tenant } = MsTeamsApps.Desktop);
} else if (Features.isLocal) {
({ id: clientId, secret: clientSecret, tenantId: tenant } = MsTeamsApps.Local);
} else {
({ id: clientId, secret: clientSecret, tenantId: tenant } = MsTeamsApps.Production);
}
}
tenant = tenant || 'common';
let scope = 'Sites.ReadWrite.All Team.ReadBasic.All';
if (!this.appSettings.shortLivedStorageToken) {
scope += ' offline_access';
}
return {
url: `https://login.microsoftonline.com/${tenant}/oauth2/v2.0/authorize`,
tokenUrl: `https://login.microsoftonline.com/${tenant}/oauth2/v2.0/token`,
scope,
clientId,
clientSecret,
pkce: true,
width: 600,
height: 500
};
}
}
export { StorageTeams };
+2
View File
@@ -5,6 +5,7 @@ import { StorageFile } from 'storage/impl/storage-file';
import { StorageFileCache } from 'storage/impl/storage-file-cache';
import { StorageGDrive } from 'storage/impl/storage-gdrive';
import { StorageOneDrive } from 'storage/impl/storage-onedrive';
import { StorageTeams } from 'storage/impl/storage-teams';
import { StorageWebDav } from 'storage/impl/storage-webdav';
import { createOAuthSession } from 'storage/pkce';
@@ -17,6 +18,7 @@ const ThirdPartyStorage = {
dropbox: new StorageDropbox(),
gdrive: new StorageGDrive(),
onedrive: new StorageOneDrive(),
msteams: new StorageTeams(),
webdav: new StorageWebDav()
};
@@ -6,8 +6,8 @@ const ExpectedFieldRefByteLength = ExpectedFieldRefChars.length;
kdbxweb.ProtectedValue.prototype.isProtected = true;
kdbxweb.ProtectedValue.prototype.forEachChar = function (fn) {
const value = this._value;
const salt = this._salt;
const value = this.value;
const salt = this.salt;
let b, b1, b2, b3;
for (let i = 0, len = value.length; i < len; i++) {
b = value[i] ^ salt[i];
@@ -154,7 +154,7 @@ kdbxweb.ProtectedValue.prototype.equals = function (other) {
return false;
}
for (let i = 0; i < len; i++) {
if ((this._value[i] ^ this._salt[i]) !== (other._value[i] ^ other._salt[i])) {
if ((this.value[i] ^ this.salt[i]) !== (other.value[i] ^ other.salt[i])) {
return false;
}
}
@@ -181,8 +181,8 @@ kdbxweb.ProtectedValue.prototype.saltedValue = function () {
if (!this.byteLength) {
return 0;
}
const value = this._value;
const salt = this._salt;
const value = this.value;
const salt = this.salt;
let salted = '';
for (let i = 0, len = value.length; i < len; i++) {
const byte = value[i] ^ salt[i];
@@ -193,8 +193,8 @@ kdbxweb.ProtectedValue.prototype.saltedValue = function () {
kdbxweb.ProtectedValue.prototype.dataAndSalt = function () {
return {
data: [...this._value],
salt: [...this._salt]
data: [...this.value],
salt: [...this.salt]
};
};
@@ -53,6 +53,7 @@ class SettingsGeneralView extends View {
'change .settings__general-use-markdown': 'changeUseMarkdown',
'change .settings__general-use-group-icon-for-entries': 'changeUseGroupIconForEntries',
'change .settings__general-direct-autotype': 'changeDirectAutotype',
'change .settings__general-autotype-title-filter': 'changeAutoTypeTitleFilter',
'change .settings__general-field-label-dblclick-autotype':
'changeFieldLabelDblClickAutoType',
'change .settings__general-device-owner-auth': 'changeDeviceOwnerAuth',
@@ -135,6 +136,7 @@ class SettingsGeneralView extends View {
useMarkdown: AppSettingsModel.useMarkdown,
useGroupIconForEntries: AppSettingsModel.useGroupIconForEntries,
directAutotype: AppSettingsModel.directAutotype,
autoTypeTitleFilterEnabled: AppSettingsModel.autoTypeTitleFilterEnabled,
fieldLabelDblClickAutoType: AppSettingsModel.fieldLabelDblClickAutoType,
supportsTitleBarStyles: Features.supportsTitleBarStyles,
supportsCustomTitleBarAndDraggableWindow:
@@ -431,6 +433,11 @@ class SettingsGeneralView extends View {
AppSettingsModel.directAutotype = directAutotype;
}
changeAutoTypeTitleFilter(e) {
const autoTypeTitleFilterEnabled = e.target.checked || false;
AppSettingsModel.autoTypeTitleFilterEnabled = autoTypeTitleFilterEnabled;
}
changeFieldLabelDblClickAutoType(e) {
const fieldLabelDblClickAutoType = e.target.checked || false;
AppSettingsModel.fieldLabelDblClickAutoType = fieldLabelDblClickAutoType;
+8 -1
View File
@@ -334,8 +334,11 @@
}
}
&__donate-btn {
$donate-border-color: #89abed;
background: #fff;
border: 2px solid #89abed;
border: 2px solid $donate-border-color;
border-bottom: 2px solid $donate-border-color;
border-radius: 2.8rem;
text-align: center;
display: inline-block;
@@ -345,6 +348,10 @@
height: 2.8rem;
margin-bottom: $base-padding-v * 2;
&:hover {
border-bottom: 2px solid $donate-border-color;
}
&-top,
&-bottom {
pointer-events: none;
+1
View File
@@ -82,6 +82,7 @@ $fa-var-file-image: next-fa-glyph();
$fa-var-file-video: next-fa-glyph();
$fa-var-file-audio: next-fa-glyph();
$fa-var-onedrive: next-fa-glyph();
$fa-var-user-friends: next-fa-glyph();
$fa-var-question: next-fa-glyph();
$fa-var-sign-out-alt: next-fa-glyph();
$fa-var-sync-alt: next-fa-glyph();
+1 -1
View File
@@ -3,7 +3,7 @@
<p>{{#res 'setAboutFirst'}}<a href="https://antelle.net" target="_blank">Antelle</a>{{/res~}}&nbsp;
{{~#res 'setAboutSecond'}}<a href="{{licenseLink}}" target="_blank">MIT</a>{{/res}}
{{#res 'setAboutSource'}}<a href="{{repoLink}}" target="_blank">GitHub <i class="fa fa-github-alt bottom"></i></a>{{/res}}</p>
<a href="{{donationLink}}" target="_blank" class="settings__donate-btn no-border">
<a href="{{donationLink}}" target="_blank" class="settings__donate-btn">
<span class="settings__donate-btn-top">Become a</span><span class="settings__donate-btn-bottom">Backer</span>
</a>
<p>{{res 'setAboutBuilt'}}:</p>
@@ -169,6 +169,11 @@
id="settings__general-direct-autotype" {{#if directAutotype}}checked{{/if}} />
<label for="settings__general-direct-autotype">{{res 'setGenDirectAutotype'}}</label>
</div>
<div>
<input type="checkbox" class="settings__input input-base settings__general-autotype-title-filter"
id="settings__general-autotype-title-filter" {{#if autoTypeTitleFilterEnabled}}checked{{/if}} />
<label for="settings__general-autotype-title-filter">{{res 'setGenAutoTypeTitleFilterEnabled'}}</label>
</div>
<div>
<input type="checkbox" class="settings__input input-base settings__general-field-label-dblclick-autotype"
id="settings__general-field-label-dblclick-autotype" {{#if fieldLabelDblClickAutoType}}checked{{/if}} />
+6 -1
View File
@@ -202,7 +202,9 @@ main.minimizeApp = function (menuItemLabels) {
if (!appIcon) {
const image = electron.nativeImage.createFromPath(path.join(__dirname, 'img', imagePath));
appIcon = new electron.Tray(image);
appIcon.on('click', restoreMainWindow);
if (process.platform !== 'darwin') {
appIcon.on('click', restoreMainWindow);
}
const contextMenu = electron.Menu.buildFromTemplate([
{ label: menuItemLabels.restore, click: restoreMainWindow },
{ label: menuItemLabels.quit, click: closeMainWindow }
@@ -376,6 +378,9 @@ function restoreMainWindow() {
}
function showAndFocusMainWindow() {
if (appIcon) {
restoreMainWindow();
}
if (mainWindowMaximized) {
mainWindow.maximize();
} else {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "KeeWeb",
"version": "1.18.3",
"version": "1.18.6",
"description": "Free cross-platform password manager compatible with KeePass",
"main": "main.js",
"homepage": "https://keeweb.info",
@@ -306,24 +306,29 @@ async function processFirstMessageFromSocket(socket, message) {
let parentProcessInfo;
try {
parentProcessInfo = await getProcessInfo(message.ppid);
} catch (e) {
logger.error(`Cannot get info for PID ${message.ppid}: ${e}`);
socket.destroy();
return;
}
if (process.platform === 'win32' && parentProcessInfo.appName === 'cmd') {
try {
parentProcessInfo = await getProcessInfo(parentProcessInfo.ppid);
parentProcessInfo = await getProcessInfo(message.ppid);
} catch (e) {
logger.error(
`Cannot get info for PID ${parentProcessInfo.ppid}: ${e}, assuming cmd is the launcher`
);
logger.error(`Cannot get info for PID ${message.ppid}: ${e}`);
throw e;
}
if (process.platform === 'win32' && parentProcessInfo.appName === 'cmd') {
try {
parentProcessInfo = await getProcessInfo(parentProcessInfo.ppid);
} catch (e) {
logger.error(
`Cannot get info for PID ${parentProcessInfo.ppid}: ${e}, assuming cmd is the launcher`
);
}
}
} catch (e) {
logger.warn('Cannot get process info, assuming the connection is not identified');
}
appName = AppNames[parentProcessInfo.appName] ?? parentProcessInfo.appName;
appName = parentProcessInfo
? AppNames[parentProcessInfo.appName] ?? parentProcessInfo.appName
: 'Unidentified browser';
appName = appName[0].toUpperCase() + appName.substr(1);
}
+2 -2
View File
@@ -39,7 +39,7 @@ function installDarwinUpdate(updateFilePath) {
`--app=${appPath}`
]);
electron.app.exit(0);
electron.app.quit();
}
function installWin32Update(updateFilePath) {
@@ -52,7 +52,7 @@ function installWin32Update(updateFilePath) {
const ps = spawnDetached('cmd');
ps.stdin.end(`${updateCommand}\nexit\n`, 'utf8', () => {
electron.app.exit(0);
electron.app.quit();
});
}
@@ -5,42 +5,67 @@ const { isDev } = require('./app-info');
const { app } = require('electron');
const { ExtensionIds } = require('../const/extension-ids');
function getManifestDir(browser) {
function getManifestDirs(browser) {
const home = app.getPath('home');
switch (process.platform) {
case 'darwin':
switch (browser) {
case 'Chrome':
return `${home}/Library/Application Support/Google/Chrome/NativeMessagingHosts/`;
return [
`${home}/Library/Application Support/Google/Chrome/NativeMessagingHosts/`,
`${home}/Library/Application Support/Google/Chrome Beta/NativeMessagingHosts/`,
`${home}/Library/Application Support/Google/Chrome Canary/NativeMessagingHosts/`,
`${home}/Library/Application Support/Chromium/NativeMessagingHosts/`,
];
case 'Firefox':
return `${home}/Library/Application Support/Mozilla/NativeMessagingHosts/`;
return [
`${home}/Library/Application Support/Mozilla/NativeMessagingHosts/`,
];
case 'Edge':
return `${home}/Library/Application Support/Microsoft Edge/NativeMessagingHosts/`;
return [
`${home}/Library/Application Support/Microsoft Edge/NativeMessagingHosts/`,
];
default:
return undefined;
}
case 'linux':
switch (browser) {
case 'Chrome':
return `${home}/.config/google-chrome/NativeMessagingHosts/`;
return [
`${home}/.config/google-chrome/NativeMessagingHosts/`,
`${home}/.config/google-chrome-dev/NativeMessagingHosts/`,
`${home}/.config/google-chrome-unstable/NativeMessagingHosts/`,
`${home}/.config/chromium/NativeMessagingHosts/`,
];
case 'Firefox':
return `${home}/.mozilla/native-messaging-hosts/`;
return [
`${home}/.mozilla/native-messaging-hosts/`,
];
case 'Edge':
return `${home}/.config/microsoft-edge/NativeMessagingHosts/`;
return [
`${home}/.config/microsoft-edge/NativeMessagingHosts/`,
];
default:
return undefined;
}
}
}
function getWindowsRegistryPath(browser) {
function getWindowsRegistryPaths(browser) {
switch (browser) {
case 'Chrome':
return 'HKCU\\Software\\Google\\Chrome\\NativeMessagingHosts\\';
return [
'HKCU\\Software\\Google\\Chrome\\NativeMessagingHosts\\',
'HKCU\\Software\\Chromium\\NativeMessagingHosts\\',
];
case 'Firefox':
return 'HKCU\\Software\\Mozilla\\NativeMessagingHosts\\';
return [
'HKCU\\Software\\Mozilla\\NativeMessagingHosts\\',
];
case 'Edge':
return 'HKCU\\Software\\Microsoft\\Edge\\NativeMessagingHosts\\';
return [
'HKCU\\Software\\Microsoft\\Edge\\NativeMessagingHosts\\',
];
default:
return undefined;
}
@@ -130,8 +155,8 @@ module.exports.install = async function (browser, extension) {
manifest.path = getNativeMessagingHostPath();
if (process.platform === 'win32') {
const registryPath = getWindowsRegistryPath(browser);
if (!registryPath) {
const registryPaths = getWindowsRegistryPaths(browser);
if (!registryPaths) {
return;
}
@@ -143,30 +168,34 @@ module.exports.install = async function (browser, extension) {
const manifestFileName = getWindowsManifestFileName(browser, extension);
await fs.promises.writeFile(manifestFileName, JSON.stringify(manifest, null, 4));
windowsRegistry.createKey(registryPath + registryKeyName, manifestFileName);
for (const path of registryPaths) {
windowsRegistry.createKey(path + registryKeyName, manifestFileName);
}
} else {
const manifestDir = getManifestDir(browser);
if (!manifestDir) {
const manifestDirs = getManifestDirs(browser);
if (!manifestDirs) {
return;
}
await fs.promises.mkdir(manifestDir, { recursive: true });
const manifestFileName = getManifestFileName(extension);
if (!manifestFileName) {
return;
}
const fullPath = path.join(manifestDir, manifestFileName);
for (const dir of manifestDirs) {
await fs.promises.mkdir(dir, { recursive: true });
await fs.promises.writeFile(fullPath, JSON.stringify(manifest, null, 4));
const fullPath = path.join(dir, manifestFileName);
await fs.promises.writeFile(fullPath, JSON.stringify(manifest, null, 4));
}
}
};
module.exports.uninstall = async function (browser, extension) {
if (process.platform === 'win32') {
const registryPath = getWindowsRegistryPath(browser);
if (!registryPath) {
const registryPaths = getWindowsRegistryPaths(browser);
if (!registryPaths) {
return;
}
@@ -175,10 +204,12 @@ module.exports.uninstall = async function (browser, extension) {
return;
}
windowsRegistry.deleteKey(registryPath + registryKeyName);
for (const path of registryPaths) {
windowsRegistry.deleteKey(path + registryKeyName);
}
} else {
const manifestDir = getManifestDir(browser);
if (!manifestDir) {
const manifestDirs = getManifestDirs(browser);
if (!manifestDirs) {
return;
}
@@ -186,8 +217,11 @@ module.exports.uninstall = async function (browser, extension) {
if (!manifestFileName) {
return;
}
const fullPath = path.join(manifestDir, manifestFileName);
await fs.promises.unlink(fullPath);
for (const dir of manifestDirs) {
const fullPath = path.join(dir, manifestFileName);
await fs.promises.unlink(fullPath);
}
}
};
+440 -21426
View File
File diff suppressed because it is too large Load Diff
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "keeweb",
"version": "1.18.3",
"version": "1.18.6",
"description": "Free cross-platform password manager compatible with KeePass",
"main": "Gruntfile.js",
"private": true,
@@ -16,7 +16,7 @@
"@babel/plugin-proposal-optional-chaining": "^7.13.12",
"@babel/preset-env": "^7.14.1",
"@fortawesome/fontawesome-free": "^5.15.3",
"@keeweb/keeweb-native-messaging-host": "https://github.com/keeweb/keeweb-connect/releases/download/0.3.3/keeweb-native-messaging-host.tgz",
"@keeweb/keeweb-native-messaging-host": "https://github.com/keeweb/keeweb-connect/releases/download/0.3.5/keeweb-native-messaging-host.tgz",
"@keeweb/keeweb-native-modules": "https://github.com/keeweb/keeweb-native-modules/releases/download/0.11.6/keeweb-native-modules.tgz",
"adm-zip": "^0.5.5",
"argon2-browser": "1.15.4",
@@ -32,7 +32,7 @@
"cross-env": "^7.0.3",
"css-loader": "^5.2.4",
"dompurify": "^2.2.8",
"electron": "^12.0.7",
"electron": "^12.2.2",
"electron-builder": "^22.11.1",
"electron-evil-feature-patcher": "^1.2.1",
"electron-notarize": "^1.0.0",
@@ -69,7 +69,7 @@
"jquery": "3.6.0",
"json-loader": "^0.5.7",
"jsqrcode": "github:antelle/jsqrcode#0.1.3",
"kdbxweb": "^2.0.1",
"kdbxweb": "^2.0.4",
"load-grunt-tasks": "5.1.0",
"lodash": "^4.17.21",
"marked": "^2.0.3",
@@ -123,7 +123,7 @@
"author": {
"name": "Antelle",
"email": "antelle.net@gmail.com",
"url": "http://antelle.net"
"url": "https://antelle.net"
},
"engines": {
"node": ">=10.0"
+16
View File
@@ -1,5 +1,21 @@
Release notes
-------------
##### v1.18.6 (2021-05-19)
`-` fix #1824: saving KDBX3 files with compression disabled
`-` fix #1818: extension connection error if browser cannot be identified
`-` fix #1820: minimize on close on macOS
##### v1.18.5 (2021-05-14)
`-` fix #1816: old Chromium support, such as Android Edge
`-` fix #1817: crash on files with large attachments as KDBX3
##### v1.18.4 (2021-05-12)
`+` #1814: option to disable auto-type title filter by default
`-` #1808: restore KeeWeb from system tray on extension request
`-` fix #1810: extension connection on old macOS (10.11)
`-` fix #1813: custom icon selection
`-` fix #1811: app doesn't quit during update
##### v1.18.3 (2021-05-09)
`-` fix #1804: filling OTP in browser extensions
`*` fix #1805: auto-unchecking auto-type filters if nothing found