Disable some menu items in read-only mode (#11733)

* 🎨 kernel supports read-only publishing services

* 🐛 Fix authentication vulnerabilities

* 🎨 Protect secret information

* 🎨 Adjust the permission control

* 🎨 Adjust the permission control

* 🎨 Fixed the vulnerability that `getFile` gets file `conf.json`

* 🎨 Add API `/api/setting/setPublish`

* 🎨 Add API `/api/setting/getPublish`

* 🐛 Fixed the issue that PWA-related files could not pass BasicAuth

* 🎨 Add a settings panel for publishing features

* 📝 Add guide for `Publish Service`

* 📝 Update Japanese user guide

* 🎨 Merge fixed static file services

* 🎨 Disable some menu items in read-only mode

* 🎨 Disable some menu items in read-only mode

* Update router.go
This commit is contained in:
Yingyi / 颖逸 2024-07-05 20:01:43 +08:00 committed by GitHub
parent 1260b14875
commit f25b36ff38
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 150 additions and 80 deletions

View file

@ -310,10 +310,12 @@ export const editor = {
if (fontFamilyElement.tagName === "SELECT") {
let fontFamilyHTML = `<option value="">${window.siyuan.languages.default}</option>`;
fetchPost("/api/system/getSysFonts", {}, (response) => {
response.data.forEach((item: string) => {
fontFamilyHTML += `<option value="${item}"${window.siyuan.config.editor.fontFamily === item ? " selected" : ""}>${item}</option>`;
});
fontFamilyElement.innerHTML = fontFamilyHTML;
if (response.code === 0) {
response.data.forEach((item: string) => {
fontFamilyHTML += `<option value="${item}"${window.siyuan.config.editor.fontFamily === item ? " selected" : ""}>${item}</option>`;
});
fontFamilyElement.innerHTML = fontFamilyHTML;
}
});
}
editor.element.querySelector("#clearHistory").addEventListener("click", () => {

View file

@ -38,6 +38,8 @@ export class App {
registerServiceWorker(`${Constants.SERVICE_WORKER_PATH}?v=${Constants.SIYUAN_VERSION}`);
/// #endif
addBaseURL();
addScriptSync(`${Constants.PROTYLE_CDN}/js/lute/lute.min.js?v=${Constants.SIYUAN_VERSION}`, "protyleLuteScript"),
addScript(`${Constants.PROTYLE_CDN}/js/protyle-html.js?v=${Constants.SIYUAN_VERSION}`, "protyleWcHtmlScript"),
this.appId = Constants.SIYUAN_APPID;
window.siyuan = {
@ -158,24 +160,40 @@ export class App {
};
fetchPost("/api/system/getConf", {}, async (response) => {
addScriptSync(`${Constants.PROTYLE_CDN}/js/lute/lute.min.js?v=${Constants.SIYUAN_VERSION}`, "protyleLuteScript");
addScript(`${Constants.PROTYLE_CDN}/js/protyle-html.js?v=${Constants.SIYUAN_VERSION}`, "protyleWcHtmlScript");
window.siyuan.config = response.data.conf;
await loadPlugins(this);
getLocalStorage(() => {
fetchGet(`/appearance/langs/${window.siyuan.config.appearance.lang}.json?v=${Constants.SIYUAN_VERSION}`, (lauguages: IObject) => {
window.siyuan.languages = lauguages;
window.siyuan.menus = new Menus(this);
bootSync();
const promises = [
loadPlugins(this),
new Promise<void>(resolve => getLocalStorage(resolve)),
new Promise<void>(resolve => fetchGet(
`/appearance/langs/${window.siyuan.config.appearance.lang}.json?v=${Constants.SIYUAN_VERSION}`,
(lauguages: IObject) => {
window.siyuan.languages = lauguages;
resolve();
},
)),
];
if (!window.siyuan.config.readonly) {
promises.push(new Promise<void>(resolve => {
fetchPost("/api/setting/getCloudUser", {}, userResponse => {
window.siyuan.user = userResponse.data;
onGetConfig(response.data.start, this);
account.onSetaccount();
setTitle(window.siyuan.languages.siyuanNote);
initMessage();
resolve();
});
});
});
}));
}
await Promise.all(promises);
if (!window.siyuan.config.readonly) {
bootSync();
}
window.siyuan.menus = new Menus(this);
onGetConfig(response.data.start, this);
account.onSetaccount();
setTitle(window.siyuan.languages.siyuanNote);
initMessage();
});
setNoteBook();
initBlockPopover(this);

View file

@ -105,7 +105,7 @@ export class Wnd {
this.headersElement.parentElement.addEventListener("click", (event) => {
let target = event.target as HTMLElement;
while (target && !target.isEqualNode(this.headersElement)) {
if (target.classList.contains("block__icon") && target.getAttribute("data-type") === "new") {
if (target.classList.contains("block__icon") && target.getAttribute("data-type") === "new" && !window.siyuan.config.readonly) {
setPanelFocus(this.headersElement.parentElement.parentElement);
newFile({
app,

View file

@ -68,6 +68,7 @@ export const initStatus = (isWindow = false) => {
window.siyuan.menus.menu.append(new MenuItem({
label: window.siyuan.languages.userGuide,
icon: "iconHelp",
disabled: window.siyuan.config.readonly,
click: () => {
mountHelp();
}

View file

@ -305,6 +305,7 @@ const openPlugin = (app: App, target: Element) => {
menu.addItem({
icon: "iconSettings",
label: window.siyuan.languages.manage,
disabled: window.siyuan.config.readonly,
click() {
openSetting(app).element.querySelector('.b3-tab-bar [data-name="bazaar"]').dispatchEvent(new CustomEvent("click"));
}
@ -374,7 +375,7 @@ const openPlugin = (app: App, target: Element) => {
}
});
if (!hasPlugin) {
window.siyuan.menus.menu.element.querySelector(".b3-menu__separator").remove();
window.siyuan.menus.menu.element.querySelector(".b3-menu__separator")?.remove();
}
let rect = target.getBoundingClientRect();
if (rect.width === 0) {

View file

@ -448,6 +448,7 @@ export const exportMd = (id: string) => {
label: window.siyuan.languages.template,
iconClass: "ft__error",
icon: "iconMarkdown",
disabled: window.siyuan.config.readonly,
click: async () => {
const result = await fetchSyncPost("/api/block/getRefText", {id: id});
@ -507,8 +508,9 @@ export const exportMd = (id: string) => {
});
});
return;
} else if (response.code === 0) {
showMessage(window.siyuan.languages.exportTplSucc);
}
showMessage(window.siyuan.languages.exportTplSucc);
});
dialog.destroy();
});

View file

@ -440,6 +440,7 @@ export const workspaceMenu = (app: App, rect: DOMRect) => {
window.siyuan.menus.menu.append(new MenuItem({
label: window.siyuan.languages.userGuide,
icon: "iconHelp",
disabled: window.siyuan.config.readonly,
click: () => {
mountHelp();
}

View file

@ -37,9 +37,10 @@ class App {
if (!window.webkit?.messageHandlers && !window.JSAndroid) {
registerServiceWorker(`${Constants.SERVICE_WORKER_PATH}?v=${Constants.SIYUAN_VERSION}`);
}
addBaseURL();
addScriptSync(`${Constants.PROTYLE_CDN}/js/lute/lute.min.js?v=${Constants.SIYUAN_VERSION}`, "protyleLuteScript");
addScript(`${Constants.PROTYLE_CDN}/js/protyle-html.js?v=${Constants.SIYUAN_VERSION}`, "protyleWcHtmlScript");
addBaseURL();
this.appId = Constants.SIYUAN_APPID;
window.siyuan = {
zIndex: 10,
@ -89,30 +90,7 @@ class App {
fetchPost("/api/system/getConf", {}, async (confResponse) => {
window.siyuan.config = confResponse.data.conf;
correctHotkey(siyuanApp);
await loadPlugins(this);
getLocalStorage(() => {
fetchGet(`/appearance/langs/${window.siyuan.config.appearance.lang}.json?v=${Constants.SIYUAN_VERSION}`, (lauguages: IObject) => {
window.siyuan.languages = lauguages;
window.siyuan.menus = new Menus(this);
document.title = window.siyuan.languages.siyuanNote;
bootSync();
loadAssets(confResponse.data.conf.appearance);
initMessage();
initAssets();
fetchPost("/api/setting/getCloudUser", {}, userResponse => {
window.siyuan.user = userResponse.data;
fetchPost("/api/system/getEmojiConf", {}, emojiResponse => {
window.siyuan.emojis = emojiResponse.data as IEmoji[];
setNoteBook(() => {
initFramework(this, confResponse.data.start);
initRightMenu(this);
openChangelog();
});
});
});
addGA();
});
});
document.addEventListener("touchstart", handleTouchStart, false);
document.addEventListener("touchmove", handleTouchMove, false);
document.addEventListener("touchend", (event) => {
@ -140,6 +118,51 @@ class App {
}
}
});
const promises = [
loadPlugins(this),
new Promise<void>(resolve => getLocalStorage(resolve)),
new Promise<void>(resolve => fetchGet(
`/appearance/langs/${window.siyuan.config.appearance.lang}.json?v=${Constants.SIYUAN_VERSION}`,
(lauguages: IObject) => {
window.siyuan.languages = lauguages;
resolve();
},
)),
new Promise<void>(resolve => {
fetchPost("/api/setting/getEmojiConf", {}, emojiResponse => {
window.siyuan.emojis = emojiResponse.data as IEmoji[];
resolve();
});
}),
];
if (!window.siyuan.config.readonly) {
promises.push(new Promise<void>(resolve => {
fetchPost("/api/setting/getCloudUser", {}, userResponse => {
window.siyuan.user = userResponse.data;
resolve();
});
}));
}
await Promise.all(promises);
if (!window.siyuan.config.readonly) {
bootSync();
}
window.siyuan.menus = new Menus(this);
document.title = window.siyuan.languages.siyuanNote;
loadAssets(confResponse.data.conf.appearance);
initMessage();
initAssets();
setNoteBook(() => {
initFramework(this, confResponse.data.start);
initRightMenu(this);
openChangelog();
});
addGA();
});
}
}

View file

@ -1667,6 +1667,7 @@ export class Gutter {
window.siyuan.menus.menu.append(new MenuItem({
label: window.siyuan.languages.wechatReminder,
icon: "iconMp",
disabled: window.siyuan.config.readonly,
click() {
openWechatNotify(nodeElement);
}
@ -1678,6 +1679,7 @@ export class Gutter {
accelerator: window.siyuan.config.keymap.editor.general.quickMakeCard.custom,
iconHTML: '<svg class="b3-menu__icon" style="color:var(--b3-theme-primary)"><use xlink:href="#iconRiffCard"></use></svg>',
icon: "iconRiffCard",
disabled: window.siyuan.config.readonly,
click() {
quickMakeCard(protyle, [nodeElement]);
}

View file

@ -112,6 +112,7 @@ export const openTitleMenu = (protyle: IProtyle, position: IPosition) => {
window.siyuan.menus.menu.append(new MenuItem({
label: window.siyuan.languages.wechatReminder,
icon: "iconMp",
disabled: window.siyuan.config.readonly,
click() {
openFileWechatNotify(protyle);
}
@ -120,6 +121,7 @@ export const openTitleMenu = (protyle: IProtyle, position: IPosition) => {
iconHTML: "",
label: window.siyuan.languages.spaceRepetition,
accelerator: window.siyuan.config.keymap.editor.general.spaceRepetition.custom,
disabled: window.siyuan.config.readonly,
click: () => {
fetchPost("/api/riff/getTreeRiffDueCards", {rootID: protyle.block.rootID}, (response) => {
openCardByData(protyle.app, response.data, "doc", protyle.block.rootID, response.data.name);
@ -128,6 +130,7 @@ export const openTitleMenu = (protyle: IProtyle, position: IPosition) => {
}, {
iconHTML: "",
label: window.siyuan.languages.manage,
disabled: window.siyuan.config.readonly,
click: () => {
fetchPost("/api/filetree/getHPathByID", {
id: protyle.block.rootID
@ -139,6 +142,7 @@ export const openTitleMenu = (protyle: IProtyle, position: IPosition) => {
iconHTML: "",
label: window.siyuan.languages.quickMakeCard,
accelerator: window.siyuan.config.keymap.editor.general.quickMakeCard.custom,
disabled: window.siyuan.config.readonly,
click: () => {
let titleElement = protyle.title?.element;
if (!titleElement) {
@ -153,6 +157,7 @@ export const openTitleMenu = (protyle: IProtyle, position: IPosition) => {
riffCardMenu.push({
iconHTML: "",
label: window.siyuan.languages.addToDeck,
disabled: window.siyuan.config.readonly,
click: () => {
makeCard(protyle.app, [protyle.block.rootID]);
}
@ -163,6 +168,7 @@ export const openTitleMenu = (protyle: IProtyle, position: IPosition) => {
type: "submenu",
icon: "iconRiffCard",
submenu: riffCardMenu,
disabled: window.siyuan.config.readonly,
}).element);
window.siyuan.menus.menu.append(new MenuItem({

View file

@ -45,7 +45,7 @@ export const saveScroll = (protyle: IProtyle, getObject = false) => {
export const getDocByScroll = (options: {
protyle: IProtyle,
scrollAttr: IScrollAttr,
scrollAttr?: IScrollAttr,
mergedOptions?: IOptions,
cb?: () => void
focus?: boolean,
@ -61,7 +61,7 @@ export const getDocByScroll = (options: {
actions = [Constants.CB_GET_UNUNDO];
}
}
if (options.scrollAttr.zoomInId) {
if (options.scrollAttr?.zoomInId) {
fetchPost("/api/filetree/getDoc", {
id: options.scrollAttr.zoomInId,
size: Constants.SIZE_GET_MAX,
@ -100,9 +100,9 @@ export const getDocByScroll = (options: {
return;
}
fetchPost("/api/filetree/getDoc", {
id: options.scrollAttr.rootId || options.mergedOptions?.blockId || options.protyle.block?.rootID || options.scrollAttr.startId,
startID: options.scrollAttr.startId,
endID: options.scrollAttr.endId,
id: options.scrollAttr?.rootId || options.mergedOptions?.blockId || options.protyle.block?.rootID || options.scrollAttr?.startId,
startID: options.scrollAttr?.startId,
endID: options.scrollAttr?.endId,
query: options.protyle.query?.key,
queryMethod: options.protyle.query?.method,
queryTypes: options.protyle.query?.types,

View file

@ -150,8 +150,10 @@ export const initAssets = () => {
return;
}
}
window.siyuan.config.appearance = response.data.appearance;
loadAssets(response.data.appearance);
if (response.code === 0) {
window.siyuan.config.appearance = response.data.appearance;
loadAssets(response.data.appearance);
}
});
});
};

View file

@ -27,9 +27,10 @@ class App {
public appId: string;
constructor() {
addBaseURL();
addScriptSync(`${Constants.PROTYLE_CDN}/js/lute/lute.min.js?v=${Constants.SIYUAN_VERSION}`, "protyleLuteScript");
addScript(`${Constants.PROTYLE_CDN}/js/protyle-html.js?v=${Constants.SIYUAN_VERSION}`, "protyleWcHtmlScript");
addBaseURL();
this.appId = Constants.SIYUAN_APPID;
window.siyuan = {
zIndex: 10,
@ -146,19 +147,34 @@ class App {
};
fetchPost("/api/system/getConf", {}, async (response) => {
window.siyuan.config = response.data.conf;
await loadPlugins(this);
getLocalStorage(() => {
fetchGet(`/appearance/langs/${window.siyuan.config.appearance.lang}.json?v=${Constants.SIYUAN_VERSION}`, (lauguages: IObject) => {
window.siyuan.languages = lauguages;
window.siyuan.menus = new Menus(this);
const promises = [
loadPlugins(this),
new Promise<void>(resolve => getLocalStorage(resolve)),
new Promise<void>(resolve => fetchGet(
`/appearance/langs/${window.siyuan.config.appearance.lang}.json?v=${Constants.SIYUAN_VERSION}`,
(lauguages: IObject) => {
window.siyuan.languages = lauguages;
resolve();
},
)),
];
if (!window.siyuan.config.readonly) {
promises.push(new Promise<void>(resolve => {
fetchPost("/api/setting/getCloudUser", {}, userResponse => {
window.siyuan.user = userResponse.data;
init(this);
setTitle(window.siyuan.languages.siyuanNote);
initMessage();
resolve();
});
});
});
}));
}
await Promise.all(promises);
window.siyuan.menus = new Menus(this);
init(this);
setTitle(window.siyuan.languages.siyuanNote);
initMessage();
});
setNoteBook();
initBlockPopover(this);

View file

@ -47,7 +47,7 @@ func ServeAPI(ginServer *gin.Engine) {
ginServer.Handle("POST", "/api/system/setDownloadInstallPkg", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, setDownloadInstallPkg)
ginServer.Handle("POST", "/api/system/setNetworkProxy", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, setNetworkProxy)
ginServer.Handle("POST", "/api/system/setWorkspaceDir", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, setWorkspaceDir)
ginServer.Handle("POST", "/api/system/getWorkspaces", model.CheckAuth, model.CheckAdminRole, getWorkspaces)
ginServer.Handle("POST", "/api/system/getWorkspaces", model.CheckAuth, getWorkspaces)
ginServer.Handle("POST", "/api/system/getMobileWorkspaces", model.CheckAuth, model.CheckAdminRole, getMobileWorkspaces)
ginServer.Handle("POST", "/api/system/checkWorkspaceDir", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, checkWorkspaceDir)
ginServer.Handle("POST", "/api/system/createWorkspaceDir", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, createWorkspaceDir)
@ -238,7 +238,7 @@ func ServeAPI(ginServer *gin.Engine) {
ginServer.Handle("POST", "/api/sync/listCloudSyncDir", model.CheckAuth, model.CheckAdminRole, listCloudSyncDir)
ginServer.Handle("POST", "/api/sync/performSync", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, performSync)
ginServer.Handle("POST", "/api/sync/performBootSync", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, performBootSync)
ginServer.Handle("POST", "/api/sync/getBootSync", model.CheckAuth, getBootSync)
ginServer.Handle("POST", "/api/sync/getBootSync", model.CheckAuth, model.CheckAdminRole, getBootSync)
ginServer.Handle("POST", "/api/sync/getSyncInfo", model.CheckAuth, model.CheckAdminRole, getSyncInfo)
ginServer.Handle("POST", "/api/sync/exportSyncProviderS3", model.CheckAuth, model.CheckAdminRole, exportSyncProviderS3)
ginServer.Handle("POST", "/api/sync/importSyncProviderS3", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, importSyncProviderS3)
@ -318,7 +318,7 @@ func ServeAPI(ginServer *gin.Engine) {
ginServer.Handle("POST", "/api/setting/setSearch", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, setSearch)
ginServer.Handle("POST", "/api/setting/setKeymap", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, setKeymap)
ginServer.Handle("POST", "/api/setting/setAppearance", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, setAppearance)
ginServer.Handle("POST", "/api/setting/getCloudUser", model.CheckAuth, getCloudUser)
ginServer.Handle("POST", "/api/setting/getCloudUser", model.CheckAuth, model.CheckAdminRole, getCloudUser)
ginServer.Handle("POST", "/api/setting/logoutCloudUser", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, logoutCloudUser)
ginServer.Handle("POST", "/api/setting/login2faCloudUser", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, login2faCloudUser)
ginServer.Handle("POST", "/api/setting/setEmoji", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, setEmoji)

View file

@ -590,10 +590,6 @@ func getCloudUser(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
if !model.IsAdminRoleContext(c) {
return
}
arg, ok := util.JsonArg(c, ret)
if !ok {
return

View file

@ -18,13 +18,14 @@ package api
import (
"encoding/hex"
"github.com/siyuan-note/logging"
"io"
"net/http"
"os"
"path/filepath"
"time"
"github.com/siyuan-note/logging"
"github.com/88250/gulu"
"github.com/gin-gonic/gin"
"github.com/siyuan-note/siyuan/kernel/conf"
@ -381,10 +382,6 @@ func getBootSync(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
if !model.IsAdminRoleContext(c) {
return
}
if model.Conf.Sync.Enabled && 1 == model.BootSyncSucc {
ret.Code = 1
ret.Msg = model.Conf.Language(17)

View file

@ -235,6 +235,13 @@ func getWorkspaces(c *gin.Context) {
return
}
if role := model.GetGinContextRole(c); !model.IsValidRole(role, []model.Role{
model.RoleAdministrator,
}) {
ret.Data = []*Workspace{}
return
}
var workspaces, openedWorkspaces, closedWorkspaces []*Workspace
for _, p := range workspacePaths {
closed := !util.IsWorkspaceLocked(p)

View file

@ -54,7 +54,3 @@ func GetGinContextRole(c *gin.Context) Role {
return RoleVisitor
}
}
func IsAdminRoleContext(c *gin.Context) bool {
return GetGinContextRole(c) == RoleAdministrator
}