feat: Disable levels of encryptions by default
All checks were successful
/ pre-commit (push) Successful in 1m11s
All checks were successful
/ pre-commit (push) Successful in 1m11s
- Add `allow_client_encryption_key` option to allow encryption key provided by the client on the web UI (false by default) - Add `allow_no_encryption` option to allow notes without encryption (disabled by default) Signed-off-by: Julien Riou <julien@riou.xyz>
This commit is contained in:
parent
75bdab55df
commit
61ca30690b
6 changed files with 105 additions and 47 deletions
|
@ -20,6 +20,8 @@ The file format is **JSON**:
|
||||||
* **database_dsn** (string): Connection string for the database (default "collerd.db")
|
* **database_dsn** (string): Connection string for the database (default "collerd.db")
|
||||||
* **node_id** (int): Number between 0 and 1023 to define the node generating identifiers (see [snowflake](https://github.com/bwmarrin/snowflake))
|
* **node_id** (int): Number between 0 and 1023 to define the node generating identifiers (see [snowflake](https://github.com/bwmarrin/snowflake))
|
||||||
* **encryption_key_length** (int): Number of characters for generated encryption key (default 16)
|
* **encryption_key_length** (int): Number of characters for generated encryption key (default 16)
|
||||||
|
* **allow_client_encryption_key** (bool): Allow encryption key provided by the client on the web UI
|
||||||
|
* **allow_no_encryption** (bool): Allow notes without encryption
|
||||||
* **expiration_interval** (int): Number of seconds to wait between two expiration runs
|
* **expiration_interval** (int): Number of seconds to wait between two expiration runs
|
||||||
* **listen_address** (string): Address to listen for the web server (default "0.0.0.0")
|
* **listen_address** (string): Address to listen for the web server (default "0.0.0.0")
|
||||||
* **listen_port** (int): Port to listen for the web server (default 8080)
|
* **listen_port** (int): Port to listen for the web server (default 8080)
|
||||||
|
|
|
@ -7,28 +7,30 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
DatabaseType string `json:"database_type"`
|
DatabaseType string `json:"database_type"`
|
||||||
DatabaseDsn string `json:"database_dsn"`
|
DatabaseDsn string `json:"database_dsn"`
|
||||||
NodeID int64 `json:"node_id"`
|
NodeID int64 `json:"node_id"`
|
||||||
EncryptionKeyLength int `json:"encryption_key_length"`
|
EncryptionKeyLength int `json:"encryption_key_length"`
|
||||||
ExpirationInterval int `json:"expiration_interval"`
|
AllowClientEncryptionKey bool `json:"allow_client_encryption_key"`
|
||||||
ListenAddress string `json:"listen_address"`
|
AllowNoEncryption bool `json:"allow_no_encryption"`
|
||||||
ListenPort int `json:"listen_port"`
|
ExpirationInterval int `json:"expiration_interval"`
|
||||||
Expirations []int `json:"expirations"`
|
ListenAddress string `json:"listen_address"`
|
||||||
Expiration int `json:"expiration"`
|
ListenPort int `json:"listen_port"`
|
||||||
MaxUploadSize int64 `json:"max_upload_size"`
|
Expirations []int `json:"expirations"`
|
||||||
ShowVersion bool `json:"show_version"`
|
Expiration int `json:"expiration"`
|
||||||
EnableMetrics bool `json:"enable_metrics"`
|
MaxUploadSize int64 `json:"max_upload_size"`
|
||||||
PrometheusRoute string `json:"prometheus_route"`
|
ShowVersion bool `json:"show_version"`
|
||||||
PrometheusNotesMetric string `json:"prometheus_notes_metric"`
|
EnableMetrics bool `json:"enable_metrics"`
|
||||||
ObservationInterval int `json:"observation_internal"`
|
PrometheusRoute string `json:"prometheus_route"`
|
||||||
Languages []string `json:"languages"`
|
PrometheusNotesMetric string `json:"prometheus_notes_metric"`
|
||||||
Language string `json:"language"`
|
ObservationInterval int `json:"observation_internal"`
|
||||||
EnableUploadFileButton bool `json:"enable_upload_file_button"`
|
Languages []string `json:"languages"`
|
||||||
TLSCertFile string `json:"tls_cert_file"`
|
Language string `json:"language"`
|
||||||
TLSKeyFile string `json:"tls_key_file"`
|
EnableUploadFileButton bool `json:"enable_upload_file_button"`
|
||||||
BootstrapDirectory string `json:"bootstrap_directory"`
|
TLSCertFile string `json:"tls_cert_file"`
|
||||||
|
TLSKeyFile string `json:"tls_key_file"`
|
||||||
|
BootstrapDirectory string `json:"bootstrap_directory"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConfig() *Config {
|
func NewConfig() *Config {
|
||||||
|
|
|
@ -16,9 +16,11 @@ func HealthHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreateNoteHandler struct {
|
type CreateNoteHandler struct {
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
db *Database
|
db *Database
|
||||||
maxUploadSize int64
|
maxUploadSize int64
|
||||||
|
allowClientEncryptionKey bool
|
||||||
|
allowNoEncryption bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreateNotePayload struct {
|
type CreateNotePayload struct {
|
||||||
|
@ -47,6 +49,16 @@ func (h *CreateNoteHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !h.allowNoEncryption && !body.Encrypted {
|
||||||
|
WriteError(w, "could not create note", fmt.Errorf("encryption is mandatory"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !h.allowClientEncryptionKey && body.EncryptionKey != "" {
|
||||||
|
WriteError(w, "could not create note", fmt.Errorf("client encryption key is not allowed"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
content, err := internal.Decode(body.Content)
|
content, err := internal.Decode(body.Content)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -17,16 +17,18 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type PageData struct {
|
type PageData struct {
|
||||||
Title string
|
Title string
|
||||||
Version string
|
Version string
|
||||||
Expirations []int
|
Expirations []int
|
||||||
Expiration int
|
Expiration int
|
||||||
Languages []string
|
Languages []string
|
||||||
Err error
|
Err error
|
||||||
URL string
|
URL string
|
||||||
Note *Note
|
Note *Note
|
||||||
EnableUploadFileButton bool
|
EnableUploadFileButton bool
|
||||||
BootstrapDirectory string
|
AllowClientEncryptionKey bool
|
||||||
|
AllowNoEncryption bool
|
||||||
|
BootstrapDirectory string
|
||||||
}
|
}
|
||||||
|
|
||||||
type HomeHandler struct {
|
type HomeHandler struct {
|
||||||
|
@ -115,7 +117,17 @@ func (h *CreateNoteWithFormHandler) ServeHTTP(w http.ResponseWriter, r *http.Req
|
||||||
deleteAfterRead := r.FormValue("delete-after-read")
|
deleteAfterRead := r.FormValue("delete-after-read")
|
||||||
language := r.FormValue("language")
|
language := r.FormValue("language")
|
||||||
|
|
||||||
if encryptionKey == "" && noEncryption == "" {
|
if !h.PageData.AllowNoEncryption && noEncryption != "" {
|
||||||
|
h.PageData.Err = fmt.Errorf("encryption is mandatory")
|
||||||
|
h.Templates.ExecuteTemplate(w, templateName, h.PageData)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !h.PageData.AllowClientEncryptionKey && encryptionKey != "" {
|
||||||
|
h.PageData.Err = fmt.Errorf("client encryption key is not allowed")
|
||||||
|
h.Templates.ExecuteTemplate(w, templateName, h.PageData)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !h.PageData.AllowClientEncryptionKey && encryptionKey == "" && noEncryption == "" {
|
||||||
h.logger.Debug("generating encryption key")
|
h.logger.Debug("generating encryption key")
|
||||||
encryptionKey = internal.GenerateChars(encryptionKeyLength)
|
encryptionKey = internal.GenerateChars(encryptionKeyLength)
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,9 +99,26 @@ func (s *Server) Start() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// API
|
// API
|
||||||
r.Path("/api/note").Handler(&CreateNoteHandler{logger: s.logger, db: s.db, maxUploadSize: s.config.MaxUploadSize}).Methods("POST")
|
createNoteHandler := &CreateNoteHandler{
|
||||||
r.Path("/{id:[a-zA-Z0-9]+}/{encryptionKey:[a-zA-Z0-9]+}").Handler(&GetEncryptedNoteHandler{logger: s.logger, db: s.db}).Methods("GET")
|
logger: s.logger,
|
||||||
r.Path("/{id:[a-zA-Z0-9]+}").Handler(&GetNoteHandler{logger: s.logger, db: s.db}).Methods("GET")
|
db: s.db,
|
||||||
|
maxUploadSize: s.config.MaxUploadSize,
|
||||||
|
allowClientEncryptionKey: s.config.AllowClientEncryptionKey,
|
||||||
|
allowNoEncryption: s.config.AllowNoEncryption,
|
||||||
|
}
|
||||||
|
r.Path("/api/note").Handler(createNoteHandler).Methods("POST")
|
||||||
|
|
||||||
|
getEncryptedNoteHandler := &GetEncryptedNoteHandler{
|
||||||
|
logger: s.logger,
|
||||||
|
db: s.db,
|
||||||
|
}
|
||||||
|
r.Path("/{id:[a-zA-Z0-9]+}/{encryptionKey:[a-zA-Z0-9]+}").Handler(getEncryptedNoteHandler).Methods("GET")
|
||||||
|
|
||||||
|
getNoteHandler := &GetNoteHandler{
|
||||||
|
logger: s.logger,
|
||||||
|
db: s.db,
|
||||||
|
}
|
||||||
|
r.Path("/{id:[a-zA-Z0-9]+}").Handler(getNoteHandler).Methods("GET")
|
||||||
|
|
||||||
// Web pages
|
// Web pages
|
||||||
funcs := template.FuncMap{
|
funcs := template.FuncMap{
|
||||||
|
@ -111,22 +128,25 @@ func (s *Server) Start() error {
|
||||||
"string": func(b []byte) string { return string(b) },
|
"string": func(b []byte) string { return string(b) },
|
||||||
}
|
}
|
||||||
p := PageData{
|
p := PageData{
|
||||||
Title: s.config.Title,
|
Title: s.config.Title,
|
||||||
Expirations: s.config.Expirations,
|
Expirations: s.config.Expirations,
|
||||||
Expiration: s.config.Expiration,
|
Expiration: s.config.Expiration,
|
||||||
Languages: s.config.Languages,
|
Languages: s.config.Languages,
|
||||||
BootstrapDirectory: s.config.BootstrapDirectory,
|
BootstrapDirectory: s.config.BootstrapDirectory,
|
||||||
|
EnableUploadFileButton: s.config.EnableUploadFileButton,
|
||||||
|
AllowClientEncryptionKey: s.config.AllowClientEncryptionKey,
|
||||||
|
AllowNoEncryption: s.config.AllowNoEncryption,
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.config.ShowVersion {
|
if s.config.ShowVersion {
|
||||||
p.Version = s.version
|
p.Version = s.version
|
||||||
}
|
}
|
||||||
p.EnableUploadFileButton = s.config.EnableUploadFileButton
|
|
||||||
|
|
||||||
templates, err := template.New("templates").Funcs(funcs).ParseFS(templatesFS, "templates/*.html")
|
templates, err := template.New("templates").Funcs(funcs).ParseFS(templatesFS, "templates/*.html")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
createNoteWithFormHandler := &CreateNoteWithFormHandler{
|
createNoteWithFormHandler := &CreateNoteWithFormHandler{
|
||||||
Templates: templates,
|
Templates: templates,
|
||||||
PageData: p,
|
PageData: p,
|
||||||
|
@ -142,7 +162,12 @@ func (s *Server) Start() error {
|
||||||
logger: s.logger,
|
logger: s.logger,
|
||||||
}
|
}
|
||||||
r.Path("/clients.html").Handler(clientsHandler).Methods("GET")
|
r.Path("/clients.html").Handler(clientsHandler).Methods("GET")
|
||||||
r.Path("/clients/{os:[a-z]+}-{arch:[a-z0-9]+}/{clientName:[a-z]+}").Handler(&ClientHandler{logger: s.logger, version: p.Version}).Methods("GET")
|
|
||||||
|
clientHandler := &ClientHandler{
|
||||||
|
logger: s.logger,
|
||||||
|
version: p.Version,
|
||||||
|
}
|
||||||
|
r.Path("/clients/{os:[a-z]+}-{arch:[a-z0-9]+}/{clientName:[a-z]+}").Handler(clientHandler).Methods("GET")
|
||||||
|
|
||||||
encryptedWebNoteHandler := &GetEncryptedWebNoteHandler{
|
encryptedWebNoteHandler := &GetEncryptedWebNoteHandler{
|
||||||
Templates: templates,
|
Templates: templates,
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="container text-center justify-content-center w-75 mb-4">
|
<div class="container text-center justify-content-center w-75 mb-4">
|
||||||
<div class="row align-items-center">
|
<div class="row align-items-center">
|
||||||
|
{{if .AllowClientEncryptionKey}}
|
||||||
<div class="col-1">
|
<div class="col-1">
|
||||||
<label class="col-form-label col-form-label-sm" for="encryption-key">Encryption key</label>
|
<label class="col-form-label col-form-label-sm" for="encryption-key">Encryption key</label>
|
||||||
</div>
|
</div>
|
||||||
|
@ -21,11 +22,14 @@
|
||||||
title="Letters and numbers with length from 16 to 256" class="form-control" id="encryption-key"
|
title="Letters and numbers with length from 16 to 256" class="form-control" id="encryption-key"
|
||||||
name="encryption-key">
|
name="encryption-key">
|
||||||
</div>
|
</div>
|
||||||
|
{{end}}
|
||||||
|
{{if .AllowNoEncryption}}
|
||||||
<div class="col-1">
|
<div class="col-1">
|
||||||
<input type="checkbox" class="form-check-input" for="no-encryption-key" id="no-encryption-key"
|
<input type="checkbox" class="form-check-input" for="no-encryption-key" id="no-encryption-key"
|
||||||
value="no-encryption-key" name="no-encryption-key">
|
value="no-encryption-key" name="no-encryption-key">
|
||||||
<label class="col-form-label col-form-label-sm" for="no-encryption-key">No encryption</label>
|
<label class="col-form-label col-form-label-sm" for="no-encryption-key">No encryption</label>
|
||||||
</div>
|
</div>
|
||||||
|
{{end}}
|
||||||
<div class="col-1">
|
<div class="col-1">
|
||||||
<input type="checkbox" class="form-check-input" for="delete-after-read" id="delete-after-read"
|
<input type="checkbox" class="form-check-input" for="delete-after-read" id="delete-after-read"
|
||||||
value="delete-after-read" name="delete-after-read">
|
value="delete-after-read" name="delete-after-read">
|
||||||
|
@ -40,7 +44,8 @@
|
||||||
<select class="form-select" aria-label="Expiration" id="expiration" name="expiration">
|
<select class="form-select" aria-label="Expiration" id="expiration" name="expiration">
|
||||||
<option disabled>Expiration</option>
|
<option disabled>Expiration</option>
|
||||||
{{range $exp := .Expirations}}
|
{{range $exp := .Expirations}}
|
||||||
<option {{ if eq $exp $.Expiration }}selected="selected"{{end}} value="{{$exp}}">{{HumanDuration $exp}}</option>
|
<option {{ if eq $exp $.Expiration }}selected="selected" {{end}} value="{{$exp}}">
|
||||||
|
{{HumanDuration $exp}}</option>
|
||||||
{{end}}
|
{{end}}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue