commit 2f9ffa5077552ed03dfde8b966634cee96386dd7
parent 232d195743583b25398775ffc305eaafa0662ae4
Author: n0tr1v <n0tr1v@protonmail.com>
Date: Sun, 29 Jan 2023 05:58:59 -0800
start of endpoint to claim onion link ownership
Diffstat:
6 files changed, 152 insertions(+), 10 deletions(-)
diff --git a/cmd/dkf/migrations/126.sql b/cmd/dkf/migrations/126.sql
@@ -0,0 +1,72 @@
+-- +migrate Up
+DROP TRIGGER links_after_insert;
+DROP TRIGGER links_before_update;
+DROP TRIGGER links_before_update_soft_delete;
+DROP INDEX links_shorthand_uniq;
+
+create table links_tmp (
+ id INTEGER not null primary key,
+ uuid VARCHAR(100) not null unique,
+ url VARCHAR(255) not null unique,
+ title VARCHAR(255) not null,
+ description TEXT not null,
+ signed_certificate TEXT,
+ owner_user_id INTEGER NULL,
+ visited_at DATETIME,
+ created_at DATETIME default CURRENT_TIMESTAMP not null,
+ deleted_at DATETIME,
+ updated_at DATETIME default CURRENT_TIMESTAMP,
+ shorthand VARCHAR(50) UNIQUE,
+ CONSTRAINT links_owner_user_id_fk
+ FOREIGN KEY (owner_user_id)
+ REFERENCES users (id)
+ ON DELETE CASCADE
+ ON UPDATE CASCADE);
+
+INSERT INTO links_tmp (id, uuid, url, title, description, visited_at, created_at, deleted_at, updated_at, shorthand)
+SELECT id, uuid, url, title, description, visited_at, created_at, deleted_at, updated_at, shorthand FROM links;
+
+DROP TABLE links;
+
+ALTER TABLE links_tmp RENAME TO links;
+
+CREATE INDEX links_owner_user_id_idx ON links(owner_user_id);
+
+-- +migrate StatementBegin
+CREATE TRIGGER links_after_insert
+ AFTER INSERT ON links BEGIN
+ INSERT INTO fts5_links(rowid, uuid, url, title, description, created_at, visited_at) VALUES
+ (new.id, new.uuid, new.url, new.title, new.description, new.created_at, new.visited_at);
+END;
+
+CREATE TRIGGER links_after_update
+ AFTER UPDATE ON links WHEN old.deleted_at IS NULL AND new.deleted_at IS NULL BEGIN
+ INSERT INTO fts5_links(fts5_links, rowid, uuid, url, title, description, created_at, visited_at) VALUES
+ ('delete', old.id, old.uuid, old.url, old.title, old.description, old.created_at, old.visited_at);
+ INSERT INTO fts5_links(rowid, uuid, url, title, description, created_at, visited_at) VALUES
+ (new.id, new.uuid, new.url, new.title, new.description, new.created_at, new.visited_at);
+END;
+
+CREATE TRIGGER links_after_update_restore
+ AFTER UPDATE ON links WHEN old.deleted_at IS NOT NULL AND new.deleted_at IS NULL BEGIN
+ INSERT INTO fts5_links(fts5_links, rowid, uuid, url, title, description, created_at, visited_at) VALUES
+ ('delete', old.id, old.uuid, old.url, old.title, old.description, old.created_at, old.visited_at);
+ INSERT INTO fts5_links(rowid, uuid, url, title, description, created_at, visited_at) VALUES
+ (new.id, new.uuid, new.url, new.title, new.description, new.created_at, new.visited_at);
+END;
+
+CREATE TRIGGER links_after_update_soft_delete
+ AFTER UPDATE ON links WHEN old.deleted_at IS NULL AND new.deleted_at IS NOT NULL BEGIN
+ INSERT INTO fts5_links(fts5_links, rowid, uuid, url, title, description, created_at, visited_at) VALUES
+ ('delete', old.id, old.uuid, old.url, old.title, old.description, old.created_at, old.visited_at);
+END;
+
+CREATE TRIGGER links_after_delete
+ AFTER DELETE ON links BEGIN
+ INSERT INTO fts5_links(fts5_links, rowid, uuid, url, title, description, created_at, visited_at) VALUES
+ ('delete', old.id, old.uuid, old.url, old.title, old.description, old.created_at, old.visited_at);
+END;
+
+-- +migrate StatementEnd
+
+-- +migrate Down
diff --git a/pkg/database/tableLinks.go b/pkg/database/tableLinks.go
@@ -11,16 +11,18 @@ import (
)
type Link struct {
- ID int64
- UUID string
- URL string
- Title string
- Description string
- Shorthand *string
- CreatedAt time.Time
- UpdatedAt time.Time
- DeletedAt *time.Time
- Mirrors []LinksMirror
+ ID int64
+ UUID string
+ URL string
+ Title string
+ Description string
+ Shorthand *string
+ SignedCertificate string
+ OwnerUserID UserID
+ CreatedAt time.Time
+ UpdatedAt time.Time
+ DeletedAt *time.Time
+ Mirrors []LinksMirror
}
func (l Link) GenOwnershipCert() string {
diff --git a/pkg/web/handlers/data.go b/pkg/web/handlers/data.go
@@ -232,6 +232,11 @@ type editLinkData struct {
Mirrors []database.LinksMirror
}
+type claimLinkData struct {
+ Link database.Link
+ Certificate string
+}
+
type linkData struct {
Link database.Link
PgpKeys []database.LinksPgp
diff --git a/pkg/web/handlers/handlers.go b/pkg/web/handlers/handlers.go
@@ -1670,6 +1670,33 @@ func EditLinkHandler(c echo.Context) error {
return c.Render(http.StatusOK, "new-link", data)
}
+func ClaimLinkHandler(c echo.Context) error {
+ linkUUID := c.Param("linkUUID")
+ link, err := database.GetLinkByUUID(linkUUID)
+ if err != nil {
+ return c.Redirect(http.StatusFound, "/")
+ }
+ var data claimLinkData
+ data.Link = link
+ data.Certificate = link.GenOwnershipCert()
+
+ // test := `-----BEGIN SIGNED MESSAGE-----
+ //message by n0tr1v
+ //-----BEGIN SIGNATURE-----
+ //EEU5ZQ7le6IH8ehZsbZaQbHlu/JRkSK72j4KpUep3nIB0akErBusv/t2STNRfGc/
+ //j6JmMytRgTWOlq1nMFrkAg==
+ //-----END SIGNATURE-----`
+
+ if c.Request().Method == http.MethodGet {
+ return c.Render(http.StatusOK, "link-claim", data)
+ }
+
+ pemSig := c.Request().PostFormValue("signature")
+ isValid := utils.VerifyTorSign(link.GetOnionAddr(), data.Certificate, pemSig)
+ fmt.Println("valid?", isValid)
+ return c.Redirect(http.StatusFound, "/links/"+link.UUID)
+}
+
func ForumHandler(c echo.Context) error {
authUser := c.Get("authUser").(*database.User)
var data forumData
diff --git a/pkg/web/public/views/pages/link-claim.gohtml b/pkg/web/public/views/pages/link-claim.gohtml
@@ -0,0 +1,33 @@
+{{ define "content" }}
+<div class="container mb-5">
+ <nav aria-label="breadcrumb">
+ <ol class="breadcrumb">
+ <li class="breadcrumb-item"><a href="/links">Links</a></li>
+ <li class="breadcrumb-item"><a href="/links/{{ .Data.Link.UUID }}">{{ .Data.Link.Title }}</a></li>
+ <li class="breadcrumb-item active">Claim ownership</li>
+ </ol>
+ <p>
+ You can claim ownership of an onion address by using your onion private key to sign the following certificate.<br />
+ Once done, send the signature here.<br />
+
+ </p>
+ <form method="post">
+ <input type="hidden" name="csrf" value="{{ .CSRF }}" />
+ <div class="form-group">
+ <textarea name="certificate" class="form-control" rows="6" readonly>{{ .Data.Certificate }}</textarea>
+ <button class="btn btn-secondary mt-2">Downlaod certificate file</button>
+ </div>
+ <p>
+ Use the <a href="/">following python script</a> to sign the certificate file.
+ </p>
+ <div class="form-group">
+ <label for="signature">Signature:</label>
+ <textarea name="signature" id="signature" class="form-control" rows="5" placeholder="-----BEGIN SIGNATURE----- ..."></textarea>
+ </div>
+ <button class="btn btn-primary">Claim ownership</button>
+ <a class="btn btn-secondary" href="/links/{{ .Data.Link.UUID }}">Cancel</a>
+ </form>
+ </nav>
+</div>
+
+{{ end }}
+\ No newline at end of file
diff --git a/pkg/web/web.go b/pkg/web/web.go
@@ -169,6 +169,8 @@ func getMainServer(i18nBundle *i18n.Bundle, renderer *tmp.Templates) echo.Handle
authGroup.GET("/l/:shorthand", handlers.LinkHandler, middlewares.AuthRateLimitMiddleware(time.Second, 2))
authGroup.GET("/links/:linkUUID", handlers.LinkHandler, middlewares.AuthRateLimitMiddleware(time.Second, 2))
authGroup.POST("/links/:linkUUID/restore", handlers.RestoreLinkHandler, middlewares.AuthRateLimitMiddleware(time.Second, 2))
+ authGroup.GET("/links/:linkUUID/claim", handlers.ClaimLinkHandler, middlewares.AuthRateLimitMiddleware(time.Second, 2))
+ authGroup.POST("/links/:linkUUID/claim", handlers.ClaimLinkHandler, middlewares.AuthRateLimitMiddleware(time.Second, 2))
authGroup.GET("/links/:linkUUID/edit", handlers.EditLinkHandler, middlewares.AuthRateLimitMiddleware(time.Second, 2))
authGroup.POST("/links/:linkUUID/edit", handlers.EditLinkHandler, middlewares.AuthRateLimitMiddleware(time.Second, 2))
authGroup.GET("/links/:linkUUID/delete", handlers.LinkDeleteHandler)