Skip to content

Commit 2713e22

Browse files
committed
contrib for jwt
Signed-off-by: Arnav Das <[email protected]>
1 parent 8f53949 commit 2713e22

25 files changed

+1151
-0
lines changed

nats/contrib/__init__.py

Whitespace-only changes.

nats/contrib/accounts/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# from nats.contrib.accounts.accounts import Account
2+
3+
# __all__ = [
4+
# "Account"
5+
# ]

nats/contrib/accounts/limits.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
2+
from dataclasses import dataclass
3+
from typing import Dict, Optional
4+
5+
from nats.contrib.flatten_model import FlatteningModel
6+
7+
8+
@dataclass
9+
class NatsLimits:
10+
data: Optional[int] = None
11+
payload: Optional[int] = None
12+
subs: Optional[int] = None
13+
14+
class AccountLimits(FlatteningModel):
15+
imports: Optional[int] # `json:"imports,omitempty"` // Max number of imports
16+
exports: Optional[int] # `json:"exports,omitempty"` // Max number of exports
17+
wildcards: Optional[bool] # `json:"wildcards,omitempty"` // Are wildcards allowed in exports
18+
disallow_bearer: Optional[bool] # `json:"disallow_bearer,omitempty"` // User JWT can't be bearer token
19+
conn: Optional[int] # `json:"conn,omitempty"` // Max number of active connections
20+
leaf: Optional[int] # `json:"leaf,omitempty"` // Max number of active leaf node connections
21+
22+
def __init__(self,
23+
imports: Optional[int] = None,
24+
exports: Optional[int] = None,
25+
wildcards: Optional[bool] = None,
26+
disallow_bearer: Optional[bool] = None,
27+
conn: Optional[int] = None,
28+
leaf: Optional[int] = None,
29+
):
30+
self.imports = imports
31+
self.exports = exports
32+
self.wildcards = wildcards
33+
self.disallow_bearer = disallow_bearer
34+
self.conn = conn
35+
self.leaf = leaf
36+
37+
class JetStreamLimits(FlatteningModel):
38+
mem_storage: Optional[int] = None
39+
disk_storage: Optional[int] = None
40+
streams: Optional[int] = None
41+
consumer: Optional[int] = None
42+
mem_max_stream_bytes: Optional[int] = None
43+
disk_max_stream_bytes: Optional[int] = None
44+
max_bytes_required: Optional[bool] = None
45+
max_ack_pending: Optional[int] = None
46+
47+
def __init__(self,
48+
mem_storage: Optional[int] = None,
49+
disk_storage: Optional[int] = None,
50+
streams: Optional[int] = None,
51+
consumer: Optional[int] = None,
52+
mem_max_stream_bytes: Optional[int] = None,
53+
disk_max_stream_bytes: Optional[int] = None,
54+
max_bytes_required: Optional[bool] = None,
55+
max_ack_pending: Optional[int] = None,
56+
):
57+
self.mem_storage = mem_storage
58+
self.disk_storage = disk_storage
59+
self.streams = streams
60+
self.consumer = consumer
61+
self.mem_max_stream_bytes = mem_max_stream_bytes
62+
self.disk_max_stream_bytes = disk_max_stream_bytes
63+
self.max_bytes_required = max_bytes_required
64+
self.max_ack_pending = max_ack_pending
65+
66+
67+
JetStreamTieredLimits = Dict[str, JetStreamLimits]
68+
69+
class OperatorLimits(FlatteningModel):
70+
nats_limits: NatsLimits
71+
account_limits: AccountLimits
72+
jetstream_limits: JetStreamLimits
73+
tiered_limits: Optional[JetStreamTieredLimits] # `json:"tiered_limits,omitempty"`
74+
75+
def __init__(self,
76+
nats_limits: NatsLimits,
77+
account_limits: AccountLimits,
78+
jetstream_limits: JetStreamLimits,
79+
tiered_limits: Optional[JetStreamTieredLimits] = None,
80+
):
81+
self.nats_limits = nats_limits
82+
self.account_limits = account_limits
83+
self.jetstream_limits = jetstream_limits
84+
self.tiered_limits = tiered_limits
85+
86+
class META:
87+
unflattened_fields = [('tiered_limits', 'tiered_limits')]

nats/contrib/accounts/models.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
from typing import Union, Optional, List, Dict
2+
from dataclasses import dataclass, field, fields
3+
4+
from nats.contrib.signingkeys import SigningKeys
5+
from nats.contrib.types import Info, Permissions, Types
6+
from nats.contrib.flatten_model import FlatteningModel
7+
8+
from nats.contrib.claims.generic import GenericFields
9+
from nats.contrib.imports import Imports
10+
from nats.contrib.exports import Exports
11+
from nats.contrib.accounts.limits import OperatorLimits
12+
13+
@dataclass
14+
class WeightedMapping:
15+
subject: str
16+
weight: Optional[int] = None # uint8 is mapped to int, with Optional for omitempty
17+
cluster: Optional[str] = None
18+
19+
20+
@dataclass
21+
class ExternalAuthorization:
22+
auth_users: Optional[List[str]]
23+
allowed_accounts: Optional[List[str]]
24+
xkey: Optional[str]
25+
26+
@dataclass
27+
class MsgTrace:
28+
# Destination is the subject the server will send message traces to
29+
# if the inbound message contains the "traceparent" header and has
30+
# its sampled field indicating that the trace should be triggered.
31+
dest: Optional[str] # `json:"dest,omitempty"`
32+
33+
# Sampling is used to set the probability sampling, that is, the
34+
# server will get a random number between 1 and 100 and trigger
35+
# the trace if the number is lower than this Sampling value.
36+
# The valid range is [1..100]. If the value is not set Validate()
37+
# will set the value to 100.
38+
sampling: Optional[int] # `json:"sampling,omitempty"`
39+
40+
41+
class Account(FlatteningModel):
42+
imports: Optional[Imports] # `json:"imports,omitempty"`
43+
exports: Optional[Exports] # `json:"exports,omitempty"`
44+
limits: Optional[OperatorLimits] # `json:"limits,omitempty"`
45+
signing_keys: Optional[SigningKeys] # `json:"signing_keys,omitempty"`
46+
revocations: Optional[Dict[str, int]] # `json:"revocations,omitempty"`
47+
default_permissions: Optional[Permissions] # `json:"default_permissions,omitempty"`
48+
mappings: Optional[Dict[str, WeightedMapping]] # `json:"mappings,omitempty"`
49+
authorization: Optional[ExternalAuthorization] # `json:"authorization,omitempty"`
50+
trace: Optional[MsgTrace] # `json:"trace,omitempty"`
51+
cluster_traffic: Optional[str] # `json:"cluster_traffic,omitempty"`
52+
53+
info: Info = field(default_factory=Info)
54+
generic_fields: GenericFields = field(default_factory=GenericFields)
55+
56+
def __init__(self,
57+
imports: Optional[Imports] = None,
58+
exports: Optional[Exports] = None,
59+
limits: Optional[OperatorLimits] = None,
60+
signing_keys: Optional[SigningKeys] = None,
61+
revocations: Optional[Dict[str, int]] = None,
62+
default_permissions: Optional[Permissions] = None,
63+
mappings: Optional[Dict[str, WeightedMapping]] = None,
64+
authorization: Optional[ExternalAuthorization] = None,
65+
trace: Optional[MsgTrace] = None,
66+
cluster_traffic: Optional[str] = None,
67+
info: Info = None,
68+
generic_fields: GenericFields = None
69+
):
70+
self.imports = imports
71+
self.exports = exports
72+
self.limits = limits
73+
self.signing_keys = signing_keys
74+
self.revocations = revocations
75+
self.default_permissions = default_permissions
76+
self.mappings = mappings
77+
self.authorization = authorization
78+
self.trace = trace
79+
self.cluster_traffic = cluster_traffic
80+
81+
self.info = info if info else Info()
82+
self.generic_fields = generic_fields if generic_fields else GenericFields()
83+
84+
class Meta:
85+
unflatten_fields = [
86+
("imports", "imports"), ("exports", "exports"), ("limits", "limits"),
87+
("signing_keys", "signing_keys"), ("revocations", "revocations"),
88+
("default_permissions", "default_permissions"),
89+
("mappings", "mappings"), ("authorization", "authorization"),
90+
("trace", "trace"), ("cluster_traffic", "cluster_traffic"),
91+
]

nats/contrib/claims/__init__.py

Whitespace-only changes.

nats/contrib/claims/generic.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
2+
from typing import List, Optional
3+
from nats.contrib.flatten_model import FlatteningModel
4+
from nats.contrib.types import Types
5+
6+
7+
class GenericFields(FlatteningModel):
8+
tags: Optional[List[str]]
9+
type: Optional[Types]
10+
version: Optional[int]
11+
12+
def __init__(self,
13+
tags: Optional[List[str]] = None,
14+
type: Optional[Types] = None,
15+
version: Optional[int] = None,
16+
):
17+
self.tags = tags
18+
self.type = type
19+
self.version = version

