Skip to content

Commit 08cc0e4

Browse files
authored
Merge pull request #390 from urnetwork/api-key
adds api key support
2 parents 3b37be8 + a1e7ded commit 08cc0e4

10 files changed

Lines changed: 610 additions & 19 deletions

File tree

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package handlers
2+
3+
import (
4+
"net/http"
5+
6+
"github.com/urnetwork/server/controller"
7+
"github.com/urnetwork/server/model"
8+
"github.com/urnetwork/server/router"
9+
)
10+
11+
func CreateApiKey(w http.ResponseWriter, r *http.Request) {
12+
router.WrapWithInputRequireAuth(model.CreateApiKey, w, r)
13+
}
14+
15+
func GetApiKeys(w http.ResponseWriter, r *http.Request) {
16+
router.WrapRequireAuth(controller.GetApiKeys, w, r)
17+
}
18+
19+
func DeleteApiKey(w http.ResponseWriter, r *http.Request) {
20+
router.WrapWithInputRequireAuth(controller.DeleteApiKey, w, r)
21+
}

api/main.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,9 @@ Options:
164164
router.NewRoute("POST", "/device/set-provide", handlers.DeviceSetProvide),
165165
router.NewRoute("POST", "/connect/control", handlers.ConnectControl),
166166
router.NewRoute("GET", "/hello", handlers.Hello),
167+
router.NewRoute("POST", "/account/api-key", handlers.CreateApiKey),
168+
router.NewRoute("POST", "/account/api-key/remove", handlers.DeleteApiKey),
169+
router.NewRoute("GET", "/account/api-keys", handlers.GetApiKeys),
167170
router.NewRoute("POST", "/account/payout-wallet", handlers.SetPayoutWallet),
168171
router.NewRoute("GET", "/account/payout-wallet", handlers.GetPayoutWallet),
169172
router.NewRoute("POST", "/account/circle-wallet", handlers.CircleWebhook),

apikey/apikey.go

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package apikey
2+
3+
import (
4+
"context"
5+
"crypto/rand"
6+
"encoding/base32"
7+
"fmt"
8+
9+
"github.com/urnetwork/server"
10+
)
11+
12+
type NetworkByApiKey struct {
13+
NetworkId server.Id
14+
UserId server.Id
15+
NetworkName string
16+
}
17+
18+
func GetNetworkByApiKey(apiKey string, ctx context.Context) *NetworkByApiKey {
19+
var result *NetworkByApiKey
20+
21+
server.Db(ctx, func(conn server.PgConn) {
22+
rows, err := conn.Query(
23+
ctx,
24+
`
25+
SELECT
26+
network.network_id,
27+
network.admin_user_id,
28+
network.network_name
29+
FROM account_api_key
30+
JOIN network ON network.network_id = account_api_key.network_id
31+
WHERE account_api_key.api_key = $1
32+
`,
33+
apiKey,
34+
)
35+
36+
server.WithPgResult(rows, err, func() {
37+
if rows.Next() {
38+
var r NetworkByApiKey
39+
server.RaisePgResult(rows.Scan(
40+
&r.NetworkId,
41+
&r.UserId,
42+
&r.NetworkName,
43+
), err)
44+
result = &r
45+
}
46+
})
47+
})
48+
49+
return result
50+
}
51+
52+
type CreateApiKeyResult struct {
53+
Id server.Id `json:"id,omitempty"`
54+
ApiKey string `json:"api_key,omitempty"`
55+
}
56+
57+
func Testing_CreateApiKey(networkId server.Id, ctx context.Context) (result *CreateApiKeyResult, err error) {
58+
var apiKeyId server.Id
59+
var apiKey string
60+
61+
server.Tx(ctx, func(tx server.PgTx) {
62+
apiKeyId = server.NewId()
63+
64+
var keyErr error
65+
apiKey, keyErr = generateApiKey()
66+
if keyErr != nil {
67+
err = keyErr
68+
return
69+
}
70+
71+
_, err = tx.Exec(
72+
ctx,
73+
`
74+
INSERT INTO account_api_key
75+
(
76+
api_key_id,
77+
network_id,
78+
api_key,
79+
name
80+
)
81+
VALUES ($1, $2, $3, $4)
82+
`,
83+
apiKeyId,
84+
networkId,
85+
apiKey,
86+
"testkey",
87+
)
88+
})
89+
90+
if err != nil {
91+
return nil, err
92+
}
93+
94+
return &CreateApiKeyResult{
95+
Id: apiKeyId,
96+
ApiKey: apiKey,
97+
}, nil
98+
}
99+
100+
// for testing
101+
func generateApiKey() (string, error) {
102+
b := make([]byte, 32)
103+
if _, err := rand.Read(b); err != nil {
104+
return "", err
105+
}
106+
e := base32.HexEncoding.WithPadding(base32.NoPadding)
107+
return fmt.Sprintf("urn_%s", e.EncodeToString(b)), nil
108+
}

