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 }