Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement basic file uploads to wiki #6024

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 75 additions & 1 deletion models/wiki.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
)

var (
reservedWikiNames = []string{"_pages", "_new", "_edit", "raw"}
reservedWikiNames = []string{"_pages", "_new", "_edit", "_upload", "raw"}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be a migration check or something if we add a new reserved name.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we should reverse all word begin with _. :)

wikiWorkingPool = sync.NewExclusivePool()
)

Expand Down Expand Up @@ -242,3 +242,77 @@ func (repo *Repository) DeleteWikiPage(doer *User, wikiName string) (err error)

return nil
}

// UploadWikiFiles uploads files to wiki's repo
func (repo *Repository) UploadWikiFiles(doer *User, message string, files []string) (err error) {
if len(files) == 0 {
return nil
}

uploads, err := GetUploadsByUUIDs(files)
if err != nil {
return fmt.Errorf("GetUploadsByUUIDs [uuids: %v]: %v", files, err)
}

wikiWorkingPool.CheckIn(com.ToStr(repo.ID))
defer wikiWorkingPool.CheckOut(com.ToStr(repo.ID))

if err = repo.InitWiki(); err != nil {
return fmt.Errorf("InitWiki: %v", err)
}

localPath := repo.LocalWikiPath()

if err = discardLocalWikiChanges(localPath); err != nil {
return fmt.Errorf("discardLocalWikiChanges: %v", err)
} else if err = repo.updateLocalWiki(); err != nil {
return fmt.Errorf("UpdateLocalWiki: %v", err)
}

// Copy uploaded files into repository.
for _, upload := range uploads {
tmpPath := upload.LocalPath()
targetPath := path.Join(localPath, upload.Name)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will allow you to write to arbitrary places in the filesystem. Admittedly you can't overwrite anything with your current code but you can certainly inject into hooks.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps this could be solved by using the hash of the file path instead of the actual path, in a way similar to what is currently done with avatars (although for avatars it's a hash of the file contents, which I don't think is the best way here).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@guillep2k I think if we look for forbidden pathern this shouldnt be a problem. If we hash files on the wiki wont have a human redable name anymore

allow normal chars a-Z numbers and '-' '_' '.' and add a check for /../ patterns

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@6543 but then the users will need to think about how they need to name their files. We should allow them to use any name, as long as it renders a serviceable path from Macaron (i.e. no slashes, no empty string).


if !com.IsFile(tmpPath) {
continue
}

if com.IsExist(targetPath) {
return ErrWikiAlreadyExist{targetPath}
}

// SECURITY: if new file is a symlink to non-exist critical file,
// attack content can be written to the target file (e.g. authorized_keys2)
// as a new page operation.
// So we want to make sure the symlink is removed before write anything.
// The new file we created will be in normal text format.
if err = os.RemoveAll(targetPath); err != nil {
return err
}

if err = com.Copy(tmpPath, targetPath); err != nil {
return fmt.Errorf("Copy: %v", err)
}
}

if len(message) == 0 {
message = "Upload files"
}

if err = git.AddChanges(localPath, true); err != nil {
return fmt.Errorf("AddChanges: %v", err)
} else if err = git.CommitChanges(localPath, git.CommitChangesOptions{
Committer: doer.NewGitSig(),
Message: message,
}); err != nil {
return fmt.Errorf("CommitChanges: %v", err)
} else if err = git.Push(localPath, git.PushOptions{
Remote: "origin",
Branch: "master",
}); err != nil {
return fmt.Errorf("Push: %v", err)
}

return DeleteUploads(uploads...)
}
11 changes: 11 additions & 0 deletions modules/auth/repo_form.go
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,17 @@ func (f *NewWikiForm) Validate(ctx *macaron.Context, errs binding.Errors) bindin
return validate(errs, ctx.Data, f, ctx.Locale)
}

// UploadWikiFileForm form for uploading wiki file
type UploadWikiFileForm struct {
Message string
Files []string
}

// Validate validates the fields
func (f *UploadWikiFileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}

// ___________ .___.__ __
// \_ _____/ __| _/|__|/ |_
// | __)_ / __ | | \ __\
Expand Down
1 change: 1 addition & 0 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -938,6 +938,7 @@ wiki.last_commit_info = %s edited this page %s
wiki.edit_page_button = Edit
wiki.new_page_button = New Page
wiki.delete_page_button = Delete Page
wiki.upload_files_button = Upload Files
wiki.delete_page_notice_1 = Deleting the wiki page '%s' cannot be undone. Continue?
wiki.page_already_exists = A wiki page with the same name already exists.
wiki.reserved_page = The wiki page name '%s' is reserved.
Expand Down
49 changes: 45 additions & 4 deletions routers/repo/wiki.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@ import (
)

const (
tplWikiStart base.TplName = "repo/wiki/start"
tplWikiView base.TplName = "repo/wiki/view"
tplWikiNew base.TplName = "repo/wiki/new"
tplWikiPages base.TplName = "repo/wiki/pages"
tplWikiStart base.TplName = "repo/wiki/start"
tplWikiView base.TplName = "repo/wiki/view"
tplWikiNew base.TplName = "repo/wiki/new"
tplWikiPages base.TplName = "repo/wiki/pages"
tplWikiUpload base.TplName = "repo/wiki/upload"
)

// MustEnableWiki check if wiki is enabled, if external then redirect
Expand Down Expand Up @@ -435,3 +436,43 @@ func DeleteWikiPagePost(ctx *context.Context) {
"redirect": ctx.Repo.RepoLink + "/wiki/",
})
}

// UploadWikiFile render wiki upload page
func UploadWikiFile(ctx *context.Context) {
ctx.Data["PageIsWiki"] = true
ctx.Data["PageIsUpload"] = true
renderUploadSettings(ctx)

if !ctx.Repo.Repository.HasWiki() {
ctx.Redirect(ctx.Repo.RepoLink + "/wiki")
return
}

renderWikiPage(ctx, false)
if ctx.Written() {
return
}

ctx.HTML(200, tplWikiUpload)
}

// UploadWikiFilePost response for wiki upload request
func UploadWikiFilePost(ctx *context.Context, form auth.UploadWikiFileForm) {
fmt.Println("UploadWikiFilePost")

ctx.Data["PageIsWiki"] = true
ctx.Data["PageIsUpload"] = true
renderUploadSettings(ctx)

if ctx.HasError() {
ctx.HTML(200, tplWikiUpload)
return
}

if err := ctx.Repo.Repository.UploadWikiFiles(ctx.User, strings.TrimSpace(form.Message), form.Files); err != nil {
ctx.ServerError("DeleteWikiPage", err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why DeleteWikiPage

return
}

ctx.Redirect(ctx.Repo.RepoLink + "/wiki/")
}
2 changes: 2 additions & 0 deletions routers/routes/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,8 @@ func RegisterRoutes(m *macaron.Macaron) {
Post(bindIgnErr(auth.NewWikiForm{}), repo.NewWikiPost)
m.Combo("/:page/_edit").Get(repo.EditWiki).
Post(bindIgnErr(auth.NewWikiForm{}), repo.EditWikiPost)
m.Combo("/_upload").Get(repo.UploadWikiFile).
Post(bindIgnErr(auth.UploadWikiFileForm{}), repo.UploadWikiFilePost)
m.Post("/:page/delete", repo.DeleteWikiPagePost)
}, context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter)
}, repo.MustEnableWiki, context.RepoRef())
Expand Down
1 change: 1 addition & 0 deletions templates/repo/wiki/pages.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
{{if and .CanWriteWiki (not .IsRepositoryMirror)}}
<div class="ui right">
<a class="ui green small button" href="{{.RepoLink}}/wiki/_new">{{.i18n.Tr "repo.wiki.new_page_button"}}</a>
<a class="ui blue small button" href="{{.RepoLink}}/wiki/_upload">{{.i18n.Tr "repo.wiki.upload_files_button"}}</a>
</div>
{{end}}
</div>
Expand Down
23 changes: 23 additions & 0 deletions templates/repo/wiki/upload.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{{template "base/head" .}}
<div class="repository file editor upload">
{{template "repo/header" .}}
<div class="ui container">
{{template "base/alert" .}}
<form class="ui comment form" method="post">
{{.CsrfTokenHtml}}
<div class="field">
<div class="files"></div>
<div class="ui basic button dropzone" id="dropzone" data-upload-url="{{.RepoLink}}/upload-file" data-remove-url="{{.RepoLink}}/upload-remove" data-csrf="{{.CsrfToken}}" data-accepts="{{.UploadAllowedTypes}}" data-max-file="{{.UploadMaxFiles}}" data-max-size="{{.UploadMaxSize}}" data-default-message="{{.i18n.Tr "dropzone.default_message"}}" data-invalid-input-type="{{.i18n.Tr "dropzone.invalid_input_type"}}" data-file-too-big="{{.i18n.Tr "dropzone.file_too_big"}}" data-remove-file="{{.i18n.Tr "dropzone.remove_file"}}"></div>
</div>
<div class="field">
<input name="message" placeholder="{{.i18n.Tr "repo.wiki.default_commit_message"}}">
</div>
<div class="text right">
<button class="ui green button">
{{.i18n.Tr "repo.wiki.upload_files_button"}}
</button>
</div>
</form>
</div>
</div>
{{template "base/footer" .}}
1 change: 1 addition & 0 deletions templates/repo/wiki/view.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
<a class="ui small button" href="{{.RepoLink}}/wiki/{{.PageURL}}/_edit">{{.i18n.Tr "repo.wiki.edit_page_button"}}</a>
<a class="ui green small button" href="{{.RepoLink}}/wiki/_new">{{.i18n.Tr "repo.wiki.new_page_button"}}</a>
<a class="ui red small button delete-button" href="" data-url="{{.RepoLink}}/wiki/{{.PageURL}}/delete" data-id="{{.PageURL}}">{{.i18n.Tr "repo.wiki.delete_page_button"}}</a>
<a class="ui blue small button" href="{{.RepoLink}}/wiki/_upload">{{.i18n.Tr "repo.wiki.upload_files_button"}}</a>
</div>
{{end}}
</div>
Expand Down