1- import copy
2- import json
3- import os
4- import tempfile
5- import time
61import unittest
2+ from unittest .mock import patch
73
8- from tornado import escape
4+ # noinspection PyProtectedMember
5+ from tornado .testing import AsyncTestCase , gen_test
96
7+ from auth .auth_abstract_oauth import _OauthUserInfo
108from auth .auth_gitlab import GitlabOAuthAuthenticator
11- from model import server_conf
12- from tests import test_utils
13- from utils import file_utils
14- from unittest import TestCase
15- from unittest .mock import patch , Mock
16-
17- if __name__ == '__main__' :
18- unittest .main ()
19-
20- mock_time = Mock ()
21- mock_time .return_value = 10000.01
22- mock_persist_session = Mock ()
23- mock_do_update_groups = Mock ()
24- mock_do_update_user = Mock ()
25- mock_request_handler = Mock (** {'get_secure_cookie.return_value' : "12345" .encode ()})
26-
27-
28- class TestAuthConfig (TestCase ):
29- @patch ('time.time' , mock_time )
30- @patch ('auth.auth_gitlab.GitlabOAuthAuthenticator.persist_session' , mock_persist_session )
31- @patch ('auth.auth_gitlab.GitlabOAuthAuthenticator.do_update_groups' , mock_do_update_groups )
32- def test_gitlab_oauth (self ):
33- now = time .time ()
34- state = {
35- "user@test.com" : {
36- "groups" : ["testgroup" ],
37- "updating" : False ,
38- "updated" : now - 10 ,
39- "visit" : now - 10 ,
40- "id" : 1 ,
41- "username" : "test" ,
42- "name" : "John" ,
43- "email" : "user@test.com" ,
44- "state" : "active"
45- },
46- "nogroups@test.com" : {
47- "groups" : None ,
48- "updating" : True ,
49- "updated" : now - 10 ,
50- "visit" : now - 10 ,
51- "id" : 2 ,
52- "username" : "nogroups" ,
53- "name" : "John" ,
54- "email" : "nogroups@test.com" ,
55- "state" : "blocked"
56- }
57- }
58-
59- state_file = test_utils .create_file ("gitlab_state.json" , text = escape .json_encode (state ))
60-
61- config = _from_json ({
62- 'auth' : {
63- "type" : "gitlab" ,
64- "url" : "https://gitlab" ,
65- "client_id" : "1234" ,
66- "secret" : "abcd" ,
67- "group_search" : "script-server" ,
68- "auth_info_ttl" : 80 ,
69- "state_dump_file" : state_file ,
70- "session_expire_minutes" : 10
71- },
72- 'access' : {
73- 'allowed_users' : []
74- }})
75-
76- self .assertIsInstance (config .authenticator , GitlabOAuthAuthenticator )
77- self .assertEqual (state_file , config .authenticator .gitlab_dump )
78- self .assertEqual ("1234" , config .authenticator ._client_visible_config ['client_id' ])
79- self .assertEqual ("https://gitlab/oauth/authorize" , config .authenticator ._client_visible_config ['oauth_url' ])
80- self .assertEqual ("api" , config .authenticator ._client_visible_config ['oauth_scope' ])
81-
82- assert_state = state .copy ()
83- for key in list (assert_state .keys ()):
84- assert_state [key ]['updating' ] = False
85- assert_state [key ]['updated' ] = 10000.01 - 80 - 1
86- self .assertDictEqual (assert_state , config .authenticator .user_states )
87- saved_state = copy .deepcopy (config .authenticator .user_states )
88-
89- self .assertEqual (False , config .authenticator .validate_user ("unknown@test.com" , mock_request_handler ))
90- self .assertEqual (False , config .authenticator .validate_user ("nogroups@test.com" , mock_request_handler ))
91- self .assertListEqual ([], config .authenticator .get_groups ("unknown@test.com" ))
92- self .assertListEqual ([], config .authenticator .get_groups ("nogroups@test.com" ))
93-
94- self .assertEqual (True , config .authenticator .validate_user ("user@test.com" , mock_request_handler ))
95- self .assertEqual (time .time (), config .authenticator .user_states ["user@test.com" ]["visit" ], "visit updated" )
96- self .assertEqual (True , mock_do_update_groups .called , "state just loaded, gitlab updating" )
97- mock_do_update_groups .reset_mock ()
98-
99- config .authenticator .user_states ["user@test.com" ]["updating" ] = True
100- self .assertEqual (True , config .authenticator .validate_user ("user@test.com" , mock_request_handler ))
101- self .assertEqual (False , mock_do_update_groups .called , "do not call parallel updated" )
102- mock_do_update_groups .reset_mock ()
103-
104- mock_time .return_value = 10000.01 + 80 * 2 + 1 # stale request
105- self .assertEqual (True , config .authenticator .validate_user ("user@test.com" , mock_request_handler ))
106- self .assertEqual (True , mock_do_update_groups .called , "parallel but stale" )
107- mock_do_update_groups .reset_mock ()
108- config .authenticator .user_states = copy .deepcopy (saved_state )
109- mock_time .return_value = 10000.01
110-
111- config .authenticator .user_states ["user@test.com" ]['updated' ] = now # gitlab info updated
112- config .authenticator .user_states ["user@test.com" ]['updating' ] = False
113- self .assertEqual (True , config .authenticator .validate_user ("user@test.com" , mock_request_handler ))
114- self .assertEqual (False , mock_do_update_groups .called , "do not update gitlab because ttl not expired" )
115- mock_do_update_groups .reset_mock ()
116-
117- mock_time .return_value = 10000.01 + 81
118- self .assertEqual (True , config .authenticator .validate_user ("user@test.com" , mock_request_handler ))
119- self .assertEqual (True , mock_do_update_groups .called , "ttl expired" )
120- mock_do_update_groups .reset_mock ()
121- config .authenticator .user_states = copy .deepcopy (saved_state )
122- mock_time .return_value = 10000.01
123-
124- # session expire test
125- mock_time .return_value = 10000.01 + 601
126- self .assertEqual (False , config .authenticator .validate_user ("user@test.com" , mock_request_handler ), "shoud be expired" )
127- self .assertEqual (True , mock_persist_session .called , "dump state to file" )
128- mock_persist_session .reset_mock ()
129- self .assertIsNone (config .authenticator .user_states .get ("user@test.com" ), "removed from state" )
130- self .assertListEqual ([], config .authenticator .get_groups ("user@test.com" ))
131- config .authenticator .user_states = copy .deepcopy (saved_state )
132- mock_time .return_value = 10000.01
133-
134- # test clean expire
135- mock_time .return_value = 10000.01 + 601
136- config .authenticator .clean_sessions ()
137- self .assertIsNone (config .authenticator .user_states .get ("user@test.com" ))
138- config .authenticator .user_states = copy .deepcopy (saved_state )
139- mock_time .return_value = 10000.01
140-
141- @patch ('time.time' , mock_time )
142- @patch ('auth.auth_gitlab.GitlabOAuthAuthenticator.do_update_user' , mock_do_update_user )
143- @patch ('auth.auth_gitlab.GitlabOAuthAuthenticator.do_update_groups' , mock_do_update_groups )
144- def test_gitlab_oauth_user_read_scope (self ):
145- now = time .time ()
146-
147- state = {
148- "user@test.com" : {
149- "groups" : ["testgroup" ],
150- "updating" : False ,
151- "updated" : 0 ,
152- "visit" : now - 10 ,
153- "id" : 1 ,
154- "username" : "test" ,
155- "name" : "John" ,
156- "email" : "user@test.com" ,
157- "state" : "active"
158- }
159- }
160-
161- config = _from_json ({
162- 'auth' : {
163- "type" : "gitlab" ,
164- "url" : "https://gitlab" ,
165- "client_id" : "1234" ,
166- "secret" : "abcd" ,
167- "group_search" : "script-server" ,
168- "auth_info_ttl" : 80 ,
169- "session_expire_minutes" : 1 ,
170- "group_support" : False
171- },
172- 'access' : {
173- 'allowed_users' : []
174- }})
175-
176- self .assertIsInstance (config .authenticator , GitlabOAuthAuthenticator )
177- self .assertEqual ("read_user" , config .authenticator ._client_visible_config ['oauth_scope' ])
178- config .authenticator .user_states = state
179- self .assertEqual (True , config .authenticator .validate_user ("user@test.com" , mock_request_handler ))
180- self .assertEqual (False , mock_do_update_groups .called , "update==0, gitlab updating but not groups" )
181- self .assertEqual (True , mock_do_update_user .called , "update==0, gitlab updating only user" )
182- mock_do_update_groups .reset_mock ()
183- mock_do_update_user .reset_mock ()
184-
185- config .authenticator .gitlab_update = None
186- self .assertEqual (True , config .authenticator .validate_user ("user@test.com" , mock_request_handler ))
187- self .assertEqual (False , mock_do_update_groups .called , "gitab update disabled" )
188- self .assertEqual (False , mock_do_update_user .called , "gitab update disabled" )
189- mock_do_update_groups .reset_mock ()
190- mock_do_update_user .reset_mock ()
191-
192- def tearDown (self ):
193- test_utils .cleanup ()
194-
195-
196- def _from_json (content ):
197- json_obj = json .dumps (content )
198- conf_path = os .path .join (test_utils .temp_folder , 'conf.json' )
199- file_utils .write_file (conf_path , json_obj )
200- return server_conf .from_json (conf_path , test_utils .temp_folder )
9+
10+
11+ def create_config (* , url = None , group_search = None , group_support = None ):
12+ config = {
13+ 'client_id' : '1234' ,
14+ 'secret' : 'hello world?'
15+ }
16+
17+ if url is not None :
18+ config ['url' ] = url
19+ if group_search is not None :
20+ config ['group_search' ] = group_search
21+ if group_support is not None :
22+ config ['group_support' ] = group_support
23+
24+ return config
25+
26+
27+ class TestAuthConfig (unittest .TestCase ):
28+ def test_client_visible_config (self ):
29+ authenticator = GitlabOAuthAuthenticator (create_config (url = 'https://my.gitlab.host' ))
30+
31+ client_visible_config = authenticator ._client_visible_config
32+ self .assertEqual ('1234' , client_visible_config ['client_id' ])
33+ self .assertEqual ('https://my.gitlab.host/oauth/authorize' , client_visible_config ['oauth_url' ])
34+ self .assertEqual ('api' , client_visible_config ['oauth_scope' ])
35+
36+ def test_client_visible_config_when_groups_disabled (self ):
37+ authenticator = GitlabOAuthAuthenticator (create_config (group_support = False ))
38+
39+ client_visible_config = authenticator ._client_visible_config
40+ self .assertEqual ('read_user' , client_visible_config ['oauth_scope' ])
41+
42+ def test_client_visible_config_when_default_url (self ):
43+ authenticator = GitlabOAuthAuthenticator (create_config ())
44+
45+ client_visible_config = authenticator ._client_visible_config
46+ self .assertEqual ('https://gitlab.com/oauth/authorize' , client_visible_config ['oauth_url' ])
47+
48+
49+ class TestFetchUserInfo (AsyncTestCase ):
50+ @patch ('tornado.auth.OAuth2Mixin.oauth2_request' )
51+ @gen_test
52+ def test_fetch_user_info (self , mock_request ):
53+ response = {'email' : 'me@gmail.com' , 'state' : 'active' }
54+ mock_request .return_value = response
55+
56+ authenticator = GitlabOAuthAuthenticator (create_config (url = 'https://my.gitlab.host' ))
57+
58+ user_info = yield authenticator .fetch_user_info ('my_token_2' )
59+ self .assertEqual (_OauthUserInfo ('me@gmail.com' , True , response ), user_info )
60+
61+ mock_request .assert_called_with ('https://my.gitlab.host/api/v4/user' , 'my_token_2' )
62+
63+ @patch ('tornado.auth.OAuth2Mixin.oauth2_request' )
64+ @gen_test
65+ def test_fetch_user_info_when_no_response (self , mock_request ):
66+ mock_request .return_value = None
67+
68+ authenticator = GitlabOAuthAuthenticator (create_config ())
69+
70+ user_info = yield authenticator .fetch_user_info ('my_token_2' )
71+ self .assertEqual (None , user_info )
72+
73+ @patch ('tornado.auth.OAuth2Mixin.oauth2_request' )
74+ @gen_test
75+ def test_fetch_user_info_when_not_active (self , mock_request ):
76+ response = {'email' : 'me@gmail.com' , 'state' : 'something' }
77+ mock_request .return_value = response
78+
79+ authenticator = GitlabOAuthAuthenticator (create_config ())
80+
81+ user_info = yield authenticator .fetch_user_info ('my_token_2' )
82+ self .assertEqual (_OauthUserInfo ('me@gmail.com' , False , response ), user_info )
83+
84+
85+ class TestFetchUserGroups (AsyncTestCase ):
86+ @patch ('tornado.auth.OAuth2Mixin.oauth2_request' )
87+ @gen_test
88+ def test_fetch_user_info (self , mock_request ):
89+ response = [{'full_path' : 'group1' }, {'full_path' : 'group2' }, {'something' : 'group3' }]
90+ mock_request .return_value = response
91+
92+ authenticator = GitlabOAuthAuthenticator (create_config (url = 'https://my.gitlab.host' ))
93+
94+ groups = yield authenticator .fetch_user_groups ('my_token_2' )
95+ self .assertEqual (['group1' , 'group2' ], groups )
96+
97+ mock_request .assert_called_with ('https://my.gitlab.host/api/v4/groups' ,
98+ access_token = 'my_token_2' ,
99+ all_available = 'false' ,
100+ per_page = 100 )
101+
102+ @patch ('tornado.auth.OAuth2Mixin.oauth2_request' )
103+ @gen_test
104+ def test_fetch_user_info_when_search (self , mock_request ):
105+ authenticator = GitlabOAuthAuthenticator (create_config (url = 'https://my.gitlab.host' , group_search = 'abc' ))
106+
107+ yield authenticator .fetch_user_groups ('my_token_2' )
108+
109+ mock_request .assert_called_with ('https://my.gitlab.host/api/v4/groups' ,
110+ access_token = 'my_token_2' ,
111+ all_available = 'false' ,
112+ per_page = 100 ,
113+ search = 'abc' )
0 commit comments