Skip to content

Commit

Permalink
feat: only-office integration pt 2, performed server-side signing, ad…
Browse files Browse the repository at this point in the history
…dressed other comments
  • Loading branch information
Alan Castro committed Mar 28, 2024
1 parent 3a577fd commit c2e2c20
Show file tree
Hide file tree
Showing 17 changed files with 228 additions and 115 deletions.
3 changes: 3 additions & 0 deletions cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ func addConfigFlags(flags *pflag.FlagSet) {
flags.String("branding.files", "", "path to directory with images and custom styles")
flags.Bool("branding.disableExternal", false, "disable external links such as GitHub links")
flags.Bool("branding.disableUsedPercentage", false, "disable used disk percentage graph")

flags.String("onlyoffice.url", "", "onlyoffice integration url")
flags.String("onlyoffice.jwtSecret", "", "onlyoffice integration secret")
}

//nolint:gocyclo
Expand Down
16 changes: 4 additions & 12 deletions files/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,21 +197,12 @@ func (i *FileInfo) Checksum(algo string) error {
}

func (i *FileInfo) RealPath() string {
if realPathFs, ok := i.Fs.(interface {
RealPath(name string) (fPath string, err error)
}); ok {
realPath, err := realPathFs.RealPath(i.Path)
if err == nil {
return realPath
}
}

return i.Path
return GetRealPath(i.Fs, i.Path)
}