apikey/apikey_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package apikey_test
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/go-playground/assert/v2"
8+
"github.com/urnetwork/server"
9+
"github.com/urnetwork/server/apikey"
10+
"github.com/urnetwork/server/jwt"
11+
"github.com/urnetwork/server/model"
12+
"github.com/urnetwork/server/session"
13+
)
14+
15+
func TestFetchNetworkByApiKey(t *testing.T) {
16+
server.DefaultTestEnv().Run(func() {
17+
server.DefaultTestEnv().Run(func() {
18+
19+
ctx := context.Background()
20+
21+
networkId := server.NewId()
22+
userId := server.NewId()
23+
networkName := "testnetwork"
24+
25+
model.Testing_CreateNetwork(ctx, networkId, networkName, userId)
26+
27+
clientId := server.NewId()
28+
userSession := session.Testing_CreateClientSession(ctx, &jwt.ByJwt{
29+
NetworkId: networkId,
30+
ClientId: &clientId,
31+
})
32+
33+
// create some api keys
34+
key, err := apikey.Testing_CreateApiKey(networkId, ctx)
35+
assert.Equal(t, err, nil)
36+
37+
// fetch api key
38+
network := apikey.GetNetworkByApiKey(key.ApiKey, ctx)
39+
assert.NotEqual(t, network, nil)
40+
assert.Equal(t, network.NetworkId, networkId)
41+
assert.Equal(t, network.UserId, userId)
42+
// assert.Equal(t, key1.ApiKeyId, key1Result.Id)
43+
44+
err = model.DeleteApiKey(&key.Id, userSession)
45+
assert.Equal(t, err, nil)
46+
47+
// attempt fetch deleted api key
48+
keyDeleted := apikey.GetNetworkByApiKey(key.ApiKey, ctx)
49+
assert.Equal(t, keyDeleted, nil)
50+
51+
})
52+
})
53+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package controller
2+
3+
import (
4+
"github.com/urnetwork/glog"
5+
"github.com/urnetwork/server"
6+
"github.com/urnetwork/server/model"
7+
"github.com/urnetwork/server/session"
8+
)
9+
10+
type GetApiKeysError struct {
11+
Message string `json:"message"`
12+
}
13+
14+
type GetApiKeysResult struct {
15+
ApiKeys []*model.PublicAccountApiKey `json:"api_keys"`
16+
Error *GetApiKeysError `json:"error,omitempty"`
17+
}
18+
19+
func GetApiKeys(session *session.ClientSession) (result *GetApiKeysResult, err error) {
20+
21+
keys, err := model.GetAccountApiKeys(session)
22+
if err != nil {
23+
glog.Info("Error getting account api keys", "error", err)
24+
return &GetApiKeysResult{
25+
Error: &GetApiKeysError{
26+
Message: "Error getting account api keys",
27+
},
28+
}, nil
29+
}
30+
31+
return &GetApiKeysResult{
32+
ApiKeys: keys,
33+
}, nil
34+
35+
}
36+
37+
type DeleteApiKeyError struct {
38+
Message string `json:"message"`
39+
}
40+
41+
type DeleteApiKeyResult struct {
42+
Error *DeleteApiKeyError `json:"error,omitempty"`
43+
}
44+
45+
type DeleteApiKeyArgs struct {
46+
Id *server.Id `json:"id"`
47+
}
48+
49+
func DeleteApiKey(deleteApiKey *DeleteApiKeyArgs, session *session.ClientSession) (*DeleteApiKeyResult, error) {
50+
err := model.DeleteApiKey(deleteApiKey.Id, session)
51+
if err != nil {
52+
glog.Info("Error deleting api key: %s", err.Error())
53+
return &DeleteApiKeyResult{
54+
Error: &DeleteApiKeyError{
55+
Message: "Error deleting api key",
56+
},
57+
}, nil
58+
}
59+
return &DeleteApiKeyResult{}, nil
60+
}

db_migrations.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2722,7 +2722,7 @@ var migrations = []any{
27222722
block varchar(128) NOT NULL,
27232723
change_id bigint GENERATED ALWAYS AS IDENTITY,
27242724
proxy_id uuid NOT NULL,
2725-
2725+
27262726
PRIMARY KEY (proxy_host, block, change_id)
27272727
)
27282728
`),
@@ -2731,7 +2731,7 @@ var migrations = []any{
27312731
CREATE TABLE proxy_client_ipv4 (
27322732
sequence_id bigint NOT NULL,
27332733
client_ipv4 bigint NOT NULL,
2734-
2734+
27352735
PRIMARY KEY (sequence_id, client_ipv4)
27362736
)
27372737
`),
@@ -2759,4 +2759,20 @@ var migrations = []any{
27592759
newSqlMigration(`
27602760
CREATE INDEX IF NOT EXISTS transfer_contract_payer_network_id ON transfer_contract (payer_network_id, open, contract_id)
27612761
`),
2762+
2763+
newSqlMigration(`
2764+
CREATE TABLE account_api_key (
2765+
api_key_id uuid NOT NULL DEFAULT gen_random_uuid(),
2766+
network_id uuid NOT NULL,
2767+
api_key varchar(128) NOT NULL,
2768+
name varchar(128) NOT NULL DEFAULT '',
2769+
create_time timestamp NOT NULL DEFAULT now(),
2770+
2771+
PRIMARY KEY (api_key_id),
2772+
UNIQUE (api_key)
2773+
)
2774+
`),
2775+
newSqlMigration(`
2776+
CREATE INDEX account_api_key_network_id ON account_api_key (network_id, api_key_id)
2777+
`),
27622778
}

0 commit comments

Comments
 (0)