You'd probably want a pivot table with attributes, or a pivot model, to represent a relationship between a user and a course. That solves the first issue of granting access to a course for a specific user. Then you'd have some middleware that checks if the current user has access to the course/lesson they're trying to access.
Then you may also want another to represent their progress. Though that could probably be managed with the same pivot table/pivot model.
I think you could manage it with a table structure for your pivot table something like this:
$table->integer('user_id');
$table->integer('course_id');
$table->integer('progress')->unsigned()->nullable();
user_id and course_id are obvious. The progress field could simply represent the highest lesson number they are able to access.
The admin, after grading a test would hit a button or something to update 'progress' to the next value, on successful pass of the test.
This would work if for each course you have lessons numbered logically 1-10, for example. So by default, when a user is granted access to a course, you could set 'progess' to 1, indicating that they have access to lesson 1, and its associated test. After they take that test, and pass, progress would be updated to 2, and so on.
Looking at your post again, I see you have some other conditions for allowing access to tests, but the same general approach would probably work out just fine I think.