Skip to content

Commit c45fc40

Browse files
authored
support multiple network: stage 1 (#30)
1 parent 061df5d commit c45fc40

File tree

25 files changed

+628
-331
lines changed

25 files changed

+628
-331
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
alter table network
2+
add column token text not null default 'network_token';
3+
comment on column network.token is 'special token to protect from brute force';
4+
5+
alter table node
6+
add column device_id integer not null default 0;
7+
8+
create index if not exists node_device_id_index on node (device_id);
9+
10+
drop index node_public_key_index;
11+
12+
alter table node
13+
drop column public_key;
14+
15+
create table device
16+
(
17+
id serial primary key,
18+
token text not null,
19+
public_key text not null,
20+
mqtt_last_leave timestamptz, -- would use in future version.
21+
created_at timestamptz not null default now()
22+
);
23+
24+
create unique index if not exists device_public_key_index on device(public_key);

backend/src/main/scala/com/timzaak/fornet/controller/AuthController.scala

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ import com.typesafe.config.Config
99
import org.hashids.Hashids
1010
import org.scalatra.BadRequest.apply
1111
import org.scalatra.json.JsonResult.apply
12-
import org.scalatra.{ BadRequest, Ok }
12+
import org.scalatra.{BadRequest, Ok}
1313
import very.util.config.get
14+
import very.util.security.ID.toIntID
1415
import very.util.web.Controller
15-
import very.util.security.IntID.toIntID
16-
import zio.json.{ DeriveJsonDecoder, JsonDecoder }
16+
import zio.json.{DeriveJsonDecoder, JsonDecoder}
1717

1818
import java.net.URLEncoder
1919
import java.nio.charset.Charset
@@ -49,12 +49,10 @@ trait AuthController(networkDao: NetworkDao, appConfig: AppConfig)(using config:
4949
networkDao
5050
.findById(networkId)
5151
.filter(n => n.status == NetworkStatus.Normal && n.groupId == groupId)
52-
.map { _ =>
53-
val nId =
54-
URLEncoder.encode(networkId.secretId, Charsets.UTF_8)
52+
.map { network =>
5553
String(
5654
Base64.getEncoder.encode(
57-
s"2|${config.getString("server.grpc.endpoint")}|${nId}"
55+
s"2|${config.getString("server.grpc.endpoint")}|${network.tokenId.secretId}"
5856
.getBytes()
5957
)
6058
)

backend/src/main/scala/com/timzaak/fornet/controller/NetworkController.scala

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,18 @@ import com.timzaak.fornet.dao.*
77
import com.timzaak.fornet.pubsub.NodeChangeNotifyService
88
import com.typesafe.config.Config
99
import org.hashids.Hashids
10-
import very.util.security.IntID
10+
import very.util.security.{IntID, TokenID}
1111

1212
import java.util.Base64
1313
import com.timzaak.fornet.dao.NetworkStatus
1414
import io.getquill.*
1515
import org.scalatra.*
1616
import org.scalatra.i18n.I18nSupport
1717
import org.scalatra.json.*
18-
import very.util.security.IntID.toIntID
18+
import very.util.security.ID.toIntID
1919
import very.util.web.Controller
2020
import very.util.web.validate.ValidationExtra
21-
import zio.json.{ DeriveJsonDecoder, JsonDecoder }
21+
import zio.json.{DeriveJsonDecoder, JsonDecoder}
2222

2323
import java.time.OffsetDateTime
2424

@@ -81,6 +81,7 @@ trait NetworkController(
8181
_.addressRange -> lift(req.addressRange),
8282
_.setting -> lift(NetworkSetting(protocol = req.protocol)),
8383
_.groupId -> lift(groupId),
84+
_.token -> lift(TokenID.randomToken()),
8485
)
8586
.returning(_.id)
8687
}
@@ -101,10 +102,10 @@ trait NetworkController(
101102
networkDao
102103
.findById(networkId)
103104
.filter(n => n.status == NetworkStatus.Normal && n.groupId == groupId)
104-
.map { _ =>
105+
.map { network =>
105106
String(
106107
Base64.getEncoder.encode(
107-
s"1|${config.getString("server.grpc.endpoint")}|${networkId.secretId}"
108+
s"1|${config.getString("server.grpc.endpoint")}|${network.tokenId.secretId}"
108109
.getBytes()
109110
)
110111
)

backend/src/main/scala/com/timzaak/fornet/controller/NodeController.scala

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ import inet.ipaddr.ipv4.IPv4Address
1212
import io.getquill.*
1313
import org.hashids.Hashids
1414
import org.scalatra.Accepted
15+
import very.util.security.ID.toIntID
16+
import very.util.security.IntID
1517
import very.util.web.Controller
1618
import very.util.web.json.JsonResponse
17-
import very.util.security.IntID
18-
import very.util.security.IntID.toIntID
1919
import zio.json.*
2020
import zio.prelude.Validation
2121

@@ -48,12 +48,13 @@ given JsonDecoder[UpdateNodeStatusReq] = DeriveJsonDecoder.gen
4848
trait NodeController(
4949
nodeDao: NodeDao,
5050
networkDao: NetworkDao,
51+
deviceDao: DeviceDao,
5152
nodeChangeNotifyService: NodeChangeNotifyService,
5253
appConfig: AppConfig,
5354
)(using quill: DB, hashId: Hashids, config: Config)
5455
extends Controller
5556
with AppAuthSupport {
56-
import quill.{ *, given }
57+
import quill.{*, given}
5758

5859
private def _networkId: IntID = params("networkId").toIntID
5960
private def _nodeId: IntID = params("nodeId").toIntID
@@ -144,6 +145,7 @@ trait NodeController(
144145
if (network.status == NetworkStatus.Normal) {
145146
nodeChangeNotifyService.nodeStatusChangeNotify(
146147
oldNode,
148+
deviceDao.findById(oldNode.deviceId).get,
147149
oldNode.status,
148150
req.status
149151
)
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.timzaak.fornet.dao
2+
3+
import org.hashids.Hashids
4+
import very.util.security.{IntID, TokenID}
5+
6+
import java.time.OffsetDateTime
7+
8+
case class Device(id: IntID, token: String, publicKey: String, createdAt: OffsetDateTime) {
9+
def tokenID: TokenID = TokenID(id, token)
10+
}
11+
12+
import io.getquill.*
13+
class DeviceDao(using quill: DB, hashids: Hashids) {
14+
import quill.{*, given}
15+
16+
def findByTokenID(tokenId: TokenID): Option[Device] = {
17+
quill
18+
.run(quote(query[Device]).filter(v => v.id == lift(tokenId.intId) && v.token == lift(tokenId.token)).single)
19+
.headOption
20+
}
21+
def findById(id:IntID): Option[Device] = {
22+
quill
23+
.run(quote(query[Device]).filter(v => v.id == lift(id)).single)
24+
.headOption
25+
}
26+
27+
def getAllDevices(ids:List[IntID]):Map[Int, Device]= {
28+
if(ids.isEmpty) {
29+
Map.empty
30+
} else {
31+
quill.run(quote(query[Device])).filter(v => liftQuery(ids).contains(v.id)).map(v => v.id.id -> v).toMap
32+
}
33+
}
34+
}

backend/src/main/scala/com/timzaak/fornet/dao/Network.scala

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ package com.timzaak.fornet.dao
55
import com.timzaak.fornet.dao.NetworkProtocol.TCP
66
import org.hashids.Hashids
77
import very.util.persistence.quill.DBSerializer
8-
import very.util.security.IntID
8+
import very.util.security.{IntID, TokenID}
99
import zio.json.*
1010

1111
import java.time.OffsetDateTime
12-
import scala.util.{ Failure, Success, Try }
12+
import scala.util.{Failure, Success, Try}
1313

1414
enum NetworkStatus {
1515
case Normal, Delete
@@ -29,7 +29,7 @@ enum NetworkProtocol {
2929
case TCP, UDP
3030

3131
import com.timzaak.fornet.protobuf.config.Protocol as PProtocol
32-
def gRPCProtocol:PProtocol = {
32+
def gRPCProtocol: PProtocol = {
3333
this match {
3434
case TCP => PProtocol.Protocol_TCP
3535
case UDP => PProtocol.Protocol_UDP
@@ -59,37 +59,39 @@ object NetworkProtocol {
5959
case class Network(
6060
id: IntID,
6161
name: String,
62+
token: String,
6263
groupId: String,
6364
addressRange: String,
6465
setting: NetworkSetting,
6566
status: NetworkStatus,
6667
createdAt: OffsetDateTime,
6768
updatedAt: OffsetDateTime,
68-
)
69+
) {
70+
def tokenId:TokenID = TokenID(id, token)
71+
}
6972
//object Network {
7073
// given networkUpdateMeta:UpdateMeta[Network] = updateMeta[Network](_.id)
7174
//}
7275
case class NetworkSetting(
7376
port: Int = 51820,
7477
keepAlive: Int = 30,
7578
mtu: Int = 1420,
76-
protocol:NetworkProtocol = NetworkProtocol.UDP,
79+
protocol: NetworkProtocol = NetworkProtocol.UDP,
7780
dns: Option[Seq[String]] = None,
7881
) extends DBSerializer
7982

8083
object Network {
81-
import very.util.web.json.{ intIDDecoder, intIDEncoder }
84+
import very.util.web.json.{intIDDecoder, intIDEncoder}
8285
given networkDerive(using hashId: Hashids): JsonCodec[Network] = DeriveJsonCodec.gen
8386
}
8487
object NetworkSetting {
8588
given JsonCodec[NetworkSetting] = DeriveJsonCodec.gen
8689
}
8790

88-
8991
import io.getquill.*
9092
import org.hashids.Hashids
9193

92-
class NetworkDao(using quill: DB, hashIds:Hashids) {
94+
class NetworkDao(using quill: DB, hashIds: Hashids) {
9395
import quill.{*, given}
9496

9597
def findById(id: IntID): Option[Network] = {
@@ -105,6 +107,8 @@ class NetworkDao(using quill: DB, hashIds:Hashids) {
105107

106108
def existGroupNetwork(networkId: IntID, groupId: String): Boolean = {
107109
quill.run(quote(query[Network]).filter(n => n.id == lift(networkId) && n.groupId == lift(groupId)).nonEmpty)
108-
109110
}
111+
112+
def findByTokenId(tokenId:TokenID): Option[Network] =
113+
quill.run(quote(query[Network]).filter(n => n.id == lift(tokenId.intId) && n.name == lift(tokenId.token)).single).headOption
110114
}

backend/src/main/scala/com/timzaak/fornet/dao/Node.scala

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ package com.timzaak.fornet.dao
33
import io.getquill.MappedEncoding
44
import org.hashids.Hashids
55
import very.util.persistence.quill.DBSerializer
6-
import very.util.security.IntID
6+
import very.util.security.{IntID, TokenID}
77
import zio.json.*
88

99
import java.time.OffsetDateTime
10-
import scala.util.{ Failure, Success, Try }
10+
import scala.util.{Failure, Success, Try}
1111

1212
enum NodeType {
1313
// Normal: Fornet Client
@@ -70,8 +70,9 @@ case class Node(
7070
id: IntID,
7171
name: String,
7272
networkId: IntID,
73+
deviceId: IntID,
7374
ip: String,
74-
publicKey: String,
75+
publicKey: String, // TODO: rm it
7576
setting: NodeSetting,
7677
nodeType: NodeType,
7778
status: NodeStatus,
@@ -135,16 +136,7 @@ import io.getquill.*
135136
class NodeDao(using quill: DB, hashids: Hashids) {
136137

137138
import quill.{ *, given }
138-
139-
def findIdByPublicKey(publicKey: String, networkId: IntID): Option[IntID] =
140-
quill.run {
141-
quote {
142-
query[Node]
143-
.filter(n => n.publicKey == lift(publicKey) && n.networkId == lift(networkId))
144-
.map(_.id)
145-
.single
146-
}
147-
}.headOption
139+
148140

149141
def findById(networkId: IntID, nodeId: IntID): Option[Node] = quill.run {
150142
quote {
@@ -154,13 +146,6 @@ class NodeDao(using quill: DB, hashids: Hashids) {
154146
}
155147
}.headOption
156148

157-
def findByPublicKey(publicKey: String): List[Node] = quill.run {
158-
quote {
159-
query[Node]
160-
.filter(_.publicKey == lift(publicKey))
161-
}
162-
}
163-
164149
def getUsedIps(networkId: IntID): Seq[String] = quill.run {
165150
quote {
166151
query[Node]
@@ -204,4 +189,13 @@ class NodeDao(using quill: DB, hashids: Hashids) {
204189
def countByNetwork(networkId: IntID): Long = quill.run {
205190
query[Node].filter(n => n.networkId == lift(networkId) && n.status == lift(NodeStatus.Normal)).size
206191
}
192+
193+
def findByDeviceWithNetwork(networkId:IntID, deviceTokenId:TokenID):List[Node] = quill.run {
194+
query[Node].filter(n => n.deviceId == lift(deviceTokenId.intId) && n.networkId == lift(networkId))
195+
}
196+
197+
def findByDeviceId(deviceTokenId: TokenID): List[Node] = quill.run {
198+
query[Node].filter(n => n.deviceId == lift(deviceTokenId.intId))
199+
}
200+
207201
}

backend/src/main/scala/com/timzaak/fornet/di/DI.scala

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
package com.timzaak.fornet.di
22

3-
import com.timzaak.fornet.config.{ AppConfig, AppConfigImpl }
3+
import com.timzaak.fornet.config.{AppConfig, AppConfigImpl}
44
import com.timzaak.fornet.controller.*
55
import com.timzaak.fornet.grpc.AuthGRPCController
6-
import com.timzaak.fornet.keycloak.{ KeycloakJWTSaaSAuthStrategy, KeycloakJWTSaaSCompatAuthStrategy }
6+
import com.timzaak.fornet.keycloak.{KeycloakJWTSaaSAuthStrategy, KeycloakJWTSaaSCompatAuthStrategy}
77
import com.timzaak.fornet.mqtt.MqttCallbackController
88
import com.timzaak.fornet.mqtt.api.RMqttApiClient
9-
import com.timzaak.fornet.pubsub.{ MqttConnectionManager, NodeChangeNotifyService }
9+
import com.timzaak.fornet.pubsub.{MqttConnectionManager, NodeChangeNotifyService}
1010
import com.timzaak.fornet.service.*
11-
import very.util.keycloak.{ JWKPublicKeyLocator, JWKTokenVerifier }
12-
import very.util.web.auth.{ AuthStrategy, AuthStrategyProvider, SingleUserAuthStrategy }
11+
import very.util.keycloak.{JWKPublicKeyLocator, JWKTokenVerifier}
12+
import very.util.web.auth.{AuthStrategy, AuthStrategyProvider, SingleUserAuthStrategy}
1313
object DI extends DaoDI { di =>
1414

1515
object appConfig extends AppConfigImpl(config)
@@ -30,6 +30,7 @@ object DI extends DaoDI { di =>
3030
extends NodeChangeNotifyService(
3131
nodeDao = di.nodeDao,
3232
networkDao = di.networkDao,
33+
deviceDao = di.deviceDao,
3334
connectionManager = di.connectionManager,
3435
nodeService = di.nodeService,
3536
)
@@ -84,6 +85,7 @@ object DI extends DaoDI { di =>
8485
extends NodeController(
8586
nodeDao = di.nodeDao,
8687
networkDao = di.networkDao,
88+
deviceDao = di.deviceDao,
8789
nodeChangeNotifyService = di.nodeChangeNotifyService,
8890
appConfig = di.appConfig,
8991
)
@@ -100,6 +102,7 @@ object DI extends DaoDI { di =>
100102
extends AuthGRPCController(
101103
nodeDao = di.nodeDao,
102104
networkDao = di.networkDao,
105+
deviceDao = di.deviceDao,
103106
nodeChangeNotifyService = di.nodeChangeNotifyService,
104107
config = di.config,
105108
appConfig = di.appConfig,
@@ -112,6 +115,7 @@ object DI extends DaoDI { di =>
112115
extends MqttCallbackController(
113116
nodeDao = di.nodeDao,
114117
networkDao = di.networkDao,
118+
deviceDao = di.deviceDao,
115119
nodeService = di.nodeService,
116120
mqttConnectionManager = di.connectionManager
117121
)

backend/src/main/scala/com/timzaak/fornet/di/DaoDI.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.timzaak.fornet.di
22

3-
import com.timzaak.fornet.dao.{DB, NetworkDao, NodeDao}
3+
import com.timzaak.fornet.dao.{DB, DeviceDao, NetworkDao, NodeDao}
44
import com.typesafe.config.{Config, ConfigFactory}
55
import org.hashids.Hashids
66

@@ -23,4 +23,6 @@ trait DaoDI {
2323
object networkDao extends NetworkDao
2424

2525
object nodeDao extends NodeDao
26+
27+
object deviceDao extends DeviceDao
2628
}

0 commit comments

Comments
 (0)