Skip to content

Commit cd2a632

Browse files
committed
jwt and claims
1 parent 8f53949 commit cd2a632

25 files changed

+1457
-0
lines changed

nats/contrib/__init__.py

Whitespace-only changes.

nats/contrib/accounts/__init__.py

Whitespace-only changes.

nats/contrib/accounts/limits.py

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

nats/contrib/accounts/models.py

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

nats/contrib/claims/__init__.py

Whitespace-only changes.

nats/contrib/claims/generic.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from typing import List, Optional
2+
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__(
13+
self,
14+
tags: Optional[List[str]] = None,
15+
type: Optional[Types] = None,
16+
version: Optional[int] = None,
17+
):
18+
self.tags = tags
19+
self.type = type
20+
self.version = version

nats/contrib/claims/models.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
from typing import List, Optional, Union
2+
3+
from nats.contrib.accounts.models import Account
4+
from nats.contrib.flatten_model import FlatteningModel
5+
from nats.contrib.operator.models import Operator
6+
from nats.contrib.types import Types
7+
from nats.contrib.users.models import User
8+
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[Union[str, bytes]]
23+
24+
def __init__(
25+
self,
26+
exp: Optional[int] = None,
27+
jti: Optional[str] = None,
28+
iat: Optional[int] = None,
29+
iss: Optional[str] = None,
30+
name: Optional[str] = None,
31+
nbf: Optional[int] = None,
32+
sub: Optional[str] = None,
33+
nats: Optional[Union[User, Account, Operator]] = None,
34+
issuer_account: Optional[Union[str, bytes]] = None,
35+
):
36+
self.exp: Optional[int] = exp
37+
self.jti: Optional[str] = jti
38+
self.iat: Optional[int] = iat
39+
self.iss: Optional[str] = iss
40+
self.name: Optional[str] = name
41+
self.nbf: Optional[int] = nbf
42+
self.sub: Optional[str] = sub
43+
44+
self.nats = nats
45+
self.issuer_account = issuer_account
46+
47+
class Meta:
48+
unflatten_fields = [('nats', 'nats')]
49+
50+
51+
class UserClaims(Claims):
52+
pass
53+
54+
55+
class AccountClaims(Claims):
56+
pass
57+
58+
59+
class OperatorClaims(Claims):
60+
pass

nats/contrib/constants.py

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

0 commit comments

Comments
 (0)