An access-control test matrix that finds defects
Goal
You write negative tests that separate no authentication, no permission, someone else's resource, and a normal request.
Why it matters
An access control test that checked only success responses was green. Even when the route forgot to inject the Header or the scope check was deleted, the test did not break. A verifier must not just list the allowed cases; it has to observe the reason for each rejection separately.
Steps
- In
/root/work/test-auth-matrix-lab/test_service.py, test the following public contract of the provided service.py: bearer(header) returns the token when the header starts with exactly 'Bearer ' and is followed by one token with no whitespace. None, an empty token, a different scheme, and extra whitespace are ValueError. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function.
Prepare this once at the start. Existing files are not overwritten.
mkdir -p /root/work/test-auth-matrix-lab
test -e /root/work/test-auth-matrix-lab/service.py || cp /opt/fixtures/ten_labs/test-auth-matrix-lab/service.py /root/work/test-auth-matrix-lab/service.py
test -e /root/work/test-auth-matrix-lab/test_service.py || cp /opt/fixtures/ten_labs/test-auth-matrix-lab/test_service.py /root/work/test-auth-matrix-lab/test_service.py
cd /root/work/test-auth-matrix-lab
-
In
/root/work/test-auth-matrix-lab/test_service.py, test the following public contract of the provided service.py: principal(token, users) returns the user {id, scopes} from the token dictionary, but copies the scopes list as well. An unknown token is ValueError. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function. -
In
/root/work/test-auth-matrix-lab/test_service.py, test the following public contract of the provided service.py: require_scope(user, scope) returns None when the scope string is exactly present in scopes, and raises PermissionError otherwise. read-all is not read. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function. -
In
/root/work/test-auth-matrix-lab/test_service.py, test the following public contract of the provided service.py: visible(user, document) is True only when document is not None and owner is exactly equal to the user's id. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function. -
In
/root/work/test-auth-matrix-lab/test_service.py, test the following public contract of the provided service.py: public_document(document) is a new dictionary that has only id and title. It does not include owner or internal_cost. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function. -
In
/root/work/test-auth-matrix-lab/test_service.py, test the following public contract of the provided service.py: authenticate(header, users) connects bearer and principal. ValueError becomes HTTPException(401), and the WWW-Authenticate value in headers is Bearer. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function. -
In
/root/work/test-auth-matrix-lab/test_service.py, test the following public contract of the provided service.py: read_document(user, documents, document_id) raises HTTPException(403) if there is no read scope, HTTPException(404) if the document does not exist or belongs to someone else, and otherwise returns the result of public_document. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function. -
In
/root/work/test-auth-matrix-lab/test_service.py, test the following public contract of the provided service.py: create_app(users, documents) returns a FastAPI app that receives the Authorization header in GET /documents/{document_id} and calls authenticate and read_document. Verify 200, 401, 403, 404, and the removal of private fields with real requests. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function.
Notes
- You work in the existing lab-dev environment with no internet and no package installation.
- Each step runs within a 45-second grading budget. Do not add real sleeps or network calls.
- The submitted tests are run in a separate temporary folder against the correct and defective implementations. Against the correct one, every test that actually runs must pass, and against a defective one, the body of a test must fail. A collection error, zero tests run, everything skipped, and forced termination are not a pass. Use only the basic pytest features and the provided libraries.
- FastAPI official documentation · pytest official documentation · Python sqlite3
- Limitation: A fixed token dictionary is teaching input. Production authentication additionally needs expiry, signatures, revocation, and safe storage. Making the 404s identical also does not remove every inference through response time or access logs. Also check that a rejected request did not change the original data. You may read the provided implementation, but grading uses a separate copy. Do not work around a defect by checking the wording of the source or by modifying files; check the execution results of the public interface.
Split the Bearer header — test
In /root/work/test-auth-matrix-lab/test_service.py, test the following public contract of the provided service.py: bearer(header) returns the token when the header starts with exactly 'Bearer ' and is followed by one token with no whitespace. None, an empty token, a different scheme, and extra whitespace are ValueError. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function.
Prepare this once at the start. Existing files are not overwritten.
mkdir -p /root/work/test-auth-matrix-lab
test -e /root/work/test-auth-matrix-lab/service.py || cp /opt/fixtures/ten_labs/test-auth-matrix-lab/service.py /root/work/test-auth-matrix-lab/service.py
test -e /root/work/test-auth-matrix-lab/test_service.py || cp /opt/fixtures/ten_labs/test-auth-matrix-lab/test_service.py /root/work/test-auth-matrix-lab/test_service.py
cd /root/work/test-auth-matrix-lab
If you split the header into several pieces arbitrarily, a whitespace error can be accepted as a normal token. Do not modify the implementation file. Use pytest.raises to check the expected exception, and assert a concrete expected value for the normal result.
After saving, check with bash /opt/lab/checks/test-auth-matrix-lab/01-contract.sh.
Copy and return the identity — test
In /root/work/test-auth-matrix-lab/test_service.py, test the following public contract of the provided service.py: principal(token, users) returns the user {id, scopes} from the token dictionary, but copies the scopes list as well. An unknown token is ValueError. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function.
If modifying the returned scopes also changes the original user's permissions, permissions get mixed between requests. Do not modify the implementation file. Use pytest.raises to check the expected exception, and assert a concrete expected value for the normal result.
After saving, check with bash /opt/lab/checks/test-auth-matrix-lab/02-contract.sh.
Compare permissions exactly — test
In /root/work/test-auth-matrix-lab/test_service.py, test the following public contract of the provided service.py: require_scope(user, scope) returns None when the scope string is exactly present in scopes, and raises PermissionError otherwise. read-all is not read. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function.
A substring comparison mistakes a longer permission name for a different permission. Do not modify the implementation file. Use pytest.raises to check the expected exception, and assert a concrete expected value for the normal result.
After saving, check with bash /opt/lab/checks/test-auth-matrix-lab/03-contract.sh.
Check ownership separately — test
In /root/work/test-auth-matrix-lab/test_service.py, test the following public contract of the provided service.py: visible(user, document) is True only when document is not None and owner is exactly equal to the user's id. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function.
A missing resource and a resource owned by someone else are bundled into the same decision. Do not modify the implementation file. Use pytest.raises to check the expected exception, and assert a concrete expected value for the normal result.
After saving, check with bash /opt/lab/checks/test-auth-matrix-lab/04-contract.sh.
Choose response fields with an allow list — test
In /root/work/test-auth-matrix-lab/test_service.py, test the following public contract of the provided service.py: public_document(document) is a new dictionary that has only id and title. It does not include owner or internal_cost. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function.
Do not delete fields from the original; assemble a new response. Do not modify the implementation file. Use pytest.raises to check the expected exception, and assert a concrete expected value for the normal result.
After saving, check with bash /opt/lab/checks/test-auth-matrix-lab/05-contract.sh.
Match errors to the HTTP contract — test
In /root/work/test-auth-matrix-lab/test_service.py, test the following public contract of the provided service.py: authenticate(header, users) connects bearer and principal. ValueError becomes HTTPException(401), and the WWW-Authenticate value in headers is Bearer. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function.
Do not lump an authentication failure and an application error together into a single 500. Do not modify the implementation file. Use pytest.raises to check the expected exception, and assert a concrete expected value for the normal result.
After saving, check with bash /opt/lab/checks/test-auth-matrix-lab/06-contract.sh.
Fix the order of rejection — test
In /root/work/test-auth-matrix-lab/test_service.py, test the following public contract of the provided service.py: read_document(user, documents, document_id) raises HTTPException(403) if there is no read scope, HTTPException(404) if the document does not exist or belongs to someone else, and otherwise returns the result of public_document. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function.
Even after authentication, scope and ownership must each be checked. Do not modify the implementation file. Use pytest.raises to check the expected exception, and assert a concrete expected value for the normal result.
After saving, check with bash /opt/lab/checks/test-auth-matrix-lab/07-contract.sh.
Close the boundary in a real request — test
In /root/work/test-auth-matrix-lab/test_service.py, test the following public contract of the provided service.py: create_app(users, documents) returns a FastAPI app that receives the Authorization header in GET /documents/{document_id} and calls authenticate and read_document. Verify 200, 401, 403, 404, and the removal of private fields with real requests. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function.
Even if the functions are each correct, access control is not applied if the route forgets to call them. Do not modify the implementation file. Use pytest.raises to check the expected exception, and assert a concrete expected value for the normal result.
After saving, check with bash /opt/lab/checks/test-auth-matrix-lab/08-contract.sh.