mirror of
https://github.com/lus/pasty.git
synced 2023-08-10 21:13:09 +03:00
Rename the deletion token to modification token
This commit is contained in:
23
README.md
23
README.md
@@ -1,6 +1,21 @@
|
|||||||
# pasty
|
# pasty
|
||||||
Pasty is a fast and lightweight code pasting server
|
Pasty is a fast and lightweight code pasting server
|
||||||
|
|
||||||
|
## !!! Important deprecation notices !!!
|
||||||
|
|
||||||
|
> This version of pasty uses a new field name for the so far called `deletionToken`: `modificationToken`.
|
||||||
|
> Instances using **PostgreSQL** are **not affected** as a corresponding SQL migration will run before the first startup.
|
||||||
|
> If you however use **another storage driver** you may have to **update the entries** by hand or using a simple query, depending on your driver as I don't plan to ship migrations for every single storage driver.
|
||||||
|
> It may be important to know that the **data migrator has been upgraded** too. This may serve as a **convenient workaround** (export data (field will be renamed) and import data with changed field names again).
|
||||||
|
|
||||||
|
> Additionally, I changed the three `DELETION_TOKEN*`environment variables to their corresponding `MODIFICATION_TOKEN*` ones:
|
||||||
|
> - `DELETION_TOKENS` -> `MODIFICATION_TOKENS`
|
||||||
|
> - `DELETION_TOKEN_MASTER` -> `MODIFICATION_TOKEN_MASTER`
|
||||||
|
> - `DELETION_TOKEN_LENGTH` -> `MODIFICATION_TOKEN_LENGTH`
|
||||||
|
>
|
||||||
|
> Again, **the old ones will still work** because I do not want to jumble your configurations. However, **please consider updating** them to stay future-proof ^^.
|
||||||
|
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
As pasty is an open source project on GitHub you can open an [issue](https://github.com/lus/pasty/issues) whenever you encounter a problem or feature request.
|
As pasty is an open source project on GitHub you can open an [issue](https://github.com/lus/pasty/issues) whenever you encounter a problem or feature request.
|
||||||
@@ -56,14 +71,14 @@ Pasty will be available at http://localhost:8080.
|
|||||||
|
|
||||||
## General environment variables
|
## General environment variables
|
||||||
| Environment Variable | Default Value | Type | Description |
|
| Environment Variable | Default Value | Type | Description |
|
||||||
|-------------------------------|---------------|----------|--------------------------------------------------------------------------------------------------------------------|
|
|-----------------------------------|---------------|----------|----------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `PASTY_WEB_ADDRESS` | `:8080` | `string` | Defines the address the web server listens to |
|
| `PASTY_WEB_ADDRESS` | `:8080` | `string` | Defines the address the web server listens to |
|
||||||
| `PASTY_STORAGE_TYPE` | `file` | `string` | Defines the storage type the pastes are saved to |
|
| `PASTY_STORAGE_TYPE` | `file` | `string` | Defines the storage type the pastes are saved to |
|
||||||
| `PASTY_HASTEBIN_SUPPORT` | `false` | `bool` | Defines whether or not the `POST /documents` endpoint should be enabled, as known from the hastebin servers |
|
| `PASTY_HASTEBIN_SUPPORT` | `false` | `bool` | Defines whether or not the `POST /documents` endpoint should be enabled, as known from the hastebin servers |
|
||||||
| `PASTY_ID_LENGTH` | `6` | `number` | Defines the length of the ID of a paste |
|
| `PASTY_ID_LENGTH` | `6` | `number` | Defines the length of the ID of a paste |
|
||||||
| `PASTY_DELETION_TOKENS` | `true` | `bool` | Defines whether or not deletion tokens should be generated |
|
| `PASTY_MODIFICATION_TOKENS` | `true` | `bool` | Defines whether or not modification tokens should be generated |
|
||||||
| `PASTY_DELETION_TOKEN_MASTER` | `<empty>` | `string` | Defines the master deletion token which is authorized to delete every paste (even if deletion tokens are disabled) |
|
| `PASTY_MODIFICATION_TOKEN_MASTER` | `` | `string` | Defines the master modification token which is authorized to modify every paste (even if modification tokens are disabled) |
|
||||||
| `PASTY_DELETION_TOKEN_LENGTH` | `12` | `number` | Defines the length of the deletion token of a paste |
|
| `PASTY_MODIFICATION_TOKEN_LENGTH` | `12` | `number` | Defines the length of the modification token of a paste |
|
||||||
| `PASTY_RATE_LIMIT` | `30-M` | `string` | Defines the rate limit of the API (see https://github.com/ulule/limiter#usage) |
|
| `PASTY_RATE_LIMIT` | `30-M` | `string` | Defines the rate limit of the API (see https://github.com/ulule/limiter#usage) |
|
||||||
| `PASTY_LENGTH_CAP` | `50000` | `number` | Defines the maximum amount of characters a paste is allowed to contain (a value `<= 0` means no limit) |
|
| `PASTY_LENGTH_CAP` | `50000` | `number` | Defines the maximum amount of characters a paste is allowed to contain (a value `<= 0` means no limit) |
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,15 @@ func main() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Move the content of the deletion token field to the modification field
|
||||||
|
if paste.DeletionToken != "" {
|
||||||
|
if paste.ModificationToken == "" {
|
||||||
|
paste.ModificationToken = paste.DeletionToken
|
||||||
|
}
|
||||||
|
paste.DeletionToken = ""
|
||||||
|
log.Println("[INFO] Paste " + id + " was a legacy one.")
|
||||||
|
}
|
||||||
|
|
||||||
// Save the paste
|
// Save the paste
|
||||||
err = to.Save(paste)
|
err = to.Save(paste)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ type Config struct {
|
|||||||
StorageType shared.StorageType
|
StorageType shared.StorageType
|
||||||
HastebinSupport bool
|
HastebinSupport bool
|
||||||
IDLength int
|
IDLength int
|
||||||
DeletionTokens bool
|
ModificationTokens bool
|
||||||
DeletionTokenMaster string
|
ModificationTokenMaster string
|
||||||
DeletionTokenLength int
|
ModificationTokenLength int
|
||||||
RateLimit string
|
RateLimit string
|
||||||
LengthCap int
|
LengthCap int
|
||||||
AutoDelete *AutoDeleteConfig
|
AutoDelete *AutoDeleteConfig
|
||||||
@@ -73,9 +73,9 @@ func Load() {
|
|||||||
StorageType: shared.StorageType(strings.ToLower(env.MustString("STORAGE_TYPE", "file"))),
|
StorageType: shared.StorageType(strings.ToLower(env.MustString("STORAGE_TYPE", "file"))),
|
||||||
HastebinSupport: env.MustBool("HASTEBIN_SUPPORT", false),
|
HastebinSupport: env.MustBool("HASTEBIN_SUPPORT", false),
|
||||||
IDLength: env.MustInt("ID_LENGTH", 6),
|
IDLength: env.MustInt("ID_LENGTH", 6),
|
||||||
DeletionTokens: env.MustBool("DELETION_TOKENS", true),
|
ModificationTokens: env.MustBool("MODIFICATION_TOKENS", env.MustBool("DELETION_TOKENS", true)), // ---
|
||||||
DeletionTokenMaster: env.MustString("DELETION_TOKEN_MASTER", ""),
|
ModificationTokenMaster: env.MustString("MODIFICATION_TOKEN_MASTER", env.MustString("DELETION_TOKEN_MASTER", "")), // - We don't want to destroy peoples old configuration
|
||||||
DeletionTokenLength: env.MustInt("DELETION_TOKEN_LENGTH", 12),
|
ModificationTokenLength: env.MustInt("MODIFICATION_TOKEN_LENGTH", env.MustInt("DELETION_TOKEN_LENGTH", 12)), // ---
|
||||||
RateLimit: env.MustString("RATE_LIMIT", "30-M"),
|
RateLimit: env.MustString("RATE_LIMIT", "30-M"),
|
||||||
LengthCap: env.MustInt("LENGTH_CAP", 50_000),
|
LengthCap: env.MustInt("LENGTH_CAP", 50_000),
|
||||||
AutoDelete: &AutoDeleteConfig{
|
AutoDelete: &AutoDeleteConfig{
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package shared
|
package shared
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
"github.com/alexedwards/argon2id"
|
"github.com/alexedwards/argon2id"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -8,23 +10,33 @@ import (
|
|||||||
type Paste struct {
|
type Paste struct {
|
||||||
ID string `json:"id" bson:"_id"`
|
ID string `json:"id" bson:"_id"`
|
||||||
Content string `json:"content" bson:"content"`
|
Content string `json:"content" bson:"content"`
|
||||||
DeletionToken string `json:"deletionToken,omitempty" bson:"deletionToken"`
|
DeletionToken string `json:"deletionToken,omitempty" bson:"deletionToken"` // Required for legacy paste storage support
|
||||||
|
ModificationToken string `json:"modificationToken,omitempty" bson:"modificationToken"`
|
||||||
Created int64 `json:"created" bson:"created"`
|
Created int64 `json:"created" bson:"created"`
|
||||||
AutoDelete bool `json:"autoDelete" bson:"autoDelete"`
|
AutoDelete bool `json:"autoDelete" bson:"autoDelete"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// HashDeletionToken hashes the current deletion token of a paste
|
// HashModificationToken hashes the current modification token of a paste
|
||||||
func (paste *Paste) HashDeletionToken() error {
|
func (paste *Paste) HashModificationToken() error {
|
||||||
hash, err := argon2id.CreateHash(paste.DeletionToken, argon2id.DefaultParams)
|
hash, err := argon2id.CreateHash(paste.ModificationToken, argon2id.DefaultParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
paste.DeletionToken = hash
|
paste.ModificationToken = hash
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckDeletionToken checks whether or not the given deletion token is correct
|
// CheckModificationToken checks whether or not the given modification token is correct
|
||||||
func (paste *Paste) CheckDeletionToken(deletionToken string) bool {
|
func (paste *Paste) CheckModificationToken(modificationToken string) bool {
|
||||||
match, err := argon2id.ComparePasswordAndHash(deletionToken, paste.DeletionToken)
|
// The modification token may be stored in the deletion token field in old pastes
|
||||||
|
usedToken := paste.ModificationToken
|
||||||
|
if usedToken == "" {
|
||||||
|
usedToken = paste.DeletionToken
|
||||||
|
if usedToken != "" {
|
||||||
|
log.Println("WARNING: You seem to have pastes with the old 'deletionToken' field stored in your storage driver. Though this does not cause any issues right now, it may in the future. Consider some kind of migration.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match, err := argon2id.ComparePasswordAndHash(modificationToken, usedToken)
|
||||||
return err == nil && match
|
return err == nil && match
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
begin;
|
||||||
|
|
||||||
|
alter table if exists "pastes" rename column "modificationToken" to "deletionToken";
|
||||||
|
|
||||||
|
commit;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
begin;
|
||||||
|
|
||||||
|
alter table if exists "pastes" rename column "deletionToken" to "modificationToken";
|
||||||
|
|
||||||
|
commit;
|
||||||
@@ -82,7 +82,7 @@ func (driver *PostgresDriver) Get(id string) (*shared.Paste, error) {
|
|||||||
row := driver.pool.QueryRow(context.Background(), query, id)
|
row := driver.pool.QueryRow(context.Background(), query, id)
|
||||||
|
|
||||||
paste := new(shared.Paste)
|
paste := new(shared.Paste)
|
||||||
if err := row.Scan(&paste.ID, &paste.Content, &paste.DeletionToken, &paste.Created, &paste.AutoDelete); err != nil {
|
if err := row.Scan(&paste.ID, &paste.Content, &paste.ModificationToken, &paste.Created, &paste.AutoDelete); err != nil {
|
||||||
if errors.Is(err, pgx.ErrNoRows) {
|
if errors.Is(err, pgx.ErrNoRows) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@@ -95,7 +95,7 @@ func (driver *PostgresDriver) Get(id string) (*shared.Paste, error) {
|
|||||||
func (driver *PostgresDriver) Save(paste *shared.Paste) error {
|
func (driver *PostgresDriver) Save(paste *shared.Paste) error {
|
||||||
query := "INSERT INTO pastes VALUES ($1, $2, $3, $4, $5)"
|
query := "INSERT INTO pastes VALUES ($1, $2, $3, $4, $5)"
|
||||||
|
|
||||||
_, err := driver.pool.Exec(context.Background(), query, paste.ID, paste.Content, paste.DeletionToken, paste.Created, paste.AutoDelete)
|
_, err := driver.pool.Exec(context.Background(), query, paste.ID, paste.Content, paste.ModificationToken, paste.Created, paste.AutoDelete)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,11 +50,11 @@ func HastebinSupportHandler(ctx *fasthttp.RequestCtx) {
|
|||||||
AutoDelete: config.Current.AutoDelete.Enabled,
|
AutoDelete: config.Current.AutoDelete.Enabled,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set a deletion token
|
// Set a modification token
|
||||||
if config.Current.DeletionTokens {
|
if config.Current.ModificationTokens {
|
||||||
paste.DeletionToken = utils.RandomString(config.Current.DeletionTokenLength)
|
paste.ModificationToken = utils.RandomString(config.Current.ModificationTokenLength)
|
||||||
|
|
||||||
err = paste.HashDeletionToken()
|
err = paste.HashModificationToken()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
|
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
|
||||||
ctx.SetBodyString(err.Error())
|
ctx.SetBodyString(err.Error())
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ func v1GetPaste(ctx *fasthttp.RequestCtx) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
paste.DeletionToken = ""
|
paste.DeletionToken = ""
|
||||||
|
paste.ModificationToken = ""
|
||||||
|
|
||||||
// Respond with the paste
|
// Respond with the paste
|
||||||
jsonData, err := json.Marshal(paste)
|
jsonData, err := json.Marshal(paste)
|
||||||
@@ -91,13 +92,13 @@ func v1PostPaste(ctx *fasthttp.RequestCtx) {
|
|||||||
AutoDelete: config.Current.AutoDelete.Enabled,
|
AutoDelete: config.Current.AutoDelete.Enabled,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set a deletion token
|
// Set a modification token
|
||||||
deletionToken := ""
|
modificationToken := ""
|
||||||
if config.Current.DeletionTokens {
|
if config.Current.ModificationTokens {
|
||||||
deletionToken = utils.RandomString(config.Current.DeletionTokenLength)
|
modificationToken = utils.RandomString(config.Current.ModificationTokenLength)
|
||||||
paste.DeletionToken = deletionToken
|
paste.ModificationToken = modificationToken
|
||||||
|
|
||||||
err = paste.HashDeletionToken()
|
err = paste.HashModificationToken()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
|
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
|
||||||
ctx.SetBodyString(err.Error())
|
ctx.SetBodyString(err.Error())
|
||||||
@@ -115,7 +116,8 @@ func v1PostPaste(ctx *fasthttp.RequestCtx) {
|
|||||||
|
|
||||||
// Respond with the paste
|
// Respond with the paste
|
||||||
pasteCopy := *paste
|
pasteCopy := *paste
|
||||||
pasteCopy.DeletionToken = deletionToken
|
pasteCopy.DeletionToken = modificationToken
|
||||||
|
pasteCopy.ModificationToken = ""
|
||||||
jsonData, err := json.Marshal(pasteCopy)
|
jsonData, err := json.Marshal(pasteCopy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
|
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
|
||||||
@@ -139,9 +141,9 @@ func v1DeletePaste(ctx *fasthttp.RequestCtx) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate the deletion token of the paste
|
// Validate the modification token of the paste
|
||||||
deletionToken := values["deletionToken"]
|
modificationToken := values["deletionToken"]
|
||||||
if deletionToken == "" {
|
if modificationToken == "" {
|
||||||
ctx.SetStatusCode(fasthttp.StatusBadRequest)
|
ctx.SetStatusCode(fasthttp.StatusBadRequest)
|
||||||
ctx.SetBodyString("missing 'deletionToken' field")
|
ctx.SetBodyString("missing 'deletionToken' field")
|
||||||
return
|
return
|
||||||
@@ -160,8 +162,8 @@ func v1DeletePaste(ctx *fasthttp.RequestCtx) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the deletion token is correct
|
// Check if the modification token is correct
|
||||||
if (config.Current.DeletionTokenMaster == "" || deletionToken != config.Current.DeletionTokenMaster) && !paste.CheckDeletionToken(deletionToken) {
|
if (config.Current.ModificationTokenMaster == "" || modificationToken != config.Current.ModificationTokenMaster) && !paste.CheckModificationToken(modificationToken) {
|
||||||
ctx.SetStatusCode(fasthttp.StatusForbidden)
|
ctx.SetStatusCode(fasthttp.StatusForbidden)
|
||||||
ctx.SetBodyString("invalid deletion token")
|
ctx.SetBodyString("invalid deletion token")
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ func Serve() error {
|
|||||||
v1Route.GET("/info", func(ctx *fasthttp.RequestCtx) {
|
v1Route.GET("/info", func(ctx *fasthttp.RequestCtx) {
|
||||||
jsonData, _ := json.Marshal(map[string]interface{}{
|
jsonData, _ := json.Marshal(map[string]interface{}{
|
||||||
"version": static.Version,
|
"version": static.Version,
|
||||||
"deletionTokens": config.Current.DeletionTokens,
|
"deletionTokens": config.Current.ModificationTokens,
|
||||||
})
|
})
|
||||||
ctx.SetBody(jsonData)
|
ctx.SetBody(jsonData)
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user