mirror of
https://github.com/pomerium/pomerium.git
synced 2025-06-22 04:28:25 +02:00
mcp: client registration/token fixes (#5649)
## Summary Fixes to MCP code registration and token requests. 1. ease some requirements on fields that are RECOMMENDED 2. fill in defaults 3. store both request and response in the client registration 4. check client secret in the /token request ## Related issues - Fixes https://linear.app/pomerium/issue/ENG-2462/mcp-ignore-unknown-grant-types-in-the-client-registration - Fixes https://linear.app/pomerium/issue/ENG-2461/mcp-support-client-secret-in-dynamic-client-registration ## User Explanation <!-- How would you explain this change to the user? If this change doesn't create any user-facing changes, you can leave this blank. If filled out, add the `docs` label --> ## Checklist - [x] reference any related issues - [x] updated unit tests - [x] add appropriate label (`enhancement`, `bug`, `breaking`, `dependencies`, `ci`) - [ ] ready for review
This commit is contained in:
parent
200f2e8164
commit
777b3b12d2
20 changed files with 1440 additions and 461 deletions
|
@ -74,8 +74,8 @@ func (srv *Handler) Authorize(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
if err := oauth21.ValidateAuthorizationRequest(client, v); err != nil {
|
||||
log.Ctx(ctx).Error().Err(err).Msg("failed to validate authorization request")
|
||||
if err := oauth21.ValidateAuthorizationRequest(client.ResponseMetadata, v); err != nil {
|
||||
log.Ctx(ctx).Error().Err(err).Msg("failed to validate authorization request for a client")
|
||||
ve := oauth21.Error{Code: oauth21.InvalidRequest}
|
||||
_ = errors.As(err, &ve)
|
||||
oauth21.ErrorResponse(w, http.StatusBadRequest, ve.Code)
|
||||
|
|
|
@ -119,7 +119,7 @@ func getAuthorizationServerMetadata(host, prefix string) AuthorizationServerMeta
|
|||
ResponseTypesSupported: []string{"code"},
|
||||
CodeChallengeMethodsSupported: []string{"S256"},
|
||||
TokenEndpoint: P(path.Join(prefix, tokenEndpoint)),
|
||||
TokenEndpointAuthMethodsSupported: []string{"none"},
|
||||
TokenEndpointAuthMethodsSupported: []string{"client_secret_basic", "none"},
|
||||
GrantTypesSupported: []string{"authorization_code", "refresh_token"},
|
||||
RevocationEndpoint: P(path.Join(prefix, revocationEndpoint)),
|
||||
RevocationEndpointAuthMethodsSupported: []string{"client_secret_post"},
|
||||
|
|
|
@ -2,15 +2,16 @@ package mcp
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/bufbuild/protovalidate-go"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
"github.com/pomerium/pomerium/internal/log"
|
||||
rfc7591v1 "github.com/pomerium/pomerium/internal/rfc7591"
|
||||
"github.com/pomerium/pomerium/pkg/cryptutil"
|
||||
)
|
||||
|
||||
const maxClientRegistrationPayload = 1024 * 1024 // 1MB
|
||||
|
@ -34,44 +35,24 @@ func (srv *Handler) RegisterClient(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
v := new(rfc7591v1.ClientMetadata)
|
||||
err = protojson.UnmarshalOptions{DiscardUnknown: true}.Unmarshal(data, v)
|
||||
clientRegistration, err := createClientRegistrationFromMetadata(data)
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Err(err).Msg("failed to unmarshal request body")
|
||||
http.Error(w, "failed to unmarshal request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
err = protovalidate.Validate(v)
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Err(err).Msg("failed to validate request body")
|
||||
log.Ctx(ctx).Error().
|
||||
Str("request", string(data)).
|
||||
Err(err).Msg("create client registration")
|
||||
clientRegistrationBadRequest(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
id, err := srv.storage.RegisterClient(ctx, v)
|
||||
id, err := srv.storage.RegisterClient(ctx, clientRegistration)
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Err(err).Msg("failed to register client")
|
||||
http.Error(w, "failed to register client", http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
resp := struct {
|
||||
*rfc7591v1.ClientMetadata
|
||||
ClientID string `json:"client_id"`
|
||||
ClientIDIssuedAt int64 `json:"client_id_issued_at"`
|
||||
}{
|
||||
ClientMetadata: v,
|
||||
ClientID: id,
|
||||
ClientIDIssuedAt: time.Now().Unix(),
|
||||
}
|
||||
data, err = json.Marshal(resp)
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Err(err).Msg("failed to marshal response")
|
||||
http.Error(w, "failed to marshal response", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, err = w.Write(data)
|
||||
err = rfc7591v1.WriteRegistrationResponse(w, id,
|
||||
clientRegistration.ClientSecret, clientRegistration.ResponseMetadata)
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Err(err).Msg("failed to write response")
|
||||
return
|
||||
|
@ -93,3 +74,34 @@ func clientRegistrationBadRequest(w http.ResponseWriter, err error) {
|
|||
w.WriteHeader(http.StatusBadRequest)
|
||||
_, _ = w.Write(data)
|
||||
}
|
||||
|
||||
func createClientRegistrationFromMetadata(
|
||||
requestMetadataText []byte,
|
||||
) (*rfc7591v1.ClientRegistration, error) {
|
||||
requestMetadata, err := rfc7591v1.ParseMetadata(requestMetadataText)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse: %w", err)
|
||||
}
|
||||
|
||||
err = requestMetadata.Validate()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("validate: %w", err)
|
||||
}
|
||||
|
||||
responseMetadata := proto.CloneOf(requestMetadata)
|
||||
responseMetadata.SetDefaults()
|
||||
|
||||
registration := &rfc7591v1.ClientRegistration{
|
||||
RequestMetadata: requestMetadata,
|
||||
ResponseMetadata: responseMetadata,
|
||||
}
|
||||
|
||||
if requestMetadata.GetTokenEndpointAuthMethod() != rfc7591v1.TokenEndpointAuthMethodNone {
|
||||
registration.ClientSecret = &rfc7591v1.ClientSecret{
|
||||
Value: cryptutil.NewRandomStringN(32),
|
||||
CreatedAt: timestamppb.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
return registration, nil
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package mcp
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
|
@ -12,6 +13,7 @@ import (
|
|||
"github.com/pomerium/pomerium/internal/log"
|
||||
"github.com/pomerium/pomerium/internal/oauth21"
|
||||
oauth21proto "github.com/pomerium/pomerium/internal/oauth21/gen"
|
||||
rfc7591v1 "github.com/pomerium/pomerium/internal/rfc7591"
|
||||
)
|
||||
|
||||
// Token handles the /token endpoint.
|
||||
|
@ -21,54 +23,65 @@ func (srv *Handler) Token(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
req, err := oauth21.ParseTokenRequest(r)
|
||||
ctx := r.Context()
|
||||
req, err := srv.getTokenRequest(r)
|
||||
if err != nil {
|
||||
log.Ctx(r.Context()).Error().Err(err).Msg("failed to parse token request")
|
||||
log.Ctx(ctx).Error().Err(err).Msg("get token request failed")
|
||||
oauth21.ErrorResponse(w, http.StatusBadRequest, oauth21.InvalidRequest)
|
||||
return
|
||||
}
|
||||
|
||||
switch req.GrantType {
|
||||
case "authorization_code":
|
||||
log.Ctx(ctx).Debug().Msg("handling authorization_code token request")
|
||||
srv.handleAuthorizationCodeToken(w, r, req)
|
||||
default:
|
||||
log.Ctx(ctx).Error().Msgf("unsupported grant type: %s", req.GrantType)
|
||||
oauth21.ErrorResponse(w, http.StatusBadRequest, oauth21.UnsupportedGrantType)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Handler) handleAuthorizationCodeToken(w http.ResponseWriter, r *http.Request, req *oauth21proto.TokenRequest) {
|
||||
func (srv *Handler) handleAuthorizationCodeToken(w http.ResponseWriter, r *http.Request, tokenReq *oauth21proto.TokenRequest) {
|
||||
ctx := r.Context()
|
||||
|
||||
if req.ClientId == nil {
|
||||
if tokenReq.ClientId == nil {
|
||||
log.Ctx(ctx).Error().Msg("missing client_id in token request")
|
||||
oauth21.ErrorResponse(w, http.StatusBadRequest, oauth21.InvalidClient)
|
||||
return
|
||||
}
|
||||
if req.Code == nil {
|
||||
if tokenReq.Code == nil {
|
||||
log.Ctx(ctx).Error().Msg("missing code in token request")
|
||||
oauth21.ErrorResponse(w, http.StatusBadRequest, oauth21.InvalidGrant)
|
||||
return
|
||||
}
|
||||
code, err := DecryptCode(CodeTypeAuthorization, *req.Code, srv.cipher, *req.ClientId, time.Now())
|
||||
code, err := DecryptCode(CodeTypeAuthorization, *tokenReq.Code, srv.cipher, *tokenReq.ClientId, time.Now())
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Err(err).Msg("failed to decrypt authorization code")
|
||||
oauth21.ErrorResponse(w, http.StatusBadRequest, oauth21.InvalidGrant)
|
||||
return
|
||||
}
|
||||
|
||||
authReq, err := srv.storage.GetAuthorizationRequest(ctx, code.Id)
|
||||
if status.Code(err) == codes.NotFound {
|
||||
log.Ctx(ctx).Error().Msg("authorization request not found")
|
||||
oauth21.ErrorResponse(w, http.StatusBadRequest, oauth21.InvalidGrant)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Err(err).Msg("failed to get authorization request and client")
|
||||
http.Error(w, "internal error", http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if *req.ClientId != authReq.ClientId {
|
||||
if *tokenReq.ClientId != authReq.ClientId {
|
||||
log.Ctx(ctx).Error().Msgf("client ID mismatch: %s != %s", *tokenReq.ClientId, authReq.ClientId)
|
||||
oauth21.ErrorResponse(w, http.StatusBadRequest, oauth21.InvalidGrant)
|
||||
return
|
||||
}
|
||||
|
||||
err = CheckPKCE(authReq.GetCodeChallengeMethod(), authReq.GetCodeChallenge(), req.GetCodeVerifier())
|
||||
err = CheckPKCE(authReq.GetCodeChallengeMethod(), authReq.GetCodeChallenge(), tokenReq.GetCodeVerifier())
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Err(err).Msg("failed to check PKCE")
|
||||
oauth21.ErrorResponse(w, http.StatusBadRequest, oauth21.InvalidGrant)
|
||||
return
|
||||
}
|
||||
|
@ -77,24 +90,28 @@ func (srv *Handler) handleAuthorizationCodeToken(w http.ResponseWriter, r *http.
|
|||
// https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-12#section-4.1.3
|
||||
err = srv.storage.DeleteAuthorizationRequest(ctx, code.Id)
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Err(err).Msg("failed to delete authorization request")
|
||||
http.Error(w, "internal error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
session, err := srv.storage.GetSession(ctx, authReq.SessionId)
|
||||
if status.Code(err) == codes.NotFound {
|
||||
log.Ctx(ctx).Error().Msg("session not found")
|
||||
oauth21.ErrorResponse(w, http.StatusBadRequest, oauth21.InvalidGrant)
|
||||
return
|
||||
}
|
||||
|
||||
accessToken, err := srv.GetAccessTokenForSession(session.Id, session.ExpiresAt.AsTime())
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Err(err).Msg("failed to get access token for session")
|
||||
http.Error(w, "internal error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
expiresIn := time.Until(session.ExpiresAt.AsTime())
|
||||
if expiresIn < 0 {
|
||||
log.Ctx(ctx).Error().Msg("session has already expired")
|
||||
oauth21.ErrorResponse(w, http.StatusBadRequest, oauth21.InvalidGrant)
|
||||
return
|
||||
}
|
||||
|
@ -107,6 +124,7 @@ func (srv *Handler) handleAuthorizationCodeToken(w http.ResponseWriter, r *http.
|
|||
|
||||
data, err := json.Marshal(resp) // not using protojson.Marshal here because it emits numbers as strings, which is valid, but for some reason Node.js / mcp typescript SDK doesn't like it
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Err(err).Msg("failed to marshal token response")
|
||||
http.Error(w, "internal error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
@ -116,3 +134,46 @@ func (srv *Handler) handleAuthorizationCodeToken(w http.ResponseWriter, r *http.
|
|||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write(data)
|
||||
}
|
||||
|
||||
func (srv *Handler) getTokenRequest(
|
||||
r *http.Request,
|
||||
) (*oauth21proto.TokenRequest, error) {
|
||||
tokenReq, err := oauth21.ParseTokenRequest(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse token request: %w", err)
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
clientReg, err := srv.storage.GetClient(ctx, tokenReq.GetClientId())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get client registration: %w", err)
|
||||
}
|
||||
|
||||
m := clientReg.ResponseMetadata.GetTokenEndpointAuthMethod()
|
||||
if m == rfc7591v1.TokenEndpointAuthMethodNone {
|
||||
return tokenReq, nil
|
||||
}
|
||||
|
||||
secret := clientReg.ClientSecret
|
||||
if secret == nil {
|
||||
return nil, fmt.Errorf("client registration does not have a client secret")
|
||||
}
|
||||
if expires := secret.ExpiresAt; expires != nil && expires.AsTime().Before(time.Now()) {
|
||||
return nil, fmt.Errorf("client registration client secret has expired")
|
||||
}
|
||||
|
||||
switch m {
|
||||
case rfc7591v1.TokenEndpointAuthMethodClientSecretBasic:
|
||||
case rfc7591v1.TokenEndpointAuthMethodClientSecretPost:
|
||||
if tokenReq.ClientSecret == nil {
|
||||
return nil, fmt.Errorf("client_secret was not provided")
|
||||
}
|
||||
if tokenReq.GetClientSecret() != secret.Value {
|
||||
return nil, fmt.Errorf("client secret mismatch")
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported token endpoint authentication method: %s", m)
|
||||
}
|
||||
|
||||
return tokenReq, nil
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ func NewStorage(
|
|||
|
||||
func (storage *Storage) RegisterClient(
|
||||
ctx context.Context,
|
||||
req *rfc7591v1.ClientMetadata,
|
||||
req *rfc7591v1.ClientRegistration,
|
||||
) (string, error) {
|
||||
data := protoutil.NewAny(req)
|
||||
id := uuid.NewString()
|
||||
|
@ -51,8 +51,8 @@ func (storage *Storage) RegisterClient(
|
|||
func (storage *Storage) GetClient(
|
||||
ctx context.Context,
|
||||
id string,
|
||||
) (*rfc7591v1.ClientMetadata, error) {
|
||||
v := new(rfc7591v1.ClientMetadata)
|
||||
) (*rfc7591v1.ClientRegistration, error) {
|
||||
v := new(rfc7591v1.ClientRegistration)
|
||||
rec, err := storage.client.Get(ctx, &databroker.GetRequest{
|
||||
Type: protoutil.GetTypeURL(v),
|
||||
Id: id,
|
||||
|
|
|
@ -62,7 +62,7 @@ func TestStorage(t *testing.T) {
|
|||
t.Run("client registration", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
id, err := storage.RegisterClient(ctx, &rfc7591v1.ClientMetadata{})
|
||||
id, err := storage.RegisterClient(ctx, &rfc7591v1.ClientRegistration{})
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, id)
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ func ParseCodeGrantAuthorizeRequest(r *http.Request) (*gen.AuthorizationRequest,
|
|||
RedirectUri: optionalFormParam(r, "redirect_uri"),
|
||||
ResponseType: r.Form.Get("response_type"),
|
||||
State: optionalFormParam(r, "state"),
|
||||
CodeChallenge: r.Form.Get("code_challenge"),
|
||||
CodeChallenge: optionalFormParam(r, "code_challenge"),
|
||||
CodeChallengeMethod: optionalFormParam(r, "code_challenge_method"),
|
||||
}
|
||||
|
||||
|
|
|
@ -2,5 +2,5 @@
|
|||
version: v2
|
||||
deps:
|
||||
- name: buf.build/bufbuild/protovalidate
|
||||
commit: 8976f5be98c146529b1cc15cd2012b60
|
||||
digest: b5:5d513af91a439d9e78cacac0c9455c7cb885a8737d30405d0b91974fe05276d19c07a876a51a107213a3d01b83ecc912996cdad4cddf7231f91379079cf7488d
|
||||
commit: b52ab10f44684cb19d1fbcad56aedd36
|
||||
digest: b5:5f464399f5ea7546eb3c6a4e822a306da538298f3d87e9f974f4523d4955de396eb2b8b52a2f3f06ac290764d80cecbaa0a4c96560558e43d9b1c722e61a9d5c
|
||||
|
|
|
@ -43,8 +43,9 @@ type AuthorizationRequest struct {
|
|||
State *string `protobuf:"bytes,4,opt,name=state,proto3,oneof" json:"state,omitempty"`
|
||||
// OPTIONAL. The scope of the access request as described by Section 1.4.1.
|
||||
Scopes []string `protobuf:"bytes,5,rep,name=scopes,proto3" json:"scopes,omitempty"`
|
||||
// REQUIRED, assumes https://www.rfc-editor.org/rfc/rfc7636.html#section-4.1
|
||||
CodeChallenge string `protobuf:"bytes,6,opt,name=code_challenge,json=codeChallenge,proto3" json:"code_challenge,omitempty"`
|
||||
// REQUIRED or RECOMMENDED, assumes https://www.rfc-editor.org/rfc/rfc7636.html#section-4.1
|
||||
// subject to whether the client is public or confidential.
|
||||
CodeChallenge *string `protobuf:"bytes,6,opt,name=code_challenge,json=codeChallenge,proto3,oneof" json:"code_challenge,omitempty"`
|
||||
// OPTIONAL, defaults to plain if not present in the request. Code verifier
|
||||
// transformation method is S256 or plain.
|
||||
CodeChallengeMethod *string `protobuf:"bytes,7,opt,name=code_challenge_method,json=codeChallengeMethod,proto3,oneof" json:"code_challenge_method,omitempty"`
|
||||
|
@ -124,8 +125,8 @@ func (x *AuthorizationRequest) GetScopes() []string {
|
|||
}
|
||||
|
||||
func (x *AuthorizationRequest) GetCodeChallenge() string {
|
||||
if x != nil {
|
||||
return x.CodeChallenge
|
||||
if x != nil && x.CodeChallenge != nil {
|
||||
return *x.CodeChallenge
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
@ -158,7 +159,7 @@ var file_authorization_request_proto_rawDesc = string([]byte{
|
|||
0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x6f,
|
||||
0x61, 0x75, 0x74, 0x68, 0x32, 0x31, 0x1a, 0x1b, 0x62, 0x75, 0x66, 0x2f, 0x76, 0x61, 0x6c, 0x69,
|
||||
0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x22, 0xcb, 0x03, 0x0a, 0x14, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a,
|
||||
0x6f, 0x74, 0x6f, 0x22, 0xe0, 0x03, 0x0a, 0x14, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x09,
|
||||
0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42,
|
||||
0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49,
|
||||
|
@ -171,33 +172,34 @@ var file_authorization_request_proto_rawDesc = string([]byte{
|
|||
0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52,
|
||||
0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x88, 0x01, 0x01, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x6f,
|
||||
0x70, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x73, 0x63, 0x6f, 0x70, 0x65,
|
||||
0x73, 0x12, 0x34, 0x0a, 0x0e, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65,
|
||||
0x6e, 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0d, 0xba, 0x48, 0x0a, 0xc8, 0x01,
|
||||
0x01, 0x72, 0x05, 0x10, 0x2b, 0x18, 0x80, 0x01, 0x52, 0x0d, 0x63, 0x6f, 0x64, 0x65, 0x43, 0x68,
|
||||
0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x12, 0x4b, 0x0a, 0x15, 0x63, 0x6f, 0x64, 0x65, 0x5f,
|
||||
0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64,
|
||||
0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x42, 0x12, 0xba, 0x48, 0x0f, 0x72, 0x0d, 0x52, 0x04, 0x53,
|
||||
0x32, 0x35, 0x36, 0x52, 0x05, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x48, 0x02, 0x52, 0x13, 0x63, 0x6f,
|
||||
0x64, 0x65, 0x43, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x4d, 0x65, 0x74, 0x68, 0x6f,
|
||||
0x64, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f,
|
||||
0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01,
|
||||
0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x07, 0x75,
|
||||
0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x42, 0x06, 0xba, 0x48,
|
||||
0x03, 0xc8, 0x01, 0x01, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x42, 0x0f, 0x0a, 0x0d,
|
||||
0x5f, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x72, 0x69, 0x42, 0x08, 0x0a,
|
||||
0x06, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x42, 0x18, 0x0a, 0x16, 0x5f, 0x63, 0x6f, 0x64, 0x65,
|
||||
0x5f, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f,
|
||||
0x64, 0x42, 0x97, 0x01, 0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x2e, 0x6f, 0x61, 0x75, 0x74, 0x68, 0x32,
|
||||
0x31, 0x42, 0x19, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x31,
|
||||
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6f, 0x6d, 0x65, 0x72,
|
||||
0x69, 0x75, 0x6d, 0x2f, 0x70, 0x6f, 0x6d, 0x65, 0x72, 0x69, 0x75, 0x6d, 0x2f, 0x69, 0x6e, 0x74,
|
||||
0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x6f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x31, 0x2f, 0x67, 0x65,
|
||||
0x6e, 0xa2, 0x02, 0x03, 0x4f, 0x58, 0x58, 0xaa, 0x02, 0x07, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x32,
|
||||
0x31, 0xca, 0x02, 0x07, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x31, 0xe2, 0x02, 0x13, 0x4f, 0x61,
|
||||
0x75, 0x74, 0x68, 0x32, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
|
||||
0x61, 0xea, 0x02, 0x07, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x33,
|
||||
0x73, 0x12, 0x36, 0x0a, 0x0e, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65,
|
||||
0x6e, 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0x72, 0x05,
|
||||
0x10, 0x2b, 0x18, 0x80, 0x01, 0x48, 0x02, 0x52, 0x0d, 0x63, 0x6f, 0x64, 0x65, 0x43, 0x68, 0x61,
|
||||
0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x88, 0x01, 0x01, 0x12, 0x4b, 0x0a, 0x15, 0x63, 0x6f, 0x64,
|
||||
0x65, 0x5f, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x68,
|
||||
0x6f, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x42, 0x12, 0xba, 0x48, 0x0f, 0x72, 0x0d, 0x52,
|
||||
0x04, 0x53, 0x32, 0x35, 0x36, 0x52, 0x05, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x48, 0x03, 0x52, 0x13,
|
||||
0x63, 0x6f, 0x64, 0x65, 0x43, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x4d, 0x65, 0x74,
|
||||
0x68, 0x6f, 0x64, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f,
|
||||
0x6e, 0x5f, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8,
|
||||
0x01, 0x01, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1f, 0x0a,
|
||||
0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x42, 0x06,
|
||||
0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x42, 0x0f,
|
||||
0x0a, 0x0d, 0x5f, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x72, 0x69, 0x42,
|
||||
0x08, 0x0a, 0x06, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x63, 0x6f,
|
||||
0x64, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x42, 0x18, 0x0a, 0x16,
|
||||
0x5f, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x5f,
|
||||
0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x42, 0x97, 0x01, 0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x2e, 0x6f,
|
||||
0x61, 0x75, 0x74, 0x68, 0x32, 0x31, 0x42, 0x19, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x50, 0x01, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
|
||||
0x70, 0x6f, 0x6d, 0x65, 0x72, 0x69, 0x75, 0x6d, 0x2f, 0x70, 0x6f, 0x6d, 0x65, 0x72, 0x69, 0x75,
|
||||
0x6d, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x6f, 0x61, 0x75, 0x74, 0x68,
|
||||
0x32, 0x31, 0x2f, 0x67, 0x65, 0x6e, 0xa2, 0x02, 0x03, 0x4f, 0x58, 0x58, 0xaa, 0x02, 0x07, 0x4f,
|
||||
0x61, 0x75, 0x74, 0x68, 0x32, 0x31, 0xca, 0x02, 0x07, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x31,
|
||||
0xe2, 0x02, 0x13, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65,
|
||||
0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x07, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x31,
|
||||
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
})
|
||||
|
||||
var (
|
||||
|
|
|
@ -34,9 +34,9 @@ message AuthorizationRequest {
|
|||
// OPTIONAL. The scope of the access request as described by Section 1.4.1.
|
||||
repeated string scopes = 5;
|
||||
|
||||
// REQUIRED, assumes https://www.rfc-editor.org/rfc/rfc7636.html#section-4.1
|
||||
string code_challenge = 6 [
|
||||
(buf.validate.field).required = true,
|
||||
// REQUIRED or RECOMMENDED, assumes https://www.rfc-editor.org/rfc/rfc7636.html#section-4.1
|
||||
// subject to whether the client is public or confidential.
|
||||
optional string code_challenge = 6 [
|
||||
(buf.validate.field).string = {min_len: 43, max_len: 128}
|
||||
];
|
||||
|
||||
|
|
|
@ -15,6 +15,9 @@ func ParseTokenRequest(r *http.Request) (*gen.TokenRequest, error) {
|
|||
return nil, fmt.Errorf("failed to parse form: %w", err)
|
||||
}
|
||||
|
||||
// extract client credentials from HTTP Basic Authorization header, if present
|
||||
basicID, basicSecret, basicOK := r.BasicAuth()
|
||||
|
||||
v := &gen.TokenRequest{
|
||||
GrantType: r.Form.Get("grant_type"),
|
||||
Code: optionalFormParam(r, "code"),
|
||||
|
@ -25,6 +28,15 @@ func ParseTokenRequest(r *http.Request) (*gen.TokenRequest, error) {
|
|||
ClientSecret: optionalFormParam(r, "client_secret"),
|
||||
}
|
||||
|
||||
if basicOK {
|
||||
if v.ClientId == nil && basicID != "" {
|
||||
v.ClientId = &basicID
|
||||
}
|
||||
if v.ClientSecret == nil && basicSecret != "" {
|
||||
v.ClientSecret = &basicSecret
|
||||
}
|
||||
}
|
||||
|
||||
err = protovalidate.Validate(v)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to validate token request: %w", err)
|
||||
|
|
48
internal/oauth21/token_test.go
Normal file
48
internal/oauth21/token_test.go
Normal file
|
@ -0,0 +1,48 @@
|
|||
package oauth21_test
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/pomerium/pomerium/internal/oauth21"
|
||||
)
|
||||
|
||||
func TestParseTokenRequest_BasicAuth(t *testing.T) {
|
||||
form := url.Values{}
|
||||
form.Set("grant_type", "authorization_code")
|
||||
form.Set("code", "abc")
|
||||
req, err := http.NewRequest(http.MethodPost, "/token", strings.NewReader(form.Encode()))
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.SetBasicAuth("myclient", "secret")
|
||||
|
||||
tr, err := oauth21.ParseTokenRequest(req)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, tr.ClientId)
|
||||
require.Equal(t, "myclient", *tr.ClientId)
|
||||
require.NotNil(t, tr.ClientSecret)
|
||||
require.Equal(t, "secret", *tr.ClientSecret)
|
||||
}
|
||||
|
||||
func TestParseTokenRequest_BasicAuthWithBodyOverride(t *testing.T) {
|
||||
form := url.Values{}
|
||||
form.Set("grant_type", "authorization_code")
|
||||
form.Set("code", "abc")
|
||||
form.Set("client_id", "bodyid")
|
||||
form.Set("client_secret", "bodysecret")
|
||||
req, err := http.NewRequest(http.MethodPost, "/token", strings.NewReader(form.Encode()))
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.SetBasicAuth("basicid", "basicsecret")
|
||||
|
||||
tr, err := oauth21.ParseTokenRequest(req)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, tr.ClientId)
|
||||
require.Equal(t, "bodyid", *tr.ClientId) // body should win
|
||||
require.NotNil(t, tr.ClientSecret)
|
||||
require.Equal(t, "bodysecret", *tr.ClientSecret)
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package oauth21
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"github.com/pomerium/pomerium/internal/oauth21/gen"
|
||||
|
@ -8,17 +9,45 @@ import (
|
|||
)
|
||||
|
||||
func ValidateAuthorizationRequest(
|
||||
client *rfc7591v1.ClientMetadata,
|
||||
client *rfc7591v1.Metadata,
|
||||
req *gen.AuthorizationRequest,
|
||||
) error {
|
||||
if err := ValidateAuthorizationRequestRedirectURI(client, req.RedirectUri); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ValidateAuthorizationRequestCodeChallenge(client, req); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ValidateAuthorizationRequestCodeChallenge(
|
||||
client *rfc7591v1.Metadata,
|
||||
req *gen.AuthorizationRequest,
|
||||
) error {
|
||||
m := client.GetTokenEndpointAuthMethod()
|
||||
switch m {
|
||||
case rfc7591v1.TokenEndpointAuthMethodNone:
|
||||
if req.GetCodeChallenge() == "" {
|
||||
return Error{
|
||||
Code: InvalidRequest,
|
||||
Description: "code challenge are required when token endpoint auth method is 'none'",
|
||||
}
|
||||
}
|
||||
case rfc7591v1.TokenEndpointAuthMethodClientSecretBasic,
|
||||
rfc7591v1.TokenEndpointAuthMethodClientSecretPost:
|
||||
// code challenge is recommended but not required for these methods
|
||||
default:
|
||||
return Error{
|
||||
Code: InvalidRequest,
|
||||
Description: fmt.Sprintf("unsupported token endpoint auth method: %s", m),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ValidateAuthorizationRequestRedirectURI(
|
||||
client *rfc7591v1.ClientMetadata,
|
||||
client *rfc7591v1.Metadata,
|
||||
redirectURI *string,
|
||||
) error {
|
||||
if len(client.RedirectUris) == 0 {
|
||||
|
|
|
@ -13,17 +13,67 @@ import (
|
|||
|
||||
func TestValidateRequest(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
clientBasic := rfc7591v1.TokenEndpointAuthMethodClientSecretBasic
|
||||
clientNone := rfc7591v1.TokenEndpointAuthMethodNone
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
client *rfc7591v1.ClientMetadata
|
||||
client *rfc7591v1.Metadata
|
||||
req *gen.AuthorizationRequest
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
"optional redirect_uri, multiple redirect_uris",
|
||||
&rfc7591v1.ClientMetadata{
|
||||
"default token auth method, no code challenge",
|
||||
&rfc7591v1.Metadata{
|
||||
RedirectUris: []string{"https://example.com/callback", "https://example.com/other-callback"},
|
||||
},
|
||||
&gen.AuthorizationRequest{
|
||||
RedirectUri: proto.String("https://example.com/callback"),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"none token auth method, no code challenge",
|
||||
&rfc7591v1.Metadata{
|
||||
RedirectUris: []string{"https://example.com/callback", "https://example.com/other-callback"},
|
||||
TokenEndpointAuthMethod: &clientNone,
|
||||
},
|
||||
&gen.AuthorizationRequest{
|
||||
RedirectUri: proto.String("https://example.com/callback"),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"none token auth method, code challenge is provided",
|
||||
&rfc7591v1.Metadata{
|
||||
RedirectUris: []string{"https://example.com/callback", "https://example.com/other-callback"},
|
||||
TokenEndpointAuthMethod: &clientNone,
|
||||
},
|
||||
&gen.AuthorizationRequest{
|
||||
RedirectUri: proto.String("https://example.com/callback"),
|
||||
CodeChallenge: proto.String("challenge"),
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"none token auth method, code challenge and method are provided",
|
||||
&rfc7591v1.Metadata{
|
||||
RedirectUris: []string{"https://example.com/callback", "https://example.com/other-callback"},
|
||||
TokenEndpointAuthMethod: &clientNone,
|
||||
},
|
||||
&gen.AuthorizationRequest{
|
||||
RedirectUri: proto.String("https://example.com/callback"),
|
||||
CodeChallenge: proto.String("challenge"),
|
||||
CodeChallengeMethod: proto.String("S256"),
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"optional redirect_uri, multiple redirect_uris",
|
||||
&rfc7591v1.Metadata{
|
||||
RedirectUris: []string{"https://example.com/callback", "https://example.com/other-callback"},
|
||||
TokenEndpointAuthMethod: &clientBasic,
|
||||
},
|
||||
&gen.AuthorizationRequest{
|
||||
RedirectUri: nil,
|
||||
},
|
||||
|
@ -31,8 +81,9 @@ func TestValidateRequest(t *testing.T) {
|
|||
},
|
||||
{
|
||||
"optional redirect_uri, single redirect_uri",
|
||||
&rfc7591v1.ClientMetadata{
|
||||
&rfc7591v1.Metadata{
|
||||
RedirectUris: []string{"https://example.com/callback"},
|
||||
TokenEndpointAuthMethod: &clientBasic,
|
||||
},
|
||||
&gen.AuthorizationRequest{
|
||||
RedirectUri: nil,
|
||||
|
@ -41,8 +92,9 @@ func TestValidateRequest(t *testing.T) {
|
|||
},
|
||||
{
|
||||
"matching redirect_uri",
|
||||
&rfc7591v1.ClientMetadata{
|
||||
&rfc7591v1.Metadata{
|
||||
RedirectUris: []string{"https://example.com/callback", "https://example.com/other-callback"},
|
||||
TokenEndpointAuthMethod: &clientBasic,
|
||||
},
|
||||
&gen.AuthorizationRequest{
|
||||
RedirectUri: proto.String("https://example.com/callback"),
|
||||
|
@ -51,8 +103,9 @@ func TestValidateRequest(t *testing.T) {
|
|||
},
|
||||
{
|
||||
"non-matching redirect_uri",
|
||||
&rfc7591v1.ClientMetadata{
|
||||
&rfc7591v1.Metadata{
|
||||
RedirectUris: []string{"https://example.com/callback", "https://example.com/other-callback"},
|
||||
TokenEndpointAuthMethod: &clientBasic,
|
||||
},
|
||||
&gen.AuthorizationRequest{
|
||||
RedirectUri: proto.String("https://example.com/invalid-callback"),
|
||||
|
|
|
@ -2,5 +2,5 @@
|
|||
version: v2
|
||||
deps:
|
||||
- name: buf.build/bufbuild/protovalidate
|
||||
commit: 7712fb530c574b95bc1d57c0877543c3
|
||||
digest: b5:b3e9c9428384357e3b73e4d5a4614328b0a4b1595b10163bbe9483fa16204749274c41797bd49b0d716479c855aa35c1172a94f471fa120ba8369637fd138829
|
||||
commit: b52ab10f44684cb19d1fbcad56aedd36
|
||||
digest: b5:5f464399f5ea7546eb3c6a4e822a306da538298f3d87e9f974f4523d4955de396eb2b8b52a2f3f06ac290764d80cecbaa0a4c96560558e43d9b1c722e61a9d5c
|
||||
|
|
103
internal/rfc7591/format.go
Normal file
103
internal/rfc7591/format.go
Normal file
|
@ -0,0 +1,103 @@
|
|||
package rfc7591v1
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/bufbuild/protovalidate-go"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
TokenEndpointAuthMethodNone = "none"
|
||||
TokenEndpointAuthMethodClientSecretBasic = "client_secret_basic"
|
||||
TokenEndpointAuthMethodClientSecretPost = "client_secret_post"
|
||||
|
||||
GrantTypesAuthorizationCode = "authorization_code"
|
||||
GrantTypesImplicit = "implicit"
|
||||
GrantTypesPassword = "password"
|
||||
GrantTypesClientCredentials = "client_credentials"
|
||||
GrantTypesRefreshToken = "refresh_token"
|
||||
GrantTypesJWTBearer = "urn:ietf:params:oauth:grant-type:jwt-bearer" //nolint:gosec
|
||||
GrantTypesSAML2Bearer = "urn:ietf:params:oauth:grant-type:saml2-bearer" //nolint:gosec
|
||||
GrantTypesDeviceCode = "urn:ietf:params:oauth:grant-type:device_code" //nolint:gosec
|
||||
|
||||
ResponseTypesCode = "code"
|
||||
ResponseTypeToken = "token"
|
||||
)
|
||||
|
||||
func (v *Metadata) SetDefaults() {
|
||||
if v.TokenEndpointAuthMethod == nil {
|
||||
v.TokenEndpointAuthMethod = proto.String(TokenEndpointAuthMethodClientSecretBasic)
|
||||
}
|
||||
|
||||
if len(v.GrantTypes) == 0 {
|
||||
v.GrantTypes = []string{GrantTypesAuthorizationCode}
|
||||
}
|
||||
|
||||
if len(v.ResponseTypes) == 0 {
|
||||
v.ResponseTypes = []string{ResponseTypesCode}
|
||||
}
|
||||
}
|
||||
|
||||
func (v *Metadata) Validate() error {
|
||||
return protovalidate.Validate(v)
|
||||
}
|
||||
|
||||
func ParseMetadata(
|
||||
data []byte,
|
||||
) (*Metadata, error) {
|
||||
v := new(Metadata)
|
||||
err := protojson.UnmarshalOptions{
|
||||
AllowPartial: false,
|
||||
DiscardUnknown: true,
|
||||
}.Unmarshal(data, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func WriteRegistrationResponse(
|
||||
w io.Writer,
|
||||
clientID string,
|
||||
clientSecret *ClientSecret,
|
||||
metadata *Metadata,
|
||||
) error {
|
||||
var metadataJSON map[string]any
|
||||
if metadata == nil {
|
||||
return fmt.Errorf("metadata cannot be nil")
|
||||
}
|
||||
metadataBytes, err := protojson.MarshalOptions{
|
||||
UseProtoNames: true,
|
||||
EmitUnpopulated: false,
|
||||
}.Marshal(metadata)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := json.Unmarshal(metadataBytes, &metadataJSON); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
metadataJSON["client_id"] = clientID
|
||||
|
||||
if clientSecret != nil {
|
||||
metadataJSON["client_secret"] = clientSecret.Value
|
||||
if clientSecret.CreatedAt != nil {
|
||||
metadataJSON["client_id_issued_at"] = clientSecret.CreatedAt.Seconds
|
||||
}
|
||||
|
||||
// Per RFC 7591: client_secret_expires_at is REQUIRED if client_secret is issued
|
||||
// Value should be 0 if the secret doesn't expire
|
||||
if clientSecret.ExpiresAt != nil {
|
||||
metadataJSON["client_secret_expires_at"] = clientSecret.ExpiresAt.Seconds
|
||||
} else {
|
||||
metadataJSON["client_secret_expires_at"] = int64(0)
|
||||
}
|
||||
}
|
||||
|
||||
return json.NewEncoder(w).Encode(metadataJSON)
|
||||
}
|
540
internal/rfc7591/format_test.go
Normal file
540
internal/rfc7591/format_test.go
Normal file
|
@ -0,0 +1,540 @@
|
|||
package rfc7591v1
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/testing/protocmp"
|
||||
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
func TestParseMetadata(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want *Metadata
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "minimal valid metadata",
|
||||
input: `{
|
||||
"redirect_uris": ["https://example.com/callback"]
|
||||
}`,
|
||||
want: &Metadata{
|
||||
RedirectUris: []string{"https://example.com/callback"},
|
||||
TokenEndpointAuthMethod: proto.String(TokenEndpointAuthMethodClientSecretBasic),
|
||||
GrantTypes: []string{GrantTypesAuthorizationCode},
|
||||
ResponseTypes: []string{ResponseTypesCode},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "full metadata with all fields",
|
||||
input: `{
|
||||
"redirect_uris": ["https://example.com/callback", "https://example.com/callback2"],
|
||||
"token_endpoint_auth_method": "client_secret_post",
|
||||
"grant_types": ["authorization_code", "refresh_token"],
|
||||
"response_types": ["code"],
|
||||
"client_name": "Test Client",
|
||||
"client_name_localized": {"en": "Test Client", "es": "Cliente de Prueba"},
|
||||
"client_uri": "https://example.com",
|
||||
"client_uri_localized": {"en": "https://example.com/en"},
|
||||
"logo_uri": "https://example.com/logo.png",
|
||||
"scope": "openid profile email",
|
||||
"contacts": ["admin@example.com", "support@example.com"],
|
||||
"tos_uri": "https://example.com/tos",
|
||||
"policy_uri": "https://example.com/privacy",
|
||||
"jwks_uri": "https://example.com/.well-known/jwks.json",
|
||||
"software_id": "test-client-v1",
|
||||
"software_version": "1.0.0"
|
||||
}`,
|
||||
want: &Metadata{
|
||||
RedirectUris: []string{"https://example.com/callback", "https://example.com/callback2"},
|
||||
TokenEndpointAuthMethod: proto.String("client_secret_post"),
|
||||
GrantTypes: []string{"authorization_code", "refresh_token"},
|
||||
ResponseTypes: []string{"code"},
|
||||
ClientName: proto.String("Test Client"),
|
||||
ClientNameLocalized: map[string]string{
|
||||
"en": "Test Client",
|
||||
"es": "Cliente de Prueba",
|
||||
},
|
||||
ClientUri: proto.String("https://example.com"),
|
||||
ClientUriLocalized: map[string]string{
|
||||
"en": "https://example.com/en",
|
||||
},
|
||||
LogoUri: proto.String("https://example.com/logo.png"),
|
||||
Scope: proto.String("openid profile email"),
|
||||
Contacts: []string{"admin@example.com", "support@example.com"},
|
||||
TosUri: proto.String("https://example.com/tos"),
|
||||
PolicyUri: proto.String("https://example.com/privacy"),
|
||||
JwksUri: proto.String("https://example.com/.well-known/jwks.json"),
|
||||
SoftwareId: proto.String("test-client-v1"),
|
||||
SoftwareVersion: proto.String("1.0.0"),
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "metadata with jwks instead of jwks_uri",
|
||||
input: `{
|
||||
"redirect_uris": ["https://example.com/callback"],
|
||||
"jwks": {
|
||||
"keys": [{
|
||||
"kty": "RSA",
|
||||
"rsa_params": {
|
||||
"n": "example-modulus",
|
||||
"e": "AQAB"
|
||||
}
|
||||
}]
|
||||
}
|
||||
}`,
|
||||
want: &Metadata{
|
||||
RedirectUris: []string{"https://example.com/callback"},
|
||||
TokenEndpointAuthMethod: proto.String(TokenEndpointAuthMethodClientSecretBasic),
|
||||
GrantTypes: []string{GrantTypesAuthorizationCode},
|
||||
ResponseTypes: []string{ResponseTypesCode},
|
||||
Jwks: &JsonWebKeySet{
|
||||
Keys: []*JsonWebKey{{
|
||||
Kty: "RSA",
|
||||
KeyTypeParameters: &JsonWebKey_RsaParams{
|
||||
RsaParams: &RsaKeyParameters{
|
||||
N: "example-modulus",
|
||||
E: "AQAB",
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "explicit token_endpoint_auth_method none",
|
||||
input: `{
|
||||
"redirect_uris": ["https://example.com/callback"],
|
||||
"token_endpoint_auth_method": "none"
|
||||
}`,
|
||||
want: &Metadata{
|
||||
RedirectUris: []string{"https://example.com/callback"},
|
||||
TokenEndpointAuthMethod: proto.String("none"),
|
||||
GrantTypes: []string{GrantTypesAuthorizationCode},
|
||||
ResponseTypes: []string{ResponseTypesCode},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "custom grant and response types",
|
||||
input: `{
|
||||
"redirect_uris": ["https://example.com/callback"],
|
||||
"grant_types": ["implicit", "client_credentials"],
|
||||
"response_types": ["token", "code"]
|
||||
}`,
|
||||
want: &Metadata{
|
||||
RedirectUris: []string{"https://example.com/callback"},
|
||||
TokenEndpointAuthMethod: proto.String(TokenEndpointAuthMethodClientSecretBasic),
|
||||
GrantTypes: []string{"implicit", "client_credentials"},
|
||||
ResponseTypes: []string{"token", "code"},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "empty input",
|
||||
input: "",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid JSON",
|
||||
input: `{"redirect_uris": [}`,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "missing required redirect_uris",
|
||||
input: `{
|
||||
"client_name": "Test Client"
|
||||
}`,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "empty redirect_uris array",
|
||||
input: `{
|
||||
"redirect_uris": []
|
||||
}`,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid redirect_uri",
|
||||
input: `{
|
||||
"redirect_uris": ["not-a-uri"]
|
||||
}`,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid token_endpoint_auth_method",
|
||||
input: `{
|
||||
"redirect_uris": ["https://example.com/callback"],
|
||||
"token_endpoint_auth_method": "invalid_method"
|
||||
}`,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid email in contacts",
|
||||
input: `{
|
||||
"redirect_uris": ["https://example.com/callback"],
|
||||
"contacts": ["not-an-email"]
|
||||
}`,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid scope format",
|
||||
input: `{
|
||||
"redirect_uris": ["https://example.com/callback"],
|
||||
"scope": " invalid spaces "
|
||||
}`,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "both jwks and jwks_uri provided",
|
||||
input: `{
|
||||
"redirect_uris": ["https://example.com/callback"],
|
||||
"jwks_uri": "https://example.com/.well-known/jwks.json",
|
||||
"jwks": {
|
||||
"keys": [{
|
||||
"kty": "RSA",
|
||||
"rsa_params": {
|
||||
"n": "example-modulus",
|
||||
"e": "AQAB"
|
||||
}
|
||||
}]
|
||||
}
|
||||
}`,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "client_name too long",
|
||||
input: `{
|
||||
"redirect_uris": ["https://example.com/callback"],
|
||||
"client_name": "` + strings.Repeat("a", 256) + `"
|
||||
}`,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "discard unknown fields",
|
||||
input: `{
|
||||
"redirect_uris": ["https://example.com/callback"],
|
||||
"unknown_field": "should be ignored",
|
||||
"another_unknown": 123
|
||||
}`,
|
||||
want: &Metadata{
|
||||
RedirectUris: []string{"https://example.com/callback"},
|
||||
TokenEndpointAuthMethod: proto.String(TokenEndpointAuthMethodClientSecretBasic),
|
||||
GrantTypes: []string{GrantTypesAuthorizationCode},
|
||||
ResponseTypes: []string{ResponseTypesCode},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "invalid BCP 47 language tag - segment too long",
|
||||
input: `{
|
||||
"redirect_uris": ["https://example.com/callback"],
|
||||
"client_name_localized": {"toolongtagsegment": "Test Client"}
|
||||
}`,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := ParseMetadata([]byte(tt.input))
|
||||
if err == nil && got != nil {
|
||||
got.SetDefaults()
|
||||
err = got.Validate()
|
||||
}
|
||||
|
||||
if tt.wantErr {
|
||||
if err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.want, got, protocmp.Transform()); diff != "" {
|
||||
t.Errorf("ParseMetadata() mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteRegistrationResponse(t *testing.T) {
|
||||
// Test timestamp for consistent testing
|
||||
testTime := time.Date(2023, 1, 1, 12, 0, 0, 0, time.UTC)
|
||||
testTimestamp := timestamppb.New(testTime)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
clientID string
|
||||
clientSecret *ClientSecret
|
||||
metadata *Metadata
|
||||
want map[string]any
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "minimal response without client secret",
|
||||
clientID: "test-client-123",
|
||||
metadata: &Metadata{
|
||||
RedirectUris: []string{"https://example.com/callback"},
|
||||
TokenEndpointAuthMethod: proto.String(TokenEndpointAuthMethodNone),
|
||||
GrantTypes: []string{GrantTypesAuthorizationCode},
|
||||
ResponseTypes: []string{ResponseTypesCode},
|
||||
},
|
||||
want: map[string]any{
|
||||
"client_id": "test-client-123",
|
||||
"redirect_uris": []any{"https://example.com/callback"},
|
||||
"token_endpoint_auth_method": TokenEndpointAuthMethodNone,
|
||||
"grant_types": []any{GrantTypesAuthorizationCode},
|
||||
"response_types": []any{ResponseTypesCode},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "response with client secret and timestamps",
|
||||
clientID: "test-client-456",
|
||||
clientSecret: &ClientSecret{
|
||||
Value: "super-secret-value",
|
||||
CreatedAt: testTimestamp,
|
||||
ExpiresAt: timestamppb.New(testTime.Add(24 * time.Hour)),
|
||||
},
|
||||
metadata: &Metadata{
|
||||
RedirectUris: []string{"https://example.com/callback"},
|
||||
TokenEndpointAuthMethod: proto.String(TokenEndpointAuthMethodClientSecretPost),
|
||||
GrantTypes: []string{GrantTypesAuthorizationCode, GrantTypesRefreshToken},
|
||||
ResponseTypes: []string{ResponseTypesCode},
|
||||
ClientName: proto.String("Test Client"),
|
||||
Scope: proto.String("openid profile email"),
|
||||
},
|
||||
want: map[string]any{
|
||||
"client_id": "test-client-456",
|
||||
"client_secret": "super-secret-value",
|
||||
"client_id_issued_at": float64(testTime.Unix()),
|
||||
"client_secret_expires_at": float64(testTime.Add(24 * time.Hour).Unix()),
|
||||
"redirect_uris": []any{"https://example.com/callback"},
|
||||
"token_endpoint_auth_method": TokenEndpointAuthMethodClientSecretPost,
|
||||
"grant_types": []any{GrantTypesAuthorizationCode, GrantTypesRefreshToken},
|
||||
"response_types": []any{ResponseTypesCode},
|
||||
"client_name": "Test Client",
|
||||
"scope": "openid profile email",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "response with client secret but no timestamps",
|
||||
clientID: "test-client-789",
|
||||
clientSecret: &ClientSecret{
|
||||
Value: "another-secret",
|
||||
// CreatedAt and ExpiresAt are nil
|
||||
},
|
||||
metadata: &Metadata{
|
||||
RedirectUris: []string{"https://example.com/callback"},
|
||||
TokenEndpointAuthMethod: proto.String(TokenEndpointAuthMethodClientSecretBasic),
|
||||
GrantTypes: []string{GrantTypesClientCredentials},
|
||||
ResponseTypes: []string{ResponseTypesCode},
|
||||
},
|
||||
want: map[string]any{
|
||||
"client_id": "test-client-789",
|
||||
"client_secret": "another-secret",
|
||||
"client_secret_expires_at": float64(0), // Required per RFC when client_secret is present
|
||||
"redirect_uris": []any{"https://example.com/callback"},
|
||||
"token_endpoint_auth_method": TokenEndpointAuthMethodClientSecretBasic,
|
||||
"grant_types": []any{GrantTypesClientCredentials},
|
||||
"response_types": []any{ResponseTypesCode},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "response with full metadata",
|
||||
clientID: "full-client-id",
|
||||
clientSecret: &ClientSecret{
|
||||
Value: "full-secret",
|
||||
CreatedAt: testTimestamp,
|
||||
},
|
||||
metadata: &Metadata{
|
||||
RedirectUris: []string{"https://example.com/cb1", "https://example.com/cb2"},
|
||||
TokenEndpointAuthMethod: proto.String(TokenEndpointAuthMethodClientSecretPost),
|
||||
GrantTypes: []string{GrantTypesAuthorizationCode, GrantTypesImplicit},
|
||||
ResponseTypes: []string{ResponseTypesCode, ResponseTypeToken},
|
||||
ClientName: proto.String("Full Test Client"),
|
||||
ClientNameLocalized: map[string]string{
|
||||
"en": "Full Test Client",
|
||||
"es": "Cliente de Prueba Completo",
|
||||
},
|
||||
ClientUri: proto.String("https://example.com"),
|
||||
ClientUriLocalized: map[string]string{
|
||||
"en": "https://example.com/en",
|
||||
},
|
||||
LogoUri: proto.String("https://example.com/logo.png"),
|
||||
Scope: proto.String("openid profile email offline_access"),
|
||||
Contacts: []string{"admin@example.com", "support@example.com"},
|
||||
TosUri: proto.String("https://example.com/tos"),
|
||||
PolicyUri: proto.String("https://example.com/privacy"),
|
||||
JwksUri: proto.String("https://example.com/.well-known/jwks.json"),
|
||||
SoftwareId: proto.String("test-software-v1"),
|
||||
SoftwareVersion: proto.String("1.2.3"),
|
||||
},
|
||||
want: map[string]any{
|
||||
"client_id": "full-client-id",
|
||||
"client_secret": "full-secret",
|
||||
"client_id_issued_at": float64(testTime.Unix()),
|
||||
"client_secret_expires_at": float64(0), // Required per RFC, 0 means no expiration
|
||||
"redirect_uris": []any{"https://example.com/cb1", "https://example.com/cb2"},
|
||||
"token_endpoint_auth_method": TokenEndpointAuthMethodClientSecretPost,
|
||||
"grant_types": []any{GrantTypesAuthorizationCode, GrantTypesImplicit},
|
||||
"response_types": []any{ResponseTypesCode, ResponseTypeToken},
|
||||
"client_name": "Full Test Client",
|
||||
"client_name_localized": map[string]any{
|
||||
"en": "Full Test Client",
|
||||
"es": "Cliente de Prueba Completo",
|
||||
},
|
||||
"client_uri": "https://example.com",
|
||||
"client_uri_localized": map[string]any{
|
||||
"en": "https://example.com/en",
|
||||
},
|
||||
"logo_uri": "https://example.com/logo.png",
|
||||
"scope": "openid profile email offline_access",
|
||||
"contacts": []any{"admin@example.com", "support@example.com"},
|
||||
"tos_uri": "https://example.com/tos",
|
||||
"policy_uri": "https://example.com/privacy",
|
||||
"jwks_uri": "https://example.com/.well-known/jwks.json",
|
||||
"software_id": "test-software-v1",
|
||||
"software_version": "1.2.3",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "response with jwks instead of jwks_uri",
|
||||
clientID: "jwks-client",
|
||||
metadata: &Metadata{
|
||||
RedirectUris: []string{"https://example.com/callback"},
|
||||
TokenEndpointAuthMethod: proto.String(TokenEndpointAuthMethodNone),
|
||||
GrantTypes: []string{GrantTypesAuthorizationCode},
|
||||
ResponseTypes: []string{ResponseTypesCode},
|
||||
Jwks: &JsonWebKeySet{
|
||||
Keys: []*JsonWebKey{{
|
||||
Kty: "RSA",
|
||||
KeyTypeParameters: &JsonWebKey_RsaParams{
|
||||
RsaParams: &RsaKeyParameters{
|
||||
N: "example-modulus",
|
||||
E: "AQAB",
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
want: map[string]any{
|
||||
"client_id": "jwks-client",
|
||||
"redirect_uris": []any{"https://example.com/callback"},
|
||||
"token_endpoint_auth_method": TokenEndpointAuthMethodNone,
|
||||
"grant_types": []any{GrantTypesAuthorizationCode},
|
||||
"response_types": []any{ResponseTypesCode},
|
||||
"jwks": map[string]any{
|
||||
"keys": []any{
|
||||
map[string]any{
|
||||
"kty": "RSA",
|
||||
"rsa_params": map[string]any{
|
||||
"n": "example-modulus",
|
||||
"e": "AQAB",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
err := WriteRegistrationResponse(&buf, tt.clientID, tt.clientSecret, tt.metadata)
|
||||
|
||||
if tt.wantErr {
|
||||
if err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// Parse the JSON output
|
||||
var got map[string]any
|
||||
if err := json.Unmarshal(buf.Bytes(), &got); err != nil {
|
||||
t.Fatalf("failed to parse JSON output: %v", err)
|
||||
}
|
||||
|
||||
// Compare the result
|
||||
if diff := cmp.Diff(tt.want, got); diff != "" {
|
||||
t.Errorf("WriteRegistrationResponse() mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
// Verify that the output is valid JSON
|
||||
if !json.Valid(buf.Bytes()) {
|
||||
t.Error("output is not valid JSON")
|
||||
}
|
||||
|
||||
// Verify that client_secret is only present when expected
|
||||
_, hasClientSecret := got["client_secret"]
|
||||
expectedHasSecret := tt.clientSecret != nil
|
||||
if hasClientSecret != expectedHasSecret {
|
||||
t.Errorf("client_secret presence mismatch: got %v, want %v", hasClientSecret, expectedHasSecret)
|
||||
}
|
||||
|
||||
// Verify timestamp fields presence per RFC 7591
|
||||
_, hasIssuedAt := got["client_id_issued_at"]
|
||||
_, hasExpiresAt := got["client_secret_expires_at"]
|
||||
|
||||
expectedHasIssuedAt := tt.clientSecret != nil && tt.clientSecret.CreatedAt != nil
|
||||
// Per RFC 7591: client_secret_expires_at is REQUIRED if client_secret is issued
|
||||
expectedHasExpiresAt := tt.clientSecret != nil
|
||||
|
||||
if hasIssuedAt != expectedHasIssuedAt {
|
||||
t.Errorf("client_id_issued_at presence mismatch: got %v, want %v", hasIssuedAt, expectedHasIssuedAt)
|
||||
}
|
||||
if hasExpiresAt != expectedHasExpiresAt {
|
||||
t.Errorf("client_secret_expires_at presence mismatch: got %v, want %v", hasExpiresAt, expectedHasExpiresAt)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteRegistrationResponseEdgeCases(t *testing.T) {
|
||||
t.Run("nil metadata", func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
err := WriteRegistrationResponse(&buf, "test-client", nil, nil)
|
||||
if err == nil {
|
||||
t.Fatalf("expected error with nil metadata: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("empty client ID", func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
metadata := &Metadata{
|
||||
RedirectUris: []string{"https://example.com/callback"},
|
||||
}
|
||||
err := WriteRegistrationResponse(&buf, "", nil, metadata)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error with empty client ID: %v", err)
|
||||
}
|
||||
|
||||
var got map[string]any
|
||||
if err := json.Unmarshal(buf.Bytes(), &got); err != nil {
|
||||
t.Fatalf("failed to parse JSON output: %v", err)
|
||||
}
|
||||
|
||||
if got["client_id"] != "" {
|
||||
t.Errorf("expected empty client_id, got %v", got["client_id"])
|
||||
}
|
||||
})
|
||||
}
|
|
@ -10,6 +10,7 @@ import (
|
|||
_ "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
|
@ -470,7 +471,7 @@ func (x *OkpKeyParameters) GetX() string {
|
|||
// Represents the client metadata fields defined in RFC 7591 Section 2.
|
||||
// These values are used both as input to registration requests and output in
|
||||
// registration responses.
|
||||
type ClientMetadata struct {
|
||||
type Metadata struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// Array of redirection URI strings. REQUIRED for clients using flows with
|
||||
// redirection.
|
||||
|
@ -525,20 +526,20 @@ type ClientMetadata struct {
|
|||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ClientMetadata) Reset() {
|
||||
*x = ClientMetadata{}
|
||||
func (x *Metadata) Reset() {
|
||||
*x = Metadata{}
|
||||
mi := &file_types_proto_msgTypes[6]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ClientMetadata) String() string {
|
||||
func (x *Metadata) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ClientMetadata) ProtoMessage() {}
|
||||
func (*Metadata) ProtoMessage() {}
|
||||
|
||||
func (x *ClientMetadata) ProtoReflect() protoreflect.Message {
|
||||
func (x *Metadata) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_types_proto_msgTypes[6]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
|
@ -550,414 +551,554 @@ func (x *ClientMetadata) ProtoReflect() protoreflect.Message {
|
|||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ClientMetadata.ProtoReflect.Descriptor instead.
|
||||
func (*ClientMetadata) Descriptor() ([]byte, []int) {
|
||||
// Deprecated: Use Metadata.ProtoReflect.Descriptor instead.
|
||||
func (*Metadata) Descriptor() ([]byte, []int) {
|
||||
return file_types_proto_rawDescGZIP(), []int{6}
|
||||
}
|
||||
|
||||
func (x *ClientMetadata) GetRedirectUris() []string {
|
||||
func (x *Metadata) GetRedirectUris() []string {
|
||||
if x != nil {
|
||||
return x.RedirectUris
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ClientMetadata) GetTokenEndpointAuthMethod() string {
|
||||
func (x *Metadata) GetTokenEndpointAuthMethod() string {
|
||||
if x != nil && x.TokenEndpointAuthMethod != nil {
|
||||
return *x.TokenEndpointAuthMethod
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ClientMetadata) GetGrantTypes() []string {
|
||||
func (x *Metadata) GetGrantTypes() []string {
|
||||
if x != nil {
|
||||
return x.GrantTypes
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ClientMetadata) GetResponseTypes() []string {
|
||||
func (x *Metadata) GetResponseTypes() []string {
|
||||
if x != nil {
|
||||
return x.ResponseTypes
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ClientMetadata) GetClientName() string {
|
||||
func (x *Metadata) GetClientName() string {
|
||||
if x != nil && x.ClientName != nil {
|
||||
return *x.ClientName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ClientMetadata) GetClientNameLocalized() map[string]string {
|
||||
func (x *Metadata) GetClientNameLocalized() map[string]string {
|
||||
if x != nil {
|
||||
return x.ClientNameLocalized
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ClientMetadata) GetClientUri() string {
|
||||
func (x *Metadata) GetClientUri() string {
|
||||
if x != nil && x.ClientUri != nil {
|
||||
return *x.ClientUri
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ClientMetadata) GetClientUriLocalized() map[string]string {
|
||||
func (x *Metadata) GetClientUriLocalized() map[string]string {
|
||||
if x != nil {
|
||||
return x.ClientUriLocalized
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ClientMetadata) GetLogoUri() string {
|
||||
func (x *Metadata) GetLogoUri() string {
|
||||
if x != nil && x.LogoUri != nil {
|
||||
return *x.LogoUri
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ClientMetadata) GetLogoUriLocalized() map[string]string {
|
||||
func (x *Metadata) GetLogoUriLocalized() map[string]string {
|
||||
if x != nil {
|
||||
return x.LogoUriLocalized
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ClientMetadata) GetScope() string {
|
||||
func (x *Metadata) GetScope() string {
|
||||
if x != nil && x.Scope != nil {
|
||||
return *x.Scope
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ClientMetadata) GetContacts() []string {
|
||||
func (x *Metadata) GetContacts() []string {
|
||||
if x != nil {
|
||||
return x.Contacts
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ClientMetadata) GetTosUri() string {
|
||||
func (x *Metadata) GetTosUri() string {
|
||||
if x != nil && x.TosUri != nil {
|
||||
return *x.TosUri
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ClientMetadata) GetTosUriLocalized() map[string]string {
|
||||
func (x *Metadata) GetTosUriLocalized() map[string]string {
|
||||
if x != nil {
|
||||
return x.TosUriLocalized
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ClientMetadata) GetPolicyUri() string {
|
||||
func (x *Metadata) GetPolicyUri() string {
|
||||
if x != nil && x.PolicyUri != nil {
|
||||
return *x.PolicyUri
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ClientMetadata) GetPolicyUriLocalized() map[string]string {
|
||||
func (x *Metadata) GetPolicyUriLocalized() map[string]string {
|
||||
if x != nil {
|
||||
return x.PolicyUriLocalized
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ClientMetadata) GetJwksUri() string {
|
||||
func (x *Metadata) GetJwksUri() string {
|
||||
if x != nil && x.JwksUri != nil {
|
||||
return *x.JwksUri
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ClientMetadata) GetJwks() *JsonWebKeySet {
|
||||
func (x *Metadata) GetJwks() *JsonWebKeySet {
|
||||
if x != nil {
|
||||
return x.Jwks
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ClientMetadata) GetSoftwareId() string {
|
||||
func (x *Metadata) GetSoftwareId() string {
|
||||
if x != nil && x.SoftwareId != nil {
|
||||
return *x.SoftwareId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ClientMetadata) GetSoftwareVersion() string {
|
||||
func (x *Metadata) GetSoftwareVersion() string {
|
||||
if x != nil && x.SoftwareVersion != nil {
|
||||
return *x.SoftwareVersion
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type ClientSecret struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// REQUIRED. The client secret value.
|
||||
Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"`
|
||||
// OPTIONAL. The expiration time of the client secret.
|
||||
ExpiresAt *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=expires_at,json=expiresAt,proto3,oneof" json:"expires_at,omitempty"`
|
||||
CreatedAt *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ClientSecret) Reset() {
|
||||
*x = ClientSecret{}
|
||||
mi := &file_types_proto_msgTypes[7]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ClientSecret) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ClientSecret) ProtoMessage() {}
|
||||
|
||||
func (x *ClientSecret) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_types_proto_msgTypes[7]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ClientSecret.ProtoReflect.Descriptor instead.
|
||||
func (*ClientSecret) Descriptor() ([]byte, []int) {
|
||||
return file_types_proto_rawDescGZIP(), []int{7}
|
||||
}
|
||||
|
||||
func (x *ClientSecret) GetValue() string {
|
||||
if x != nil {
|
||||
return x.Value
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ClientSecret) GetExpiresAt() *timestamppb.Timestamp {
|
||||
if x != nil {
|
||||
return x.ExpiresAt
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ClientSecret) GetCreatedAt() *timestamppb.Timestamp {
|
||||
if x != nil {
|
||||
return x.CreatedAt
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Represents the client registration storage structure.
|
||||
type ClientRegistration struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// Contains the client metadata as requested by the client.
|
||||
RequestMetadata *Metadata `protobuf:"bytes,1,opt,name=request_metadata,json=requestMetadata,proto3" json:"request_metadata,omitempty"`
|
||||
// Contains the client metadata as was returned by the server.
|
||||
ResponseMetadata *Metadata `protobuf:"bytes,2,opt,name=response_metadata,json=responseMetadata,proto3" json:"response_metadata,omitempty"`
|
||||
// OPTIONAL. The "client_secret" parameter is the secret used by the client
|
||||
ClientSecret *ClientSecret `protobuf:"bytes,3,opt,name=client_secret,json=clientSecret,proto3" json:"client_secret,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ClientRegistration) Reset() {
|
||||
*x = ClientRegistration{}
|
||||
mi := &file_types_proto_msgTypes[8]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ClientRegistration) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ClientRegistration) ProtoMessage() {}
|
||||
|
||||
func (x *ClientRegistration) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_types_proto_msgTypes[8]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ClientRegistration.ProtoReflect.Descriptor instead.
|
||||
func (*ClientRegistration) Descriptor() ([]byte, []int) {
|
||||
return file_types_proto_rawDescGZIP(), []int{8}
|
||||
}
|
||||
|
||||
func (x *ClientRegistration) GetRequestMetadata() *Metadata {
|
||||
if x != nil {
|
||||
return x.RequestMetadata
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ClientRegistration) GetResponseMetadata() *Metadata {
|
||||
if x != nil {
|
||||
return x.ResponseMetadata
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ClientRegistration) GetClientSecret() *ClientSecret {
|
||||
if x != nil {
|
||||
return x.ClientSecret
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_types_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_types_proto_rawDesc = string([]byte{
|
||||
0x0a, 0x0b, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x69,
|
||||
0x65, 0x74, 0x66, 0x2e, 0x72, 0x66, 0x63, 0x37, 0x35, 0x39, 0x31, 0x2e, 0x76, 0x31, 0x1a, 0x1b,
|
||||
0x62, 0x75, 0x66, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c,
|
||||
0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x4d, 0x0a, 0x0d, 0x4a,
|
||||
0x73, 0x6f, 0x6e, 0x57, 0x65, 0x62, 0x4b, 0x65, 0x79, 0x53, 0x65, 0x74, 0x12, 0x3c, 0x0a, 0x04,
|
||||
0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x69, 0x65, 0x74,
|
||||
0x66, 0x2e, 0x72, 0x66, 0x63, 0x37, 0x35, 0x39, 0x31, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x73, 0x6f,
|
||||
0x6e, 0x57, 0x65, 0x62, 0x4b, 0x65, 0x79, 0x42, 0x0b, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, 0x92,
|
||||
0x01, 0x02, 0x08, 0x01, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x22, 0xcf, 0x07, 0x0a, 0x0a, 0x4a,
|
||||
0x73, 0x6f, 0x6e, 0x57, 0x65, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x2f, 0x0a, 0x03, 0x6b, 0x74, 0x79,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1d, 0xba, 0x48, 0x1a, 0xc8, 0x01, 0x01, 0x72, 0x15,
|
||||
0x10, 0x01, 0x52, 0x03, 0x52, 0x53, 0x41, 0x52, 0x02, 0x45, 0x43, 0x52, 0x03, 0x6f, 0x63, 0x74,
|
||||
0x52, 0x03, 0x4f, 0x4b, 0x50, 0x52, 0x03, 0x6b, 0x74, 0x79, 0x12, 0x26, 0x0a, 0x03, 0x75, 0x73,
|
||||
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0f, 0xba, 0x48, 0x0c, 0x72, 0x0a, 0x52, 0x03,
|
||||
0x73, 0x69, 0x67, 0x52, 0x03, 0x65, 0x6e, 0x63, 0x48, 0x01, 0x52, 0x03, 0x75, 0x73, 0x65, 0x88,
|
||||
0x01, 0x01, 0x12, 0x25, 0x0a, 0x07, 0x6b, 0x65, 0x79, 0x5f, 0x6f, 0x70, 0x73, 0x18, 0x03, 0x20,
|
||||
0x03, 0x28, 0x09, 0x42, 0x0c, 0xba, 0x48, 0x09, 0x92, 0x01, 0x06, 0x22, 0x04, 0x72, 0x02, 0x10,
|
||||
0x01, 0x52, 0x06, 0x6b, 0x65, 0x79, 0x4f, 0x70, 0x73, 0x12, 0x1e, 0x0a, 0x03, 0x61, 0x6c, 0x67,
|
||||
0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x48,
|
||||
0x02, 0x52, 0x03, 0x61, 0x6c, 0x67, 0x88, 0x01, 0x01, 0x12, 0x1e, 0x0a, 0x03, 0x6b, 0x69, 0x64,
|
||||
0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x48,
|
||||
0x03, 0x52, 0x03, 0x6b, 0x69, 0x64, 0x88, 0x01, 0x01, 0x12, 0x42, 0x0a, 0x0a, 0x72, 0x73, 0x61,
|
||||
0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e,
|
||||
0x69, 0x65, 0x74, 0x66, 0x2e, 0x72, 0x66, 0x63, 0x37, 0x35, 0x39, 0x31, 0x2e, 0x76, 0x31, 0x2e,
|
||||
0x52, 0x73, 0x61, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73,
|
||||
0x48, 0x00, 0x52, 0x09, 0x72, 0x73, 0x61, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x3f, 0x0a,
|
||||
0x09, 0x65, 0x63, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x20, 0x2e, 0x69, 0x65, 0x74, 0x66, 0x2e, 0x72, 0x66, 0x63, 0x37, 0x35, 0x39, 0x31, 0x2e,
|
||||
0x76, 0x31, 0x2e, 0x45, 0x63, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65,
|
||||
0x72, 0x73, 0x48, 0x00, 0x52, 0x08, 0x65, 0x63, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x54,
|
||||
0x0a, 0x10, 0x73, 0x79, 0x6d, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x70, 0x61, 0x72, 0x61,
|
||||
0x6d, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x69, 0x65, 0x74, 0x66, 0x2e,
|
||||
0x72, 0x66, 0x63, 0x37, 0x35, 0x39, 0x31, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x79, 0x6d, 0x6d, 0x65,
|
||||
0x74, 0x72, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72,
|
||||
0x73, 0x48, 0x00, 0x52, 0x0f, 0x73, 0x79, 0x6d, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x50, 0x61,
|
||||
0x72, 0x61, 0x6d, 0x73, 0x12, 0x42, 0x0a, 0x0a, 0x6f, 0x6b, 0x70, 0x5f, 0x70, 0x61, 0x72, 0x61,
|
||||
0x6d, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x69, 0x65, 0x74, 0x66, 0x2e,
|
||||
0x72, 0x66, 0x63, 0x37, 0x35, 0x39, 0x31, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6b, 0x70, 0x4b, 0x65,
|
||||
0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x48, 0x00, 0x52, 0x09, 0x6f,
|
||||
0x6b, 0x70, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x3a, 0xab, 0x03, 0xba, 0x48, 0xa7, 0x03, 0x1a,
|
||||
0x66, 0x0a, 0x12, 0x6a, 0x77, 0x6b, 0x2e, 0x6b, 0x74, 0x79, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d,
|
||||
0x73, 0x2e, 0x72, 0x73, 0x61, 0x12, 0x25, 0x72, 0x73, 0x61, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d,
|
||||
0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x66,
|
||||
0x6f, 0x72, 0x20, 0x6b, 0x74, 0x79, 0x20, 0x27, 0x52, 0x53, 0x41, 0x27, 0x1a, 0x29, 0x74, 0x68,
|
||||
0x69, 0x73, 0x2e, 0x6b, 0x74, 0x79, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x52, 0x53, 0x41, 0x27, 0x20,
|
||||
0x7c, 0x7c, 0x20, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x72, 0x73, 0x61, 0x5f,
|
||||
0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x29, 0x1a, 0x61, 0x0a, 0x11, 0x6a, 0x77, 0x6b, 0x2e, 0x6b,
|
||||
0x74, 0x79, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x65, 0x63, 0x12, 0x23, 0x65, 0x63,
|
||||
0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x72, 0x65, 0x71, 0x75,
|
||||
0x69, 0x72, 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6b, 0x74, 0x79, 0x20, 0x27, 0x45, 0x43,
|
||||
0x27, 0x1a, 0x27, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x74, 0x79, 0x20, 0x21, 0x3d, 0x20, 0x27,
|
||||
0x45, 0x43, 0x27, 0x20, 0x7c, 0x7c, 0x20, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e,
|
||||
0x65, 0x63, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x29, 0x1a, 0x72, 0x0a, 0x12, 0x6a, 0x77,
|
||||
0x6b, 0x2e, 0x6b, 0x74, 0x79, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x6f, 0x63, 0x74,
|
||||
0x12, 0x2b, 0x73, 0x79, 0x6d, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x70, 0x61, 0x72, 0x61,
|
||||
0x6d, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20,
|
||||
0x66, 0x6f, 0x72, 0x20, 0x6b, 0x74, 0x79, 0x20, 0x27, 0x6f, 0x63, 0x74, 0x27, 0x1a, 0x2f, 0x74,
|
||||
0x68, 0x69, 0x73, 0x2e, 0x6b, 0x74, 0x79, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x6f, 0x63, 0x74, 0x27,
|
||||
0x20, 0x7c, 0x7c, 0x20, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x73, 0x79, 0x6d,
|
||||
0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x29, 0x1a, 0x66,
|
||||
0x0a, 0x12, 0x6a, 0x77, 0x6b, 0x2e, 0x6b, 0x74, 0x79, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73,
|
||||
0x2e, 0x6f, 0x6b, 0x70, 0x12, 0x25, 0x6f, 0x6b, 0x70, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73,
|
||||
0x20, 0x61, 0x72, 0x65, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x66, 0x6f,
|
||||
0x72, 0x20, 0x6b, 0x74, 0x79, 0x20, 0x27, 0x4f, 0x4b, 0x50, 0x27, 0x1a, 0x29, 0x74, 0x68, 0x69,
|
||||
0x73, 0x2e, 0x6b, 0x74, 0x79, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x4f, 0x4b, 0x50, 0x27, 0x20, 0x7c,
|
||||
0x7c, 0x20, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6f, 0x6b, 0x70, 0x5f, 0x70,
|
||||
0x61, 0x72, 0x61, 0x6d, 0x73, 0x29, 0x42, 0x1c, 0x0a, 0x13, 0x6b, 0x65, 0x79, 0x5f, 0x74, 0x79,
|
||||
0x70, 0x65, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x05, 0xba,
|
||||
0x48, 0x02, 0x08, 0x00, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x75, 0x73, 0x65, 0x42, 0x06, 0x0a, 0x04,
|
||||
0x5f, 0x61, 0x6c, 0x67, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x6b, 0x69, 0x64, 0x22, 0x46, 0x0a, 0x10,
|
||||
0x52, 0x73, 0x61, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73,
|
||||
0x12, 0x18, 0x0a, 0x01, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07,
|
||||
0xc8, 0x01, 0x01, 0x72, 0x02, 0x10, 0x01, 0x52, 0x01, 0x6e, 0x12, 0x18, 0x0a, 0x01, 0x65, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0xc8, 0x01, 0x01, 0x72, 0x02, 0x10,
|
||||
0x01, 0x52, 0x01, 0x65, 0x22, 0x76, 0x0a, 0x0f, 0x45, 0x63, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x72,
|
||||
0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x2f, 0x0a, 0x03, 0x63, 0x72, 0x76, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x09, 0x42, 0x1d, 0xba, 0x48, 0x1a, 0xc8, 0x01, 0x01, 0x72, 0x15, 0x52, 0x05,
|
||||
0x50, 0x2d, 0x32, 0x35, 0x36, 0x52, 0x05, 0x50, 0x2d, 0x33, 0x38, 0x34, 0x52, 0x05, 0x50, 0x2d,
|
||||
0x35, 0x32, 0x31, 0x52, 0x03, 0x63, 0x72, 0x76, 0x12, 0x18, 0x0a, 0x01, 0x78, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0xc8, 0x01, 0x01, 0x72, 0x02, 0x10, 0x01, 0x52,
|
||||
0x01, 0x78, 0x12, 0x18, 0x0a, 0x01, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba,
|
||||
0x48, 0x07, 0xc8, 0x01, 0x01, 0x72, 0x02, 0x10, 0x01, 0x52, 0x01, 0x79, 0x22, 0x32, 0x0a, 0x16,
|
||||
0x53, 0x79, 0x6d, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x72, 0x61,
|
||||
0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x18, 0x0a, 0x01, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0xc8, 0x01, 0x01, 0x72, 0x02, 0x10, 0x01, 0x52, 0x01, 0x6b,
|
||||
0x22, 0x7e, 0x0a, 0x10, 0x4f, 0x6b, 0x70, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65,
|
||||
0x74, 0x65, 0x72, 0x73, 0x12, 0x38, 0x0a, 0x03, 0x63, 0x72, 0x76, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x09, 0x42, 0x26, 0xba, 0x48, 0x23, 0xc8, 0x01, 0x01, 0x72, 0x1e, 0x52, 0x07, 0x45, 0x64, 0x32,
|
||||
0x35, 0x35, 0x31, 0x39, 0x52, 0x05, 0x45, 0x64, 0x34, 0x34, 0x38, 0x52, 0x06, 0x58, 0x32, 0x35,
|
||||
0x35, 0x31, 0x39, 0x52, 0x04, 0x58, 0x34, 0x34, 0x38, 0x52, 0x03, 0x63, 0x72, 0x76, 0x12, 0x30,
|
||||
0x0a, 0x01, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x22, 0xba, 0x48, 0x1f, 0xc8, 0x01,
|
||||
0x01, 0x72, 0x1a, 0x10, 0x01, 0x32, 0x16, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30,
|
||||
0x2d, 0x39, 0x2d, 0x5f, 0x5d, 0x2b, 0x3d, 0x7b, 0x30, 0x2c, 0x32, 0x7d, 0x24, 0x52, 0x01, 0x78,
|
||||
0x22, 0x9b, 0x13, 0x0a, 0x0e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64,
|
||||
0x61, 0x74, 0x61, 0x12, 0x36, 0x0a, 0x0d, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f,
|
||||
0x75, 0x72, 0x69, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x42, 0x11, 0xba, 0x48, 0x0e, 0x92,
|
||||
0x01, 0x0b, 0x08, 0x01, 0x22, 0x07, 0x72, 0x05, 0x10, 0x01, 0x88, 0x01, 0x01, 0x52, 0x0c, 0x72,
|
||||
0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x55, 0x72, 0x69, 0x73, 0x12, 0x76, 0x0a, 0x1a, 0x74,
|
||||
0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x61, 0x75,
|
||||
0x74, 0x68, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42,
|
||||
0x34, 0xba, 0x48, 0x31, 0x72, 0x2f, 0x52, 0x04, 0x6e, 0x6f, 0x6e, 0x65, 0x52, 0x12, 0x63, 0x6c,
|
||||
0x69, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x70, 0x6f, 0x73, 0x74,
|
||||
0x52, 0x13, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f,
|
||||
0x62, 0x61, 0x73, 0x69, 0x63, 0x48, 0x00, 0x52, 0x17, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x45, 0x6e,
|
||||
0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64,
|
||||
0x88, 0x01, 0x01, 0x12, 0xd7, 0x01, 0x0a, 0x0b, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x5f, 0x74, 0x79,
|
||||
0x70, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x42, 0xb5, 0x01, 0xba, 0x48, 0xb1, 0x01,
|
||||
0x92, 0x01, 0xad, 0x01, 0x22, 0xaa, 0x01, 0x72, 0xa7, 0x01, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68,
|
||||
0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x52, 0x08,
|
||||
0x69, 0x6d, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f,
|
||||
0x72, 0x64, 0x52, 0x12, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65,
|
||||
0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x52, 0x0d, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f,
|
||||
0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x2b, 0x75, 0x72, 0x6e, 0x3a, 0x69, 0x65, 0x74, 0x66, 0x3a,
|
||||
0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x3a, 0x6f, 0x61, 0x75, 0x74, 0x68, 0x3a, 0x67, 0x72, 0x61,
|
||||
0x6e, 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x3a, 0x6a, 0x77, 0x74, 0x2d, 0x62, 0x65, 0x61, 0x72,
|
||||
0x65, 0x72, 0x52, 0x2d, 0x75, 0x72, 0x6e, 0x3a, 0x69, 0x65, 0x74, 0x66, 0x3a, 0x70, 0x61, 0x72,
|
||||
0x61, 0x6d, 0x73, 0x3a, 0x6f, 0x61, 0x75, 0x74, 0x68, 0x3a, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x2d,
|
||||
0x74, 0x79, 0x70, 0x65, 0x3a, 0x73, 0x61, 0x6d, 0x6c, 0x32, 0x2d, 0x62, 0x65, 0x61, 0x72, 0x65,
|
||||
0x72, 0x52, 0x0a, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x3e, 0x0a,
|
||||
0x0e, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18,
|
||||
0x04, 0x20, 0x03, 0x28, 0x09, 0x42, 0x17, 0xba, 0x48, 0x14, 0x92, 0x01, 0x11, 0x22, 0x0f, 0x72,
|
||||
0x0d, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x0d,
|
||||
0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x30, 0x0a,
|
||||
0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01,
|
||||
0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0x72, 0x05, 0x10, 0x01, 0x18, 0xff, 0x01, 0x48, 0x01,
|
||||
0x52, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12,
|
||||
0xa6, 0x01, 0x0a, 0x15, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f,
|
||||
0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||
0x38, 0x2e, 0x69, 0x65, 0x74, 0x66, 0x2e, 0x72, 0x66, 0x63, 0x37, 0x35, 0x39, 0x31, 0x2e, 0x76,
|
||||
0x31, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
|
||||
0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x6f, 0x63, 0x61, 0x6c,
|
||||
0x69, 0x7a, 0x65, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x38, 0xba, 0x48, 0x35, 0x9a, 0x01,
|
||||
0x32, 0x22, 0x27, 0x72, 0x25, 0x32, 0x23, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x5d,
|
||||
0x7b, 0x31, 0x2c, 0x38, 0x7d, 0x28, 0x2d, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d,
|
||||
0x39, 0x5d, 0x7b, 0x31, 0x2c, 0x38, 0x7d, 0x29, 0x2a, 0x24, 0x2a, 0x07, 0x72, 0x05, 0x10, 0x01,
|
||||
0x18, 0xff, 0x01, 0x52, 0x13, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x4c,
|
||||
0x6f, 0x63, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x12, 0x2c, 0x0a, 0x0a, 0x63, 0x6c, 0x69, 0x65,
|
||||
0x6e, 0x74, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48,
|
||||
0x05, 0x72, 0x03, 0x88, 0x01, 0x01, 0x48, 0x02, 0x52, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74,
|
||||
0x55, 0x72, 0x69, 0x88, 0x01, 0x01, 0x12, 0xa1, 0x01, 0x0a, 0x14, 0x63, 0x6c, 0x69, 0x65, 0x6e,
|
||||
0x74, 0x5f, 0x75, 0x72, 0x69, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x18,
|
||||
0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x69, 0x65, 0x74, 0x66, 0x2e, 0x72, 0x66, 0x63,
|
||||
0x37, 0x35, 0x39, 0x31, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4d, 0x65,
|
||||
0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x55, 0x72, 0x69,
|
||||
0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x36,
|
||||
0xba, 0x48, 0x33, 0x9a, 0x01, 0x30, 0x22, 0x27, 0x72, 0x25, 0x32, 0x23, 0x5e, 0x5b, 0x61, 0x2d,
|
||||
0x7a, 0x41, 0x2d, 0x5a, 0x5d, 0x7b, 0x31, 0x2c, 0x38, 0x7d, 0x28, 0x2d, 0x5b, 0x61, 0x2d, 0x7a,
|
||||
0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x7b, 0x31, 0x2c, 0x38, 0x7d, 0x29, 0x2a, 0x24, 0x2a,
|
||||
0x05, 0x72, 0x03, 0x88, 0x01, 0x01, 0x52, 0x12, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x55, 0x72,
|
||||
0x69, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x12, 0x28, 0x0a, 0x08, 0x6c, 0x6f,
|
||||
0x67, 0x6f, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48,
|
||||
0x05, 0x72, 0x03, 0x88, 0x01, 0x01, 0x48, 0x03, 0x52, 0x07, 0x6c, 0x6f, 0x67, 0x6f, 0x55, 0x72,
|
||||
0x69, 0x88, 0x01, 0x01, 0x12, 0x9b, 0x01, 0x0a, 0x12, 0x6c, 0x6f, 0x67, 0x6f, 0x5f, 0x75, 0x72,
|
||||
0x69, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x03, 0x28,
|
||||
0x0b, 0x32, 0x35, 0x2e, 0x69, 0x65, 0x74, 0x66, 0x2e, 0x72, 0x66, 0x63, 0x37, 0x35, 0x39, 0x31,
|
||||
0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
|
||||
0x74, 0x61, 0x2e, 0x4c, 0x6f, 0x67, 0x6f, 0x55, 0x72, 0x69, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x69,
|
||||
0x7a, 0x65, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x36, 0xba, 0x48, 0x33, 0x9a, 0x01, 0x30,
|
||||
0x22, 0x27, 0x72, 0x25, 0x32, 0x23, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x5d, 0x7b,
|
||||
0x31, 0x2c, 0x38, 0x7d, 0x28, 0x2d, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39,
|
||||
0x5d, 0x7b, 0x31, 0x2c, 0x38, 0x7d, 0x29, 0x2a, 0x24, 0x2a, 0x05, 0x72, 0x03, 0x88, 0x01, 0x01,
|
||||
0x52, 0x10, 0x6c, 0x6f, 0x67, 0x6f, 0x55, 0x72, 0x69, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x7a,
|
||||
0x65, 0x64, 0x12, 0x30, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28,
|
||||
0x09, 0x42, 0x15, 0xba, 0x48, 0x12, 0x72, 0x10, 0x10, 0x01, 0x32, 0x0c, 0x5e, 0x5c, 0x53, 0x2b,
|
||||
0x28, 0x20, 0x5c, 0x53, 0x2b, 0x29, 0x2a, 0x24, 0x48, 0x04, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70,
|
||||
0x65, 0x88, 0x01, 0x01, 0x12, 0x28, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x73,
|
||||
0x18, 0x0c, 0x20, 0x03, 0x28, 0x09, 0x42, 0x0c, 0xba, 0x48, 0x09, 0x92, 0x01, 0x06, 0x22, 0x04,
|
||||
0x72, 0x02, 0x60, 0x01, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x73, 0x12, 0x26,
|
||||
0x0a, 0x07, 0x74, 0x6f, 0x73, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x42,
|
||||
0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0x88, 0x01, 0x01, 0x48, 0x05, 0x52, 0x06, 0x74, 0x6f, 0x73,
|
||||
0x55, 0x72, 0x69, 0x88, 0x01, 0x01, 0x12, 0x98, 0x01, 0x0a, 0x11, 0x74, 0x6f, 0x73, 0x5f, 0x75,
|
||||
0x72, 0x69, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x18, 0x0e, 0x20, 0x03,
|
||||
0x28, 0x0b, 0x32, 0x34, 0x2e, 0x69, 0x65, 0x74, 0x66, 0x2e, 0x72, 0x66, 0x63, 0x37, 0x35, 0x39,
|
||||
0x31, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64,
|
||||
0x61, 0x74, 0x61, 0x2e, 0x54, 0x6f, 0x73, 0x55, 0x72, 0x69, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x69,
|
||||
0x7a, 0x65, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x36, 0xba, 0x48, 0x33, 0x9a, 0x01, 0x30,
|
||||
0x22, 0x27, 0x72, 0x25, 0x32, 0x23, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x5d, 0x7b,
|
||||
0x31, 0x2c, 0x38, 0x7d, 0x28, 0x2d, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39,
|
||||
0x5d, 0x7b, 0x31, 0x2c, 0x38, 0x7d, 0x29, 0x2a, 0x24, 0x2a, 0x05, 0x72, 0x03, 0x88, 0x01, 0x01,
|
||||
0x52, 0x0f, 0x74, 0x6f, 0x73, 0x55, 0x72, 0x69, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x7a, 0x65,
|
||||
0x64, 0x12, 0x2c, 0x0a, 0x0a, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x75, 0x72, 0x69, 0x18,
|
||||
0x0f, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0x88, 0x01, 0x01, 0x48,
|
||||
0x06, 0x52, 0x09, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x72, 0x69, 0x88, 0x01, 0x01, 0x12,
|
||||
0xa1, 0x01, 0x0a, 0x14, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x75, 0x72, 0x69, 0x5f, 0x6c,
|
||||
0x6f, 0x63, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x18, 0x10, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x37,
|
||||
0x2e, 0x69, 0x65, 0x74, 0x66, 0x2e, 0x72, 0x66, 0x63, 0x37, 0x35, 0x39, 0x31, 0x2e, 0x76, 0x31,
|
||||
0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e,
|
||||
0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x72, 0x69, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x7a,
|
||||
0x65, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x36, 0xba, 0x48, 0x33, 0x9a, 0x01, 0x30, 0x22,
|
||||
0x27, 0x72, 0x25, 0x32, 0x23, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x5d, 0x7b, 0x31,
|
||||
0x2c, 0x38, 0x7d, 0x28, 0x2d, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d,
|
||||
0x7b, 0x31, 0x2c, 0x38, 0x7d, 0x29, 0x2a, 0x24, 0x2a, 0x05, 0x72, 0x03, 0x88, 0x01, 0x01, 0x52,
|
||||
0x12, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x72, 0x69, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x69,
|
||||
0x7a, 0x65, 0x64, 0x12, 0x28, 0x0a, 0x08, 0x6a, 0x77, 0x6b, 0x73, 0x5f, 0x75, 0x72, 0x69, 0x18,
|
||||
0x11, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0x88, 0x01, 0x01, 0x48,
|
||||
0x07, 0x52, 0x07, 0x6a, 0x77, 0x6b, 0x73, 0x55, 0x72, 0x69, 0x88, 0x01, 0x01, 0x12, 0x37, 0x0a,
|
||||
0x04, 0x6a, 0x77, 0x6b, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x69, 0x65,
|
||||
0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f,
|
||||
0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d,
|
||||
0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x4d, 0x0a, 0x0d,
|
||||
0x4a, 0x73, 0x6f, 0x6e, 0x57, 0x65, 0x62, 0x4b, 0x65, 0x79, 0x53, 0x65, 0x74, 0x12, 0x3c, 0x0a,
|
||||
0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x69, 0x65,
|
||||
0x74, 0x66, 0x2e, 0x72, 0x66, 0x63, 0x37, 0x35, 0x39, 0x31, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x73,
|
||||
0x6f, 0x6e, 0x57, 0x65, 0x62, 0x4b, 0x65, 0x79, 0x53, 0x65, 0x74, 0x48, 0x08, 0x52, 0x04, 0x6a,
|
||||
0x77, 0x6b, 0x73, 0x88, 0x01, 0x01, 0x12, 0x30, 0x0a, 0x0b, 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61,
|
||||
0x72, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x13, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07,
|
||||
0x72, 0x05, 0x10, 0x01, 0x18, 0xff, 0x01, 0x48, 0x09, 0x52, 0x0a, 0x73, 0x6f, 0x66, 0x74, 0x77,
|
||||
0x61, 0x72, 0x65, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x3a, 0x0a, 0x10, 0x73, 0x6f, 0x66, 0x74,
|
||||
0x77, 0x61, 0x72, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x14, 0x20, 0x01,
|
||||
0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0x72, 0x05, 0x10, 0x01, 0x18, 0xff, 0x01, 0x48, 0x0a,
|
||||
0x52, 0x0f, 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f,
|
||||
0x6e, 0x88, 0x01, 0x01, 0x1a, 0x46, 0x0a, 0x18, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4e, 0x61,
|
||||
0x6d, 0x65, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79,
|
||||
0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
|
||||
0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x45, 0x0a, 0x17,
|
||||
0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x55, 0x72, 0x69, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x7a,
|
||||
0x6f, 0x6e, 0x57, 0x65, 0x62, 0x4b, 0x65, 0x79, 0x42, 0x0b, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01,
|
||||
0x92, 0x01, 0x02, 0x08, 0x01, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x22, 0xcf, 0x07, 0x0a, 0x0a,
|
||||
0x4a, 0x73, 0x6f, 0x6e, 0x57, 0x65, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x2f, 0x0a, 0x03, 0x6b, 0x74,
|
||||
0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1d, 0xba, 0x48, 0x1a, 0xc8, 0x01, 0x01, 0x72,
|
||||
0x15, 0x10, 0x01, 0x52, 0x03, 0x52, 0x53, 0x41, 0x52, 0x02, 0x45, 0x43, 0x52, 0x03, 0x6f, 0x63,
|
||||
0x74, 0x52, 0x03, 0x4f, 0x4b, 0x50, 0x52, 0x03, 0x6b, 0x74, 0x79, 0x12, 0x26, 0x0a, 0x03, 0x75,
|
||||
0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0f, 0xba, 0x48, 0x0c, 0x72, 0x0a, 0x52,
|
||||
0x03, 0x73, 0x69, 0x67, 0x52, 0x03, 0x65, 0x6e, 0x63, 0x48, 0x01, 0x52, 0x03, 0x75, 0x73, 0x65,
|
||||
0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x07, 0x6b, 0x65, 0x79, 0x5f, 0x6f, 0x70, 0x73, 0x18, 0x03,
|
||||
0x20, 0x03, 0x28, 0x09, 0x42, 0x0c, 0xba, 0x48, 0x09, 0x92, 0x01, 0x06, 0x22, 0x04, 0x72, 0x02,
|
||||
0x10, 0x01, 0x52, 0x06, 0x6b, 0x65, 0x79, 0x4f, 0x70, 0x73, 0x12, 0x1e, 0x0a, 0x03, 0x61, 0x6c,
|
||||
0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01,
|
||||
0x48, 0x02, 0x52, 0x03, 0x61, 0x6c, 0x67, 0x88, 0x01, 0x01, 0x12, 0x1e, 0x0a, 0x03, 0x6b, 0x69,
|
||||
0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01,
|
||||
0x48, 0x03, 0x52, 0x03, 0x6b, 0x69, 0x64, 0x88, 0x01, 0x01, 0x12, 0x42, 0x0a, 0x0a, 0x72, 0x73,
|
||||
0x61, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21,
|
||||
0x2e, 0x69, 0x65, 0x74, 0x66, 0x2e, 0x72, 0x66, 0x63, 0x37, 0x35, 0x39, 0x31, 0x2e, 0x76, 0x31,
|
||||
0x2e, 0x52, 0x73, 0x61, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72,
|
||||
0x73, 0x48, 0x00, 0x52, 0x09, 0x72, 0x73, 0x61, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x3f,
|
||||
0x0a, 0x09, 0x65, 0x63, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x20, 0x2e, 0x69, 0x65, 0x74, 0x66, 0x2e, 0x72, 0x66, 0x63, 0x37, 0x35, 0x39, 0x31,
|
||||
0x2e, 0x76, 0x31, 0x2e, 0x45, 0x63, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74,
|
||||
0x65, 0x72, 0x73, 0x48, 0x00, 0x52, 0x08, 0x65, 0x63, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12,
|
||||
0x54, 0x0a, 0x10, 0x73, 0x79, 0x6d, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x70, 0x61, 0x72,
|
||||
0x61, 0x6d, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x69, 0x65, 0x74, 0x66,
|
||||
0x2e, 0x72, 0x66, 0x63, 0x37, 0x35, 0x39, 0x31, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x79, 0x6d, 0x6d,
|
||||
0x65, 0x74, 0x72, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65,
|
||||
0x72, 0x73, 0x48, 0x00, 0x52, 0x0f, 0x73, 0x79, 0x6d, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x50,
|
||||
0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x42, 0x0a, 0x0a, 0x6f, 0x6b, 0x70, 0x5f, 0x70, 0x61, 0x72,
|
||||
0x61, 0x6d, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x69, 0x65, 0x74, 0x66,
|
||||
0x2e, 0x72, 0x66, 0x63, 0x37, 0x35, 0x39, 0x31, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6b, 0x70, 0x4b,
|
||||
0x65, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x48, 0x00, 0x52, 0x09,
|
||||
0x6f, 0x6b, 0x70, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x3a, 0xab, 0x03, 0xba, 0x48, 0xa7, 0x03,
|
||||
0x1a, 0x66, 0x0a, 0x12, 0x6a, 0x77, 0x6b, 0x2e, 0x6b, 0x74, 0x79, 0x5f, 0x70, 0x61, 0x72, 0x61,
|
||||
0x6d, 0x73, 0x2e, 0x72, 0x73, 0x61, 0x12, 0x25, 0x72, 0x73, 0x61, 0x5f, 0x70, 0x61, 0x72, 0x61,
|
||||
0x6d, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20,
|
||||
0x66, 0x6f, 0x72, 0x20, 0x6b, 0x74, 0x79, 0x20, 0x27, 0x52, 0x53, 0x41, 0x27, 0x1a, 0x29, 0x74,
|
||||
0x68, 0x69, 0x73, 0x2e, 0x6b, 0x74, 0x79, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x52, 0x53, 0x41, 0x27,
|
||||
0x20, 0x7c, 0x7c, 0x20, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x72, 0x73, 0x61,
|
||||
0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x29, 0x1a, 0x61, 0x0a, 0x11, 0x6a, 0x77, 0x6b, 0x2e,
|
||||
0x6b, 0x74, 0x79, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x65, 0x63, 0x12, 0x23, 0x65,
|
||||
0x63, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x72, 0x65, 0x71,
|
||||
0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6b, 0x74, 0x79, 0x20, 0x27, 0x45,
|
||||
0x43, 0x27, 0x1a, 0x27, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x74, 0x79, 0x20, 0x21, 0x3d, 0x20,
|
||||
0x27, 0x45, 0x43, 0x27, 0x20, 0x7c, 0x7c, 0x20, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73,
|
||||
0x2e, 0x65, 0x63, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x29, 0x1a, 0x72, 0x0a, 0x12, 0x6a,
|
||||
0x77, 0x6b, 0x2e, 0x6b, 0x74, 0x79, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x6f, 0x63,
|
||||
0x74, 0x12, 0x2b, 0x73, 0x79, 0x6d, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x70, 0x61, 0x72,
|
||||
0x61, 0x6d, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64,
|
||||
0x20, 0x66, 0x6f, 0x72, 0x20, 0x6b, 0x74, 0x79, 0x20, 0x27, 0x6f, 0x63, 0x74, 0x27, 0x1a, 0x2f,
|
||||
0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x74, 0x79, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x6f, 0x63, 0x74,
|
||||
0x27, 0x20, 0x7c, 0x7c, 0x20, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x73, 0x79,
|
||||
0x6d, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x29, 0x1a,
|
||||
0x66, 0x0a, 0x12, 0x6a, 0x77, 0x6b, 0x2e, 0x6b, 0x74, 0x79, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d,
|
||||
0x73, 0x2e, 0x6f, 0x6b, 0x70, 0x12, 0x25, 0x6f, 0x6b, 0x70, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d,
|
||||
0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x66,
|
||||
0x6f, 0x72, 0x20, 0x6b, 0x74, 0x79, 0x20, 0x27, 0x4f, 0x4b, 0x50, 0x27, 0x1a, 0x29, 0x74, 0x68,
|
||||
0x69, 0x73, 0x2e, 0x6b, 0x74, 0x79, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x4f, 0x4b, 0x50, 0x27, 0x20,
|
||||
0x7c, 0x7c, 0x20, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6f, 0x6b, 0x70, 0x5f,
|
||||
0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x29, 0x42, 0x1c, 0x0a, 0x13, 0x6b, 0x65, 0x79, 0x5f, 0x74,
|
||||
0x79, 0x70, 0x65, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x05,
|
||||
0xba, 0x48, 0x02, 0x08, 0x00, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x75, 0x73, 0x65, 0x42, 0x06, 0x0a,
|
||||
0x04, 0x5f, 0x61, 0x6c, 0x67, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x6b, 0x69, 0x64, 0x22, 0x46, 0x0a,
|
||||
0x10, 0x52, 0x73, 0x61, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72,
|
||||
0x73, 0x12, 0x18, 0x0a, 0x01, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48,
|
||||
0x07, 0xc8, 0x01, 0x01, 0x72, 0x02, 0x10, 0x01, 0x52, 0x01, 0x6e, 0x12, 0x18, 0x0a, 0x01, 0x65,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0xc8, 0x01, 0x01, 0x72, 0x02,
|
||||
0x10, 0x01, 0x52, 0x01, 0x65, 0x22, 0x76, 0x0a, 0x0f, 0x45, 0x63, 0x4b, 0x65, 0x79, 0x50, 0x61,
|
||||
0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x2f, 0x0a, 0x03, 0x63, 0x72, 0x76, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1d, 0xba, 0x48, 0x1a, 0xc8, 0x01, 0x01, 0x72, 0x15, 0x52,
|
||||
0x05, 0x50, 0x2d, 0x32, 0x35, 0x36, 0x52, 0x05, 0x50, 0x2d, 0x33, 0x38, 0x34, 0x52, 0x05, 0x50,
|
||||
0x2d, 0x35, 0x32, 0x31, 0x52, 0x03, 0x63, 0x72, 0x76, 0x12, 0x18, 0x0a, 0x01, 0x78, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0xc8, 0x01, 0x01, 0x72, 0x02, 0x10, 0x01,
|
||||
0x52, 0x01, 0x78, 0x12, 0x18, 0x0a, 0x01, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a,
|
||||
0xba, 0x48, 0x07, 0xc8, 0x01, 0x01, 0x72, 0x02, 0x10, 0x01, 0x52, 0x01, 0x79, 0x22, 0x32, 0x0a,
|
||||
0x16, 0x53, 0x79, 0x6d, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x72,
|
||||
0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x18, 0x0a, 0x01, 0x6b, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0xc8, 0x01, 0x01, 0x72, 0x02, 0x10, 0x01, 0x52, 0x01,
|
||||
0x6b, 0x22, 0x7e, 0x0a, 0x10, 0x4f, 0x6b, 0x70, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d,
|
||||
0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x38, 0x0a, 0x03, 0x63, 0x72, 0x76, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x09, 0x42, 0x26, 0xba, 0x48, 0x23, 0xc8, 0x01, 0x01, 0x72, 0x1e, 0x52, 0x07, 0x45, 0x64,
|
||||
0x32, 0x35, 0x35, 0x31, 0x39, 0x52, 0x05, 0x45, 0x64, 0x34, 0x34, 0x38, 0x52, 0x06, 0x58, 0x32,
|
||||
0x35, 0x35, 0x31, 0x39, 0x52, 0x04, 0x58, 0x34, 0x34, 0x38, 0x52, 0x03, 0x63, 0x72, 0x76, 0x12,
|
||||
0x30, 0x0a, 0x01, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x22, 0xba, 0x48, 0x1f, 0xc8,
|
||||
0x01, 0x01, 0x72, 0x1a, 0x10, 0x01, 0x32, 0x16, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a,
|
||||
0x30, 0x2d, 0x39, 0x2d, 0x5f, 0x5d, 0x2b, 0x3d, 0x7b, 0x30, 0x2c, 0x32, 0x7d, 0x24, 0x52, 0x01,
|
||||
0x78, 0x22, 0xa5, 0x11, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x36,
|
||||
0x0a, 0x0d, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x72, 0x69, 0x73, 0x18,
|
||||
0x01, 0x20, 0x03, 0x28, 0x09, 0x42, 0x11, 0xba, 0x48, 0x0e, 0x92, 0x01, 0x0b, 0x08, 0x01, 0x22,
|
||||
0x07, 0x72, 0x05, 0x10, 0x01, 0x88, 0x01, 0x01, 0x52, 0x0c, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65,
|
||||
0x63, 0x74, 0x55, 0x72, 0x69, 0x73, 0x12, 0x76, 0x0a, 0x1a, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f,
|
||||
0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6d, 0x65,
|
||||
0x74, 0x68, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x34, 0xba, 0x48, 0x31, 0x72,
|
||||
0x2f, 0x52, 0x04, 0x6e, 0x6f, 0x6e, 0x65, 0x52, 0x12, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f,
|
||||
0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x70, 0x6f, 0x73, 0x74, 0x52, 0x13, 0x63, 0x6c, 0x69,
|
||||
0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x62, 0x61, 0x73, 0x69, 0x63,
|
||||
0x48, 0x00, 0x52, 0x17, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e,
|
||||
0x74, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x88, 0x01, 0x01, 0x12, 0x1f,
|
||||
0x0a, 0x0b, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x03, 0x20,
|
||||
0x03, 0x28, 0x09, 0x52, 0x0a, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12,
|
||||
0x25, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65,
|
||||
0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x30, 0x0a, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74,
|
||||
0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07,
|
||||
0x72, 0x05, 0x10, 0x01, 0x18, 0xff, 0x01, 0x48, 0x01, 0x52, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e,
|
||||
0x74, 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0xa0, 0x01, 0x0a, 0x15, 0x63, 0x6c, 0x69,
|
||||
0x65, 0x6e, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x7a,
|
||||
0x65, 0x64, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x69, 0x65, 0x74, 0x66, 0x2e,
|
||||
0x72, 0x66, 0x63, 0x37, 0x35, 0x39, 0x31, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64,
|
||||
0x61, 0x74, 0x61, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x6f,
|
||||
0x63, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x38, 0xba, 0x48,
|
||||
0x35, 0x9a, 0x01, 0x32, 0x22, 0x27, 0x72, 0x25, 0x32, 0x23, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x41,
|
||||
0x2d, 0x5a, 0x5d, 0x7b, 0x31, 0x2c, 0x38, 0x7d, 0x28, 0x2d, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d,
|
||||
0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x7b, 0x31, 0x2c, 0x38, 0x7d, 0x29, 0x2a, 0x24, 0x2a, 0x07, 0x72,
|
||||
0x05, 0x10, 0x01, 0x18, 0xff, 0x01, 0x52, 0x13, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4e, 0x61,
|
||||
0x6d, 0x65, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x12, 0x2c, 0x0a, 0x0a, 0x63,
|
||||
0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x42,
|
||||
0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0x88, 0x01, 0x01, 0x48, 0x02, 0x52, 0x09, 0x63, 0x6c, 0x69,
|
||||
0x65, 0x6e, 0x74, 0x55, 0x72, 0x69, 0x88, 0x01, 0x01, 0x12, 0x9b, 0x01, 0x0a, 0x14, 0x63, 0x6c,
|
||||
0x69, 0x65, 0x6e, 0x74, 0x5f, 0x75, 0x72, 0x69, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x7a,
|
||||
0x65, 0x64, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x69, 0x65, 0x74, 0x66, 0x2e,
|
||||
0x72, 0x66, 0x63, 0x37, 0x35, 0x39, 0x31, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64,
|
||||
0x61, 0x74, 0x61, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x55, 0x72, 0x69, 0x4c, 0x6f, 0x63,
|
||||
0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x36, 0xba, 0x48, 0x33,
|
||||
0x9a, 0x01, 0x30, 0x22, 0x27, 0x72, 0x25, 0x32, 0x23, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d,
|
||||
0x5a, 0x5d, 0x7b, 0x31, 0x2c, 0x38, 0x7d, 0x28, 0x2d, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a,
|
||||
0x30, 0x2d, 0x39, 0x5d, 0x7b, 0x31, 0x2c, 0x38, 0x7d, 0x29, 0x2a, 0x24, 0x2a, 0x05, 0x72, 0x03,
|
||||
0x88, 0x01, 0x01, 0x52, 0x12, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x55, 0x72, 0x69, 0x4c, 0x6f,
|
||||
0x63, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x12, 0x28, 0x0a, 0x08, 0x6c, 0x6f, 0x67, 0x6f, 0x5f,
|
||||
0x75, 0x72, 0x69, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03,
|
||||
0x88, 0x01, 0x01, 0x48, 0x03, 0x52, 0x07, 0x6c, 0x6f, 0x67, 0x6f, 0x55, 0x72, 0x69, 0x88, 0x01,
|
||||
0x01, 0x12, 0x95, 0x01, 0x0a, 0x12, 0x6c, 0x6f, 0x67, 0x6f, 0x5f, 0x75, 0x72, 0x69, 0x5f, 0x6c,
|
||||
0x6f, 0x63, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f,
|
||||
0x2e, 0x69, 0x65, 0x74, 0x66, 0x2e, 0x72, 0x66, 0x63, 0x37, 0x35, 0x39, 0x31, 0x2e, 0x76, 0x31,
|
||||
0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x4c, 0x6f, 0x67, 0x6f, 0x55, 0x72,
|
||||
0x69, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42,
|
||||
0x36, 0xba, 0x48, 0x33, 0x9a, 0x01, 0x30, 0x22, 0x27, 0x72, 0x25, 0x32, 0x23, 0x5e, 0x5b, 0x61,
|
||||
0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x5d, 0x7b, 0x31, 0x2c, 0x38, 0x7d, 0x28, 0x2d, 0x5b, 0x61, 0x2d,
|
||||
0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x7b, 0x31, 0x2c, 0x38, 0x7d, 0x29, 0x2a, 0x24,
|
||||
0x2a, 0x05, 0x72, 0x03, 0x88, 0x01, 0x01, 0x52, 0x10, 0x6c, 0x6f, 0x67, 0x6f, 0x55, 0x72, 0x69,
|
||||
0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x12, 0x30, 0x0a, 0x05, 0x73, 0x63, 0x6f,
|
||||
0x70, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x42, 0x15, 0xba, 0x48, 0x12, 0x72, 0x10, 0x10,
|
||||
0x01, 0x32, 0x0c, 0x5e, 0x5c, 0x53, 0x2b, 0x28, 0x20, 0x5c, 0x53, 0x2b, 0x29, 0x2a, 0x24, 0x48,
|
||||
0x04, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x88, 0x01, 0x01, 0x12, 0x28, 0x0a, 0x08, 0x63,
|
||||
0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x09, 0x42, 0x0c, 0xba,
|
||||
0x48, 0x09, 0x92, 0x01, 0x06, 0x22, 0x04, 0x72, 0x02, 0x60, 0x01, 0x52, 0x08, 0x63, 0x6f, 0x6e,
|
||||
0x74, 0x61, 0x63, 0x74, 0x73, 0x12, 0x26, 0x0a, 0x07, 0x74, 0x6f, 0x73, 0x5f, 0x75, 0x72, 0x69,
|
||||
0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0x88, 0x01, 0x01,
|
||||
0x48, 0x05, 0x52, 0x06, 0x74, 0x6f, 0x73, 0x55, 0x72, 0x69, 0x88, 0x01, 0x01, 0x12, 0x92, 0x01,
|
||||
0x0a, 0x11, 0x74, 0x6f, 0x73, 0x5f, 0x75, 0x72, 0x69, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x69,
|
||||
0x7a, 0x65, 0x64, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x69, 0x65, 0x74, 0x66,
|
||||
0x2e, 0x72, 0x66, 0x63, 0x37, 0x35, 0x39, 0x31, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x61,
|
||||
0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x6f, 0x73, 0x55, 0x72, 0x69, 0x4c, 0x6f, 0x63, 0x61, 0x6c,
|
||||
0x69, 0x7a, 0x65, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x36, 0xba, 0x48, 0x33, 0x9a, 0x01,
|
||||
0x30, 0x22, 0x27, 0x72, 0x25, 0x32, 0x23, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x5d,
|
||||
0x7b, 0x31, 0x2c, 0x38, 0x7d, 0x28, 0x2d, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d,
|
||||
0x39, 0x5d, 0x7b, 0x31, 0x2c, 0x38, 0x7d, 0x29, 0x2a, 0x24, 0x2a, 0x05, 0x72, 0x03, 0x88, 0x01,
|
||||
0x01, 0x52, 0x0f, 0x74, 0x6f, 0x73, 0x55, 0x72, 0x69, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x7a,
|
||||
0x65, 0x64, 0x12, 0x2c, 0x0a, 0x0a, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x75, 0x72, 0x69,
|
||||
0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0x88, 0x01, 0x01,
|
||||
0x48, 0x06, 0x52, 0x09, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x72, 0x69, 0x88, 0x01, 0x01,
|
||||
0x12, 0x9b, 0x01, 0x0a, 0x14, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x75, 0x72, 0x69, 0x5f,
|
||||
0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x18, 0x10, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||
0x31, 0x2e, 0x69, 0x65, 0x74, 0x66, 0x2e, 0x72, 0x66, 0x63, 0x37, 0x35, 0x39, 0x31, 0x2e, 0x76,
|
||||
0x31, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63,
|
||||
0x79, 0x55, 0x72, 0x69, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x45, 0x6e, 0x74,
|
||||
0x72, 0x79, 0x42, 0x36, 0xba, 0x48, 0x33, 0x9a, 0x01, 0x30, 0x22, 0x27, 0x72, 0x25, 0x32, 0x23,
|
||||
0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x5d, 0x7b, 0x31, 0x2c, 0x38, 0x7d, 0x28, 0x2d,
|
||||
0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x7b, 0x31, 0x2c, 0x38, 0x7d,
|
||||
0x29, 0x2a, 0x24, 0x2a, 0x05, 0x72, 0x03, 0x88, 0x01, 0x01, 0x52, 0x12, 0x70, 0x6f, 0x6c, 0x69,
|
||||
0x63, 0x79, 0x55, 0x72, 0x69, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x12, 0x28,
|
||||
0x0a, 0x08, 0x6a, 0x77, 0x6b, 0x73, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09,
|
||||
0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0x88, 0x01, 0x01, 0x48, 0x07, 0x52, 0x07, 0x6a, 0x77,
|
||||
0x6b, 0x73, 0x55, 0x72, 0x69, 0x88, 0x01, 0x01, 0x12, 0x37, 0x0a, 0x04, 0x6a, 0x77, 0x6b, 0x73,
|
||||
0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x69, 0x65, 0x74, 0x66, 0x2e, 0x72, 0x66,
|
||||
0x63, 0x37, 0x35, 0x39, 0x31, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x73, 0x6f, 0x6e, 0x57, 0x65, 0x62,
|
||||
0x4b, 0x65, 0x79, 0x53, 0x65, 0x74, 0x48, 0x08, 0x52, 0x04, 0x6a, 0x77, 0x6b, 0x73, 0x88, 0x01,
|
||||
0x01, 0x12, 0x30, 0x0a, 0x0b, 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x69, 0x64,
|
||||
0x18, 0x13, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0x72, 0x05, 0x10, 0x01, 0x18,
|
||||
0xff, 0x01, 0x48, 0x09, 0x52, 0x0a, 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x49, 0x64,
|
||||
0x88, 0x01, 0x01, 0x12, 0x3a, 0x0a, 0x10, 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x5f,
|
||||
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba,
|
||||
0x48, 0x07, 0x72, 0x05, 0x10, 0x01, 0x18, 0xff, 0x01, 0x48, 0x0a, 0x52, 0x0f, 0x73, 0x6f, 0x66,
|
||||
0x74, 0x77, 0x61, 0x72, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x1a,
|
||||
0x46, 0x0a, 0x18, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x6f, 0x63,
|
||||
0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b,
|
||||
0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a,
|
||||
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61,
|
||||
0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x45, 0x0a, 0x17, 0x43, 0x6c, 0x69, 0x65, 0x6e,
|
||||
0x74, 0x55, 0x72, 0x69, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x45, 0x6e, 0x74,
|
||||
0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x43,
|
||||
0x0a, 0x15, 0x4c, 0x6f, 0x67, 0x6f, 0x55, 0x72, 0x69, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x7a,
|
||||
0x65, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c,
|
||||
0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a,
|
||||
0x02, 0x38, 0x01, 0x1a, 0x43, 0x0a, 0x15, 0x4c, 0x6f, 0x67, 0x6f, 0x55, 0x72, 0x69, 0x4c, 0x6f,
|
||||
0x63, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03,
|
||||
0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14,
|
||||
0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76,
|
||||
0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x42, 0x0a, 0x14, 0x54, 0x6f, 0x73, 0x55,
|
||||
0x72, 0x69, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79,
|
||||
0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
|
||||
0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x45, 0x0a, 0x17,
|
||||
0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x72, 0x69, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x7a,
|
||||
0x65, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c,
|
||||
0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a,
|
||||
0x02, 0x38, 0x01, 0x3a, 0x7e, 0xba, 0x48, 0x7b, 0x1a, 0x79, 0x0a, 0x25, 0x63, 0x6c, 0x69, 0x65,
|
||||
0x6e, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x6a, 0x77, 0x6b, 0x73,
|
||||
0x5f, 0x6d, 0x75, 0x74, 0x75, 0x61, 0x6c, 0x5f, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f,
|
||||
0x6e, 0x12, 0x28, 0x6a, 0x77, 0x6b, 0x73, 0x5f, 0x75, 0x72, 0x69, 0x20, 0x61, 0x6e, 0x64, 0x20,
|
||||
0x6a, 0x77, 0x6b, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x6d, 0x75, 0x74, 0x75, 0x61, 0x6c, 0x6c,
|
||||
0x79, 0x20, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x1a, 0x26, 0x21, 0x68, 0x61,
|
||||
0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6a, 0x77, 0x6b, 0x73, 0x5f, 0x75, 0x72, 0x69, 0x29,
|
||||
0x20, 0x7c, 0x7c, 0x20, 0x21, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6a, 0x77,
|
||||
0x6b, 0x73, 0x29, 0x42, 0x1d, 0x0a, 0x1b, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x65, 0x6e,
|
||||
0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6d, 0x65, 0x74, 0x68,
|
||||
0x6f, 0x64, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x6e, 0x61,
|
||||
0x6d, 0x65, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x75, 0x72,
|
||||
0x69, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x6c, 0x6f, 0x67, 0x6f, 0x5f, 0x75, 0x72, 0x69, 0x42, 0x08,
|
||||
0x0a, 0x06, 0x5f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x74, 0x6f, 0x73,
|
||||
0x5f, 0x75, 0x72, 0x69, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f,
|
||||
0x75, 0x72, 0x69, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x6a, 0x77, 0x6b, 0x73, 0x5f, 0x75, 0x72, 0x69,
|
||||
0x42, 0x07, 0x0a, 0x05, 0x5f, 0x6a, 0x77, 0x6b, 0x73, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x73, 0x6f,
|
||||
0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x69, 0x64, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x73, 0x6f,
|
||||
0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0xba,
|
||||
0x01, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x69, 0x65, 0x74, 0x66, 0x2e, 0x72, 0x66, 0x63, 0x37,
|
||||
0x35, 0x39, 0x31, 0x2e, 0x76, 0x31, 0x42, 0x0a, 0x54, 0x79, 0x70, 0x65, 0x73, 0x50, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x50, 0x01, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
|
||||
0x2f, 0x70, 0x6f, 0x6d, 0x65, 0x72, 0x69, 0x75, 0x6d, 0x2f, 0x70, 0x6f, 0x6d, 0x65, 0x72, 0x69,
|
||||
0x75, 0x6d, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x72, 0x66, 0x63, 0x37,
|
||||
0x35, 0x39, 0x31, 0x76, 0x31, 0x3b, 0x72, 0x66, 0x63, 0x37, 0x35, 0x39, 0x31, 0x76, 0x31, 0xa2,
|
||||
0x02, 0x03, 0x49, 0x52, 0x58, 0xaa, 0x02, 0x0f, 0x49, 0x65, 0x74, 0x66, 0x2e, 0x52, 0x66, 0x63,
|
||||
0x37, 0x35, 0x39, 0x31, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0f, 0x49, 0x65, 0x74, 0x66, 0x5c, 0x52,
|
||||
0x66, 0x63, 0x37, 0x35, 0x39, 0x31, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x1b, 0x49, 0x65, 0x74, 0x66,
|
||||
0x5c, 0x52, 0x66, 0x63, 0x37, 0x35, 0x39, 0x31, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d,
|
||||
0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x11, 0x49, 0x65, 0x74, 0x66, 0x3a, 0x3a,
|
||||
0x52, 0x66, 0x63, 0x37, 0x35, 0x39, 0x31, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x33,
|
||||
0x02, 0x38, 0x01, 0x1a, 0x42, 0x0a, 0x14, 0x54, 0x6f, 0x73, 0x55, 0x72, 0x69, 0x4c, 0x6f, 0x63,
|
||||
0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b,
|
||||
0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a,
|
||||
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61,
|
||||
0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x45, 0x0a, 0x17, 0x50, 0x6f, 0x6c, 0x69, 0x63,
|
||||
0x79, 0x55, 0x72, 0x69, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x45, 0x6e, 0x74,
|
||||
0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x3a, 0x7e,
|
||||
0xba, 0x48, 0x7b, 0x1a, 0x79, 0x0a, 0x25, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x65,
|
||||
0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x6a, 0x77, 0x6b, 0x73, 0x5f, 0x6d, 0x75, 0x74, 0x75,
|
||||
0x61, 0x6c, 0x5f, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x6a, 0x77,
|
||||
0x6b, 0x73, 0x5f, 0x75, 0x72, 0x69, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6a, 0x77, 0x6b, 0x73, 0x20,
|
||||
0x61, 0x72, 0x65, 0x20, 0x6d, 0x75, 0x74, 0x75, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x65, 0x78, 0x63,
|
||||
0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x1a, 0x26, 0x21, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69,
|
||||
0x73, 0x2e, 0x6a, 0x77, 0x6b, 0x73, 0x5f, 0x75, 0x72, 0x69, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x21,
|
||||
0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6a, 0x77, 0x6b, 0x73, 0x29, 0x42, 0x1d,
|
||||
0x0a, 0x1b, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e,
|
||||
0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x42, 0x0e, 0x0a,
|
||||
0x0c, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x0d, 0x0a,
|
||||
0x0b, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x75, 0x72, 0x69, 0x42, 0x0b, 0x0a, 0x09,
|
||||
0x5f, 0x6c, 0x6f, 0x67, 0x6f, 0x5f, 0x75, 0x72, 0x69, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x73, 0x63,
|
||||
0x6f, 0x70, 0x65, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x74, 0x6f, 0x73, 0x5f, 0x75, 0x72, 0x69, 0x42,
|
||||
0x0d, 0x0a, 0x0b, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x75, 0x72, 0x69, 0x42, 0x0b,
|
||||
0x0a, 0x09, 0x5f, 0x6a, 0x77, 0x6b, 0x73, 0x5f, 0x75, 0x72, 0x69, 0x42, 0x07, 0x0a, 0x05, 0x5f,
|
||||
0x6a, 0x77, 0x6b, 0x73, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72,
|
||||
0x65, 0x5f, 0x69, 0x64, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72,
|
||||
0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xc2, 0x01, 0x0a, 0x0c, 0x43, 0x6c,
|
||||
0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x20, 0x0a, 0x05, 0x76, 0x61,
|
||||
0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0xc8, 0x01,
|
||||
0x01, 0x72, 0x02, 0x10, 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3e, 0x0a, 0x0a,
|
||||
0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
||||
0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x00, 0x52, 0x09,
|
||||
0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x88, 0x01, 0x01, 0x12, 0x41, 0x0a, 0x0a,
|
||||
0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
||||
0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x06, 0xba, 0x48,
|
||||
0x03, 0xc8, 0x01, 0x01, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x42,
|
||||
0x0d, 0x0a, 0x0b, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x61, 0x74, 0x22, 0xf6,
|
||||
0x01, 0x0a, 0x12, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4c, 0x0a, 0x10, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x19, 0x2e, 0x69, 0x65, 0x74, 0x66, 0x2e, 0x72, 0x66, 0x63, 0x37, 0x35, 0x39, 0x31, 0x2e, 0x76,
|
||||
0x31, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8,
|
||||
0x01, 0x01, 0x52, 0x0f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64,
|
||||
0x61, 0x74, 0x61, 0x12, 0x4e, 0x0a, 0x11, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f,
|
||||
0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19,
|
||||
0x2e, 0x69, 0x65, 0x74, 0x66, 0x2e, 0x72, 0x66, 0x63, 0x37, 0x35, 0x39, 0x31, 0x2e, 0x76, 0x31,
|
||||
0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01,
|
||||
0x01, 0x52, 0x10, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64,
|
||||
0x61, 0x74, 0x61, 0x12, 0x42, 0x0a, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65,
|
||||
0x63, 0x72, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x69, 0x65, 0x74,
|
||||
0x66, 0x2e, 0x72, 0x66, 0x63, 0x37, 0x35, 0x39, 0x31, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x69,
|
||||
0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x0c, 0x63, 0x6c, 0x69, 0x65, 0x6e,
|
||||
0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x42, 0xba, 0x01, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e,
|
||||
0x69, 0x65, 0x74, 0x66, 0x2e, 0x72, 0x66, 0x63, 0x37, 0x35, 0x39, 0x31, 0x2e, 0x76, 0x31, 0x42,
|
||||
0x0a, 0x54, 0x79, 0x70, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x39, 0x67,
|
||||
0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6f, 0x6d, 0x65, 0x72, 0x69,
|
||||
0x75, 0x6d, 0x2f, 0x70, 0x6f, 0x6d, 0x65, 0x72, 0x69, 0x75, 0x6d, 0x2f, 0x69, 0x6e, 0x74, 0x65,
|
||||
0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x72, 0x66, 0x63, 0x37, 0x35, 0x39, 0x31, 0x76, 0x31, 0x3b, 0x72,
|
||||
0x66, 0x63, 0x37, 0x35, 0x39, 0x31, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x49, 0x52, 0x58, 0xaa, 0x02,
|
||||
0x0f, 0x49, 0x65, 0x74, 0x66, 0x2e, 0x52, 0x66, 0x63, 0x37, 0x35, 0x39, 0x31, 0x2e, 0x56, 0x31,
|
||||
0xca, 0x02, 0x0f, 0x49, 0x65, 0x74, 0x66, 0x5c, 0x52, 0x66, 0x63, 0x37, 0x35, 0x39, 0x31, 0x5c,
|
||||
0x56, 0x31, 0xe2, 0x02, 0x1b, 0x49, 0x65, 0x74, 0x66, 0x5c, 0x52, 0x66, 0x63, 0x37, 0x35, 0x39,
|
||||
0x31, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
|
||||
0xea, 0x02, 0x11, 0x49, 0x65, 0x74, 0x66, 0x3a, 0x3a, 0x52, 0x66, 0x63, 0x37, 0x35, 0x39, 0x31,
|
||||
0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
})
|
||||
|
||||
var (
|
||||
|
@ -972,7 +1113,7 @@ func file_types_proto_rawDescGZIP() []byte {
|
|||
return file_types_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_types_proto_msgTypes = make([]protoimpl.MessageInfo, 12)
|
||||
var file_types_proto_msgTypes = make([]protoimpl.MessageInfo, 14)
|
||||
var file_types_proto_goTypes = []any{
|
||||
(*JsonWebKeySet)(nil), // 0: ietf.rfc7591.v1.JsonWebKeySet
|
||||
(*JsonWebKey)(nil), // 1: ietf.rfc7591.v1.JsonWebKey
|
||||
|
@ -980,12 +1121,15 @@ var file_types_proto_goTypes = []any{
|
|||
(*EcKeyParameters)(nil), // 3: ietf.rfc7591.v1.EcKeyParameters
|
||||
(*SymmetricKeyParameters)(nil), // 4: ietf.rfc7591.v1.SymmetricKeyParameters
|
||||
(*OkpKeyParameters)(nil), // 5: ietf.rfc7591.v1.OkpKeyParameters
|
||||
(*ClientMetadata)(nil), // 6: ietf.rfc7591.v1.ClientMetadata
|
||||
nil, // 7: ietf.rfc7591.v1.ClientMetadata.ClientNameLocalizedEntry
|
||||
nil, // 8: ietf.rfc7591.v1.ClientMetadata.ClientUriLocalizedEntry
|
||||
nil, // 9: ietf.rfc7591.v1.ClientMetadata.LogoUriLocalizedEntry
|
||||
nil, // 10: ietf.rfc7591.v1.ClientMetadata.TosUriLocalizedEntry
|
||||
nil, // 11: ietf.rfc7591.v1.ClientMetadata.PolicyUriLocalizedEntry
|
||||
(*Metadata)(nil), // 6: ietf.rfc7591.v1.Metadata
|
||||
(*ClientSecret)(nil), // 7: ietf.rfc7591.v1.ClientSecret
|
||||
(*ClientRegistration)(nil), // 8: ietf.rfc7591.v1.ClientRegistration
|
||||
nil, // 9: ietf.rfc7591.v1.Metadata.ClientNameLocalizedEntry
|
||||
nil, // 10: ietf.rfc7591.v1.Metadata.ClientUriLocalizedEntry
|
||||
nil, // 11: ietf.rfc7591.v1.Metadata.LogoUriLocalizedEntry
|
||||
nil, // 12: ietf.rfc7591.v1.Metadata.TosUriLocalizedEntry
|
||||
nil, // 13: ietf.rfc7591.v1.Metadata.PolicyUriLocalizedEntry
|
||||
(*timestamppb.Timestamp)(nil), // 14: google.protobuf.Timestamp
|
||||
}
|
||||
var file_types_proto_depIdxs = []int32{
|
||||
1, // 0: ietf.rfc7591.v1.JsonWebKeySet.keys:type_name -> ietf.rfc7591.v1.JsonWebKey
|
||||
|
@ -993,17 +1137,22 @@ var file_types_proto_depIdxs = []int32{
|
|||
3, // 2: ietf.rfc7591.v1.JsonWebKey.ec_params:type_name -> ietf.rfc7591.v1.EcKeyParameters
|
||||
4, // 3: ietf.rfc7591.v1.JsonWebKey.symmetric_params:type_name -> ietf.rfc7591.v1.SymmetricKeyParameters
|
||||
5, // 4: ietf.rfc7591.v1.JsonWebKey.okp_params:type_name -> ietf.rfc7591.v1.OkpKeyParameters
|
||||
7, // 5: ietf.rfc7591.v1.ClientMetadata.client_name_localized:type_name -> ietf.rfc7591.v1.ClientMetadata.ClientNameLocalizedEntry
|
||||
8, // 6: ietf.rfc7591.v1.ClientMetadata.client_uri_localized:type_name -> ietf.rfc7591.v1.ClientMetadata.ClientUriLocalizedEntry
|
||||
9, // 7: ietf.rfc7591.v1.ClientMetadata.logo_uri_localized:type_name -> ietf.rfc7591.v1.ClientMetadata.LogoUriLocalizedEntry
|
||||
10, // 8: ietf.rfc7591.v1.ClientMetadata.tos_uri_localized:type_name -> ietf.rfc7591.v1.ClientMetadata.TosUriLocalizedEntry
|
||||
11, // 9: ietf.rfc7591.v1.ClientMetadata.policy_uri_localized:type_name -> ietf.rfc7591.v1.ClientMetadata.PolicyUriLocalizedEntry
|
||||
0, // 10: ietf.rfc7591.v1.ClientMetadata.jwks:type_name -> ietf.rfc7591.v1.JsonWebKeySet
|
||||
11, // [11:11] is the sub-list for method output_type
|
||||
11, // [11:11] is the sub-list for method input_type
|
||||
11, // [11:11] is the sub-list for extension type_name
|
||||
11, // [11:11] is the sub-list for extension extendee
|
||||
0, // [0:11] is the sub-list for field type_name
|
||||
9, // 5: ietf.rfc7591.v1.Metadata.client_name_localized:type_name -> ietf.rfc7591.v1.Metadata.ClientNameLocalizedEntry
|
||||
10, // 6: ietf.rfc7591.v1.Metadata.client_uri_localized:type_name -> ietf.rfc7591.v1.Metadata.ClientUriLocalizedEntry
|
||||
11, // 7: ietf.rfc7591.v1.Metadata.logo_uri_localized:type_name -> ietf.rfc7591.v1.Metadata.LogoUriLocalizedEntry
|
||||
12, // 8: ietf.rfc7591.v1.Metadata.tos_uri_localized:type_name -> ietf.rfc7591.v1.Metadata.TosUriLocalizedEntry
|
||||
13, // 9: ietf.rfc7591.v1.Metadata.policy_uri_localized:type_name -> ietf.rfc7591.v1.Metadata.PolicyUriLocalizedEntry
|
||||
0, // 10: ietf.rfc7591.v1.Metadata.jwks:type_name -> ietf.rfc7591.v1.JsonWebKeySet
|
||||
14, // 11: ietf.rfc7591.v1.ClientSecret.expires_at:type_name -> google.protobuf.Timestamp
|
||||
14, // 12: ietf.rfc7591.v1.ClientSecret.created_at:type_name -> google.protobuf.Timestamp
|
||||
6, // 13: ietf.rfc7591.v1.ClientRegistration.request_metadata:type_name -> ietf.rfc7591.v1.Metadata
|
||||
6, // 14: ietf.rfc7591.v1.ClientRegistration.response_metadata:type_name -> ietf.rfc7591.v1.Metadata
|
||||
7, // 15: ietf.rfc7591.v1.ClientRegistration.client_secret:type_name -> ietf.rfc7591.v1.ClientSecret
|
||||
16, // [16:16] is the sub-list for method output_type
|
||||
16, // [16:16] is the sub-list for method input_type
|
||||
16, // [16:16] is the sub-list for extension type_name
|
||||
16, // [16:16] is the sub-list for extension extendee
|
||||
0, // [0:16] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_types_proto_init() }
|
||||
|
@ -1018,13 +1167,14 @@ func file_types_proto_init() {
|
|||
(*JsonWebKey_OkpParams)(nil),
|
||||
}
|
||||
file_types_proto_msgTypes[6].OneofWrappers = []any{}
|
||||
file_types_proto_msgTypes[7].OneofWrappers = []any{}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_types_proto_rawDesc), len(file_types_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 12,
|
||||
NumMessages: 14,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
|
|
@ -3,6 +3,7 @@ syntax = "proto3";
|
|||
package ietf.rfc7591.v1;
|
||||
|
||||
import "buf/validate/validate.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
option go_package = "github.com/pomerium/pomerium/internal/rfc7591";
|
||||
|
||||
|
@ -153,7 +154,7 @@ message OkpKeyParameters {
|
|||
// Represents the client metadata fields defined in RFC 7591 Section 2.
|
||||
// These values are used both as input to registration requests and output in
|
||||
// registration responses.
|
||||
message ClientMetadata {
|
||||
message Metadata {
|
||||
// Array of redirection URI strings. REQUIRED for clients using flows with
|
||||
// redirection.
|
||||
repeated string redirect_uris = 1 [(buf.validate.field).repeated = {
|
||||
|
@ -169,23 +170,11 @@ message ClientMetadata {
|
|||
|
||||
// OPTIONAL. Array of OAuth 2.0 grant type strings that the client can use.
|
||||
// If omitted, defaults to ["authorization_code"].
|
||||
repeated string grant_types = 3 [(buf.validate.field).repeated.items.string = {
|
||||
in: [
|
||||
"authorization_code",
|
||||
"implicit",
|
||||
"password",
|
||||
"client_credentials",
|
||||
"refresh_token",
|
||||
"urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
"urn:ietf:params:oauth:grant-type:saml2-bearer"
|
||||
],
|
||||
}];
|
||||
repeated string grant_types = 3;
|
||||
|
||||
// OPTIONAL. Array of the OAuth 2.0 response type strings that the client can
|
||||
// use. If omitted, defaults to ["code"].
|
||||
repeated string response_types = 4 [(buf.validate.field).repeated.items.string = {
|
||||
in: ["code", "token"],
|
||||
}];
|
||||
repeated string response_types = 4;
|
||||
|
||||
// OPTIONAL. Human-readable string name of the client. RECOMMENDED.
|
||||
optional string client_name = 5 [(buf.validate.field).string = {min_len: 1, max_len: 255}];
|
||||
|
@ -266,3 +255,32 @@ message ClientMetadata {
|
|||
message: "jwks_uri and jwks are mutually exclusive",
|
||||
};
|
||||
}
|
||||
|
||||
message ClientSecret {
|
||||
// REQUIRED. The client secret value.
|
||||
string value = 1 [
|
||||
(buf.validate.field).required = true,
|
||||
(buf.validate.field).string.min_len = 1
|
||||
];
|
||||
// OPTIONAL. The expiration time of the client secret.
|
||||
optional google.protobuf.Timestamp expires_at = 2;
|
||||
google.protobuf.Timestamp created_at = 3 [
|
||||
(buf.validate.field).required = true
|
||||
];
|
||||
}
|
||||
|
||||
// Represents the client registration storage structure.
|
||||
message ClientRegistration {
|
||||
// Contains the client metadata as requested by the client.
|
||||
Metadata request_metadata = 1 [
|
||||
(buf.validate.field).required = true
|
||||
];
|
||||
|
||||
// Contains the client metadata as was returned by the server.
|
||||
Metadata response_metadata = 2 [
|
||||
(buf.validate.field).required = true
|
||||
];
|
||||
|
||||
// OPTIONAL. The "client_secret" parameter is the secret used by the client
|
||||
ClientSecret client_secret = 3;
|
||||
}
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
package rfc7591v1_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/bufbuild/protovalidate-go"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/testing/protocmp"
|
||||
|
||||
rfc7591 "github.com/pomerium/pomerium/internal/rfc7591"
|
||||
)
|
||||
|
||||
func TestValidation(t *testing.T) {
|
||||
v := &rfc7591.JsonWebKey{Kty: "Invalid"}
|
||||
require.ErrorContains(t, protovalidate.Validate(v), `kty: value must be in list [RSA, EC, oct, OKP] [string.in]`)
|
||||
}
|
||||
|
||||
func TestJSONMarshal(t *testing.T) {
|
||||
data := `
|
||||
{
|
||||
"redirect_uris": [
|
||||
"http://localhost:8002/oauth/callback"
|
||||
],
|
||||
"token_endpoint_auth_method": "none",
|
||||
"grant_types": [
|
||||
"authorization_code",
|
||||
"refresh_token"
|
||||
],
|
||||
"response_types": [
|
||||
"code"
|
||||
],
|
||||
"client_name": "MCP Inspector",
|
||||
"client_uri": "https://github.com/modelcontextprotocol/inspector"
|
||||
}`
|
||||
v := &rfc7591.ClientMetadata{}
|
||||
require.NoError(t, protojson.Unmarshal([]byte(data), v))
|
||||
diff := cmp.Diff(&rfc7591.ClientMetadata{
|
||||
RedirectUris: []string{"http://localhost:8002/oauth/callback"},
|
||||
TokenEndpointAuthMethod: proto.String("none"),
|
||||
GrantTypes: []string{"authorization_code", "refresh_token"},
|
||||
ResponseTypes: []string{"code"},
|
||||
ClientName: proto.String("MCP Inspector"),
|
||||
ClientUri: proto.String("https://github.com/modelcontextprotocol/inspector"),
|
||||
}, v, protocmp.Transform())
|
||||
require.Empty(t, diff)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue