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:
parent
4cf982bb23
commit
f61c98af71
37
README.md
37
README.md
@ -1,6 +1,21 @@
|
||||
# pasty
|
||||
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
|
||||
|
||||
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.
|
||||
@ -55,17 +70,17 @@ Pasty will be available at http://localhost:8080.
|
||||
---
|
||||
|
||||
## General environment variables
|
||||
| Environment Variable | Default Value | Type | Description |
|
||||
|-------------------------------|---------------|----------|--------------------------------------------------------------------------------------------------------------------|
|
||||
| `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_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_DELETION_TOKENS` | `true` | `bool` | Defines whether or not deletion 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_DELETION_TOKEN_LENGTH` | `12` | `number` | Defines the length of the deletion 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_LENGTH_CAP` | `50000` | `number` | Defines the maximum amount of characters a paste is allowed to contain (a value `<= 0` means no limit) |
|
||||
| Environment Variable | Default Value | Type | Description |
|
||||
|-----------------------------------|---------------|----------|----------------------------------------------------------------------------------------------------------------------------|
|
||||
| `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_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_MODIFICATION_TOKENS` | `true` | `bool` | Defines whether or not modification tokens should be generated |
|
||||
| `PASTY_MODIFICATION_TOKEN_MASTER` | `` | `string` | Defines the master modification token which is authorized to modify every paste (even if modification tokens are disabled) |
|
||||
| `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_LENGTH_CAP` | `50000` | `number` | Defines the maximum amount of characters a paste is allowed to contain (a value `<= 0` means no limit) |
|
||||
|
||||
## AutoDelete
|
||||
Pasty provides an intuitive system to automatically delete pastes after a specific amount of time. You can configure it with the following variables:
|
||||
|
@ -56,6 +56,15 @@ func main() {
|
||||
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
|
||||
err = to.Save(paste)
|
||||
if err != nil {
|
||||
|
@ -10,20 +10,20 @@ import (
|
||||
|
||||
// Config represents the general application configuration structure
|
||||
type Config struct {
|
||||
WebAddress string
|
||||
StorageType shared.StorageType
|
||||
HastebinSupport bool
|
||||
IDLength int
|
||||
DeletionTokens bool
|
||||
DeletionTokenMaster string
|
||||
DeletionTokenLength int
|
||||
RateLimit string
|
||||
LengthCap int
|
||||
AutoDelete *AutoDeleteConfig
|
||||
File *FileConfig
|
||||
Postgres *PostgresConfig
|
||||
MongoDB *MongoDBConfig
|
||||
S3 *S3Config
|
||||
WebAddress string
|
||||
StorageType shared.StorageType
|
||||
HastebinSupport bool
|
||||
IDLength int
|
||||
ModificationTokens bool
|
||||
ModificationTokenMaster string
|
||||
ModificationTokenLength int
|
||||
RateLimit string
|
||||
LengthCap int
|
||||
AutoDelete *AutoDeleteConfig
|
||||
File *FileConfig
|
||||
Postgres *PostgresConfig
|
||||
MongoDB *MongoDBConfig
|
||||
S3 *S3Config
|
||||
}
|
||||
|
||||
// AutoDeleteConfig represents the configuration specific for the AutoDelete behaviour
|
||||
@ -69,15 +69,15 @@ func Load() {
|
||||
env.Load()
|
||||
|
||||
Current = &Config{
|
||||
WebAddress: env.MustString("WEB_ADDRESS", ":8080"),
|
||||
StorageType: shared.StorageType(strings.ToLower(env.MustString("STORAGE_TYPE", "file"))),
|
||||
HastebinSupport: env.MustBool("HASTEBIN_SUPPORT", false),
|
||||
IDLength: env.MustInt("ID_LENGTH", 6),
|
||||
DeletionTokens: env.MustBool("DELETION_TOKENS", true),
|
||||
DeletionTokenMaster: env.MustString("DELETION_TOKEN_MASTER", ""),
|
||||
DeletionTokenLength: env.MustInt("DELETION_TOKEN_LENGTH", 12),
|
||||
RateLimit: env.MustString("RATE_LIMIT", "30-M"),
|
||||
LengthCap: env.MustInt("LENGTH_CAP", 50_000),
|
||||
WebAddress: env.MustString("WEB_ADDRESS", ":8080"),
|
||||
StorageType: shared.StorageType(strings.ToLower(env.MustString("STORAGE_TYPE", "file"))),
|
||||
HastebinSupport: env.MustBool("HASTEBIN_SUPPORT", false),
|
||||
IDLength: env.MustInt("ID_LENGTH", 6),
|
||||
ModificationTokens: env.MustBool("MODIFICATION_TOKENS", env.MustBool("DELETION_TOKENS", true)), // ---
|
||||
ModificationTokenMaster: env.MustString("MODIFICATION_TOKEN_MASTER", env.MustString("DELETION_TOKEN_MASTER", "")), // - We don't want to destroy peoples old configuration
|
||||
ModificationTokenLength: env.MustInt("MODIFICATION_TOKEN_LENGTH", env.MustInt("DELETION_TOKEN_LENGTH", 12)), // ---
|
||||
RateLimit: env.MustString("RATE_LIMIT", "30-M"),
|
||||
LengthCap: env.MustInt("LENGTH_CAP", 50_000),
|
||||
AutoDelete: &AutoDeleteConfig{
|
||||
Enabled: env.MustBool("AUTODELETE", false),
|
||||
Lifetime: env.MustDuration("AUTODELETE_LIFETIME", 720*time.Hour),
|
||||
|
@ -1,30 +1,42 @@
|
||||
package shared
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/alexedwards/argon2id"
|
||||
)
|
||||
|
||||
// Paste represents a saved paste
|
||||
type Paste struct {
|
||||
ID string `json:"id" bson:"_id"`
|
||||
Content string `json:"content" bson:"content"`
|
||||
DeletionToken string `json:"deletionToken,omitempty" bson:"deletionToken"`
|
||||
Created int64 `json:"created" bson:"created"`
|
||||
AutoDelete bool `json:"autoDelete" bson:"autoDelete"`
|
||||
ID string `json:"id" bson:"_id"`
|
||||
Content string `json:"content" bson:"content"`
|
||||
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"`
|
||||
AutoDelete bool `json:"autoDelete" bson:"autoDelete"`
|
||||
}
|
||||
|
||||
// HashDeletionToken hashes the current deletion token of a paste
|
||||
func (paste *Paste) HashDeletionToken() error {
|
||||
hash, err := argon2id.CreateHash(paste.DeletionToken, argon2id.DefaultParams)
|
||||
// HashModificationToken hashes the current modification token of a paste
|
||||
func (paste *Paste) HashModificationToken() error {
|
||||
hash, err := argon2id.CreateHash(paste.ModificationToken, argon2id.DefaultParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
paste.DeletionToken = hash
|
||||
paste.ModificationToken = hash
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckDeletionToken checks whether or not the given deletion token is correct
|
||||
func (paste *Paste) CheckDeletionToken(deletionToken string) bool {
|
||||
match, err := argon2id.ComparePasswordAndHash(deletionToken, paste.DeletionToken)
|
||||
// CheckModificationToken checks whether or not the given modification token is correct
|
||||
func (paste *Paste) CheckModificationToken(modificationToken string) bool {
|
||||
// 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
|
||||
}
|
||||
|
@ -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)
|
||||
|
||||
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) {
|
||||
return nil, nil
|
||||
}
|
||||
@ -95,7 +95,7 @@ func (driver *PostgresDriver) Get(id string) (*shared.Paste, error) {
|
||||
func (driver *PostgresDriver) Save(paste *shared.Paste) error {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -50,11 +50,11 @@ func HastebinSupportHandler(ctx *fasthttp.RequestCtx) {
|
||||
AutoDelete: config.Current.AutoDelete.Enabled,
|
||||
}
|
||||
|
||||
// Set a deletion token
|
||||
if config.Current.DeletionTokens {
|
||||
paste.DeletionToken = utils.RandomString(config.Current.DeletionTokenLength)
|
||||
// Set a modification token
|
||||
if config.Current.ModificationTokens {
|
||||
paste.ModificationToken = utils.RandomString(config.Current.ModificationTokenLength)
|
||||
|
||||
err = paste.HashDeletionToken()
|
||||
err = paste.HashModificationToken()
|
||||
if err != nil {
|
||||
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
|
||||
ctx.SetBodyString(err.Error())
|
||||
|
@ -38,6 +38,7 @@ func v1GetPaste(ctx *fasthttp.RequestCtx) {
|
||||
return
|
||||
}
|
||||
paste.DeletionToken = ""
|
||||
paste.ModificationToken = ""
|
||||
|
||||
// Respond with the paste
|
||||
jsonData, err := json.Marshal(paste)
|
||||
@ -91,13 +92,13 @@ func v1PostPaste(ctx *fasthttp.RequestCtx) {
|
||||
AutoDelete: config.Current.AutoDelete.Enabled,
|
||||
}
|
||||
|
||||
// Set a deletion token
|
||||
deletionToken := ""
|
||||
if config.Current.DeletionTokens {
|
||||
deletionToken = utils.RandomString(config.Current.DeletionTokenLength)
|
||||
paste.DeletionToken = deletionToken
|
||||
// Set a modification token
|
||||
modificationToken := ""
|
||||
if config.Current.ModificationTokens {
|
||||
modificationToken = utils.RandomString(config.Current.ModificationTokenLength)
|
||||
paste.ModificationToken = modificationToken
|
||||
|
||||
err = paste.HashDeletionToken()
|
||||
err = paste.HashModificationToken()
|
||||
if err != nil {
|
||||
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
|
||||
ctx.SetBodyString(err.Error())
|
||||
@ -115,7 +116,8 @@ func v1PostPaste(ctx *fasthttp.RequestCtx) {
|
||||
|
||||
// Respond with the paste
|
||||
pasteCopy := *paste
|
||||
pasteCopy.DeletionToken = deletionToken
|
||||
pasteCopy.DeletionToken = modificationToken
|
||||
pasteCopy.ModificationToken = ""
|
||||
jsonData, err := json.Marshal(pasteCopy)
|
||||
if err != nil {
|
||||
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
|
||||
@ -139,9 +141,9 @@ func v1DeletePaste(ctx *fasthttp.RequestCtx) {
|
||||
return
|
||||
}
|
||||
|
||||
// Validate the deletion token of the paste
|
||||
deletionToken := values["deletionToken"]
|
||||
if deletionToken == "" {
|
||||
// Validate the modification token of the paste
|
||||
modificationToken := values["deletionToken"]
|
||||
if modificationToken == "" {
|
||||
ctx.SetStatusCode(fasthttp.StatusBadRequest)
|
||||
ctx.SetBodyString("missing 'deletionToken' field")
|
||||
return
|
||||
@ -160,8 +162,8 @@ func v1DeletePaste(ctx *fasthttp.RequestCtx) {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the deletion token is correct
|
||||
if (config.Current.DeletionTokenMaster == "" || deletionToken != config.Current.DeletionTokenMaster) && !paste.CheckDeletionToken(deletionToken) {
|
||||
// Check if the modification token is correct
|
||||
if (config.Current.ModificationTokenMaster == "" || modificationToken != config.Current.ModificationTokenMaster) && !paste.CheckModificationToken(modificationToken) {
|
||||
ctx.SetStatusCode(fasthttp.StatusForbidden)
|
||||
ctx.SetBodyString("invalid deletion token")
|
||||
return
|
||||
|
@ -61,7 +61,7 @@ func Serve() error {
|
||||
v1Route.GET("/info", func(ctx *fasthttp.RequestCtx) {
|
||||
jsonData, _ := json.Marshal(map[string]interface{}{
|
||||
"version": static.Version,
|
||||
"deletionTokens": config.Current.DeletionTokens,
|
||||
"deletionTokens": config.Current.ModificationTokens,
|
||||
})
|
||||
ctx.SetBody(jsonData)
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user