diff --git a/.gitignore b/.gitignore index 76936b6fa9..449b577734 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,6 @@ compose.yml cli-tests/node_modules/ cli-tests/playwright-report/ + +api-tests/pmm-api-tests-output.txt +api-tests/pmm-api-tests-junit-report.xml diff --git a/admin/commands/management/register.go b/admin/commands/management/register.go index 93e16cd2d8..b075eb537b 100644 --- a/admin/commands/management/register.go +++ b/admin/commands/management/register.go @@ -28,12 +28,16 @@ var registerResultT = commands.ParseTemplate(` pmm-agent registered. pmm-agent ID: {{ .PMMAgent.AgentID }} Node ID : {{ .PMMAgent.RunsOnNodeID }} +{{ if .Warning }} +Warning: {{ .Warning }} +{{- end -}} `) type registerResult struct { GenericNode *node.RegisterNodeOKBodyGenericNode `json:"generic_node"` ContainerNode *node.RegisterNodeOKBodyContainerNode `json:"container_node"` PMMAgent *node.RegisterNodeOKBodyPMMAgent `json:"pmm_agent"` + Warning string `json:"warning"` } func (res *registerResult) Result() {} @@ -96,5 +100,6 @@ func (cmd *RegisterCommand) RunCmd() (commands.Result, error) { GenericNode: resp.Payload.GenericNode, ContainerNode: resp.Payload.ContainerNode, PMMAgent: resp.Payload.PMMAgent, + Warning: resp.Payload.Warning, }, nil } diff --git a/admin/commands/management/register_test.go b/admin/commands/management/register_test.go new file mode 100644 index 0000000000..afbad22fcb --- /dev/null +++ b/admin/commands/management/register_test.go @@ -0,0 +1,67 @@ +// Copyright (C) 2023 Percona LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package management + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/percona/pmm/api/managementpb/json/client/node" +) + +func TestRegisterResult(t *testing.T) { + tests := []struct { + name string + result registerResult + want string + }{ + { + name: "Success", + result: registerResult{ + PMMAgent: &node.RegisterNodeOKBodyPMMAgent{ + AgentID: "/agent_id/new_id", + RunsOnNodeID: "/node_id/second_id", + }, + Warning: "", + }, + want: `pmm-agent registered. +pmm-agent ID: /agent_id/new_id +Node ID : /node_id/second_id +`, + }, + { + name: "With warning", + result: registerResult{ + PMMAgent: &node.RegisterNodeOKBodyPMMAgent{ + AgentID: "/agent_id/warning", + RunsOnNodeID: "/node_id/warning_node", + }, + Warning: "Couldn't create Admin API Key", + }, + want: `pmm-agent registered. +pmm-agent ID: /agent_id/warning +Node ID : /node_id/warning_node + +Warning: Couldn't create Admin API Key +`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equalf(t, tt.want, tt.result.String(), "String()") + }) + } +} diff --git a/api/managementpb/json/client/node/register_node_responses.go b/api/managementpb/json/client/node/register_node_responses.go index 747c5965d7..4f72dc8936 100644 --- a/api/managementpb/json/client/node/register_node_responses.go +++ b/api/managementpb/json/client/node/register_node_responses.go @@ -463,6 +463,9 @@ type RegisterNodeOKBody struct { // Token represents token for vmagent auth config. Token string `json:"token,omitempty"` + // Warning message. + Warning string `json:"warning,omitempty"` + // container node ContainerNode *RegisterNodeOKBodyContainerNode `json:"container_node,omitempty"` diff --git a/api/managementpb/json/managementpb.json b/api/managementpb/json/managementpb.json index 5ad34a2a41..5b3430fad2 100644 --- a/api/managementpb/json/managementpb.json +++ b/api/managementpb/json/managementpb.json @@ -3899,6 +3899,11 @@ "description": "Token represents token for vmagent auth config.", "type": "string", "x-order": 3 + }, + "warning": { + "description": "Warning message.", + "type": "string", + "x-order": 4 } } } diff --git a/api/managementpb/node.pb.go b/api/managementpb/node.pb.go index 2f557c0dab..61784a6679 100644 --- a/api/managementpb/node.pb.go +++ b/api/managementpb/node.pb.go @@ -212,6 +212,8 @@ type RegisterNodeResponse struct { PmmAgent *inventorypb.PMMAgent `protobuf:"bytes,3,opt,name=pmm_agent,json=pmmAgent,proto3" json:"pmm_agent,omitempty"` // Token represents token for vmagent auth config. Token string `protobuf:"bytes,4,opt,name=token,proto3" json:"token,omitempty"` + // Warning message. + Warning string `protobuf:"bytes,5,opt,name=warning,proto3" json:"warning,omitempty"` } func (x *RegisterNodeResponse) Reset() { @@ -274,6 +276,13 @@ func (x *RegisterNodeResponse) GetToken() string { return "" } +func (x *RegisterNodeResponse) GetWarning() string { + if x != nil { + return x.Warning + } + return "" +} + var File_managementpb_node_proto protoreflect.FileDescriptor var file_managementpb_node_proto_rawDesc = []byte{ @@ -332,7 +341,7 @@ var file_managementpb_node_proto_rawDesc = []byte{ 0x6d, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 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, 0x22, 0xda, 0x01, 0x0a, 0x14, 0x52, 0x65, 0x67, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xf4, 0x01, 0x0a, 0x14, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x0c, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x69, 0x6e, 0x76, 0x65, 0x6e, 0x74, @@ -346,28 +355,30 @@ var file_managementpb_node_proto_rawDesc = []byte{ 0x32, 0x13, 0x2e, 0x69, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x50, 0x4d, 0x4d, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x08, 0x70, 0x6d, 0x6d, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x32, 0xba, 0x01, 0x0a, 0x04, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0xb1, - 0x01, 0x0a, 0x0c, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x12, - 0x1f, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x20, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, - 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x5e, 0x92, 0x41, 0x34, 0x12, 0x0d, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, - 0x72, 0x20, 0x4e, 0x6f, 0x64, 0x65, 0x1a, 0x23, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, - 0x73, 0x20, 0x61, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x4e, 0x6f, 0x64, 0x65, 0x20, 0x61, 0x6e, 0x64, - 0x20, 0x70, 0x6d, 0x6d, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x21, 0x3a, 0x01, 0x2a, 0x22, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, - 0x6d, 0x65, 0x6e, 0x74, 0x2f, 0x4e, 0x6f, 0x64, 0x65, 0x2f, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x65, 0x72, 0x42, 0x8c, 0x01, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x09, 0x4e, 0x6f, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, - 0x50, 0x01, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, - 0x65, 0x72, 0x63, 0x6f, 0x6e, 0x61, 0x2f, 0x70, 0x6d, 0x6d, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x6d, - 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x70, 0x62, 0xa2, 0x02, 0x03, 0x4d, 0x58, - 0x58, 0xaa, 0x02, 0x0a, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0xca, 0x02, - 0x0a, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0xe2, 0x02, 0x16, 0x4d, 0x61, - 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0a, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x32, + 0xba, 0x01, 0x0a, 0x04, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0xb1, 0x01, 0x0a, 0x0c, 0x52, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x1f, 0x2e, 0x6d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4e, + 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, + 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5e, 0x92, 0x41, + 0x34, 0x12, 0x0d, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4e, 0x6f, 0x64, 0x65, + 0x1a, 0x23, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x73, 0x20, 0x61, 0x20, 0x6e, 0x65, + 0x77, 0x20, 0x4e, 0x6f, 0x64, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x70, 0x6d, 0x6d, 0x2d, 0x61, + 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x21, 0x3a, 0x01, 0x2a, 0x22, 0x1c, + 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2f, 0x4e, + 0x6f, 0x64, 0x65, 0x2f, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x42, 0x8c, 0x01, 0x0a, + 0x0e, 0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, + 0x09, 0x4e, 0x6f, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x27, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x65, 0x72, 0x63, 0x6f, 0x6e, 0x61, + 0x2f, 0x70, 0x6d, 0x6d, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x70, 0x62, 0xa2, 0x02, 0x03, 0x4d, 0x58, 0x58, 0xaa, 0x02, 0x0a, 0x4d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0xca, 0x02, 0x0a, 0x4d, 0x61, 0x6e, 0x61, 0x67, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0xe2, 0x02, 0x16, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, + 0x0a, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( diff --git a/api/managementpb/node.pb.validate.go b/api/managementpb/node.pb.validate.go index a92c8487c9..1a4e1b48ad 100644 --- a/api/managementpb/node.pb.validate.go +++ b/api/managementpb/node.pb.validate.go @@ -289,6 +289,8 @@ func (m *RegisterNodeResponse) validate(all bool) error { // no validation rules for Token + // no validation rules for Warning + if len(errors) > 0 { return RegisterNodeResponseMultiError(errors) } diff --git a/api/managementpb/node.proto b/api/managementpb/node.proto index ddb52efe7a..5118d9bd03 100644 --- a/api/managementpb/node.proto +++ b/api/managementpb/node.proto @@ -52,6 +52,8 @@ message RegisterNodeResponse { inventory.PMMAgent pmm_agent = 3; // Token represents token for vmagent auth config. string token = 4; + // Warning message. + string warning = 5; } // Node service provides public Management API methods for Nodes. diff --git a/api/swagger/swagger-dev.json b/api/swagger/swagger-dev.json index 7d4c9028c8..3f645c0418 100644 --- a/api/swagger/swagger-dev.json +++ b/api/swagger/swagger-dev.json @@ -27492,6 +27492,11 @@ "description": "Token represents token for vmagent auth config.", "type": "string", "x-order": 3 + }, + "warning": { + "description": "Warning message.", + "type": "string", + "x-order": 4 } } } diff --git a/api/swagger/swagger.json b/api/swagger/swagger.json index ac0d245d84..2d0068d90e 100644 --- a/api/swagger/swagger.json +++ b/api/swagger/swagger.json @@ -18661,6 +18661,11 @@ "description": "Token represents token for vmagent auth config.", "type": "string", "x-order": 3 + }, + "warning": { + "description": "Warning message.", + "type": "string", + "x-order": 4 } } } diff --git a/managed/services/grafana/auth_server.go b/managed/services/grafana/auth_server.go index 9e6ef0fa3f..2a599805dc 100644 --- a/managed/services/grafana/auth_server.go +++ b/managed/services/grafana/auth_server.go @@ -540,15 +540,7 @@ func cleanPath(p string) (string, error) { func (s *AuthServer) getAuthUser(ctx context.Context, req *http.Request, l *logrus.Entry) (*authUser, *authError) { // check Grafana with some headers from request - authHeaders := make(http.Header) - for _, k := range []string{ - "Authorization", - "Cookie", - } { - if v := req.Header.Get(k); v != "" { - authHeaders.Set(k, v) - } - } + authHeaders := s.authHeaders(req) j, err := json.Marshal(authHeaders) if err != nil { l.Warnf("%s", err) @@ -565,6 +557,19 @@ func (s *AuthServer) getAuthUser(ctx context.Context, req *http.Request, l *logr return s.retrieveRole(ctx, hash, authHeaders, l) } +func (s *AuthServer) authHeaders(req *http.Request) http.Header { + authHeaders := make(http.Header) + for _, k := range []string{ + "Authorization", + "Cookie", + } { + if v := req.Header.Get(k); v != "" { + authHeaders.Set(k, v) + } + } + return authHeaders +} + func (s *AuthServer) retrieveRole(ctx context.Context, hash string, authHeaders http.Header, l *logrus.Entry) (*authUser, *authError) { authUser, err := s.c.getAuthUser(ctx, authHeaders) if err != nil { diff --git a/managed/services/grafana/client.go b/managed/services/grafana/client.go index e65b196974..8fdcc8139d 100644 --- a/managed/services/grafana/client.go +++ b/managed/services/grafana/client.go @@ -219,7 +219,7 @@ func (c *Client) GetUserID(ctx context.Context) (int, error) { // Ctx is used only for cancelation. func (c *Client) getAuthUser(ctx context.Context, authHeaders http.Header) (authUser, error) { // Check if it's API Key - if c.isAPIKeyAuth(authHeaders.Get("Authorization")) { + if c.IsAPIKeyAuth(authHeaders) { role, err := c.getRoleForAPIKey(ctx, authHeaders) return authUser{ role: role, @@ -277,7 +277,9 @@ func (c *Client) getAuthUser(ctx context.Context, authHeaders http.Header) (auth }, nil } -func (c *Client) isAPIKeyAuth(authHeader string) bool { +// IsAPIKeyAuth checks if the request is made using an API Key. +func (c *Client) IsAPIKeyAuth(headers http.Header) bool { + authHeader := headers.Get("Authorization") switch { case strings.HasPrefix(authHeader, "Bearer"): return true diff --git a/managed/services/management/mock_api_key_provider_test.go b/managed/services/management/mock_api_key_provider_test.go index 7e3c8a64ef..b85f9fc04c 100644 --- a/managed/services/management/mock_api_key_provider_test.go +++ b/managed/services/management/mock_api_key_provider_test.go @@ -4,6 +4,7 @@ package management import ( context "context" + http "net/http" mock "github.com/stretchr/testify/mock" ) @@ -44,6 +45,20 @@ func (_m *mockApiKeyProvider) CreateAdminAPIKey(ctx context.Context, name string return r0, r1, r2 } +// IsAPIKeyAuth provides a mock function with given fields: headers +func (_m *mockApiKeyProvider) IsAPIKeyAuth(headers http.Header) bool { + ret := _m.Called(headers) + + var r0 bool + if rf, ok := ret.Get(0).(func(http.Header) bool); ok { + r0 = rf(headers) + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + // newMockApiKeyProvider creates a new instance of mockApiKeyProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func newMockApiKeyProvider(t interface { diff --git a/managed/services/management/node.go b/managed/services/management/node.go index f8a43a2220..25f9a6d3b3 100644 --- a/managed/services/management/node.go +++ b/managed/services/management/node.go @@ -19,9 +19,12 @@ import ( "context" "fmt" "math/rand" + "net/http" "github.com/AlekSi/pointer" + "github.com/sirupsen/logrus" "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" "gopkg.in/reform.v1" @@ -35,12 +38,15 @@ import ( type apiKeyProvider interface { CreateAdminAPIKey(ctx context.Context, name string) (int64, string, error) + IsAPIKeyAuth(headers http.Header) bool } // NodeService represents service for working with nodes. type NodeService struct { db *reform.DB akp apiKeyProvider + + l *logrus.Entry } // NewNodeService creates NodeService instance. @@ -48,6 +54,7 @@ func NewNodeService(db *reform.DB, akp apiKeyProvider) *NodeService { return &NodeService{ db: db, akp: akp, + l: logrus.WithField("component", "node"), } } @@ -136,10 +143,28 @@ func (s *NodeService) Register(ctx context.Context, req *managementpb.RegisterNo return nil, e } - apiKeyName := fmt.Sprintf("pmm-agent-%s-%d", req.NodeName, rand.Int63()) //nolint:gosec - _, res.Token, e = s.akp.CreateAdminAPIKey(ctx, apiKeyName) - if e != nil { - return nil, e + // get authorization from headers. + md, ok := metadata.FromIncomingContext(ctx) + if !ok { + msg := "Couldn't create Admin API Key: cannot get headers from metadata" + s.l.Errorln(msg) + res.Warning = msg + return res, nil + } + authorizationHeaders := md.Get("Authorization") + if len(authorizationHeaders) == 0 { + return nil, status.Error(codes.Unauthenticated, "Authorization error.") + } + headers := make(http.Header) + headers.Set("Authorization", authorizationHeaders[0]) + if !s.akp.IsAPIKeyAuth(headers) { + apiKeyName := fmt.Sprintf("pmm-agent-%s-%d", req.NodeName, rand.Int63()) //nolint:gosec + _, res.Token, e = s.akp.CreateAdminAPIKey(ctx, apiKeyName) + if e != nil { + msg := fmt.Sprintf("Couldn't create Admin API Key: %s", e) + s.l.Errorln(msg) + res.Warning = msg + } } return res, nil diff --git a/managed/services/management/node_test.go b/managed/services/management/node_test.go index 8f6bcf9f14..d4f6d8472c 100644 --- a/managed/services/management/node_test.go +++ b/managed/services/management/node_test.go @@ -24,6 +24,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" "gopkg.in/reform.v1" "gopkg.in/reform.v1/dialects/postgresql" @@ -53,8 +54,13 @@ func TestNodeService(t *testing.T) { require.NoError(t, sqlDB.Close()) } + md := metadata.New(map[string]string{ + "Authorization": "Basic username:password", + }) + ctx = metadata.NewIncomingContext(ctx, md) var apiKeyProvider mockApiKeyProvider apiKeyProvider.Test(t) + apiKeyProvider.On("IsAPIKeyAuth", mock.Anything).Return(false) apiKeyProvider.On("CreateAdminAPIKey", ctx, mock.AnythingOfType("string")).Return(int64(0), "test-token", nil) s = NewNodeService(db, &apiKeyProvider)