Authorization and access control security guidance based on Project CodeGuard — covers RBAC/ABAC/ReBAC, IDOR prevention, mass assignment, and transaction authorization
87
82%
Does it follow best practices?
Impact
93%
1.45xAverage score across 6 eval scenarios
Passed
No findings from the security scan
A startup has built a content collaboration tool where users belong to teams and teams own collections of articles. The original access control was simple: admins can do anything, editors can edit, readers can view. As the product has grown, this has broken down — a contractor who is a reader for one team is accidentally able to view private articles owned by a completely different team, and a paying subscriber who upgrades to "premium" should unlock additional capabilities that a free reader with the same role label cannot access.
The engineering team has realized their flat role-based system can't express the real rules of who should see what: access depends not just on the user's label, but on which team they belong to, whether a specific article is marked sensitive, and the user's subscription tier. The backend lead wants the access control refactored to properly handle these nuances before rolling out the new premium tier, and wants the design documented so future engineers understand the intent.
Produce content_api.py containing the refactored Flask application with improved access control logic. Also produce security_notes.md explaining the authorization design decisions, including which access control approach you used and why you chose it.
The following files are provided as inputs. Extract them before beginning.
=============== FILE: inputs/content_api.py =============== from flask import Flask, jsonify, request, abort, g
app = Flask(name)
def get_current_user(): return { "user_id": int(request.headers.get("X-User-Id", "1")), "role": request.headers.get("X-Role", "reader"), # admin | editor | reader "team_id": int(request.headers.get("X-Team-Id", "1")), "tier": request.headers.get("X-Tier", "free"), # free | premium }
_teams = { 1: {"id": 1, "name": "Marketing"}, 2: {"id": 2, "name": "Engineering"}, }
_articles = { 1: {"id": 1, "team_id": 1, "title": "Q2 Campaign", "sensitive": False, "content": "..."}, 2: {"id": 2, "team_id": 1, "title": "Budget Plan", "sensitive": True, "content": "..."}, 3: {"id": 3, "team_id": 2, "title": "API Roadmap", "sensitive": False, "content": "..."}, 4: {"id": 4, "team_id": 2, "title": "Security Audit", "sensitive": True, "content": "..."}, }
PERMISSIONS = { "admin": ["articles:read", "articles:write", "articles:delete", "articles:export"], "editor": ["articles:read", "articles:write"], "reader": ["articles:read"], }
@app.route("/api/articles/int:article_id", methods=["GET"]) def get_article(article_id): user = get_current_user() if "articles:read" not in PERMISSIONS.get(user["role"], []): abort(403) article = _articles.get(article_id) if article is None: abort(404) return jsonify(article)
@app.route("/api/articles/int:article_id", methods=["PUT"]) def update_article(article_id): user = get_current_user() if "articles:write" not in PERMISSIONS.get(user["role"], []): abort(403) article = _articles.get(article_id) if article is None: abort(404) article.update(request.get_json()) return jsonify(article)
@app.route("/api/articles/int:article_id/export", methods=["POST"]) def export_article(article_id): user = get_current_user() if "articles:export" not in PERMISSIONS.get(user["role"], []): abort(403) article = _articles.get(article_id) if article is None: abort(404) return jsonify({"url": f"https://cdn.example.com/articles/{article_id}.pdf"})
@app.route("/api/articles/int:article_id", methods=["DELETE"]) def delete_article(article_id): user = get_current_user() if user["role"] != "admin": abort(403) if article_id not in _articles: abort(404) del _articles[article_id] return "", 204
if name == "main": app.run(debug=True) =============== END FILE ===============