force expire login cred

This commit is contained in:
fyears 2022-01-01 18:37:48 +08:00
parent 34d1b01c7b
commit 6832c9f649
5 changed files with 129 additions and 15 deletions

View File

@ -25,6 +25,7 @@
"@types/chai": "^4.2.22", "@types/chai": "^4.2.22",
"@types/chai-as-promised": "^7.1.4", "@types/chai-as-promised": "^7.1.4",
"@types/jsdom": "^16.2.13", "@types/jsdom": "^16.2.13",
"@types/lodash": "^4.14.178",
"@types/mime-types": "^2.1.1", "@types/mime-types": "^2.1.1",
"@types/mocha": "^9.0.0", "@types/mocha": "^9.0.0",
"@types/node": "^14.14.37", "@types/node": "^14.14.37",
@ -60,6 +61,7 @@
"crypto-browserify": "^3.12.0", "crypto-browserify": "^3.12.0",
"dropbox": "^10.22.0", "dropbox": "^10.22.0",
"localforage": "^1.10.0", "localforage": "^1.10.0",
"lodash": "^4.17.21",
"mime-types": "^2.1.33", "mime-types": "^2.1.33",
"obsidian": "^0.12.0", "obsidian": "^0.12.0",
"path-browserify": "^1.0.1", "path-browserify": "^1.0.1",

View File

@ -21,6 +21,7 @@ export interface DropboxConfig {
accessTokenExpiresAtTime: number; accessTokenExpiresAtTime: number;
accountID: string; accountID: string;
username: string; username: string;
credentialsShouldBeDeletedAtTime?: number;
} }
export type WebdavAuthType = "digest" | "basic"; export type WebdavAuthType = "digest" | "basic";
@ -41,6 +42,7 @@ export interface OnedriveConfig {
accessTokenExpiresAtTime: number; accessTokenExpiresAtTime: number;
deltaLink: string; deltaLink: string;
username: string; username: string;
credentialsShouldBeDeletedAtTime?: number;
} }
export interface RemotelySavePluginSettings { export interface RemotelySavePluginSettings {
@ -71,3 +73,6 @@ export interface UriParams {
ver?: string; ver?: string;
data?: string; data?: string;
} }
// 80 days
export const OAUTH2_FORCE_EXPIRE_MILLISECONDS = 1000 * 60 * 60 * 24 * 80;

View File

@ -1,4 +1,5 @@
import { Modal, Notice, Plugin, Setting } from "obsidian"; import { Modal, Notice, Plugin, Setting } from "obsidian";
import cloneDeep from "lodash/cloneDeep";
import type { RemotelySavePluginSettings } from "./baseTypes"; import type { RemotelySavePluginSettings } from "./baseTypes";
import { import {
COMMAND_CALLBACK, COMMAND_CALLBACK,
@ -20,12 +21,13 @@ import {
DEFAULT_DROPBOX_CONFIG, DEFAULT_DROPBOX_CONFIG,
getAuthUrlAndVerifier as getAuthUrlAndVerifierDropbox, getAuthUrlAndVerifier as getAuthUrlAndVerifierDropbox,
sendAuthReq as sendAuthReqDropbox, sendAuthReq as sendAuthReqDropbox,
setConfigBySuccessfullAuthInplace, setConfigBySuccessfullAuthInplace as setConfigBySuccessfullAuthInplaceDropbox,
} from "./remoteForDropbox"; } from "./remoteForDropbox";
import { import {
AccessCodeResponseSuccessfulType, AccessCodeResponseSuccessfulType,
DEFAULT_ONEDRIVE_CONFIG, DEFAULT_ONEDRIVE_CONFIG,
sendAuthReq as sendAuthReqOnedrive, sendAuthReq as sendAuthReqOnedrive,
setConfigBySuccessfullAuthInplace as setConfigBySuccessfullAuthInplaceOnedrive,
} from "./remoteForOnedrive"; } from "./remoteForOnedrive";
import { DEFAULT_S3_CONFIG } from "./remoteForS3"; import { DEFAULT_S3_CONFIG } from "./remoteForS3";
import { DEFAULT_WEBDAV_CONFIG } from "./remoteForWebdav"; import { DEFAULT_WEBDAV_CONFIG } from "./remoteForWebdav";
@ -69,6 +71,7 @@ export default class RemotelySavePlugin extends Plugin {
}; // init }; // init
await this.loadSettings(); await this.loadSettings();
await this.checkIfOauthExpires();
await this.prepareDB(); await this.prepareDB();
@ -91,7 +94,7 @@ export default class RemotelySavePlugin extends Plugin {
if (parsed.status === "error") { if (parsed.status === "error") {
new Notice(parsed.message); new Notice(parsed.message);
} else { } else {
const copied = JSON.parse(JSON.stringify(parsed.result)); const copied = cloneDeep(parsed.result);
// new Notice(JSON.stringify(copied)) // new Notice(JSON.stringify(copied))
this.settings = copied; this.settings = copied;
this.saveSettings(); this.saveSettings();
@ -133,7 +136,7 @@ export default class RemotelySavePlugin extends Plugin {
); );
const self = this; const self = this;
setConfigBySuccessfullAuthInplace( setConfigBySuccessfullAuthInplaceDropbox(
this.settings.dropbox, this.settings.dropbox,
authRes, authRes,
() => self.saveSettings() () => self.saveSettings()
@ -212,15 +215,13 @@ export default class RemotelySavePlugin extends Plugin {
throw Error(`${JSON.stringify(rsp)}`); throw Error(`${JSON.stringify(rsp)}`);
} }
rsp = rsp as AccessCodeResponseSuccessfulType;
this.settings.onedrive.accessToken = rsp.access_token;
this.settings.onedrive.accessTokenExpiresAtTime =
Date.now() + rsp.expires_in - 5 * 60 * 1000;
this.settings.onedrive.accessTokenExpiresInSeconds = rsp.expires_in;
this.settings.onedrive.refreshToken = rsp.refresh_token;
await this.saveSettings();
const self = this; const self = this;
setConfigBySuccessfullAuthInplaceOnedrive(
this.settings.onedrive,
rsp as AccessCodeResponseSuccessfulType,
() => self.saveSettings()
);
const client = new RemoteClient( const client = new RemoteClient(
"onedrive", "onedrive",
undefined, undefined,
@ -372,7 +373,7 @@ export default class RemotelySavePlugin extends Plugin {
async loadSettings() { async loadSettings() {
this.settings = Object.assign( this.settings = Object.assign(
{}, {},
JSON.parse(JSON.stringify(DEFAULT_SETTINGS)) /* copy an object */, cloneDeep(DEFAULT_SETTINGS),
await this.loadData() await this.loadData()
); );
if (this.settings.dropbox.clientID === "") { if (this.settings.dropbox.clientID === "") {
@ -390,6 +391,76 @@ export default class RemotelySavePlugin extends Plugin {
await this.saveData(this.settings); await this.saveData(this.settings);
} }
async checkIfOauthExpires() {
let needSave: boolean = false;
const current = Date.now();
// fullfill old version settings
if (
this.settings.dropbox.refreshToken !== "" &&
this.settings.dropbox.credentialsShouldBeDeletedAtTime === undefined
) {
// It has a refreshToken, but not expire time.
// Likely to be a setting from old version.
// we set it to a month.
this.settings.dropbox.credentialsShouldBeDeletedAtTime =
current + 1000 * 60 * 60 * 24 * 30;
needSave = true;
}
if (
this.settings.onedrive.refreshToken !== "" &&
this.settings.onedrive.credentialsShouldBeDeletedAtTime === undefined
) {
this.settings.onedrive.credentialsShouldBeDeletedAtTime =
current + 1000 * 60 * 60 * 24 * 30;
needSave = true;
}
// check expired or not
let dropboxExpired = false;
if (
this.settings.dropbox.refreshToken !== "" &&
current >= this.settings.dropbox.credentialsShouldBeDeletedAtTime
) {
dropboxExpired = true;
this.settings.dropbox = cloneDeep(DEFAULT_DROPBOX_CONFIG);
needSave = true;
}
let onedriveExpired = false;
if (
this.settings.onedrive.refreshToken !== "" &&
current >= this.settings.onedrive.credentialsShouldBeDeletedAtTime
) {
onedriveExpired = true;
this.settings.onedrive = cloneDeep(DEFAULT_ONEDRIVE_CONFIG);
needSave = true;
}
// save back
if (needSave) {
await this.saveSettings();
}
// send notice
if (dropboxExpired && onedriveExpired) {
new Notice(
`${this.manifest.name}: You haven't manually auth Dropbox and OneDrive for a while, you need to re-auth them again.`,
6000
);
} else if (dropboxExpired) {
new Notice(
`${this.manifest.name}: You haven't manually auth Dropbox for a while, you need to re-auth it again.`,
6000
);
} else if (onedriveExpired) {
new Notice(
`${this.manifest.name}: You haven't manually auth OneDrive for a while, you need to re-auth it again.`,
6000
);
}
}
async prepareDB() { async prepareDB() {
this.db = await prepareDBs(); this.db = await prepareDBs();
} }

View File

@ -5,13 +5,14 @@ import {
DropboxConfig, DropboxConfig,
RemoteItem, RemoteItem,
COMMAND_CALLBACK_DROPBOX, COMMAND_CALLBACK_DROPBOX,
OAUTH2_FORCE_EXPIRE_MILLISECONDS,
} from "./baseTypes"; } from "./baseTypes";
import { decryptArrayBuffer, encryptArrayBuffer } from "./encrypt"; import { decryptArrayBuffer, encryptArrayBuffer } from "./encrypt";
import { bufferToArrayBuffer, getFolderLevels, mkdirpInVault } from "./misc"; import { bufferToArrayBuffer, getFolderLevels, mkdirpInVault } from "./misc";
export { Dropbox } from "dropbox"; export { Dropbox } from "dropbox";
export const DEFAULT_DROPBOX_CONFIG = { export const DEFAULT_DROPBOX_CONFIG: DropboxConfig = {
accessToken: "", accessToken: "",
clientID: process.env.DEFAULT_DROPBOX_APP_KEY, clientID: process.env.DEFAULT_DROPBOX_APP_KEY,
refreshToken: "", refreshToken: "",
@ -19,6 +20,7 @@ export const DEFAULT_DROPBOX_CONFIG = {
accessTokenExpiresAtTime: 0, accessTokenExpiresAtTime: 0,
accountID: "", accountID: "",
username: "", username: "",
credentialsShouldBeDeletedAtTime: 0,
}; };
export const getDropboxPath = (fileOrFolderPath: string, vaultName: string) => { export const getDropboxPath = (fileOrFolderPath: string, vaultName: string) => {
@ -232,6 +234,10 @@ export const setConfigBySuccessfullAuthInplace = async (
config.accessTokenExpiresAtTime = config.accessTokenExpiresAtTime =
Date.now() + parseInt(authRes.expires_in) * 1000 - 10 * 1000; Date.now() + parseInt(authRes.expires_in) * 1000 - 10 * 1000;
// manually set it expired after 80 days;
config.credentialsShouldBeDeletedAtTime =
Date.now() + OAUTH2_FORCE_EXPIRE_MILLISECONDS;
if (authRes.refresh_token !== undefined) { if (authRes.refresh_token !== undefined) {
config.refreshToken = authRes.refresh_token; config.refreshToken = authRes.refresh_token;
} }

View File

@ -11,9 +11,15 @@ import {
UploadResult, UploadResult,
} from "@microsoft/microsoft-graph-client"; } from "@microsoft/microsoft-graph-client";
import type { DriveItem, User } from "@microsoft/microsoft-graph-types"; import type { DriveItem, User } from "@microsoft/microsoft-graph-types";
import cloneDeep from "lodash/cloneDeep";
import { request, Vault } from "obsidian"; import { request, Vault } from "obsidian";
import * as path from "path"; import * as path from "path";
import type { OnedriveConfig, RemoteItem } from "./baseTypes"; import {
DropboxConfig,
OAUTH2_FORCE_EXPIRE_MILLISECONDS,
OnedriveConfig,
RemoteItem,
} from "./baseTypes";
import { COMMAND_CALLBACK_ONEDRIVE } from "./baseTypes"; import { COMMAND_CALLBACK_ONEDRIVE } from "./baseTypes";
import { decryptArrayBuffer, encryptArrayBuffer } from "./encrypt"; import { decryptArrayBuffer, encryptArrayBuffer } from "./encrypt";
import { import {
@ -34,6 +40,7 @@ export const DEFAULT_ONEDRIVE_CONFIG: OnedriveConfig = {
accessTokenExpiresAtTime: 0, accessTokenExpiresAtTime: 0,
deltaLink: "", deltaLink: "",
username: "", username: "",
credentialsShouldBeDeletedAtTime: 0,
}; };
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -173,6 +180,29 @@ export const sendRefreshTokenReq = async (
} }
}; };
export const setConfigBySuccessfullAuthInplace = async (
config: OnedriveConfig,
authRes: AccessCodeResponseSuccessfulType,
saveUpdatedConfigFunc: () => Promise<any> | undefined
) => {
console.log("start updating local info of OneDrive token");
config.accessToken = authRes.access_token;
config.accessTokenExpiresAtTime =
Date.now() + authRes.expires_in - 5 * 60 * 1000;
config.accessTokenExpiresInSeconds = authRes.expires_in;
config.refreshToken = authRes.refresh_token;
// manually set it expired after 80 days;
config.credentialsShouldBeDeletedAtTime =
Date.now() + OAUTH2_FORCE_EXPIRE_MILLISECONDS;
if (saveUpdatedConfigFunc !== undefined) {
await saveUpdatedConfigFunc();
}
console.log("finish updating local info of Onedrive token");
};
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Other usual common methods // Other usual common methods
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -405,7 +435,7 @@ export const listFromRemote = async (
while (NEXT_LINK_KEY in res) { while (NEXT_LINK_KEY in res) {
res = await client.client.api(res[NEXT_LINK_KEY]).get(); res = await client.client.api(res[NEXT_LINK_KEY]).get();
driveItems.push(...JSON.parse(JSON.stringify(res.value as DriveItem[]))); driveItems.push(...cloneDeep(res.value as DriveItem[]));
} }
// lastly we should have delta link? // lastly we should have delta link?