Skip to content

Commit

Permalink
Added reservation repository
Browse files Browse the repository at this point in the history
  • Loading branch information
jaytaph committed Mar 17, 2021
1 parent be09f44 commit 5172662
Show file tree
Hide file tree
Showing 7 changed files with 269 additions and 49 deletions.
5 changes: 3 additions & 2 deletions internal/handler/address.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ import (
"github.com/bitmaelum/bitmaelum-suite/pkg/bmcrypto"
"github.com/bitmaelum/bitmaelum-suite/pkg/hash"
"github.com/bitmaelum/bitmaelum-suite/pkg/proofofwork"
"github.com/bitmaelum/key-resolver-go/internal"
"github.com/bitmaelum/key-resolver-go/internal/address"
"github.com/bitmaelum/key-resolver-go/internal/http"
"github.com/bitmaelum/key-resolver-go/internal/organisation"
"github.com/bitmaelum/key-resolver-go/internal/reservation"
)

type addressUploadBody struct {
Expand Down Expand Up @@ -103,7 +103,8 @@ func PostAddressHash(addrHash hash.Hash, req http.Request) *http.Response {
return http.CreateError("cannot validate organisation token", 400)
}

if !internal.CheckReservations(addrHash, uploadBody.PublicKey) {
ok, err := reservation.ReservationService.IsValidated(addrHash, uploadBody.PublicKey)
if !ok || err != nil {
return http.CreateError("reserved address", 400)
}

Expand Down
4 changes: 2 additions & 2 deletions internal/handler/address_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ import (
"github.com/bitmaelum/bitmaelum-suite/pkg/bmcrypto"
"github.com/bitmaelum/bitmaelum-suite/pkg/hash"
"github.com/bitmaelum/bitmaelum-suite/pkg/proofofwork"
"github.com/bitmaelum/key-resolver-go/internal"
"github.com/bitmaelum/key-resolver-go/internal/address"
"github.com/bitmaelum/key-resolver-go/internal/http"
"github.com/bitmaelum/key-resolver-go/internal/organisation"
"github.com/bitmaelum/key-resolver-go/internal/reservation"
"github.com/bitmaelum/key-resolver-go/internal/routing"
testing2 "github.com/bitmaelum/key-resolver-go/internal/testing"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -715,7 +715,7 @@ func TestHistory(t *testing.T) {

func setupRepo() {
// NO reservation checks
internal.SkipReservationCheck = true
reservation.ReservationService = reservation.NewMockRepository()

sr := address.NewSqliteResolver(":memory:")
address.SetDefaultRepository(sr)
Expand Down
7 changes: 4 additions & 3 deletions internal/handler/organisation.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ import (
"github.com/bitmaelum/bitmaelum-suite/pkg/bmcrypto"
"github.com/bitmaelum/bitmaelum-suite/pkg/hash"
"github.com/bitmaelum/bitmaelum-suite/pkg/proofofwork"
"github.com/bitmaelum/key-resolver-go/internal"
"github.com/bitmaelum/key-resolver-go/internal/http"
"github.com/bitmaelum/key-resolver-go/internal/organisation"
"github.com/bitmaelum/key-resolver-go/internal/reservation"
)

var (
Expand Down Expand Up @@ -145,8 +145,9 @@ func createOrganisation(orgHash hash.Hash, uploadBody organisationUploadBody) *h
return http.CreateError(fmt.Sprintf("proof-of-work too weak (need %d bits)", MinimumProofBitsAddress), 401)
}

if !internal.CheckReservations(orgHash, uploadBody.PublicKey) {
return http.CreateError("reserved organisation but validation in DNS found", 401)
ok, err := reservation.ReservationService.IsValidated(orgHash, uploadBody.PublicKey)
if !ok || err != nil {
return http.CreateError("reserved organisation but validation in DNS not found", 401)
}

repo := organisation.GetResolveRepository()
Expand Down
102 changes: 102 additions & 0 deletions internal/reservation/mock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Copyright (c) 2020 BitMaelum Authors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package reservation

import (
"github.com/bitmaelum/bitmaelum-suite/pkg/bmcrypto"
"github.com/bitmaelum/bitmaelum-suite/pkg/hash"
)

type domainEntry struct {
Hash hash.Hash
Domain string
}

// MockRepository is a simple repository that allows you to easily mock reserved accounts and organisations
type MockRepository struct {
Entries []domainEntry
DNS map[string]string
}

func NewMockRepository() *MockRepository {
return &MockRepository{
Entries: []domainEntry{},
DNS: make(map[string]string),
}
}

// Adds a new entry to the mock reservations
func (m *MockRepository) AddEntry(h hash.Hash, domains []string) {
for i := range domains {
m.Entries = append(m.Entries, domainEntry{
Hash: h,
Domain: domains[i],
})
}
}

// Adds a DNS entry so we can verify
func (m *MockRepository) AddDNS(domain, value string) {
m.DNS[domain] = value
}

// IsValidated will check if a hash has a DNS entry with the correct value
func (m *MockRepository) IsValidated(h hash.Hash, pk *bmcrypto.PubKey) (bool, error) {
domains, err := m.GetDomains(h)
if err != nil {
return false, err
}

// NO domains, so not a reserved hash
if len(domains) == 0 {
return true, nil
}

for i := range domains {
if m.DNS[domains[i]] == pk.Fingerprint() {
return true, nil
}
}

return false, nil
}

// IsReserved will return true when the hash is a reserved hash
func (m *MockRepository) IsReserved(h hash.Hash) (bool, error) {
d, err := m.GetDomains(h)
if err != nil {
return false, err
}

return len(d) > 0, nil
}

// GetDomains will return the domains for the given reserved hash, or empty slice when not reserved
func (m *MockRepository) GetDomains(h hash.Hash) ([]string, error) {
var domains []string

for i := range m.Entries {
if m.Entries[i].Hash.String() == h.String() {
domains = append(domains, m.Entries[i].Domain)
}
}

return domains, nil
}
63 changes: 63 additions & 0 deletions internal/reservation/mock_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright (c) 2020 BitMaelum Authors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package reservation

import (
"testing"

"github.com/bitmaelum/bitmaelum-suite/pkg/bmcrypto"
"github.com/bitmaelum/bitmaelum-suite/pkg/hash"
"github.com/stretchr/testify/assert"
)

func TestMockRepository(t *testing.T) {
r := NewMockRepository()

h := hash.New("foobar")
r.AddEntry(h, []string{"foobar.com", "foobar.nl"})

ok, err := r.IsReserved(h)
assert.NoError(t, err)
assert.True(t, ok)

ok, err = r.IsReserved(hash.New("not-reserved"))
assert.NoError(t, err)
assert.False(t, ok)

d, err := r.GetDomains(hash.New("not-reserved"))
assert.NoError(t, err)
assert.Len(t, d, 0)

d, err = r.GetDomains(hash.New("foobar"))
assert.NoError(t, err)
assert.Len(t, d, 2)

_, pk, _ := bmcrypto.GenerateKeyPair(bmcrypto.KeyTypeED25519)

ok, err = r.IsValidated(h, pk)
assert.NoError(t, err)
assert.False(t, ok)

r.AddDNS("foobar.nl", pk.Fingerprint())

ok, err = r.IsValidated(h, pk)
assert.NoError(t, err)
assert.True(t, ok)
}
102 changes: 60 additions & 42 deletions internal/reservation.go → internal/reservation/remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package internal
package reservation

import (
"encoding/json"
Expand All @@ -31,19 +31,70 @@ import (
"github.com/bitmaelum/bitmaelum-suite/pkg/hash"
)

// @TODO: Make this dynamic
const baseReservedUrl = "https://resolver.bitmaelum.org/reserved/"
// RemoteRepository allows you to fetch reservations from a remote server (the keyserver)
type RemoteRepository struct {
c *http.Client
baseUrl string
}

// NewRemoteRepository creates a new repository for fetching reservations through HTTP
func NewRemoteRepository(baseUrl string, client *http.Client) ReservationRepository {
if client == nil {
client = http.DefaultClient
}

return &RemoteRepository{
c: client,
baseUrl: baseUrl,
}
}

// IsValidated will check if a hash has a DNS entry with the correct value
func (r RemoteRepository) IsValidated(h hash.Hash, pk *bmcrypto.PubKey) (bool, error) {
domains, err := r.GetDomains(h)
if err != nil {
return false, err
}

// Not reserved
if len(domains) == 0 {
return true, err
}

for _, domain := range domains {
entries, err := net.LookupTXT("_bitmaelum." + domain)
if err != nil {
continue
}

for _, entry := range entries {
if entry == pk.Fingerprint() {
return true, nil
}
}
}

// No domain found that verifies
return false, nil
}

var SkipReservationCheck = false
// IsReserved will return true when the hash is a reserved hash
func (r RemoteRepository) IsReserved(h hash.Hash) (bool, error) {
d, err := r.GetDomains(h)
if err != nil {
return false, err
}

return len(d) > 0, nil
}

func getDomainReservations(hash hash.Hash) ([]string, error) {
// call /reserved/<hash>
client := http.DefaultClient
url := baseReservedUrl + hash.String()
// GetDomains will return the domains for the given reserved hash, or empty slice when not reserved
func (r RemoteRepository) GetDomains(h hash.Hash) ([]string, error) {
url := r.baseUrl + h.String()

var domains = make([]string, 100)

response, err := client.Get(url)
response, err := r.c.Get(url)
if err != nil {
return nil, errors.New("not found")
}
Expand All @@ -70,36 +121,3 @@ func getDomainReservations(hash hash.Hash) ([]string, error) {

return nil, errors.New("not found")
}

func CheckReservations(h hash.Hash, pk *bmcrypto.PubKey) bool {
if SkipReservationCheck {
return true
}

domains, err := getDomainReservations(h)
if err != nil {
// Error while fetching domains
return false
}

// Not reserved
if len(domains) == 0 {
return true
}

for _, domain := range domains {
entries, err := net.LookupTXT("_bitmaelum." + domain)
if err != nil {
continue
}

for _, entry := range entries {
if entry == pk.Fingerprint() {
return true
}
}
}

// No domain found for verification
return false
}
35 changes: 35 additions & 0 deletions internal/reservation/repository.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) 2020 BitMaelum Authors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package reservation

import (
"github.com/bitmaelum/bitmaelum-suite/pkg/bmcrypto"
"github.com/bitmaelum/bitmaelum-suite/pkg/hash"
)

type ReservationRepository interface {
IsValidated(h hash.Hash, pk *bmcrypto.PubKey) (bool, error)
IsReserved(h hash.Hash) (bool, error)
GetDomains(h hash.Hash) ([]string, error)
}

const baseReservedUrl = "https://resolver.bitmaelum.org/reserved/"

var ReservationService = NewRemoteRepository(baseReservedUrl, nil)

0 comments on commit 5172662

Please sign in to comment.