test_auth.py 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. """
  2. Tests for authentication module
  3. """
  4. import pytest
  5. import json
  6. from app import db
  7. from app.models import User
  8. @pytest.fixture
  9. def test_user(app):
  10. """Create a test user"""
  11. with app.app_context():
  12. user = User(
  13. username='testuser',
  14. email='test@example.com',
  15. role='user'
  16. )
  17. user.set_password('testpassword123')
  18. db.session.add(user)
  19. db.session.commit()
  20. # Return user data (not the object, as it will be detached)
  21. return {
  22. 'id': user.id,
  23. 'username': user.username,
  24. 'email': user.email,
  25. 'role': user.role
  26. }
  27. @pytest.fixture
  28. def admin_user(app):
  29. """Create an admin user"""
  30. with app.app_context():
  31. user = User(
  32. username='adminuser',
  33. email='admin@example.com',
  34. role='admin'
  35. )
  36. user.set_password('adminpassword123')
  37. db.session.add(user)
  38. db.session.commit()
  39. return {
  40. 'id': user.id,
  41. 'username': user.username,
  42. 'email': user.email,
  43. 'role': user.role
  44. }
  45. class TestLogin:
  46. """Tests for login endpoint"""
  47. def test_login_success(self, client, test_user):
  48. """Test successful login"""
  49. response = client.post('/api/auth/login',
  50. data=json.dumps({
  51. 'username': 'testuser',
  52. 'password': 'testpassword123'
  53. }),
  54. content_type='application/json'
  55. )
  56. assert response.status_code == 200
  57. data = json.loads(response.data)
  58. assert 'token' in data
  59. assert 'refresh_token' in data
  60. assert 'user' in data
  61. assert data['user']['username'] == 'testuser'
  62. def test_login_invalid_password(self, client, test_user):
  63. """Test login with invalid password"""
  64. response = client.post('/api/auth/login',
  65. data=json.dumps({
  66. 'username': 'testuser',
  67. 'password': 'wrongpassword'
  68. }),
  69. content_type='application/json'
  70. )
  71. assert response.status_code == 401
  72. data = json.loads(response.data)
  73. assert 'error' in data
  74. def test_login_missing_fields(self, client):
  75. """Test login with missing fields"""
  76. response = client.post('/api/auth/login',
  77. data=json.dumps({
  78. 'username': 'testuser'
  79. }),
  80. content_type='application/json'
  81. )
  82. assert response.status_code == 400
  83. data = json.loads(response.data)
  84. assert 'error' in data
  85. class TestGetCurrentUser:
  86. """Tests for get current user endpoint"""
  87. def test_get_current_user_success(self, client, test_user):
  88. """Test getting current user with valid token"""
  89. # First login to get token
  90. login_response = client.post('/api/auth/login',
  91. data=json.dumps({
  92. 'username': 'testuser',
  93. 'password': 'testpassword123'
  94. }),
  95. content_type='application/json'
  96. )
  97. token = json.loads(login_response.data)['token']
  98. # Get current user
  99. response = client.get('/api/auth/me',
  100. headers={'Authorization': f'Bearer {token}'}
  101. )
  102. assert response.status_code == 200
  103. data = json.loads(response.data)
  104. assert data['username'] == 'testuser'
  105. def test_get_current_user_no_token(self, client):
  106. """Test getting current user without token"""
  107. response = client.get('/api/auth/me')
  108. assert response.status_code == 401
  109. class TestRefreshToken:
  110. """Tests for token refresh endpoint"""
  111. def test_refresh_token_success(self, client, test_user):
  112. """Test successful token refresh"""
  113. # First login to get tokens
  114. login_response = client.post('/api/auth/login',
  115. data=json.dumps({
  116. 'username': 'testuser',
  117. 'password': 'testpassword123'
  118. }),
  119. content_type='application/json'
  120. )
  121. refresh_token = json.loads(login_response.data)['refresh_token']
  122. # Refresh token
  123. response = client.post('/api/auth/refresh',
  124. data=json.dumps({
  125. 'refresh_token': refresh_token
  126. }),
  127. content_type='application/json'
  128. )
  129. assert response.status_code == 200
  130. data = json.loads(response.data)
  131. assert 'access_token' in data
  132. def test_refresh_token_invalid(self, client):
  133. """Test refresh with invalid token"""
  134. response = client.post('/api/auth/refresh',
  135. data=json.dumps({
  136. 'refresh_token': 'invalid_token'
  137. }),
  138. content_type='application/json'
  139. )
  140. assert response.status_code == 401
  141. class TestLogout:
  142. """Tests for logout endpoint"""
  143. def test_logout_success(self, client, test_user):
  144. """Test successful logout"""
  145. # First login to get token
  146. login_response = client.post('/api/auth/login',
  147. data=json.dumps({
  148. 'username': 'testuser',
  149. 'password': 'testpassword123'
  150. }),
  151. content_type='application/json'
  152. )
  153. token = json.loads(login_response.data)['token']
  154. # Logout
  155. response = client.post('/api/auth/logout',
  156. headers={'Authorization': f'Bearer {token}'}
  157. )
  158. assert response.status_code == 200
  159. @pytest.fixture
  160. def power_user(app):
  161. """Create a power user"""
  162. with app.app_context():
  163. user = User(
  164. username='poweruser',
  165. email='power@example.com',
  166. role='power_user'
  167. )
  168. user.set_password('powerpassword123')
  169. db.session.add(user)
  170. db.session.commit()
  171. return {
  172. 'id': user.id,
  173. 'username': user.username,
  174. 'email': user.email,
  175. 'role': user.role
  176. }
  177. class TestRBAC:
  178. """Tests for role-based access control"""
  179. def test_admin_access(self, client, admin_user):
  180. """Test admin can access admin-only endpoints"""
  181. # Login as admin
  182. login_response = client.post('/api/auth/login',
  183. data=json.dumps({
  184. 'username': 'adminuser',
  185. 'password': 'adminpassword123'
  186. }),
  187. content_type='application/json'
  188. )
  189. token = json.loads(login_response.data)['token']
  190. # Admin should be able to access user list (admin endpoint)
  191. response = client.get('/api/users',
  192. headers={'Authorization': f'Bearer {token}'}
  193. )
  194. # Should not return 403 (may return 200 or other status depending on implementation)
  195. assert response.status_code != 403
  196. def test_regular_user_denied_admin_access(self, client, test_user):
  197. """Test regular user cannot access admin-only endpoints"""
  198. # Login as regular user
  199. login_response = client.post('/api/auth/login',
  200. data=json.dumps({
  201. 'username': 'testuser',
  202. 'password': 'testpassword123'
  203. }),
  204. content_type='application/json'
  205. )
  206. token = json.loads(login_response.data)['token']
  207. # Regular user should be denied access to admin endpoints
  208. response = client.get('/api/users',
  209. headers={'Authorization': f'Bearer {token}'}
  210. )
  211. assert response.status_code == 403
  212. def test_power_user_access(self, client, power_user):
  213. """Test power user can access power_user endpoints"""
  214. # Login as power user
  215. login_response = client.post('/api/auth/login',
  216. data=json.dumps({
  217. 'username': 'poweruser',
  218. 'password': 'powerpassword123'
  219. }),
  220. content_type='application/json'
  221. )
  222. token = json.loads(login_response.data)['token']
  223. # Power user should be able to access credentials list
  224. response = client.get('/api/credentials',
  225. headers={'Authorization': f'Bearer {token}'}
  226. )
  227. # Should not return 403
  228. assert response.status_code != 403