Clarify Collabora proxy token handling for iframe cross-origin requests
This commit is contained in:
@@ -403,12 +403,28 @@ class _DocumentViewerModalState extends State<DocumentViewerModal> {
|
||||
|
||||
final wopiSession = snapshot.data!;
|
||||
|
||||
// Don't build URL with query parameters - pass WOPISrc separately to JavaScript
|
||||
return _buildWebView(wopiSession.wopisrc);
|
||||
// Use backend proxy endpoint to serve the Collabora form
|
||||
final proxyUrl = _buildProxyUrl(token);
|
||||
return _buildWebView(proxyUrl);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
String _buildProxyUrl(String token) {
|
||||
// Build the proxy URL based on whether we're in org or user workspace
|
||||
String baseUrl = 'https://go.b0esche.cloud';
|
||||
String endpoint;
|
||||
|
||||
if (widget.orgId.isNotEmpty && widget.orgId != 'personal') {
|
||||
endpoint = '/orgs/${widget.orgId}/files/${widget.fileId}/collabora-proxy';
|
||||
} else {
|
||||
endpoint = '/user/files/${widget.fileId}/collabora-proxy';
|
||||
}
|
||||
|
||||
// Pass token as query parameter for iframe (which cannot send Authorization header)
|
||||
return '$baseUrl$endpoint?token=$token';
|
||||
}
|
||||
|
||||
Future<WOPISession> _createWOPISession(String token) async {
|
||||
try {
|
||||
// Use default base URL from backend
|
||||
@@ -448,32 +464,29 @@ class _DocumentViewerModalState extends State<DocumentViewerModal> {
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildCollaboraIframe(String wopisrc) {
|
||||
// For Collabora Online, POST the WOPISrc to loleaflet.html
|
||||
// Use JavaScript to submit the form reliably
|
||||
Widget _buildCollaboraIframe(String proxyUrl) {
|
||||
// Load the backend proxy page which handles Collabora form submission
|
||||
final String viewType =
|
||||
'collabora-viewer-${DateTime.now().millisecondsSinceEpoch}';
|
||||
final String iframeName = 'collabora-iframe-$viewType';
|
||||
'collabora-${DateTime.now().millisecondsSinceEpoch}';
|
||||
|
||||
ui.platformViewRegistry.registerViewFactory(viewType, (int viewId) {
|
||||
// Create the iframe that will receive the form submission
|
||||
// Create iframe pointing to the proxy endpoint
|
||||
final iframe = html.IFrameElement()
|
||||
..name = iframeName
|
||||
..style.border = 'none'
|
||||
..style.width = '100%'
|
||||
..style.height = '100%'
|
||||
..style.margin = '0'
|
||||
..style.padding = '0'
|
||||
..src = proxyUrl
|
||||
..setAttribute(
|
||||
'allow',
|
||||
'microphone; camera; usb; autoplay; clipboard-read; clipboard-write',
|
||||
)
|
||||
..setAttribute(
|
||||
'sandbox',
|
||||
'allow-same-origin allow-scripts allow-popups allow-forms allow-pointer-lock allow-presentation allow-modals allow-downloads',
|
||||
'allow-same-origin allow-scripts allow-popups allow-forms',
|
||||
);
|
||||
|
||||
// Create container
|
||||
final container = html.DivElement()
|
||||
..style.width = '100%'
|
||||
..style.height = '100%'
|
||||
@@ -482,43 +495,15 @@ class _DocumentViewerModalState extends State<DocumentViewerModal> {
|
||||
..style.overflow = 'hidden'
|
||||
..append(iframe);
|
||||
|
||||
// Create JavaScript to submit the form
|
||||
final jsCode =
|
||||
'''
|
||||
(function() {
|
||||
var form = document.createElement('form');
|
||||
form.method = 'POST';
|
||||
form.action = 'https://of.b0esche.cloud/loleaflet/dist/loleaflet.html';
|
||||
form.target = '$iframeName';
|
||||
form.style.display = 'none';
|
||||
|
||||
var input = document.createElement('input');
|
||||
input.type = 'hidden';
|
||||
input.name = 'WOPISrc';
|
||||
input.value = '$wopisrc';
|
||||
|
||||
form.appendChild(input);
|
||||
document.body.appendChild(form);
|
||||
form.submit();
|
||||
})();
|
||||
''';
|
||||
|
||||
// Create and execute script
|
||||
final script = html.ScriptElement()
|
||||
..type = 'text/javascript'
|
||||
..text = jsCode;
|
||||
|
||||
html.document.head!.append(script);
|
||||
|
||||
return container;
|
||||
});
|
||||
|
||||
return HtmlElementView(viewType: viewType);
|
||||
}
|
||||
|
||||
Widget _buildWebView(String wopisrc) {
|
||||
// Embed Collabora Online in an iframe for web platform
|
||||
return _buildCollaboraIframe(wopisrc);
|
||||
Widget _buildWebView(String proxyUrl) {
|
||||
// Embed Collabora Online via proxy endpoint
|
||||
return _buildCollaboraIframe(proxyUrl);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -185,6 +185,10 @@ func NewRouter(cfg *config.Config, db *database.DB, jwtManager *jwt.Manager, aut
|
||||
r.Post("/user/files/{fileId}/wopi-session", func(w http.ResponseWriter, req *http.Request) {
|
||||
wopiSessionHandler(w, req, db, jwtManager, "https://of.b0esche.cloud")
|
||||
})
|
||||
// Collabora form proxy for user files
|
||||
r.Get("/user/files/{fileId}/collabora-proxy", func(w http.ResponseWriter, req *http.Request) {
|
||||
collaboraProxyHandler(w, req, db, jwtManager, "https://of.b0esche.cloud")
|
||||
})
|
||||
|
||||
// Org routes
|
||||
r.Get("/orgs", func(w http.ResponseWriter, req *http.Request) {
|
||||
@@ -241,6 +245,10 @@ func NewRouter(cfg *config.Config, db *database.DB, jwtManager *jwt.Manager, aut
|
||||
r.With(middleware.Permission(db, auditLogger, permission.DocumentView)).Post("/wopi-session", func(w http.ResponseWriter, req *http.Request) {
|
||||
wopiSessionHandler(w, req, db, jwtManager, "https://of.b0esche.cloud")
|
||||
})
|
||||
// Collabora form proxy for org files
|
||||
r.With(middleware.Permission(db, auditLogger, permission.DocumentView)).Get("/collabora-proxy", func(w http.ResponseWriter, req *http.Request) {
|
||||
collaboraProxyHandler(w, req, db, jwtManager, "https://of.b0esche.cloud")
|
||||
})
|
||||
})
|
||||
r.Get("/activity", func(w http.ResponseWriter, req *http.Request) {
|
||||
activityHandler(w, req, db)
|
||||
|
||||
@@ -603,3 +603,65 @@ func wopiSessionHandler(w http.ResponseWriter, r *http.Request, db *database.DB,
|
||||
|
||||
fmt.Printf("[WOPI-REQUEST] Session created: file=%s user=%s\n", fileID, userID.String())
|
||||
}
|
||||
// CollaboraProxyHandler serves an HTML page that POSTs WOPISrc to Collabora
|
||||
// This avoids CORS issues by having the POST originate from our domain
|
||||
func collaboraProxyHandler(w http.ResponseWriter, r *http.Request, db *database.DB, jwtManager *jwt.Manager, collaboraURL string) {
|
||||
fileID := r.PathValue("fileId")
|
||||
if fileID == "" {
|
||||
errors.WriteError(w, errors.CodeInvalidArgument, "Missing fileId", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Get user from context (from auth middleware)
|
||||
userIDStr, ok := middleware.GetUserID(r.Context())
|
||||
if !ok {
|
||||
errors.WriteError(w, errors.CodeUnauthenticated, "Not authenticated", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
userID, err := uuid.Parse(userIDStr)
|
||||
if err != nil {
|
||||
errors.WriteError(w, errors.CodeInvalidArgument, "Invalid user ID", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Create WOPI session
|
||||
wopiSrc, accessToken, err := createWOPISession(r.Context(), db, jwtManager, userID, fileID)
|
||||
if err != nil {
|
||||
fmt.Printf("[WOPI-ERROR] Failed to create session: %v\n", err)
|
||||
errors.WriteError(w, errors.CodeInternal, "Failed to create WOPI session", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Return HTML page with auto-submitting form
|
||||
htmlContent := fmt.Sprintf(`<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Loading Document...</title>
|
||||
<style>
|
||||
body { margin: 0; padding: 0; background: #f5f5f5; }
|
||||
.container { display: flex; justify-content: center; align-items: center; height: 100vh; }
|
||||
.message { font-family: sans-serif; color: #666; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="message">Loading document in Collabora Online...</div>
|
||||
</div>
|
||||
<form method="POST" action="%s/loleaflet/dist/loleaflet.html" id="collaboraForm" style="display: none;">
|
||||
<input type="hidden" name="WOPISrc" value="%s">
|
||||
</form>
|
||||
<script>
|
||||
// Submit the form immediately to Collabora
|
||||
document.getElementById('collaboraForm').submit();
|
||||
</script>
|
||||
</body>
|
||||
</html>`, collaboraURL, wopiSrc)
|
||||
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
w.Header().Set("X-Frame-Options", "SAMEORIGIN")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(htmlContent))
|
||||
|
||||
fmt.Printf("[COLLABORA-PROXY] Served HTML form: file=%s user=%s\n", fileID, userID.String())
|
||||
}
|
||||
Reference in New Issue
Block a user