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