diff --git a/README.md b/README.md index 150e42d..278d978 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ Guide mkdir logs sudo chmod 0600 config/config.yml data/db.sqlite3 sudo chown www-data:www-data config/config.yml data data/db.sqlite3 logs + ... # TODO: convert these into scripts, add module instructions ### Test diff --git a/app.py b/app.py index 3b951ed..698e2fc 100755 --- a/app.py +++ b/app.py @@ -35,6 +35,7 @@ config.install(app) @app.before_request def prepare_request(): + """Set up the Flask global context variable with important objects.""" g.auth = auth g.config = config g.eve = eve @@ -47,6 +48,11 @@ app.teardown_appcontext(Database.post_hook) @app.route("/") @app.catch_exceptions def index(): + """Render and return the index page. + + This is a informational landing page for non-logged-in users, and the corp + homepage for those who are logged in. + """ success, _ = try_func(auth.is_authenticated) if success: module = config.get("modules.home") @@ -58,6 +64,7 @@ def index(): @app.route("/login", methods=["GET", "POST"]) @app.catch_exceptions def login(): + """Handle the last step of a SSO login request.""" code = request.args.get("code") state = request.args.get("state") @@ -71,6 +78,7 @@ def login(): @app.route("/logout", methods=["GET", "POST"]) @app.catch_exceptions def logout(): + """Log the user out (POST), or ask them to confirm a log out (GET).""" if request.method == "GET": return render_template("logout.mako") @@ -82,12 +90,14 @@ def logout(): @app.catch_exceptions @app.route_restricted def set_style(style): + """Set the user's style preference.""" if not auth.set_character_style(style): abort(404) return "", 204 @app.errorhandler(404) def page_not_found(err): + """Render and return the 404 error template.""" return render_template("404.mako"), 404 if __name__ == "__main__": diff --git a/calefaction/auth.py b/calefaction/auth.py index f152f99..dc77bcf 100644 --- a/calefaction/auth.py +++ b/calefaction/auth.py @@ -10,8 +10,6 @@ from .exceptions import AccessDeniedError __all__ = ["AuthManager"] -_SCOPES = [] # ... - class AuthManager: """Authentication manager. Handles user access and management.""" EXPIRY_THRESHOLD = 30 @@ -316,7 +314,7 @@ class AuthManager: """Return a complete EVE SSO link that the user can use to log in.""" cid = self._config.get("auth.client_id") target = url_for("login", _external=True, _scheme=self._config.scheme) - scopes = _SCOPES + scopes = self._config.collect_scopes() state = self._get_state_hash() return self._eve.sso.get_authorize_url(cid, target, scopes, state) diff --git a/calefaction/config.py b/calefaction/config.py index 2f1edab..d9ac891 100644 --- a/calefaction/config.py +++ b/calefaction/config.py @@ -89,3 +89,12 @@ class Config: return yaml.load(fp) except FileNotFoundError: return None + + def collect_scopes(self): + """Return a list of SSO scopes required for all/regular users.""" + scopes = set() + for module in self.modules: + scopes |= module.scopes() + if not scopes: + scopes.add("publicData") + return sorted(list(scopes)) diff --git a/calefaction/module.py b/calefaction/module.py index f2b22fb..a033ece 100644 --- a/calefaction/module.py +++ b/calefaction/module.py @@ -51,3 +51,9 @@ class Module: """Return a navigation bar HTML snippet for this module, or None.""" if hasattr(self._module, "navitem"): return self._module.navitem() + + def scopes(self): + """Return a set of SSO scopes required by this module.""" + if hasattr(self._module, "SCOPES"): + return set(self._module.SCOPES) + return set() diff --git a/calefaction/modules/campaigns.py b/calefaction/modules/campaigns.py index bd61be5..3f88bb4 100644 --- a/calefaction/modules/campaigns.py +++ b/calefaction/modules/campaigns.py @@ -15,9 +15,11 @@ def get_current(): return setting def home(): + """Render and return the main campaign page.""" return render_template("campaigns/campaign.mako", current=get_current()) def navitem(): + """Render and return the navigation item for this module.""" current = get_current() if current: result = render_template("campaigns/navitem.mako", current=current) @@ -25,10 +27,12 @@ def navitem(): @blueprint.rroute("/campaign") def campaign(): + """Render and return the current campaign page.""" return home() @blueprint.rroute("/settings/campaign/", methods=["POST"]) def set_campaign(campaign): + """Update the user's currently selected campaign.""" if campaign not in config["enabled"]: abort(404) g.auth.set_character_modprop("campaigns", "current", campaign) diff --git a/calefaction/modules/members.py b/calefaction/modules/members.py index ab90f0f..1ae7c29 100644 --- a/calefaction/modules/members.py +++ b/calefaction/modules/members.py @@ -2,5 +2,7 @@ # ... +SCOPES = {"esi-corporations.read_corporation_membership.v1"} + def navitem(): return "Members" diff --git a/config/config.yml.sample b/config/config.yml.sample index 884b622..2faf298 100644 --- a/config/config.yml.sample +++ b/config/config.yml.sample @@ -38,7 +38,8 @@ auth: # https://developers.eveonline.com/applications for this corp's website. # Set the callback URL to http(s):///login (match the protocol # with "site.https" above) and the scopes to whatever is required by the - # modules you've enabled. + # modules you've enabled. If none of your modules require scopes, select at + # least "publicData". # SSO client ID: client_id: a290afea820b8dd8c46d3883898ab66d # SSO client secret: