Skip to content

Commit

Permalink
Implement paste privacy (#27)
Browse files Browse the repository at this point in the history
  • Loading branch information
iliafrenkel authored Jul 29, 2021
1 parent eb83939 commit 0483906
Show file tree
Hide file tree
Showing 11 changed files with 107 additions and 38 deletions.
2 changes: 2 additions & 0 deletions src/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type Paste struct {
Body string `json:"body"`
Expires time.Time `json:"expires" gorm:"index"`
DeleteAfterRead bool `json:"delete_after_read"`
Privacy string `json:"privacy"`
Password string `json:"password"`
Created time.Time `json:"created"`
Syntax string `json:"syntax"`
Expand Down Expand Up @@ -105,6 +106,7 @@ type PasteForm struct {
Body string `json:"body" form:"body" binding:"required"`
Expires string `json:"expires" form:"expires" binding:"required"`
DeleteAfterRead bool `json:"delete_after_read" form:"delete_after_read" binding:"-"`
Privacy string `json:"privacy" form:"privacy" binding:"required"`
Password string `json:"password" form:"password"`
Syntax string `json:"syntax" form:"syntax" binding:"required"`
UserID int64 `json:"user_id"`
Expand Down
17 changes: 13 additions & 4 deletions src/api/http/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -348,10 +348,10 @@ func (h *APIServer) handlePasteGetWithPassword(c *gin.Context) {
// prepared it for us.
var pwd string
if data, ok := c.Get("payload"); !ok {
log.Println("handlePasteGetWithPassword: unexpected error: ", err.Error())
log.Println("handlePasteGetWithPassword: unexpected error: can't get the payload")
c.JSON(http.StatusInternalServerError, api.HTTPError{
Code: http.StatusInternalServerError,
Message: fmt.Sprintf("%s: %s", http.StatusText(http.StatusInternalServerError), err.Error()),
Message: fmt.Sprintf("%s: can't get the payload", http.StatusText(http.StatusInternalServerError)),
})
return
} else {
Expand Down Expand Up @@ -407,9 +407,18 @@ func (h *APIServer) handlePasteGetWithPassword(c *gin.Context) {
// error. Body size is currently limited to a configurable value of
// Options.MaxBodySize.
func (h *APIServer) handlePasteCreate(c *gin.Context) {
data := c.MustGet("payload").(*api.PasteForm)
var data interface{}
var ok bool
if data, ok = c.Get("payload"); !ok {
log.Println("handlePasteGetWithPassword: unexpected error: can't get the payload")
c.JSON(http.StatusInternalServerError, api.HTTPError{
Code: http.StatusInternalServerError,
Message: fmt.Sprintf("%s: can't get the payload", http.StatusText(http.StatusInternalServerError)),
})
return
}

p, err := h.PasteService.Create(*data)
p, err := h.PasteService.Create(*data.(*api.PasteForm))
if err != nil {
log.Printf("handleCreate: failed to create paste: %v\n", err)
c.JSON(http.StatusBadRequest, api.HTTPError{
Expand Down
2 changes: 2 additions & 0 deletions src/api/http/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ func createTestPaste() *api.PasteForm {
Body: "Test body",
Expires: "never",
DeleteAfterRead: false,
Privacy: "public",
Password: "",
Syntax: "none",
UserID: 0,
Expand Down Expand Up @@ -343,6 +344,7 @@ func Test_CreatePasteWrongFieldType(t *testing.T) {
"title": 1,
"body": "body",
"expires":"never",
"privacy":"public",
"syntax":"none"
}`
resp, err := http.Post(mckSrv.URL+"/paste", "application/json", bytes.NewBuffer([]byte(body)))
Expand Down
1 change: 1 addition & 0 deletions src/api/paste/memory/memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ func (s *PasteService) Create(p api.PasteForm) (*api.Paste, error) {
Body: p.Body,
Expires: expires,
DeleteAfterRead: p.DeleteAfterRead,
Privacy: p.Privacy,
Password: p.Password,
Created: created,
Syntax: p.Syntax,
Expand Down
1 change: 1 addition & 0 deletions src/api/paste/sqldb/sqldb.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ func (s *PasteService) Create(p api.PasteForm) (*api.Paste, error) {
Body: p.Body,
Expires: expires,
DeleteAfterRead: p.DeleteAfterRead,
Privacy: p.Privacy,
Password: p.Password,
Created: created,
Syntax: p.Syntax,
Expand Down
12 changes: 11 additions & 1 deletion src/web/http/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -554,8 +554,18 @@ func (h *WebServer) handleGetPaste(c *gin.Context) {
return
}

// Get user pastes
userid, _ := c.Get("user_id")

// Check if paste is private
if p.Privacy == "private" && p.UserID != userid {
c.Set("errorCode", http.StatusNotFound)
c.Set("errorText", http.StatusText(http.StatusNotFound))
c.Set("errorMessage", "The paste cannot be found.")
h.showError(c)
return
}

// Get user pastes
var pastes []api.Paste

if userid != nil && userid.(int64) != 0 {
Expand Down
54 changes: 30 additions & 24 deletions src/web/templates/form.html
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
<form method="POST" action="/p/">
<div class="form-floating mb-3">
<input type="text" name="title" id="pasteTitle" placeholder="Paste title" class="form-control" aria-describedby="titleHelpBlock">
<label for="pasteTitle" class="form-label text-muted">Title (optional)</label>
</div>
<div class="form-floating mb-3">
<textarea name="body" id="pasteBody" required value="" placeholder="" maxlength="10240" style="height: 20em; font-size: 80%;" class="form-control" aria-describedby="bodyHelpBlock"></textarea>
<label for="pasteBody" class="form-label text-muted">Content (max 10K)</label>
</div>
<div class="mb-5 row">
<div class="col">
<div class="form-floating">
<select class="form-select" id="pasteSyntax" name="syntax" aria-describedby="syntaxHelpBlock">
<div class="col-9 pt-3">
<div class="form-floating mb-3">
<input type="text" name="title" id="pasteTitle" placeholder="Paste title" class="form-control" aria-describedby="titleHelpBlock">
<label for="pasteTitle" class="form-label text-muted">Title (optional)</label>
</div>
<div class="form-floating mb-3">
<textarea name="body" id="pasteBody" required value="" placeholder="" maxlength="10240" style="height: 22em; font-size: 80%;" class="form-control" aria-describedby="bodyHelpBlock"></textarea>
<label for="pasteBody" class="form-label text-muted">Content</label>
</div>
</div>
<div class="col-3 d-flex flex-column justify-content-top pt-3 bg-light rounded">
<div class="form-floating mb-3">
<select class="form-select" id="pasteSyntax" name="syntax" aria-describedby="syntaxHelpBlock">
<option value="none" selected>Plain Text</option>
<option value="markup">Markup</option>
<option value="css">CSS</option>
Expand Down Expand Up @@ -168,20 +170,16 @@
</select>
<label for="pasteSyntax" class="form-label text-muted">Syntax highlighting</label>
</div>
</div>
<div class="col">
<div class="form-floating">
<div class="form-floating mb-3">
<select class="form-select" id="pasteDeleteAfterRead" name="delete_after_read" aria-describedby="deleteafterreadHelpBlock">
<option value="false" selected>No</option>
<option value="true">Yes</option>
</select>
<label for="pasteDeleteAfterRead" class="form-label text-muted">Delete after first read</label>
</div>
</div>
<div class="col">
<div class="form-floating">
<div class="form-floating mb-3">
<select class="form-select" id="pasteExpires" name="expires" aria-describedby="expiresHelpBlock">
<option value="never">Never</option>
<option value="never" selected>Never</option>
<option value="10m">10 minutes</option>
<option value="30m">30 minutes</option>
<option value="1h">1 hour</option>
Expand All @@ -198,16 +196,24 @@
</select>
<label for="pasteExpires" class="form-label text-muted">Paste will expire after</label>
</div>
</div>
<div class="col">
<div class="form-floating">
<div class="form-floating mb-3">
<select class="form-select" id="pastePrivacy" name="privacy" aria-describedby="privacyHelpBlock">
<option value="public" selected>Public</option>
<option value="private">Private</option>
<option value="unlisted">Unlisted</option>
</select>
<label for="pasteExpires" class="form-label text-muted">Paste privacy</label>
</div>
<div class="form-floating mb-3">
<input type="text" name="password" id="pastePassword" placeholder="Paste password" class="form-control" aria-describedby="pastePasswordHelpBlock">
<label for="pastePassword" class="form-label text-muted">Password (optional)</label>
</div>
</div>
</div>
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
<input type="reset" value="Clear" class="btn btn-outline-secondary btn-lg me-md-2 w-25">
<input type="submit" value="Paste" class="btn btn-primary btn-lg me-md-2 w-25">
<div class="mb-5 row">
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
<input type="reset" value="Clear" class="btn btn-outline-secondary btn-lg me-md-2 w-25">
<input type="submit" value="Paste" class="btn btn-primary btn-lg me-md-2 w-25">
</div>
</div>
</form>
4 changes: 3 additions & 1 deletion src/web/templates/header.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ <h1>
{{.username}}
</a>
<ul class="dropdown-menu" aria-labelledby="navbarUserDropdownLink">
<li><a class="dropdown-item" href="/u/logout">Logout</a></li>
<li><a class="dropdown-item" href="/p/list">My pastes</a></li>
<li><a class="dropdown-item disabled" href="#" tabindex="-1" aria-disabled="true">Account</a></li>
<li><a class="dropdown-item disabled" href="#" tabindex="-1" aria-disabled="true">Prefernces</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="/u/logout">Logout</a></li>
</ul>
</li>
{{else}}
Expand Down
2 changes: 1 addition & 1 deletion src/web/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<div class="col-9">
<div class="card border-0">
<div class="card-body">
<h5 class="card-title text-center">New Paste</h5>
<h5 class="card-title text-left">New Paste</h5>
{{template "form.html"}}
</div>
</div>
Expand Down
21 changes: 18 additions & 3 deletions src/web/templates/paste.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<a href="/p/{{.URL}}" class="list-group-item list-group-item-action">
<div class="">
<p class="mb-1 text-truncate" title="{{if .Title}}{{.Title}}{{end}}">{{if .Title}}{{.Title}}{{else}}untitled{{end}}</p>
<p class="mb-1 text-truncate" title="{{if .Title}}{{.Title}}{{end}}">{{if .Title}}{{.Title}}{{else}}<span class="text-muted">&lt;untitled&gt;</span>{{end}}</p>
</div>
<p class="mb-1" style="font-size:80%">
<span class="badge bg-transparent text-dark fw-light text-uppercase border" title="Created">
Expand All @@ -24,12 +24,27 @@
</svg>
0
</span>
<span class="badge bg-transparent text-dark fw-light text-uppercase border" title="Private">
{{if eq .Privacy "private" }}
<span class="badge bg-transparent text-danger fw-light text-uppercase border" title="Private">
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-lock align-text-bottom" viewBox="0 0 16 16">
<path d="M8 1a2 2 0 0 1 2 2v4H6V3a2 2 0 0 1 2-2zm3 6V3a3 3 0 0 0-6 0v4a2 2 0 0 0-2 2v5a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2zM5 8h6a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1z"/>
</svg>
</span>
{{else if eq .Privacy "unlisted" }}
<span class="badge bg-transparent text-warning fw-light text-uppercase border" title="Unlisted">
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-eye-slash align-text-bottom" viewBox="0 0 16 16">
<path d="M13.359 11.238C15.06 9.72 16 8 16 8s-3-5.5-8-5.5a7.028 7.028 0 0 0-2.79.588l.77.771A5.944 5.944 0 0 1 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.134 13.134 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755-.165.165-.337.328-.517.486l.708.709z"/>
<path d="M11.297 9.176a3.5 3.5 0 0 0-4.474-4.474l.823.823a2.5 2.5 0 0 1 2.829 2.829l.822.822zm-2.943 1.299.822.822a3.5 3.5 0 0 1-4.474-4.474l.823.823a2.5 2.5 0 0 0 2.829 2.829z"/>
<path d="M3.35 5.47c-.18.16-.353.322-.518.487A13.134 13.134 0 0 0 1.172 8l.195.288c.335.48.83 1.12 1.465 1.755C4.121 11.332 5.881 12.5 8 12.5c.716 0 1.39-.133 2.02-.36l.77.772A7.029 7.029 0 0 1 8 13.5C3 13.5 0 8 0 8s.939-1.721 2.641-3.238l.708.709zm10.296 8.884-12-12 .708-.708 12 12-.708.708z"/>
</svg>
</span>
{{else}}
<span class="badge bg-transparent text-success fw-light text-uppercase border" title="Public">
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-lock align-text-bottom" viewBox="0 0 16 16">
<!--path d="M8 1a2 2 0 0 1 2 2v4H6V3a2 2 0 0 1 2-2zm3 6V3a3 3 0 0 0-6 0v4a2 2 0 0 0-2 2v5a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2zM5 8h6a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1z"/-->
<path d="M11 1a2 2 0 0 0-2 2v4a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h5V3a3 3 0 0 1 6 0v4a.5.5 0 0 1-1 0V3a2 2 0 0 0-2-2zM3 8a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V9a1 1 0 0 0-1-1H3z"/>
</svg>
</span>
{{end}}
{{if .DeleteAfterRead }}
<span class="badge bg-transparent text-danger fw-light text-uppercase border border-danger" title="Delete after read">
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-exclamation-triangle align-text-bottom" viewBox="0 0 16 16">
Expand Down
29 changes: 25 additions & 4 deletions src/web/templates/view.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,27 @@ <h6 class="card-subtitle mb-2 text-muted" style="font-size: 90%;">
</svg>
0
</span>
<span class="badge bg-transparent text-dark fw-light text-uppercase border shadow-sm" title="Public">
{{if eq .Privacy "private" }}
<span class="badge bg-transparent text-danger fw-light text-uppercase border shadow-sm" title="Private">
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-lock align-text-bottom" viewBox="0 0 16 16">
<path d="M8 1a2 2 0 0 1 2 2v4H6V3a2 2 0 0 1 2-2zm3 6V3a3 3 0 0 0-6 0v4a2 2 0 0 0-2 2v5a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2zM5 8h6a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1z"/>
</svg>
</span>
{{else if eq .Privacy "unlisted" }}
<span class="badge bg-transparent text-warning fw-light text-uppercase border shadow-sm" title="Unlisted">
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-eye-slash align-text-bottom" viewBox="0 0 16 16">
<path d="M13.359 11.238C15.06 9.72 16 8 16 8s-3-5.5-8-5.5a7.028 7.028 0 0 0-2.79.588l.77.771A5.944 5.944 0 0 1 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.134 13.134 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755-.165.165-.337.328-.517.486l.708.709z"/>
<path d="M11.297 9.176a3.5 3.5 0 0 0-4.474-4.474l.823.823a2.5 2.5 0 0 1 2.829 2.829l.822.822zm-2.943 1.299.822.822a3.5 3.5 0 0 1-4.474-4.474l.823.823a2.5 2.5 0 0 0 2.829 2.829z"/>
<path d="M3.35 5.47c-.18.16-.353.322-.518.487A13.134 13.134 0 0 0 1.172 8l.195.288c.335.48.83 1.12 1.465 1.755C4.121 11.332 5.881 12.5 8 12.5c.716 0 1.39-.133 2.02-.36l.77.772A7.029 7.029 0 0 1 8 13.5C3 13.5 0 8 0 8s.939-1.721 2.641-3.238l.708.709zm10.296 8.884-12-12 .708-.708 12 12-.708.708z"/>
</svg>
</span>
{{else}}
<span class="badge bg-transparent text-success fw-light text-uppercase border shadow-sm" title="Public">
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-lock align-text-bottom" viewBox="0 0 16 16">
<!--path d="M8 1a2 2 0 0 1 2 2v4H6V3a2 2 0 0 1 2-2zm3 6V3a3 3 0 0 0-6 0v4a2 2 0 0 0-2 2v5a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2zM5 8h6a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1z"/-->
<path d="M11 1a2 2 0 0 0-2 2v4a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h5V3a3 3 0 0 1 6 0v4a.5.5 0 0 1-1 0V3a2 2 0 0 0-2-2zM3 8a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V9a1 1 0 0 0-1-1H3z"/>
</svg>
</span>
{{end}}
{{if .DeleteAfterRead }}
<span class="badge bg-danger text-light fw-light text-uppercase border shadow-sm" title="Paste deletion imminent">
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-exclamation-triangle align-text-bottom" viewBox="0 0 16 16">
Expand Down Expand Up @@ -87,13 +102,19 @@ <h2 class="accordion-header" id="panelsStayOpen-headingTwo">
<div class="accordion-body">
{{if .URL }}
<div class="input-group mb-3">
<span class="input-group-text" id="basic-addon1">
<span class="input-group-text border-primary bg-white text-primary" id="basic-addon1">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-link align-text-bottom" viewBox="0 0 16 16">
<path d="M6.354 5.5H4a3 3 0 0 0 0 6h3a3 3 0 0 0 2.83-4H9c-.086 0-.17.01-.25.031A2 2 0 0 1 7 10.5H4a2 2 0 1 1 0-4h1.535c.218-.376.495-.714.82-1z"/>
<path d="M9 5.5a3 3 0 0 0-2.83 4h1.098A2 2 0 0 1 9 6.5h3a2 2 0 1 1 0 4h-1.535a4.02 4.02 0 0 1-.82 1H12a3 3 0 1 0 0-6H9z"/>
</svg>
</span>
<input type="text" class="form-control" id="url" onClick="this.select();" readonly value="{{ .Server }}/p/{{ .URL }}" placeholder="URL" aria-label="URL" aria-describedby="basic-addon1">
<input type="text" class="form-control border-primary bg-white text-primary" id="url" onClick="this.select();" readonly value="{{ .Server }}/p/{{ .URL }}" placeholder="URL" aria-label="URL" aria-describedby="basic-addon1">
<button class="btn btn-primary" type="button" id="buttonClippboard">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-clipboard" viewBox="0 0 16 16">
<path d="M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1h1a1 1 0 0 1 1 1V14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3.5a1 1 0 0 1 1-1h1v-1z"/>
<path d="M9.5 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5h3zm-3-1A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0h-3z"/>
</svg>
</button>
</div>
{{end}}
</div>
Expand Down

0 comments on commit 0483906

Please sign in to comment.