From 6832c9f649b8548137fdcbb66e67c52a6aa9790b Mon Sep 17 00:00:00 2001 From: fyears Date: Sat, 1 Jan 2022 18:37:48 +0800 Subject: [PATCH] force expire login cred --- package.json | 2 + src/baseTypes.ts | 5 +++ src/main.ts | 95 +++++++++++++++++++++++++++++++++++----- src/remoteForDropbox.ts | 8 +++- src/remoteForOnedrive.ts | 34 +++++++++++++- 5 files changed, 129 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index a622987..80127dd 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "@types/chai": "^4.2.22", "@types/chai-as-promised": "^7.1.4", "@types/jsdom": "^16.2.13", + "@types/lodash": "^4.14.178", "@types/mime-types": "^2.1.1", "@types/mocha": "^9.0.0", "@types/node": "^14.14.37", @@ -60,6 +61,7 @@ "crypto-browserify": "^3.12.0", "dropbox": "^10.22.0", "localforage": "^1.10.0", + "lodash": "^4.17.21", "mime-types": "^2.1.33", "obsidian": "^0.12.0", "path-browserify": "^1.0.1", diff --git a/src/baseTypes.ts b/src/baseTypes.ts index 99099f1..fc02a0c 100644 --- a/src/baseTypes.ts +++ b/src/baseTypes.ts @@ -21,6 +21,7 @@ export interface DropboxConfig { accessTokenExpiresAtTime: number; accountID: string; username: string; + credentialsShouldBeDeletedAtTime?: number; } export type WebdavAuthType = "digest" | "basic"; @@ -41,6 +42,7 @@ export interface OnedriveConfig { accessTokenExpiresAtTime: number; deltaLink: string; username: string; + credentialsShouldBeDeletedAtTime?: number; } export interface RemotelySavePluginSettings { @@ -71,3 +73,6 @@ export interface UriParams { ver?: string; data?: string; } + +// 80 days +export const OAUTH2_FORCE_EXPIRE_MILLISECONDS = 1000 * 60 * 60 * 24 * 80; diff --git a/src/main.ts b/src/main.ts index d7b3f56..c200ce4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,4 +1,5 @@ import { Modal, Notice, Plugin, Setting } from "obsidian"; +import cloneDeep from "lodash/cloneDeep"; import type { RemotelySavePluginSettings } from "./baseTypes"; import { COMMAND_CALLBACK, @@ -20,12 +21,13 @@ import { DEFAULT_DROPBOX_CONFIG, getAuthUrlAndVerifier as getAuthUrlAndVerifierDropbox, sendAuthReq as sendAuthReqDropbox, - setConfigBySuccessfullAuthInplace, + setConfigBySuccessfullAuthInplace as setConfigBySuccessfullAuthInplaceDropbox, } from "./remoteForDropbox"; import { AccessCodeResponseSuccessfulType, DEFAULT_ONEDRIVE_CONFIG, sendAuthReq as sendAuthReqOnedrive, + setConfigBySuccessfullAuthInplace as setConfigBySuccessfullAuthInplaceOnedrive, } from "./remoteForOnedrive"; import { DEFAULT_S3_CONFIG } from "./remoteForS3"; import { DEFAULT_WEBDAV_CONFIG } from "./remoteForWebdav"; @@ -69,6 +71,7 @@ export default class RemotelySavePlugin extends Plugin { }; // init await this.loadSettings(); + await this.checkIfOauthExpires(); await this.prepareDB(); @@ -91,7 +94,7 @@ export default class RemotelySavePlugin extends Plugin { if (parsed.status === "error") { new Notice(parsed.message); } else { - const copied = JSON.parse(JSON.stringify(parsed.result)); + const copied = cloneDeep(parsed.result); // new Notice(JSON.stringify(copied)) this.settings = copied; this.saveSettings(); @@ -133,7 +136,7 @@ export default class RemotelySavePlugin extends Plugin { ); const self = this; - setConfigBySuccessfullAuthInplace( + setConfigBySuccessfullAuthInplaceDropbox( this.settings.dropbox, authRes, () => self.saveSettings() @@ -212,15 +215,13 @@ export default class RemotelySavePlugin extends Plugin { 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; + setConfigBySuccessfullAuthInplaceOnedrive( + this.settings.onedrive, + rsp as AccessCodeResponseSuccessfulType, + () => self.saveSettings() + ); + const client = new RemoteClient( "onedrive", undefined, @@ -372,7 +373,7 @@ export default class RemotelySavePlugin extends Plugin { async loadSettings() { this.settings = Object.assign( {}, - JSON.parse(JSON.stringify(DEFAULT_SETTINGS)) /* copy an object */, + cloneDeep(DEFAULT_SETTINGS), await this.loadData() ); if (this.settings.dropbox.clientID === "") { @@ -390,6 +391,76 @@ export default class RemotelySavePlugin extends Plugin { 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() { this.db = await prepareDBs(); } diff --git a/src/remoteForDropbox.ts b/src/remoteForDropbox.ts index 858e971..ce7d9a1 100644 --- a/src/remoteForDropbox.ts +++ b/src/remoteForDropbox.ts @@ -5,13 +5,14 @@ import { DropboxConfig, RemoteItem, COMMAND_CALLBACK_DROPBOX, + OAUTH2_FORCE_EXPIRE_MILLISECONDS, } from "./baseTypes"; import { decryptArrayBuffer, encryptArrayBuffer } from "./encrypt"; import { bufferToArrayBuffer, getFolderLevels, mkdirpInVault } from "./misc"; export { Dropbox } from "dropbox"; -export const DEFAULT_DROPBOX_CONFIG = { +export const DEFAULT_DROPBOX_CONFIG: DropboxConfig = { accessToken: "", clientID: process.env.DEFAULT_DROPBOX_APP_KEY, refreshToken: "", @@ -19,6 +20,7 @@ export const DEFAULT_DROPBOX_CONFIG = { accessTokenExpiresAtTime: 0, accountID: "", username: "", + credentialsShouldBeDeletedAtTime: 0, }; export const getDropboxPath = (fileOrFolderPath: string, vaultName: string) => { @@ -232,6 +234,10 @@ export const setConfigBySuccessfullAuthInplace = async ( config.accessTokenExpiresAtTime = 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) { config.refreshToken = authRes.refresh_token; } diff --git a/src/remoteForOnedrive.ts b/src/remoteForOnedrive.ts index 5776249..2cfee1d 100644 --- a/src/remoteForOnedrive.ts +++ b/src/remoteForOnedrive.ts @@ -11,9 +11,15 @@ import { UploadResult, } from "@microsoft/microsoft-graph-client"; import type { DriveItem, User } from "@microsoft/microsoft-graph-types"; +import cloneDeep from "lodash/cloneDeep"; import { request, Vault } from "obsidian"; 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 { decryptArrayBuffer, encryptArrayBuffer } from "./encrypt"; import { @@ -34,6 +40,7 @@ export const DEFAULT_ONEDRIVE_CONFIG: OnedriveConfig = { accessTokenExpiresAtTime: 0, deltaLink: "", username: "", + credentialsShouldBeDeletedAtTime: 0, }; //////////////////////////////////////////////////////////////////////////////// @@ -173,6 +180,29 @@ export const sendRefreshTokenReq = async ( } }; +export const setConfigBySuccessfullAuthInplace = async ( + config: OnedriveConfig, + authRes: AccessCodeResponseSuccessfulType, + saveUpdatedConfigFunc: () => Promise | 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 //////////////////////////////////////////////////////////////////////////////// @@ -405,7 +435,7 @@ export const listFromRemote = async ( while (NEXT_LINK_KEY in res) { 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?