// TODO: use constants
//
//nolint:goconst
//nolint:goconst,gocyclo
func (i *FileInfo) detectType(modify, saveContent, readHeader bool) error {
if IsNamedPipe(i.Mode) {
i.Type = "blob"
Expand Down Expand Up @@ -270,7 +261,8 @@ func (i *FileInfo) detectType(modify, saveContent, readHeader bool) error {
i.Content = string(content)
}
return nil
case strings.HasPrefix(mimetype, "application/vnd.openxmlformats-officedocument"):
case strings.HasPrefix(mimetype, "application/vnd.openxmlformats-officedocument"),
strings.HasPrefix(mimetype, "application/vnd.oasis.opendocument"):
i.Type = "officedocument"
return nil
default:
Expand Down
15 changes: 15 additions & 0 deletions files/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package files
import (
"os"
"unicode/utf8"

"github.com/spf13/afero"
)

func isBinary(content []byte) bool {
Expand Down Expand Up @@ -57,3 +59,16 @@ func IsNamedPipe(mode os.FileMode) bool {
func IsSymlink(mode os.FileMode) bool {
return mode&os.ModeSymlink != 0
}

func GetRealPath(fs afero.Fs, path string) string {
if realPathFs, ok := fs.(interface {
RealPath(name string) (fPath string, err error)
}); ok {
realPath, err := realPathFs.RealPath(path)
if err == nil {
return realPath
}
}

return path
}
8 changes: 0 additions & 8 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
"core-js": "^3.32.0",
"css-vars-ponyfill": "^2.4.8",
"filesize": "^10.0.8",
"jose": "^4.13.1",
"js-base64": "^3.7.5",
"lodash.clonedeep": "^4.5.0",
"lodash.throttle": "^4.1.1",
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/utils/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const enableExec = window.FileBrowser.EnableExec;
const tusSettings = window.FileBrowser.TusSettings;
const origin = window.location.origin;
const tusEndpoint = `/api/tus`;
const onlyOffice = window.FileBrowser.OnlyOffice;
const onlyOfficeUrl = window.FileBrowser.OnlyOfficeUrl;

export {
name,
Expand All @@ -40,5 +40,5 @@ export {
tusSettings,
origin,
tusEndpoint,
onlyOffice,
onlyOfficeUrl,
};
4 changes: 2 additions & 2 deletions frontend/src/views/Files.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<script>
import { files as api } from "@/api";
import { mapState, mapMutations } from "vuex";
import { onlyOffice } from "@/utils/constants";
import { onlyOfficeUrl } from "@/utils/constants";
import HeaderBar from "@/components/header/HeaderBar.vue";
import Breadcrumbs from "@/components/Breadcrumbs.vue";
Expand Down Expand Up @@ -61,7 +61,7 @@ export default {
this.req.type === "textImmutable"
) {
return "editor";
} else if (this.req.type === "officedocument" && onlyOffice !== "") {
} else if (this.req.type === "officedocument" && onlyOfficeUrl) {
return "OnlyOfficeEditor";
} else {
return "preview";
Expand Down
98 changes: 26 additions & 72 deletions frontend/src/views/files/OnlyOfficeEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,38 @@
<action icon="close" :label="$t('buttons.close')" @action="close()" />
<title>{{ req.name }}</title>
</header-bar>

<breadcrumbs base="/files" noLink />

<div id="editor"></div>
<errors v-if="error" :errorCode="error.status" />
<div id="editor">
<div id="onlyoffice-editor"></div>
</div>
</div>
</template>

<style scoped>
#editor-container {
height: 100vh;
width: 100vw;
}
</style>

<script>
import { mapState } from "vuex";
import url from "@/utils/url";
import { baseURL, onlyOffice } from "@/utils/constants";
import * as jose from "jose";
import { onlyOfficeUrl } from "@/utils/constants";
import HeaderBar from "@/components/header/HeaderBar.vue";
import Action from "@/components/header/Action.vue";
import Breadcrumbs from "@/components/Breadcrumbs.vue";
import Errors from "@/views/Errors.vue";
import { fetchJSON } from "@/api/utils";
export default {
name: "onlyofficeeditor",
components: {
HeaderBar,
Action,
Breadcrumbs,
Errors,
},
data: function () {
return {};
return {
error: null,
clientConfig: null,
};
},
computed: {
...mapState(["req", "user", "jwt"]),
Expand Down Expand Up @@ -71,76 +70,31 @@ export default {
},
},
created() {
const isMobile = window.innerWidth <= 736;
this.clientConfigPromise = fetchJSON(
`/api/onlyoffice/client-config${this.req.path}?isMobile=${isMobile}`
);
window.addEventListener("keydown", this.keyEvent);
},
beforeDestroy() {
window.removeEventListener("keydown", this.keyEvent);
this.editor.destroyEditor();
},
mounted: function () {
let onlyofficeScript = document.createElement("script");
onlyofficeScript.setAttribute(
"src",
`${onlyOffice.url}/web-apps/apps/api/documents/api.js`
);
const scriptUrl = `${onlyOfficeUrl}/web-apps/apps/api/documents/api.js`;
const onlyofficeScript = document.createElement("script");
onlyofficeScript.setAttribute("src", scriptUrl);
document.head.appendChild(onlyofficeScript);
/*eslint-disable */
onlyofficeScript.onload = () => {
let fileUrl = `${window.location.protocol}//${window.location.host}${baseURL}/api/raw${url.encodePath(
this.req.path
)}?auth=${this.jwt}`;
// create a key from the last modified timestamp and the reversed file path (most specific part first)
// replace all special characters (only these symbols are supported: 0-9, a-z, A-Z, -._=)
// and truncate it (max length is 20 characters)
const key = (
Date.parse(this.req.modified).valueOf()
+ url
.encodePath(this.req.path.split('/').reverse().join(''))
.replaceAll(/[!~[\]*'()/,;:\-%+. ]/g, "")
).substring(0, 20);
const config = {
document: {
fileType: this.req.extension.substring(1),
key: key,
title: this.req.name,
url: fileUrl,
permissions: {
edit: this.user.perm.modify,
download: this.user.perm.download,
print: this.user.perm.download
}
},
editorConfig: {
callbackUrl: `${window.location.protocol}//${window.location.host}${baseURL}/api/onlyoffice/callback?auth=${this.jwt}&save=${encodeURIComponent(this.req.path)}`,
user: {
id: this.user.id,
name: `User ${this.user.id}`
},
customization: {
autosave: true,
forcesave: true
},
lang: this.user.locale,
mode: this.user.perm.modify ? "edit" : "view"
}
};
if(onlyOffice.jwtSecret != "") {
const alg = 'HS256';
new jose.SignJWT(config)
.setProtectedHeader({ alg })
.sign(new TextEncoder().encode(onlyOffice.jwtSecret)).then((jwt) => {
config.token = jwt;
this.editor = new DocsAPI.DocEditor("editor", config);
})
} else {
this.editor = new DocsAPI.DocEditor("editor", config);
onlyofficeScript.onload = async () => {
try {
const clientConfig = await this.clientConfigPromise;
// eslint-disable-next-line no-undef
this.editor = new DocsAPI.DocEditor("onlyoffice-editor", clientConfig);
} catch (e) {
this.error = e;
}
};
/*eslint-enable */
},
methods: {
back() {
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ require (
)

require (
github.com/allegro/bigcache v1.2.1 // indirect
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM=
github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
github.com/Sereal/Sereal v0.0.0-20190618215532-0b8ac451a863 h1:BRrxwOZBolJN4gIwvZMJY1tzqBvQgpaZiQRuIDD40jM=
github.com/Sereal/Sereal v0.0.0-20190618215532-0b8ac451a863/go.mod h1:D0JMgToj/WdxCgd30Kc1UcA9E+WdZoJqeVOuYW7iTBM=
github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc=
github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
Expand Down
1 change: 1 addition & 0 deletions http/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ func withUser(fn handleFunc) handleFunc {
w.Header().Add("X-Renew-Token", "true")
}

d.authToken = token.Raw
d.user, err = d.store.Users.Get(d.server.Root, tk.User.ID)
if err != nil {
return http.StatusInternalServerError, err
Expand Down
11 changes: 6 additions & 5 deletions http/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ type handleFunc func(w http.ResponseWriter, r *http.Request, d *data) (int, erro

type data struct {
*runner.Runner
settings *settings.Settings
server *settings.Server
store *storage.Storage
user *users.User
raw interface{}
authToken string
settings *settings.Settings
server *settings.Server
store *storage.Storage
user *users.User
raw interface{}
}

// Check implements rules.Checker.
Expand Down
1 change: 1 addition & 0 deletions http/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ func NewHandler(
users.Handle("/{id:[0-9]+}", monkey(userGetHandler, "")).Methods("GET")
users.Handle("/{id:[0-9]+}", monkey(userDeleteHandler, "")).Methods("DELETE")

api.PathPrefix("/onlyoffice").Handler(monkey(onlyofficeClientConfigGetHandler, "/api/onlyoffice/client-config")).Methods("GET")
api.PathPrefix("/onlyoffice").Handler(monkey(onlyofficeCallbackHandler, "/api/onlyoffice/callback")).Methods("POST")

api.PathPrefix("/resources").Handler(monkey(resourceGetHandler, "/api/resources")).Methods("GET")
Expand Down

0 comments on commit c2e2c20

Please sign in to comment.