@@ -54,3 +54,221 @@ keycloak-status: ## Show Keycloak status and connection info
5454.PHONY : keycloak-logs
5555keycloak-logs : # # Tail Keycloak logs
5656	@kubectl logs -n $(KEYCLOAK_NAMESPACE )  -l app=keycloak -f --tail=100
57+ 
58+ .PHONY : keycloak-setup-realm
59+ keycloak-setup-realm : # # Setup OpenShift realm with token exchange support
60+ 	@echo " =========================================" 
61+ 	@echo " Setting up OpenShift Realm for Token Exchange" 
62+ 	@echo " =========================================" 
63+ 	@echo " Using Keycloak at http://localhost:8090" 
64+ 	@echo " (Ensure 'make keycloak-forward' is running in another terminal)" 
65+ 	@echo " " 
66+ 	@echo " Getting admin access token..." 
67+ 	@TOKEN=$$(curl -s -X POST "http://localhost:8090/realms/master/protocol/openid-connect/token" \  
68+ 		-H " Content-Type: application/x-www-form-urlencoded"   \ 
69+ 		-d " username=$( KEYCLOAK_ADMIN_USER) "   \ 
70+ 		-d " password=$( KEYCLOAK_ADMIN_PASSWORD) "   \ 
71+ 		-d " grant_type=password"   \ 
72+ 		-d " client_id=admin-cli"   \ 
73+ 		2> /dev/null |  jq -r ' .access_token // empty'  );  \ 
74+ 	if  [ -z  " $$ TOKEN"   ] ||  [ " $$ TOKEN"   =  " null"   ];  then  \
75+ 		echo  " ❌ Failed to get access token. Check if:" ;  \ 
76+ 		echo  "   - Keycloak is running (make keycloak-install)" ;  \ 
77+ 		echo  "   - Port forwarding is active (make keycloak-forward)" ;  \ 
78+ 		echo  "   - Admin credentials are correct: $( KEYCLOAK_ADMIN_USER)  /$( KEYCLOAK_ADMIN_PASSWORD) " ;  \ 
79+ 		exit  1;  \ 
80+ 	fi ;  \ 
81+ 	echo  " ✅ Successfully obtained access token" ;  \ 
82+ 	echo  " " ;  \ 
83+ 	echo  " Creating OpenShift realm..." ;  \ 
84+ 	REALM_RESPONSE=$$(curl -s -w "%{http_code}" -X POST "http://localhost:8090/admin/realms" \  
85+ 		-H " Authorization: Bearer $$ TOKEN"   \ 
86+ 		-H " Content-Type: application/json"   \ 
87+ 		-d ' {"realm":"openshift","enabled":true}'  );  \ 
88+ 	REALM_CODE=$$(echo "$$REALM_RESPONSE" | tail -c 4 ) ;  \ 
89+ 	if  [ " $$ REALM_CODE"   =  " 201"   ] ||  [ " $$ REALM_CODE"   =  " 409"   ];  then  \
90+ 		if  [ " $$ REALM_CODE"   =  " 201"   ];  then  echo  " ✅ OpenShift realm created" ;  \
91+ 		else  echo  " ✅ OpenShift realm already exists" ;  fi ;  \ 
92+ 	else  \ 
93+ 		echo  " ❌ Failed to create OpenShift realm (HTTP $$ REALM_CODE)" ;  \ 
94+ 		exit  1;  \ 
95+ 	fi ;  \ 
96+ 	echo  " " ;  \ 
97+ 	echo  " Creating mcp:openshift client scope..." ;  \ 
98+ 	SCOPE_RESPONSE=$$(curl -s -w "HTTPCODE:%{http_code}" -X POST "http://localhost:8090/admin/realms/openshift/client-scopes" \  
99+ 		-H " Authorization: Bearer $$ TOKEN"   \ 
100+ 		-H " Content-Type: application/json"   \ 
101+ 		-d ' {"name":"mcp:openshift","protocol":"openid-connect","attributes":{"display.on.consent.screen":"false","include.in.token.scope":"true"}}'  );  \ 
102+ 	SCOPE_CODE=$$(echo "$$SCOPE_RESPONSE" | grep -o "HTTPCODE:[0-9]*" | cut -d: -f2 ) ;  \ 
103+ 	if  [ " $$ SCOPE_CODE"   =  " 201"   ] ||  [ " $$ SCOPE_CODE"   =  " 409"   ];  then  \
104+ 		if  [ " $$ SCOPE_CODE"   =  " 201"   ];  then  echo  " ✅ mcp:openshift client scope created" ;  \
105+ 		else  echo  " ✅ mcp:openshift client scope already exists" ;  fi ;  \ 
106+ 	else  \ 
107+ 		echo  " ❌ Failed to create mcp:openshift scope (HTTP $$ SCOPE_CODE)" ;  \ 
108+ 		exit  1;  \ 
109+ 	fi ;  \ 
110+ 	echo  " " ;  \ 
111+ 	echo  " Adding audience mapper to mcp:openshift scope..." ;  \ 
112+ 	SCOPES_LIST=$$(curl -s -X GET "http://localhost:8090/admin/realms/openshift/client-scopes" \  
113+ 		-H " Authorization: Bearer $$ TOKEN"   \ 
114+ 		-H " Accept: application/json"  );  \ 
115+ 	SCOPE_ID=$$(echo "$$SCOPES_LIST" | jq -r '.[] | select(.name == "mcp:openshift" )  |  .id // empty'  2>/dev/null); \
116+ 	if  [ -z  " $$ SCOPE_ID"   ];  then  \
117+ 		echo  " ❌ Failed to find mcp:openshift scope" ;  \ 
118+ 		exit  1;  \ 
119+ 	fi ;  \ 
120+ 	MAPPER_RESPONSE=$$(curl -s -w "HTTPCODE:%{http_code}" -X POST "http://localhost:8090/admin/realms/openshift/client-scopes/$$SCOPE_ID/protocol-mappers/models" \  
121+ 		-H " Authorization: Bearer $$ TOKEN"   \ 
122+ 		-H " Content-Type: application/json"   \ 
123+ 		-d ' {"name":"openshift-audience","protocol":"openid-connect","protocolMapper":"oidc-audience-mapper","config":{"included.client.audience":"openshift","id.token.claim":"true","access.token.claim":"true"}}'  );  \ 
124+ 	MAPPER_CODE=$$(echo "$$MAPPER_RESPONSE" | grep -o "HTTPCODE:[0-9]*" | cut -d: -f2 ) ;  \ 
125+ 	if  [ " $$ MAPPER_CODE"   =  " 201"   ] ||  [ " $$ MAPPER_CODE"   =  " 409"   ];  then  \
126+ 		if  [ " $$ MAPPER_CODE"   =  " 201"   ];  then  echo  " ✅ Audience mapper added" ;  \
127+ 		else  echo  " ✅ Audience mapper already exists" ;  fi ;  \ 
128+ 	else  \ 
129+ 		echo  " ❌ Failed to create audience mapper (HTTP $$ MAPPER_CODE)" ;  \ 
130+ 		exit  1;  \ 
131+ 	fi ;  \ 
132+ 	echo  " " ;  \ 
133+ 	echo  " Creating groups client scope..." ;  \ 
134+ 	GROUPS_SCOPE_RESPONSE=$$(curl -s -w "HTTPCODE:%{http_code}" -X POST "http://localhost:8090/admin/realms/openshift/client-scopes" \  
135+ 		-H " Authorization: Bearer $$ TOKEN"   \ 
136+ 		-H " Content-Type: application/json"   \ 
137+ 		-d ' {"name":"groups","protocol":"openid-connect","attributes":{"display.on.consent.screen":"false","include.in.token.scope":"true"}}'  );  \ 
138+ 	GROUPS_SCOPE_CODE=$$(echo "$$GROUPS_SCOPE_RESPONSE" | grep -o "HTTPCODE:[0-9]*" | cut -d: -f2 ) ;  \ 
139+ 	if  [ " $$ GROUPS_SCOPE_CODE"   =  " 201"   ] ||  [ " $$ GROUPS_SCOPE_CODE"   =  " 409"   ];  then  \
140+ 		if  [ " $$ GROUPS_SCOPE_CODE"   =  " 201"   ];  then  echo  " ✅ groups client scope created" ;  \
141+ 		else  echo  " ✅ groups client scope already exists" ;  fi ;  \ 
142+ 	else  \ 
143+ 		echo  " ❌ Failed to create groups scope (HTTP $$ GROUPS_SCOPE_CODE)" ;  \ 
144+ 		exit  1;  \ 
145+ 	fi ;  \ 
146+ 	echo  " " ;  \ 
147+ 	echo  " Adding group membership mapper to groups scope..." ;  \ 
148+ 	SCOPES_LIST=$$(curl -s -X GET "http://localhost:8090/admin/realms/openshift/client-scopes" \  
149+ 		-H " Authorization: Bearer $$ TOKEN"   \ 
150+ 		-H " Accept: application/json"  );  \ 
151+ 	GROUPS_SCOPE_ID=$$(echo "$$SCOPES_LIST" | jq -r '.[] | select(.name == "groups" )  |  .id // empty'  2>/dev/null); \
152+ 	if  [ -z  " $$ GROUPS_SCOPE_ID"   ];  then  \
153+ 		echo  " ❌ Failed to find groups scope" ;  \ 
154+ 		exit  1;  \ 
155+ 	fi ;  \ 
156+ 	GROUPS_MAPPER_RESPONSE=$$(curl -s -w "HTTPCODE:%{http_code}" -X POST "http://localhost:8090/admin/realms/openshift/client-scopes/$$GROUPS_SCOPE_ID/protocol-mappers/models" \  
157+ 		-H " Authorization: Bearer $$ TOKEN"   \ 
158+ 		-H " Content-Type: application/json"   \ 
159+ 		-d ' {"name":"groups","protocol":"openid-connect","protocolMapper":"oidc-group-membership-mapper","config":{"claim.name":"groups","full.path":"false","id.token.claim":"true","access.token.claim":"true","userinfo.token.claim":"true"}}'  );  \ 
160+ 	GROUPS_MAPPER_CODE=$$(echo "$$GROUPS_MAPPER_RESPONSE" | grep -o "HTTPCODE:[0-9]*" | cut -d: -f2 ) ;  \ 
161+ 	if  [ " $$ GROUPS_MAPPER_CODE"   =  " 201"   ] ||  [ " $$ GROUPS_MAPPER_CODE"   =  " 409"   ];  then  \
162+ 		if  [ " $$ GROUPS_MAPPER_CODE"   =  " 201"   ];  then  echo  " ✅ Group membership mapper added" ;  \
163+ 		else  echo  " ✅ Group membership mapper already exists" ;  fi ;  \ 
164+ 	else  \ 
165+ 		echo  " ❌ Failed to create group mapper (HTTP $$ GROUPS_MAPPER_CODE)" ;  \ 
166+ 		exit  1;  \ 
167+ 	fi ;  \ 
168+ 	echo  " " ;  \ 
169+ 	echo  " Creating openshift service client..." ;  \ 
170+ 	OPENSHIFT_CLIENT_RESPONSE=$$(curl -s -w "HTTPCODE:%{http_code}" -X POST "http://localhost:8090/admin/realms/openshift/clients" \  
171+ 		-H " Authorization: Bearer $$ TOKEN"   \ 
172+ 		-H " Content-Type: application/json"   \ 
173+ 		-d ' {"clientId":"openshift","enabled":true,"publicClient":false,"standardFlowEnabled":true,"directAccessGrantsEnabled":true,"serviceAccountsEnabled":true,"authorizationServicesEnabled":false,"redirectUris":["*"],"defaultClientScopes":["groups"],"optionalClientScopes":[]}'  );  \ 
174+ 	OPENSHIFT_CLIENT_CODE=$$(echo "$$OPENSHIFT_CLIENT_RESPONSE" | grep -o "HTTPCODE:[0-9]*" | cut -d: -f2 ) ;  \ 
175+ 	if  [ " $$ OPENSHIFT_CLIENT_CODE"   =  " 201"   ] ||  [ " $$ OPENSHIFT_CLIENT_CODE"   =  " 409"   ];  then  \
176+ 		if  [ " $$ OPENSHIFT_CLIENT_CODE"   =  " 201"   ];  then  echo  " ✅ openshift client created" ;  \
177+ 		else  echo  " ✅ openshift client already exists" ;  fi ;  \ 
178+ 	else  \ 
179+ 		echo  " ❌ Failed to create openshift client (HTTP $$ OPENSHIFT_CLIENT_CODE)" ;  \ 
180+ 		exit  1;  \ 
181+ 	fi ;  \ 
182+ 	echo  " " ;  \ 
183+ 	echo  " Creating mcp-server client with token exchange..." ;  \ 
184+ 	MCP_CLIENT_RESPONSE=$$(curl -s -w "HTTPCODE:%{http_code}" -X POST "http://localhost:8090/admin/realms/openshift/clients" \  
185+ 		-H " Authorization: Bearer $$ TOKEN"   \ 
186+ 		-H " Content-Type: application/json"   \ 
187+ 		-d ' {"clientId":"mcp-server","enabled":true,"publicClient":false,"standardFlowEnabled":true,"directAccessGrantsEnabled":true,"serviceAccountsEnabled":true,"authorizationServicesEnabled":false,"redirectUris":["*"],"defaultClientScopes":["groups"],"optionalClientScopes":["mcp:openshift"],"attributes":{"oauth2.device.authorization.grant.enabled":"false","oidc.ciba.grant.enabled":"false","backchannel.logout.session.required":"true","backchannel.logout.revoke.offline.tokens":"false"}}'  );  \ 
188+ 	MCP_CLIENT_CODE=$$(echo "$$MCP_CLIENT_RESPONSE" | grep -o "HTTPCODE:[0-9]*" | cut -d: -f2 ) ;  \ 
189+ 	if  [ " $$ MCP_CLIENT_CODE"   =  " 201"   ] ||  [ " $$ MCP_CLIENT_CODE"   =  " 409"   ];  then  \
190+ 		if  [ " $$ MCP_CLIENT_CODE"   =  " 201"   ];  then  echo  " ✅ mcp-server client created" ;  \
191+ 		else  echo  " ✅ mcp-server client already exists" ;  fi ;  \ 
192+ 	else  \ 
193+ 		echo  " ❌ Failed to create mcp-server client (HTTP $$ MCP_CLIENT_CODE)" ;  \ 
194+ 		exit  1;  \ 
195+ 	fi ;  \ 
196+ 	echo  " " ;  \ 
197+ 	echo  " Enabling token exchange for mcp-server..." ;  \ 
198+ 	CLIENTS_LIST=$$(curl -s -X GET "http://localhost:8090/admin/realms/openshift/clients" \  
199+ 		-H " Authorization: Bearer $$ TOKEN"   \ 
200+ 		-H " Accept: application/json"  );  \ 
201+ 	MCP_CLIENT_ID=$$(echo "$$CLIENTS_LIST" | jq -r '.[] | select(.clientId == "mcp-server" )  |  .id // empty'  2>/dev/null); \
202+ 	if  [ -z  " $$ MCP_CLIENT_ID"   ];  then  \
203+ 		echo  " ❌ Failed to find mcp-server client" ;  \ 
204+ 		exit  1;  \ 
205+ 	fi ;  \ 
206+ 	PERMS_RESPONSE=$$(curl -s -w "HTTPCODE:%{http_code}" -X PUT "http://localhost:8090/admin/realms/openshift/clients/$$MCP_CLIENT_ID/management/permissions" \  
207+ 		-H " Authorization: Bearer $$ TOKEN"   \ 
208+ 		-H " Content-Type: application/json"   \ 
209+ 		-d ' {"enabled":true}'  );  \ 
210+ 	PERMS_CODE=$$(echo "$$PERMS_RESPONSE" | grep -o "HTTPCODE:[0-9]*" | cut -d: -f2 ) ;  \ 
211+ 	if  [ " $$ PERMS_CODE"   =  " 200"   ];  then  \
212+ 		echo  " ✅ Token exchange permissions enabled" ;  \ 
213+ 	else  \ 
214+ 		echo  " ⚠️  Could not enable permissions (HTTP $$ PERMS_CODE) - may need manual configuration" ;  \ 
215+ 	fi ;  \ 
216+ 	echo  " " ;  \ 
217+ 	echo  " Getting mcp-server client secret..." ;  \ 
218+ 	SECRET_RESPONSE=$$(curl -s -X GET "http://localhost:8090/admin/realms/openshift/clients/$$MCP_CLIENT_ID/client-secret" \  
219+ 		-H " Authorization: Bearer $$ TOKEN"   \ 
220+ 		-H " Accept: application/json"  );  \ 
221+ 	CLIENT_SECRET=$$(echo "$$SECRET_RESPONSE" | jq -r '.value // empty' 2>/dev/null ) ;  \ 
222+ 	if  [ -z  " $$ CLIENT_SECRET"   ];  then  \
223+ 		echo  " ❌ Failed to get client secret" ;  \ 
224+ 	else  \ 
225+ 		echo  " ✅ Client secret retrieved" ;  \ 
226+ 	fi ;  \ 
227+ 	echo  " " ;  \ 
228+ 	echo  " Creating test user developer/developer..." ;  \ 
229+ 	USER_RESPONSE=$$(curl -s -w "%{http_code}" -X POST "http://localhost:8090/admin/realms/openshift/users" \  
230+ 		-H " Authorization: Bearer $$ TOKEN"   \ 
231+ 		-H " Content-Type: application/json"   \ 
232+ 		-d 
' {"username":"developer","email":"[email protected] ","firstName":"Developer","lastName":"User","enabled":true,"emailVerified":true,"credentials":[{"type":"password","value":"developer","temporary":false}]}' )
;  \ 233+ 	USER_CODE=$$(echo "$$USER_RESPONSE" | tail -c 4 ) ;  \ 
234+ 	if  [ " $$ USER_CODE"   =  " 201"   ] ||  [ " $$ USER_CODE"   =  " 409"   ];  then  \
235+ 		if  [ " $$ USER_CODE"   =  " 201"   ];  then  echo  " ✅ developer user created" ;  \
236+ 		else  echo  " ✅ developer user already exists" ;  fi ;  \ 
237+ 	else  \ 
238+ 		echo  " ❌ Failed to create developer user (HTTP $$ USER_CODE)" ;  \ 
239+ 		exit  1;  \ 
240+ 	fi ;  \ 
241+ 	echo  " " ;  \ 
242+ 	echo  " 🎉 OpenShift realm setup complete!" ;  \ 
243+ 	echo  " " ;  \ 
244+ 	echo  " ========================================" ;  \ 
245+ 	echo  " Configuration Summary" ;  \ 
246+ 	echo  " ========================================" ;  \ 
247+ 	echo  " Realm: openshift" ;  \ 
248+ 	echo  " Authorization URL: http://localhost:8090/realms/openshift" ;  \ 
249+ 	echo  " " ;  \ 
250+ 	echo  " Test User:" ;  \ 
251+ 	echo  "   Username: developer" ;  \ 
252+ 	echo  "   Password: developer" ;  \ 
253+ 	echo  "   Email: [email protected] " ;  \  254+ 	echo  " " ;  \ 
255+ 	echo  " Clients:" ;  \ 
256+ 	echo  "   mcp-server (confidential, token exchange enabled)" ;  \ 
257+ 	echo  "     Client ID: mcp-server" ;  \ 
258+ 	echo  "     Client Secret: $$ CLIENT_SECRET" ;  \ 
259+ 	echo  "   openshift (service account)" ;  \ 
260+ 	echo  "     Client ID: openshift" ;  \ 
261+ 	echo  " " ;  \ 
262+ 	echo  " Client Scopes:" ;  \ 
263+ 	echo  "   mcp:openshift (optional) - Audience: openshift" ;  \ 
264+ 	echo  "   groups (default) - Group membership mapper" ;  \ 
265+ 	echo  " " ;  \ 
266+ 	echo  " TOML Configuration:" ;  \ 
267+ 	echo  "   require_oauth = true" ;  \ 
268+ 	echo  "   oauth_audience = \" mcp-server\" " ;  \ 
269+ 	echo  "   authorization_url = \" http://localhost:8090/realms/openshift\" " ;  \ 
270+ 	echo  "   sts_client_id = \" mcp-server\" " ;  \ 
271+ 	echo  "   sts_client_secret = \" $$ CLIENT_SECRET\" " ;  \ 
272+ 	echo  "   sts_audience = \" openshift\" " ;  \ 
273+ 	echo  "   sts_scopes = [\" mcp:openshift\" ]" ;  \ 
274+ 	echo  " ========================================" 
0 commit comments