github.com/confluentinc/cli@v1.100.0/test/mds.go (about)

     1  package test
     2  
     3  import (
     4  	"encoding/json"
     5  	"io"
     6  	"net/http"
     7  	"net/http/httptest"
     8  	"path"
     9  	"sort"
    10  	"strings"
    11  	"testing"
    12  
    13  	"github.com/stretchr/testify/require"
    14  
    15  	mds "github.com/confluentinc/mds-sdk-go/mdsv1"
    16  )
    17  
    18  var (
    19  	rbacRoles = map[string]string{
    20  		"DeveloperRead": `{
    21                        "name":"DeveloperRead",
    22                        "accessPolicy":{
    23                                "scopeType":"Resource",
    24                                "allowedOperations":[
    25                                        {"resourceType":"Cluster","operations":[]},
    26                                        {"resourceType":"TransactionalId","operations":["Describe"]},
    27                                        {"resourceType":"Group","operations":["Read","Describe"]},
    28                                        {"resourceType":"Subject","operations":["Read","ReadCompatibility"]},
    29                                        {"resourceType":"Connector","operations":["ReadStatus","ReadConfig"]},
    30                                        {"resourceType":"Topic","operations":["Read","Describe"]}]}}`,
    31  		"DeveloperWrite": `{
    32                        "name":"DeveloperWrite",
    33                        "accessPolicy":{
    34                                "scopeType":"Resource",
    35                                "allowedOperations":[
    36                                        {"resourceType":"Subject","operations":["Write"]},
    37                                        {"resourceType":"Group","operations":[]},
    38                                        {"resourceType":"Topic","operations":["Write","Describe"]},
    39                                        {"resourceType":"Cluster","operations":["IdempotentWrite"]},
    40                                        {"resourceType":"KsqlCluster","operations":["Contribute"]},
    41                                        {"resourceType":"Connector","operations":["ReadStatus","Configure"]},
    42                                        {"resourceType":"TransactionalId","operations":["Write","Describe"]}]}}`,
    43  		"SecurityAdmin": `{
    44                        "name":"SecurityAdmin",
    45                        "accessPolicy":{
    46                                "scopeType":"Cluster",
    47                                "allowedOperations":[
    48                                        {"resourceType":"All","operations":["DescribeAccess"]}]}}`,
    49  		"SystemAdmin": `{
    50                        "name":"SystemAdmin",
    51                        "accessPolicy":{
    52                                "scopeType":"Cluster",
    53                                "allowedOperations":[
    54                                        {"resourceType":"All","operations":["All"]}]}}`,
    55  	}
    56  )
    57  
    58  func serveMds(t *testing.T) *httptest.Server {
    59  	req := require.New(t)
    60  	router := http.NewServeMux()
    61  	router.HandleFunc("/security/1.0/authenticate", func(w http.ResponseWriter, r *http.Request) {
    62  		w.Header().Set("Content-Type", "text/json")
    63  		reply := &mds.AuthenticationResponse{
    64  			AuthToken: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE1NjE2NjA4NTcsImV4cCI6MjUzMzg2MDM4NDU3LCJhdWQiOiJ3d3cuZXhhbXBsZS5jb20iLCJzdWIiOiJqcm9ja2V0QGV4YW1wbGUuY29tIn0.G6IgrFm5i0mN7Lz9tkZQ2tZvuZ2U7HKnvxMuZAooPmE",
    65  			TokenType: "dunno",
    66  			ExpiresIn: 9999999999,
    67  		}
    68  		b, err := json.Marshal(&reply)
    69  		req.NoError(err)
    70  		_, err = io.WriteString(w, string(b))
    71  		req.NoError(err)
    72  	})
    73  
    74  	routesAndReplies := map[string]string{
    75  		"/security/1.0/principals/User:frodo/groups": `[
    76                         "hobbits",
    77                         "ringBearers"]`,
    78  		"/security/1.0/principals/User:frodo/roleNames": `[
    79                         "DeveloperRead",
    80                         "DeveloperWrite",
    81                         "SecurityAdmin"]`,
    82  		"/security/1.0/principals/User:frodo/roles/DeveloperRead/resources":  `[]`,
    83  		"/security/1.0/principals/User:frodo/roles/DeveloperWrite/resources": `[]`,
    84  		"/security/1.0/principals/User:frodo/roles/SecurityAdmin/resources":  `[]`,
    85  		"/security/1.0/principals/Group:hobbits/roles/DeveloperRead/resources": `[
    86                         {"resourceType":"Topic","name":"drink","patternType":"LITERAL"},
    87                         {"resourceType":"Topic","name":"food","patternType":"LITERAL"}]`,
    88  		"/security/1.0/principals/Group:hobbits/roles/DeveloperWrite/resources": `[
    89                         {"resourceType":"Topic","name":"shire-","patternType":"PREFIXED"}]`,
    90  		"/security/1.0/principals/Group:hobbits/roles/SecurityAdmin/resources":     `[]`,
    91  		"/security/1.0/principals/Group:ringBearers/roles/DeveloperRead/resources": `[]`,
    92  		"/security/1.0/principals/Group:ringBearers/roles/DeveloperWrite/resources": `[
    93                         {"resourceType":"Topic","name":"ring-","patternType":"PREFIXED"}]`,
    94  		"/security/1.0/principals/Group:ringBearers/roles/SecurityAdmin/resources": `[]`,
    95  		"/security/1.0/lookup/principal/User:frodo/resources": `{
    96                         "Group:hobbits":{
    97                                 "DeveloperWrite":[
    98                                         {"resourceType":"Topic","name":"shire-","patternType":"PREFIXED"}],
    99                                 "DeveloperRead":[
   100                                         {"resourceType":"Topic","name":"drink","patternType":"LITERAL"},
   101                                         {"resourceType":"Topic","name":"food","patternType":"LITERAL"}]},
   102                         "Group:ringBearers":{
   103                                 "DeveloperWrite":[
   104                                         {"resourceType":"Topic","name":"ring-","patternType":"PREFIXED"}]},
   105                         "User:frodo":{
   106                                 "SecurityAdmin": []}}`,
   107  		"/security/1.0/lookup/principal/Group:hobbits/resources": `{
   108                         "Group:hobbits":{
   109                                 "DeveloperWrite":[
   110                                         {"resourceType":"Topic","name":"shire-","patternType":"PREFIXED"}],
   111                                 "DeveloperRead":[
   112                                         {"resourceType":"Topic","name":"drink","patternType":"LITERAL"},
   113                                         {"resourceType":"Topic","name":"food","patternType":"LITERAL"}]}}`,
   114  		"/security/1.0/lookup/role/DeveloperRead":                                    `["Group:hobbits"]`,
   115  		"/security/1.0/lookup/role/DeveloperWrite":                                   `["Group:hobbits","Group:ringBearers"]`,
   116  		"/security/1.0/lookup/role/SecurityAdmin":                                    `["User:frodo"]`,
   117  		"/security/1.0/lookup/role/SystemAdmin":                                      `[]`,
   118  		"/security/1.0/lookup/role/DeveloperRead/resource/Topic/name/food":           `["Group:hobbits"]`,
   119  		"/security/1.0/lookup/role/DeveloperRead/resource/Topic/name/shire-parties":  `[]`,
   120  		"/security/1.0/lookup/role/DeveloperWrite/resource/Topic/name/shire-parties": `["Group:hobbits"]`,
   121  	}
   122  	addRoles(routesAndReplies)
   123  
   124  	for route, reply := range routesAndReplies {
   125  		s := reply
   126  		router.HandleFunc(route, func(w http.ResponseWriter, r *http.Request) {
   127  			w.Header().Set("Content-Type", "text/json")
   128  			_, err := io.WriteString(w, s)
   129  			req.NoError(err)
   130  		})
   131  	}
   132  
   133  	router.HandleFunc("/security/1.0/registry/clusters", handleRegistryClusters(t))
   134  
   135  	router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
   136  		_, err := io.WriteString(w, `{"error": {"message": "unexpected call to mds `+r.URL.Path+`"}}`)
   137  		require.NoError(t, err)
   138  	})
   139  	return httptest.NewServer(router)
   140  }
   141  
   142  func addRoles(routesAndReplies map[string]string) {
   143  	base := "/security/1.0/roles"
   144  	var roleNameList []string
   145  	for roleName, roleInfo := range rbacRoles {
   146  		routesAndReplies[path.Join(base, roleName)] = roleInfo
   147  		roleNameList = append(roleNameList, roleName)
   148  	}
   149  
   150  	sort.Strings(roleNameList)
   151  
   152  	var allRoles []string
   153  	for _, roleName := range roleNameList {
   154  		allRoles = append(allRoles, rbacRoles[roleName])
   155  	}
   156  	routesAndReplies[base] = "[" + strings.Join(allRoles, ",") + "]"
   157  }
   158  
   159  func handleRegistryClusters(t *testing.T) func(w http.ResponseWriter, r *http.Request) {
   160  	return func(w http.ResponseWriter, r *http.Request) {
   161  		if r.Method == "GET" {
   162  			w.Header().Set("Content-Type", "text/json")
   163  			clusterType := r.URL.Query().Get("clusterType")
   164  			response := `[ {
   165  		"clusterName": "theMdsConnectCluster",
   166  		"scope": { "clusters": { "kafka-cluster": "kafka-GUID", "connect-cluster": "connect-name" } },
   167  		"hosts": [ { "host": "10.5.5.5", "port": 9005 } ],
   168          "protocol": "HTTPS"
   169  	  },{
   170  		"clusterName": "theMdsKSQLCluster",
   171  		"scope": { "clusters": { "kafka-cluster": "kafka-GUID", "ksql-cluster": "ksql-name" } },
   172  		"hosts": [ { "host": "10.4.4.4", "port": 9004 } ],
   173          "protocol": "HTTPS"
   174  	  },{
   175  		"clusterName": "theMdsKafkaCluster",
   176  		"scope": { "clusters": { "kafka-cluster": "kafka-GUID" } },
   177  		"hosts": [ { "host": "10.10.10.10", "port": 8090 },{ "host": "mds.example.com", "port": 8090 } ],
   178          "protocol": "SASL_PLAINTEXT"
   179  	  },{
   180  		"clusterName": "theMdsSchemaRegistryCluster",
   181  		"scope": { "clusters": { "kafka-cluster": "kafka-GUID", "schema-registry-cluster": "schema-registry-name" } },
   182  		"hosts": [ { "host": "10.3.3.3", "port": 9003 } ],
   183          "protocol": "HTTPS"
   184  	} ]`
   185  			if clusterType == "ksql-cluster" {
   186  				response = `[ {
   187  		    "clusterName": "theMdsKSQLCluster",
   188  		    "scope": { "clusters": { "kafka-cluster": "kafka-GUID", "ksql-cluster": "ksql-name" } },
   189  		    "hosts": [ { "host": "10.4.4.4", "port": 9004 } ],
   190              "protocol": "HTTPS"
   191  			} ]`
   192  			}
   193  			if clusterType == "kafka-cluster" {
   194  				response = `[ {
   195  			"clusterName": "theMdsKafkaCluster",
   196  			"scope": { "clusters": { "kafka-cluster": "kafka-GUID" } },
   197  			"hosts": [ { "host": "10.10.10.10", "port": 8090 },{ "host": "mds.example.com", "port": 8090 } ],
   198          	"protocol": "SASL_PLAINTEXT"
   199  			} ]`
   200  			}
   201  			if clusterType == "connect-cluster" {
   202  				response = `[ {
   203  			"clusterName": "theMdsConnectCluster",
   204  			"scope": { "clusters": { "kafka-cluster": "kafka-GUID", "connect-cluster": "connect-name" } },
   205  			"hosts": [ { "host": "10.5.5.5", "port": 9005 } ],
   206          	"protocol": "HTTPS"
   207  			} ]`
   208  			}
   209  			if clusterType == "schema-registry-cluster" {
   210  				response = `[ {
   211  			"clusterName": "theMdsSchemaRegistryCluster",
   212  			"scope": { "clusters": { "kafka-cluster": "kafka-GUID", "schema-registry-cluster": "schema-registry-name" } },
   213  			"hosts": [ { "host": "10.3.3.3", "port": 9003 } ],
   214          	"protocol": "HTTPS"
   215  			} ]`
   216  			}
   217  			_, err := io.WriteString(w, response)
   218  			require.NoError(t, err)
   219  		}
   220  
   221  		if r.Method == "DELETE" {
   222  			clusterName := r.URL.Query().Get("clusterName")
   223  			require.NotEmpty(t, clusterName)
   224  		}
   225  
   226  		if r.Method == "POST" {
   227  			var clusterInfos []*mds.ClusterInfo
   228  			err := json.NewDecoder(r.Body).Decode(&clusterInfos)
   229  			require.NoError(t, err)
   230  			require.NotEmpty(t, clusterInfos)
   231  			for _, clusterInfo := range clusterInfos {
   232  				require.NotEmpty(t, clusterInfo.ClusterName)
   233  				require.NotEmpty(t, clusterInfo.Hosts)
   234  				require.NotEmpty(t, clusterInfo.Scope)
   235  				require.NotEmpty(t, clusterInfo.Protocol)
   236  			}
   237  		}
   238  	}
   239  }