-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathprovisioning.go
138 lines (114 loc) · 4.14 KB
/
provisioning.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// Copyright (c) 2021-2023, R.I. Pienaar and the Choria Project contributors
//
// SPDX-License-Identifier: Apache-2.0
package tokens
import (
"fmt"
"os"
"strings"
"time"
"github.com/golang-jwt/jwt/v4"
)
type ProvisioningClaims struct {
Token string `json:"cht"`
Secure bool `json:"chs"`
URLs string `json:"chu,omitempty"`
SRVDomain string `json:"chsrv,omitempty"`
ProvDefault bool `json:"chpd"`
ProvRegData string `json:"chrd,omitempty"`
ProvFacts string `json:"chf,omitempty"`
ProvNatsUser string `json:"chusr,omitempty"`
ProvNatsPass string `json:"chpwd,omitempty"`
Extensions MapClaims `json:"extensions"`
OrganizationUnit string `json:"ou,omitempty"`
ProtoV2 bool `json:"v2,omitempty"`
AllowUpdate bool `json:"update,omitempty"`
StandardClaims
}
// NewProvisioningClaims generates new ProvisioningClaims
func NewProvisioningClaims(secure bool, byDefault bool, token string, user string, password string, urls []string, srvDomain string, registrationDataFile string, factsDataFile string, org string, issuer string, validity time.Duration) (*ProvisioningClaims, error) {
if org == "" {
org = defaultOrg
}
if srvDomain == "" && len(urls) == 0 {
return nil, fmt.Errorf("srv domain or urls required")
}
stdClaims, err := newStandardClaims(issuer, ProvisioningPurpose, validity, true)
if err != nil {
return nil, err
}
return &ProvisioningClaims{
Secure: secure,
ProvDefault: byDefault,
Token: token,
ProvNatsUser: user,
ProvNatsPass: password,
URLs: strings.Join(urls, ","),
SRVDomain: srvDomain,
ProvRegData: registrationDataFile,
ProvFacts: factsDataFile,
StandardClaims: *stdClaims,
OrganizationUnit: org,
}, nil
}
// IsProvisioningToken determines if this is a provisioning token
func IsProvisioningToken(claims StandardClaims) bool {
if claims.Subject == string(ProvisioningPurpose) {
return true
}
return claims.Purpose == ProvisioningPurpose
}
// ParseProvisioningToken parses token and verifies it with pk
func ParseProvisioningToken(token string, pk any) (*ProvisioningClaims, error) {
claims := &ProvisioningClaims{}
err := ParseToken(token, claims, pk)
if err != nil {
return nil, fmt.Errorf("could not parse provisioner token: %s", err)
}
if !IsProvisioningToken(claims.StandardClaims) {
return nil, fmt.Errorf("not a provisioning token")
}
if claims.OrganizationUnit == "" {
claims.OrganizationUnit = defaultOrg
}
// if we have a tcs we require an issuer expiry to be set and it to not have expired
if !claims.verifyIssuerExpiry(claims.TrustChainSignature != "") {
return nil, jwt.ErrTokenExpired
}
return claims, nil
}
// ParseProvisioningTokenWithKeyfile parses token and verifies it with the RSA Public key in pkFile, does not support ed25519
func ParseProvisioningTokenWithKeyfile(token string, pkFile string) (*ProvisioningClaims, error) {
if pkFile == "" {
return nil, fmt.Errorf("invalid public key file")
}
certdat, err := os.ReadFile(pkFile)
if err != nil {
return nil, fmt.Errorf("could not read validation certificate: %s", err)
}
pk, err := readRSAOrED25519PublicData(certdat)
if err != nil {
return nil, err
}
return ParseProvisioningToken(token, pk)
}
// ParseProvisionTokenUnverified parses the provisioning token in an unverified manner.
//
// This is intended to be used for nodes to figure out their settings, they will go try them
// and if nothing's there no biggie. The broker and provisioner WILL validate this token so
// parsing it unverified there is about equivalent to just a configuration file, which is the
// intended purpose of this token and function.
func ParseProvisionTokenUnverified(token string) (*ProvisioningClaims, error) {
claims := &ProvisioningClaims{}
_, _, err := new(jwt.Parser).ParseUnverified(token, claims)
if err != nil {
return nil, err
}
if !IsProvisioningToken(claims.StandardClaims) {
return nil, fmt.Errorf("token is not a provisioning token")
}
if claims.OrganizationUnit == "" {
claims.OrganizationUnit = defaultOrg
}
return claims, nil
}