2 Documentation Test suite: Authentication Support for Keystone
4 ... This feature implements the user management for ODL NBI REST APIs integrated with OpenStack, so that
5 ... the authentication functionality provided by Keystone can be used. This allows consuming ODL NBI REST
6 ... APIs using the same authentication procedures as any OpenStack project, such as Nova, Neutron, etc.
7 ... bringing the benefits of a centralized / unified user management framework.
9 ... As a first step, It shall be possible to authenticate users against Keystone by using passwords
10 ... provided by the users.
14 Library OperatingSystem
15 Library RequestsLibrary
16 Resource ../../../libraries/Utils.robot
17 Resource ../../../libraries/TemplatedRequests.robot
18 Resource ../../../libraries/KarafKeywords.robot
19 Resource ../../../libraries/ClusterManagement.robot
20 Resource ../../../variables/Variables.robot
21 Resource ../../../libraries/AAA/DockerKeystone.robot
23 Suite Setup Init Suite
24 Suite Teardown Cleanup Suite
28 ${URI_CERTIFICATE} /restconf/operations/aaa-cert-rpc:getODLCertificate
29 ${URI_RESTCONF} /restconf/operational/ietf-restconf-monitoring:restconf-state
33 Successful Authentication Including Domain
34 [Documentation] *Test Case: Successful Authentication with user@domain/password credentials*
38 ... - Create an HTTP session with ODL as "sdnadmin" user in "sdn" domain
39 ... - Check that the access to URLs of ODL NBI is allowed \ because "sdnadmin" user is associated to domain "sdn" in Keystone and the provided password is the right one.
43 ... - URL "/restconf/operations/aaa-cert-rpc:getODLCertificate" ia authorized just for "admin" roles according to shiro.ini configuration. As "sdnadmin" has "admin" role in keystone the access is authorized too
45 ... - URL "/restconf/operational/ietf-restconf-monitoring:restconf-state" is not specified neither in shiro.ini nor in MDSAL Dynamic Authorization so no specific role is required
48 ... http://${ODL_SYSTEM_IP}:${RESTCONFPORT}
49 ... auth=${AUTH_SDN_DOMAIN}
50 ... headers=${HEADERS}
51 ${resp} RequestsLibrary.POST On Session session url=${URI_CERTIFICATE} headers=${HEADERS}
52 ... expected_status=anything
53 Should Contain ${ALLOWED_STATUS_CODES} ${resp.status_code}
54 ${resp} RequestsLibrary.GET On Session session url=${URI_RESTCONF} headers=${HEADERS}
55 ... expected_status=anything
56 Should Contain ${ALLOWED_STATUS_CODES} ${resp.status_code}
58 Successful Authentication Without Domain
59 [Documentation] *Test Case: Successful Authentication with user/password credentials. No domain included*
63 ... - Create an HTTP session with ODL as "CSC_user" user without specifying any domain then domain "Default" is considered
64 ... - Check that the access to URLs of ODL NBI is allowed because "CSC_user" user is associated to domain "Default" in Keystone and the provided password is the right one
68 ... - URL "/restconf/operations/aaa-cert-rpc:getODLCertificate" ia authorized just for "admin" roles according to shiro.ini configuration. As "CSC_user" has "admin" role in keystone the access is authorized too
70 ... - URL "/restconf/operational/ietf-restconf-monitoring:restconf-state" is not specified neither in shiro.ini nor in MDSAL Dynamic Authorization so no specific role is required
71 Create Session session http://${ODL_SYSTEM_IP}:${RESTCONFPORT} auth=${AUTH_CSC_SDN} headers=${HEADERS}
72 ${resp} RequestsLibrary.POST On Session session url=${URI_CERTIFICATE} headers=${HEADERS}
73 ... expected_status=anything
74 Should Contain ${ALLOWED_STATUS_CODES} ${resp.status_code}
75 ${resp} RequestsLibrary.GET On Session session url=${URI_RESTCONF} headers=${HEADERS}
76 ... expected_status=anything
77 Should Contain ${ALLOWED_STATUS_CODES} ${resp.status_code}
79 Unsuccessful Authentication Wrong User
80 [Documentation] *Test Case: UnSuccessful Authentication with worng user/password credentials*
84 ... - Create an HTTP session with ODL as an invalid user
85 ... - Check that the access to URLs of ODL NBI is NOT allowed \ because "invaliduser" user does not exist in Keystone
89 ... Due to authentication fails, authorization is not evaluated
90 Create Session session http://${ODL_SYSTEM_IP}:${RESTCONFPORT} auth=${AUTH_INVALID} headers=${HEADERS}
91 ${resp} RequestsLibrary.POST On Session session url=${URI_CERTIFICATE} headers=${HEADERS}
92 ... expected_status=anything
93 Should Contain ${UNAUTHORIZED_STATUS_CODES} ${resp.status_code}
94 ${resp} RequestsLibrary.GET On Session session url=${URI_RESTCONF} headers=${HEADERS}
95 ... expected_status=anything
96 Should Contain ${UNAUTHORIZED_STATUS_CODES} ${resp.status_code}
98 UnSuccessful Authentication Without Domain
99 [Documentation] *Test Case: UnSuccessful Authentication without domain*
104 ... - Create an HTTP session with ODL as "sdnadmin" user without specifying any domain then domain "Default" is considered
105 ... - Check that the access to URLs of ODL NBI is NOT allowed because "sdnadmin" user is not associated to domain "Default" in Keystone but to "sdn" which is not included in the credentials
109 ... Due to authentication fails, authorization is not evaluated
110 Create Session session http://${ODL_SYSTEM_IP}:${RESTCONFPORT} auth=${AUTH_SDN} headers=${HEADERS}
111 ${resp} RequestsLibrary.POST On Session session url=${URI_CERTIFICATE} headers=${HEADERS}
112 ... expected_status=anything
113 Should Contain ${UNAUTHORIZED_STATUS_CODES} ${resp.status_code}
114 ${resp} RequestsLibrary.GET On Session session url=${URI_RESTCONF} headers=${HEADERS}
115 ... expected_status=anything
116 Should Contain ${UNAUTHORIZED_STATUS_CODES} ${resp.status_code}
118 Unsuccessful Authentication Wrong Domain
119 [Documentation] *Test Case: UnSuccessful Authentication with wrong domain*
125 ... - Create an HTTP session with ODL as "sdnadmin" user with "wrong" as domain
126 ... - Check that the access to URLs of ODL NBI is NOT allowed because "sdnadmin" user is not associated to domain "wrong" in Keystone but to "sdn"
130 ... Due to authentication fails, authorization is not evaluated
133 ... http://${ODL_SYSTEM_IP}:${RESTCONFPORT}
134 ... auth=${AUTH_SDN_WRONG_DOM}
135 ... headers=${HEADERS}
136 ${resp} RequestsLibrary.POST On Session session url=${URI_CERTIFICATE} headers=${HEADERS}
137 ... expected_status=anything
138 Should Contain ${UNAUTHORIZED_STATUS_CODES} ${resp.status_code}
139 ${resp} RequestsLibrary.GET On Session session url=${URI_RESTCONF} headers=${HEADERS}
140 ... expected_status=anything
141 Should Contain ${UNAUTHORIZED_STATUS_CODES} ${resp.status_code}
143 Unsuccessful Basic Authorization
144 [Documentation] *Test Case: UnSuccessful Basic Authorization*
148 ... - Provision MDSAL so that users with role "admin" or "user" are authorized to access all URIs
149 ... - Create an HTTP session with ODL as "CSC_user_no_admin" user
150 ... - Check that the access to URL "/restconf/operations/aaa-cert-rpc:getODLCertificate" is NOT authorized because in shiro.ini configuration the access is allowed just to "admin" roles and "CSC_user_no_admin" does not have \ "admin" role in keystone but "user" role even though the MDSAL Dynamic Authorization would allow the access, that is, authorization process is an "AND" operation between shiro.ini and MDSAL Dynamic Authorization
151 ... - Check that the access to URL "/restconf/operational/ietf-restconf-monitoring:restconf-state" is authorized becaiuse that URL is not specified in shiro.ini and in MDSAL Dynamic Authorization access to all URLs is allowed to all user with "user" role
152 Set Suite Variable ${PUT_DYNAMIC_AUTH_FILE} ${CURDIR}/../../../variables/aaa/put-dynamic-auth.json
153 Provision MDSAL ${PUT_DYNAMIC_AUTH_FILE}
156 ... http://${ODL_SYSTEM_IP}:${RESTCONFPORT}
157 ... auth=${AUTH_CSC_NO_ADMIN}
158 ... headers=${HEADERS}
159 ${resp_ok} RequestsLibrary.GET On Session session url=${URI_RESTCONF} headers=${HEADERS}
160 ... expected_status=anything
161 Should Contain ${ALLOWED_STATUS_CODES} ${resp_ok.status_code}
162 ${resp_nook} RequestsLibrary.POST On Session session url=${URI_CERTIFICATE} headers=${HEADERS}
163 ... expected_status=anything
164 Should Contain ${UNAUTHORIZED_STATUS_CODES} ${resp_nook.status_code}
166 Unsuccessful Dynamic Authorization
167 [Documentation] *Test Case: UnSuccessful Dynamic Authorization*
171 ... - Provision MDSAL so that just users with role "admin" are authorized to access all URIs
172 ... - Create an HTTP session with ODL as "CSC_user_no_admin" user
173 ... - Check that the access to URL "/restconf/operations/aaa-cert-rpc:getODLCertificate" is NOT authorized because in shiro.ini configuration the access is allowed just to "admin" roles and "CSC_user_no_admin" does not have \ "admin" role in keystone but "user" role even though the MDSAL Dynamic Authorization would allow the access, that is, authorization process is an "AND" operation between shiro.ini and MDSAL Dynamic Authorization
174 ... - Check that the access to URL "/restconf/operational/ietf-restconf-monitoring:restconf-state" is NOT authorized because although the URL is not specified in shiro.ini, in MDSAL Dynamic Authorization access to all URLs is allowed just for users with "admin" role and "CSC_user_no_admin" does not have \ "admin" role in keystone but "user" role
175 Set Suite Variable ${PUT_DYNAMIC_AUTH_FILE} ${CURDIR}/../../../variables/aaa/put-dynamic-auth-2.json
176 Provision MDSAL ${PUT_DYNAMIC_AUTH_FILE}
179 ... http://${ODL_SYSTEM_IP}:${RESTCONFPORT}
180 ... auth=${AUTH_CSC_NO_ADMIN}
181 ... headers=${HEADERS}
182 ${resp_nook} RequestsLibrary.GET On Session session url=${URI_RESTCONF} headers=${HEADERS}
183 ... expected_status=anything
184 Should Contain ${UNAUTHORIZED_STATUS_CODES} ${resp_nook.status_code}
185 ${resp_nook} RequestsLibrary.POST On Session session url=${URI_CERTIFICATE} headers=${HEADERS}
186 ... expected_status=anything
187 Should Contain ${UNAUTHORIZED_STATUS_CODES} ${resp_nook.status_code}
189 Unsuccessful Dynamic Authorization 2
190 [Documentation] *Test Case: UnSuccessful Dynamic Authorization 2*
194 ... - Provision MDSAL so that:
195 ... \ \ - URI "/restconf/operations/aaa-cert-rpc:getODLCertificate" is authorized just for users with "user" role
196 ... \ - URI "/restconf/operational/**" is authorized just for users with "user" role
198 ... - Create an HTTP session with ODL as "sdnadmin" user
199 ... - Check that the access to URL "/restconf/operations/aaa-cert-rpc:getODLCertificate" is NOT authorized because although in shiro.ini configuration the access is allowed to "admin" roles and "cscadmin" does have \ "admin" role, \ in MDSAL Dynamic Authorization access to that URL is allowed just for users with "user" role and "cscadmin" does not have \ "user" role in keystone but "admin" role
200 ... - Check that the access to URL "/restconf/operational/ietf-restconf-monitoring:restconf-state" is NOT authorized because although in shiro.ini configuration that URL is not considered, \ in MDSAL Dynamic Authorization access to that URL is allowed just for users with "user" role and "cscadmin" does not have \ "user" role in keystone but "admin" role
203 ... - Create an HTTP session with ODL as "CSC_user_no_admin" user
204 ... - Check that the access to URL "/restconf/operations/aaa-cert-rpc:getODLCertificate" is NOT authorized because in shiro.ini configuration the access is allowed just to "admin" roles and "CSC_user_no_admin" does not have \ "admin" role in keystone but "user" role even though the MDSAL Dynamic Authorization would allow the access, that is, authorization process is an "AND" operation between shiro.ini and MDSAL Dynamic Authorization
205 ... - Check that the access to URL "/restconf/operational/ietf-restconf-monitoring:restconf-state" is authorized because the URL is not specified in shiro.ini and in MDSAL Dynamic Authorization access to that URL is allowed just for users with "user" role and "CSC_user_no_admin" does \ have \ "user" role in keystone
206 Set Suite Variable ${PUT_DYNAMIC_AUTH_FILE} ${CURDIR}/../../../variables/aaa/put-dynamic-auth-3.json
207 Provision MDSAL ${PUT_DYNAMIC_AUTH_FILE}
210 ... http://${ODL_SYSTEM_IP}:${RESTCONFPORT}
211 ... auth=${AUTH_SDN_DOMAIN}
212 ... headers=${HEADERS}
213 ${resp} RequestsLibrary.POST On Session session url=${URI_CERTIFICATE} headers=${HEADERS}
214 ... expected_status=anything
215 Should Contain ${UNAUTHORIZED_STATUS_CODES} ${resp.status_code}
216 ${resp} RequestsLibrary.GET On Session session url=${URI_RESTCONF} headers=${HEADERS}
217 ... expected_status=anything
218 Should Contain ${UNAUTHORIZED_STATUS_CODES} ${resp.status_code}
221 ... http://${ODL_SYSTEM_IP}:${RESTCONFPORT}
222 ... auth=${AUTH_CSC_NO_ADMIN}
223 ... headers=${HEADERS}
224 ${resp} RequestsLibrary.GET On Session session url=${URI_RESTCONF} headers=${HEADERS}
225 ... expected_status=anything
226 Should Contain ${ALLOWED_STATUS_CODES} ${resp.status_code}
227 ${resp} RequestsLibrary.POST On Session session url=${URI_CERTIFICATE} headers=${HEADERS}
228 ... expected_status=anything
229 Should Contain ${UNAUTHORIZED_STATUS_CODES} ${resp.status_code}
231 Unsuccessful No Keystone Connection
232 [Documentation] *Test Case: Unsuccessful No Keystone Connection*
236 ... - Put down Keystone
237 ... - All accesses are forbidden
241 ... http://${ODL_SYSTEM_IP}:${RESTCONFPORT}
242 ... auth=${AUTH_SDN_DOMAIN}
243 ... headers=${HEADERS}
244 ${resp} RequestsLibrary.POST On Session session url=${URI_CERTIFICATE} headers=${HEADERS}
245 ... expected_status=anything
246 Should Contain ${UNAUTHORIZED_STATUS_CODES} ${resp.status_code}
247 ${resp} RequestsLibrary.GET On Session session url=${URI_RESTCONF} headers=${HEADERS}
248 ... expected_status=anything
249 Should Contain ${UNAUTHORIZED_STATUS_CODES} ${resp.status_code}
252 ... http://${ODL_SYSTEM_IP}:${RESTCONFPORT}
253 ... auth=${AUTH_CSC_NO_ADMIN}
254 ... headers=${HEADERS}
255 ${resp} RequestsLibrary.GET On Session session url=${URI_RESTCONF} headers=${HEADERS}
256 ... expected_status=anything
257 Should Contain ${UNAUTHORIZED_STATUS_CODES} ${resp.status_code}
258 ${resp} RequestsLibrary.POST On Session session url=${URI_CERTIFICATE} headers=${HEADERS}
259 Should Contain ${UNAUTHORIZED_STATUS_CODES} ${resp.status_code}
264 [Documentation] The steps included in the Initialization phase are:
266 ... - Run Docker Keystone: Deploy a container in the SYSTEM TOOL node containing the Keystone
268 ... - Configure AAA in Controller: shiro.ini file is modified to add new authentication realm based on Keystone
270 ... - Restart Controller: This restart is needed in order to activate new shiro.ini configuration
272 ... - Provision Keystone: Populate keystone database with the needed users and roles
274 ... - Install Keystone certificate into ODL so that the protocol used in the ODL-Keystone communication is HTTPS with server certificate authentication
275 ${TOOLS_SYSTEM_NAME} Run Command On Remote System
276 ... ${TOOLS_SYSTEM_IP}
278 ... user=${TOOLS_SYSTEM_USER}
279 ... password=${TOOLS_SYSTEM_PASSWORD}
281 Configure AAA In Controller ${TOOLS_SYSTEM_NAME}
282 Set Suite Variable ${PUT_KEYSTONE_CERT_FILE} ${CURDIR}/../../../variables/aaa/put-keystone-cert.json
283 Set Keystone Certificate into ODL ${PUT_KEYSTONE_CERT_FILE} ${TOOLS_SYSTEM_NAME}
286 Set Suite Variable ${PUT_DYNAMIC_AUTH_FILE} ${CURDIR}/../../../variables/aaa/put-dynamic-auth.json
287 Provision MDSAL ${PUT_DYNAMIC_AUTH_FILE}
290 [Documentation] Destoy keystone container
291 ${result} Run Keyword And Return Status Set Domain To False ${domain} ${HEADERS_TOKEN}
293 Delete Keystone Domain ${domain} ${HEADERS_TOKEN}
295 IF ${result} == True Destroy Docker Keystone
296 SSHLibrary.Close All Connections
298 Configure AAA In Controller
299 [Documentation] With this keyword shiro.ini and aaa-cert-config.xml are modified to configure Keystone Authentication Realm using TLS1.2. Here you have the settings:
303 ... keystoneAuthRealm = org.opendaylight.aaa.shiro.realm.KeystoneAuthRealm
304 ... keystoneAuthRealm.url = https://sandbox-29591-30-docker-0:35357
305 ... keystoneAuthRealm.sslVerification = true
307 ... securityManager.realms = $tokenAuthRealm, $keystoneAuthRealm
312 ... - aaa-cert-config.xml:
313 ... <use-config>true</use-config>
315 ... <tls-protocols>TLSv1.2</tls-protocols>
316 [Arguments] ${TOOLS_SYSTEM_NAME}
317 ${shiro_path} Run Command On Controller cmd=cd /;find /|grep shiro.ini|grep etc|grep -v denied
318 ${cert_path} Run Command On Controller cmd=cd /;find /|grep aaa-cert-config.xml|grep etc|grep -v denied
319 ${result} Run Command On Controller
320 ... cmd=sed -ie 's/#keystoneAuthRealm =.*/keystoneAuthRealm = org.opendaylight.aaa.shiro.realm.KeystoneAuthRealm/g' ${shiro_path}
321 ${result} Run Command On Controller
322 ... cmd=sed -ie 's/#keystoneAuthRealm.url =.*/keystoneAuthRealm.url = https:\\/\\/${TOOLS_SYSTEM_NAME}:35357/g' ${shiro_path}
323 ${result} Run Command On Controller
324 ... cmd=sed -ie 's/securityManager.realms =.*/securityManager.realms = $tokenAuthRealm, $keystoneAuthRealm/g' ${shiro_path}
325 ${result} Run Command On Controller
326 ... cmd=sed -ie 's/#keystoneAuthRealm.sslVerification =.*/keystoneAuthRealm.sslVerification = true/g' ${shiro_path}
327 ${result} Run Command On Controller
328 ... cmd=sed -ie 's/\\/operations\\/aaa-cert-rpc.*/\\/operations\\/aaa-cert-rpc** = authcBasic, roles[admin], dynamicAuthorization/g' ${shiro_path}
329 ${result} Run Command On Controller
330 ... cmd=sed -ie 's/<use-config>.*/<use-config>true<\\/use-config>/g' ${cert_path}
331 ${result} Run Command On Controller
332 ... cmd=sed -ie 's/<tls-protocols.*/<tls-protocols>TLSv1.2<\\/tls-protocols>/g' ${cert_path}
333 ${result} Run Command On Controller cmd=cat ${shiro_path}
335 ${result} Run Command On Controller cmd=cat ${cert_path}
337 ${result} Run Command On Controller
338 ... cmd=sudo sed -i "2i${TOOLS_SYSTEM_IP} \ \ ${TOOLS_SYSTEM_NAME}" /etc/hosts
339 ${result} Run Command On Controller cmd=cat /etc/hosts
343 [Documentation] As CSC_user provision:
345 ... - User "sdnadmin"
346 ... - Role "admin" to "sdnadmin" user in "sdn" domain
347 ${result} Create Keystone session ${TOOLS_SYSTEM_IP}
349 Set Suite Variable ${CREATE_TOKEN_FILE} ${CURDIR}/../../../variables/aaa/create-token.json
350 ${token} Get Keystone Token ${TOOLS_SYSTEM_IP} ${CREATE_TOKEN_FILE}
352 &{HEADERS} Create Dictionary X-Auth-Token=${token} Content-Type=application/json
353 Set Suite Variable ${HEADERS_TOKEN} ${HEADERS}
354 ${admin_role_id} Get Admin Role Id ${HEADERS_TOKEN}
355 Set Suite Variable ${CREATE_DOMAIN_FILE} ${CURDIR}/../../../variables/aaa/create-domain.json
356 ${domain_local} Create Keystone Domain ${HEADERS_TOKEN} ${CREATE_DOMAIN_FILE}
357 Set Suite Variable ${domain} ${domain_local}
358 Set Suite Variable ${CREATE_USERS_FILE} ${CURDIR}/../../../variables/aaa/create-user.json
359 ${normalized_file} OperatingSystem.Normalize Path ${CREATE_USERS_FILE}
360 ${output} OperatingSystem.Run
361 ... sed -i 's/\"domain_id\".*/\"domain_id\"\: \"${domain}\",/g' ${CREATE_USERS_FILE}
362 ${user} Create Keystone User in a Domain ${HEADERS_TOKEN} ${CREATE_USERS_FILE}
363 Grant Admin Role ${domain} ${user} ${admin_role_id} ${HEADERS_TOKEN}
366 [Arguments] ${PUT_DYNAMIC_AUTH_FILE}
367 Create Session session_admin http://${ODL_SYSTEM_IP}:${RESTCONFPORT} auth=${AUTH} headers=${HEADERS}
368 Set Suite Variable ${PUT_DYNAMIC_AUTH_URI} /restconf/config/aaa:http-authorization
369 ${body_dyn} OperatingSystem.Get File ${PUT_DYNAMIC_AUTH_FILE}
370 ${resp} RequestsLibrary.PUT On Session
372 ... url=${PUT_DYNAMIC_AUTH_URI}
374 ... headers=${HEADERS}
375 ... expected_status=anything
376 Should Contain ${ALLOWED_STATUS_CODES} ${resp.status_code}
377 DELETE On Session session_admin url=http://${ODL_SYSTEM_IP}:${RESTCONFPORT}
380 [Documentation] Controller restart is needed in order the new shiro.ini config takes effect
381 ClusterManagement.ClusterManagement_Setup
382 Wait Until Keyword Succeeds 5x 20 Stop_Single_Member 1
383 Start_Single_Member 1 wait_for_sync=False timeout=120
384 # TODO: the below Get Controller Modules keyword ends up giving a lot of WARN messages in the robot
385 # log as the controller is coming up and the initial requests are failing. This is just cosmetic at this point, but
386 # would be nice to clean up somehow.
387 Wait Until Keyword Succeeds 30x 5s Get Controller Modules
389 Get Controller Modules
390 [Documentation] Get the restconf modules, check 200 status and ietf-restconf presence
391 Create Session session1 http://${ODL_SYSTEM_IP}:${RESTCONFPORT} auth=${AUTH} headers=${HEADERS}
392 ${resp} RequestsLibrary.GET On Session session1 url=${MODULES_API}
393 BuiltIn.Log ${resp.content}
394 BuiltIn.Should_Be_Equal ${resp.status_code} ${200}
395 BuiltIn.Should_Contain ${resp.content} ietf-restconf