nats/contrib/claims/models.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
from typing import List, Optional, Union
2+
3+
from nats.contrib.users.models import User
4+
from nats.contrib.accounts.models import Account
5+
from nats.contrib.operator.models import Operator
6+
7+
from nats.contrib.types import Types
8+
from nats.contrib.flatten_model import FlatteningModel
9+
10+
class Claims(FlatteningModel):
11+
# Claims Data
12+
exp: Optional[int] # Expires int64 `json:"exp,omitempty"`
13+
jti: Optional[str] # ID string `json:"jti,omitempty"`
14+
iat: Optional[int] # IssuedAt int64 `json:"iat,omitempty"`
15+
iss: Optional[str] # Issuer string `json:"iss,omitempty"`
16+
name: Optional[str] # Name string `json:"name,omitempty"`
17+
nbf: Optional[int] # NotBefore int64 `json:"nbf,omitempty"`
18+
sub: Optional[str] # Subject string `json:"sub,omitempty"`
19+
20+
# Nats Data
21+
nats: Optional[Union[User, Account, Operator]]
22+
issuer_account: Optional[str | bytes]
23+
24+
def __init__(self,
25+
exp: Optional[int] = None,
26+
jti: Optional[str] = None,
27+
iat: Optional[int] = None,
28+
iss: Optional[str] = None,
29+
name: Optional[str] = None,
30+
nbf: Optional[int] = None,
31+
sub: Optional[str] = None,
32+
nats: Optional[Union[User, Account, Operator]] = None,
33+
issuer_account: Optional[str | bytes] = None,
34+
):
35+
self.exp: Optional[int] = exp
36+
self.jti: Optional[str] = jti
37+
self.iat: Optional[int] = iat
38+
self.iss: Optional[str] = iss
39+
self.name: Optional[str] = name
40+
self.nbf: Optional[int] = nbf
41+
self.sub: Optional[str] = sub
42+
43+
self.nats = nats
44+
self.issuer_account = issuer_account
45+
46+
class Meta:
47+
unflatten_fields = [('nats', 'nats')]
48+
49+
50+
class UserClaims(Claims):
51+
pass
52+
53+
54+
class AccountClaims(Claims):
55+
pass
56+
57+
58+
class OperatorClaims(Claims):
59+
pass

nats/contrib/constants.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
from enum import Enum
2+
3+
class Prefix(Enum):
4+
Unknown = -1
5+
6+
# Seed is the version byte used for encoded NATS Seeds
7+
Seed = 18 << 3 # Base32-encodes to 'S...'
8+
9+
# PrefixBytePrivate is the version byte used for encoded NATS Private keys
10+
Private = 15 << 3 # Base32-encodes to 'P...'
11+
12+
# PrefixByteOperator is the version byte used for encoded NATS Operators
13+
Operator = 14 << 3 # Base32-encodes to 'O...'
14+
15+
# PrefixByteServer is the version byte used for encoded NATS Servers
16+
Server = 13 << 3 # Base32-encodes to 'N...'
17+
18+
# PrefixByteCluster is the version byte used for encoded NATS Clusters
19+
Cluster = 2 << 3 # Base32-encodes to 'C...'
20+
21+
# PrefixByteAccount is the version byte used for encoded NATS Accounts
22+
Account = 0 # Base32-encodes to 'A...'
23+
24+
# PrefixByteUser is the version byte used for encoded NATS Users
25+
User = 20 << 3 # Base32-encodes to 'U...'
26+
27+
Curve = 23 << 3 # Base32-encodes to 'X...'
28+
29+
accLookupReqTokens = 6
30+
accLookupReqSubj = "$SYS.REQ.ACCOUNT.{subject}.CLAIMS.LOOKUP"
31+
accPackReqSubj = "$SYS.REQ.CLAIMS.PACK"
32+
accListReqSubj = "$SYS.REQ.CLAIMS.LIST"
33+
accClaimsReqSubj = "$SYS.REQ.CLAIMS.UPDATE"
34+
accDeleteReqSubj = "$SYS.REQ.CLAIMS.DELETE"
35+
36+
connectEventSubj = "$SYS.ACCOUNT.{subject}.CONNECT"
37+
disconnectEventSubj = "$SYS.ACCOUNT.{subject}.DISCONNECT"
38+
accDirectReqSubj = "$SYS.REQ.ACCOUNT.{account_name}.{subject}"
39+
accPingReqSubj = "$SYS.REQ.ACCOUNT.PING.{subject}" # atm. only used for STATZ and CONNZ import from system account
40+
# kept for backward compatibility when using http resolver
41+
# this overlaps with the names for events but you'd have to have the operator private key in order to succeed.
42+
accUpdateEventSubjOld = "$SYS.ACCOUNT.{subject}.CLAIMS.UPDATE"
43+
accUpdateEventSubjNew = "$SYS.REQ.ACCOUNT.{subject}.CLAIMS.UPDATE"
44+
connsRespSubj = "$SYS._INBOX_.{subject}"
45+
accConnsEventSubjNew = "$SYS.ACCOUNT.{subject}.SERVER.CONNS"
46+
accConnsEventSubjOld = "$SYS.SERVER.ACCOUNT.{subject}.CONNS" # kept for backward compatibility
47+
lameDuckEventSubj = "$SYS.SERVER.{subject}.LAMEDUCK"
48+
shutdownEventSubj = "$SYS.SERVER.{subject}.SHUTDOWN"
49+
clientKickReqSubj = "$SYS.REQ.SERVER.{subject}.KICK"
50+
clientLDMReqSubj = "$SYS.REQ.SERVER.{subject}.LDM"
51+
authErrorEventSubj = "$SYS.SERVER.{subject}.CLIENT.AUTH.ERR"
52+
authErrorAccountEventSubj = "$SYS.ACCOUNT.CLIENT.AUTH.ERR"
53+
serverStatsSubj = "$SYS.SERVER.{subject}.STATSZ"
54+
serverDirectReqSubj = "$SYS.REQ.SERVER.{server_id}.{subject}"
55+
serverPingReqSubj = "$SYS.REQ.SERVER.PING.{subject}"
56+
serverStatsPingReqSubj = "$SYS.REQ.SERVER.PING" # use $SYS.REQ.SERVER.PING.STATSZ instead
57+
serverReloadReqSubj = "$SYS.REQ.SERVER.{subject}.RELOAD" # with server ID
58+
leafNodeConnectEventSubj = "$SYS.ACCOUNT.{subject}.LEAFNODE.CONNECT" # for internal use only
59+
remoteLatencyEventSubj = "$SYS.LATENCY.M2.{subject}"
60+
inboxRespSubj = "$SYS._INBOX.{subject}.{subject}"
61+
62+
# Used to return information to a user on bound account and user permissions.
63+
userDirectInfoSubj = "$SYS.REQ.USER.INFO"
64+
userDirectReqSubj = "$SYS.REQ.USER.{subject}.INFO"
65+
66+
# FIXME(dlc) - Should account scope, even with wc for now, but later on
67+
# we can then shard as needed.
68+
accNumSubsReqSubj = "$SYS.REQ.ACCOUNT.NSUBS"
69+
70+
# These are for exported debug services. These are local to this server only.
71+
accSubsSubj = "$SYS.DEBUG.SUBSCRIBERS"
72+
73+
shutdownEventTokens = 4
74+
serverSubjectIndex = 2
75+
accUpdateTokensNew = 6
76+
accUpdateTokensOld = 5
77+
accUpdateAccIdxOld = 2
78+
79+
accReqTokens = 5
80+
accReqAccIndex = 3
81+
82+
ocspPeerRejectEventSubj = "$SYS.SERVER.%s.OCSP.PEER.CONN.REJECT"
83+
ocspPeerChainlinkInvalidEventSubj = "$SYS.SERVER.%s.OCSP.PEER.LINK.INVALID"
84+
85+
CurveKeyLen = 32
86+
CurveDecodeLen = 35
87+
CurveNonceLen = 24

nats/contrib/exports.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
2+
from dataclasses import dataclass, field
3+
from typing import Dict, List, Literal, Optional
4+
5+
from nats.contrib.flatten_model import FlatteningModel
6+
from nats.contrib.types import Info
7+
8+
9+
@dataclass
10+
class ServiceLatency:
11+
sampling: int
12+
results: str
13+
14+
@dataclass
15+
class Export(FlatteningModel):
16+
name: str
17+
subject: str
18+
type: Literal["stream", "service"]
19+
token_req: Optional[bool] = None
20+
revocations: Optional[Dict[str, int]] = None
21+
response_type: Optional[Literal["Singleton", "Stream", "Chunked"]] = None
22+
response_threshold: Optional[int] = None
23+
service_latency: Optional[ServiceLatency] = None
24+
account_token_position: Optional[int] = None
25+
advertise: Optional[bool] = None
26+
allow_trace: Optional[bool] = None
27+
28+
info: Optional[Info] = field(default_factory=Info)
29+
30+
class Meta:
31+
unflatten_fields = [
32+
("service_latency", "service_latency")
33+
]
34+
35+
36+
Exports = List[Export]

0 commit comments

Comments
 (0)