mirror of
				https://github.com/arcan1s/ahriman.git
				synced 2025-11-03 23:33:41 +00:00 
			
		
		
		
	add static files support and cookie expiration settings
This commit is contained in:
		
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							@ -3,7 +3,7 @@
 | 
			
		||||
 | 
			
		||||
PROJECT := ahriman
 | 
			
		||||
 | 
			
		||||
FILES := AUTHORS COPYING CONFIGURING.md README.md docs package src setup.cfg setup.py
 | 
			
		||||
FILES := AUTHORS COPYING README.md docs package src setup.cfg setup.py
 | 
			
		||||
TARGET_FILES := $(addprefix $(PROJECT)/, $(FILES))
 | 
			
		||||
IGNORE_FILES := package/archlinux src/.mypy_cache
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -26,6 +26,7 @@ Base authorization settings.
 | 
			
		||||
* `allow_read_only` - allow requesting read only pages without authorization, boolean, required.
 | 
			
		||||
* `allowed_paths` - URI paths (exact match) which can be accessed without authorization, space separated list of strings, optional.
 | 
			
		||||
* `allowed_paths_groups` - URI paths prefixes which can be accessed without authorization, space separated list of strings, optional.
 | 
			
		||||
* `max_age` - parameter which controls both cookie expiration and token expiration inside the service, integer, optional, default is 7 days.
 | 
			
		||||
* `salt` - password hash salt, string, required in case if authorization enabled (automatically generated by `create-user` subcommand).
 | 
			
		||||
 | 
			
		||||
## `auth:*` groups
 | 
			
		||||
@ -124,5 +125,6 @@ Web server settings. If any of `host`/`port` is not set, web integration will be
 | 
			
		||||
* `host` - host to bind, string, optional.
 | 
			
		||||
* `password` - password to authorize in web service in order to update service status, string, required in case if authorization enabled.  
 | 
			
		||||
* `port` - port to bind, int, optional.
 | 
			
		||||
* `static_path` - path to directory with static files, string, required.
 | 
			
		||||
* `templates` - path to templates directory, string, required.
 | 
			
		||||
* `username` - username to authorize in web service in order to update service status, string, required in case if authorization enabled.  
 | 
			
		||||
 | 
			
		||||
@ -11,6 +11,7 @@ root = /
 | 
			
		||||
[auth]
 | 
			
		||||
target = disabled
 | 
			
		||||
allow_read_only = yes
 | 
			
		||||
max_age = 604800
 | 
			
		||||
 | 
			
		||||
[build]
 | 
			
		||||
archbuild_flags =
 | 
			
		||||
@ -49,4 +50,5 @@ chunk_size = 8388608
 | 
			
		||||
 | 
			
		||||
[web]
 | 
			
		||||
host = 127.0.0.1
 | 
			
		||||
static_path = /usr/share/ahriman/static
 | 
			
		||||
templates = /usr/share/ahriman
 | 
			
		||||
@ -8,8 +8,5 @@ ExecStart=/usr/bin/ahriman --architecture %i web
 | 
			
		||||
User=ahriman
 | 
			
		||||
Group=ahriman
 | 
			
		||||
 | 
			
		||||
KillSignal=SIGQUIT
 | 
			
		||||
SuccessExitStatus=SIGQUIT
 | 
			
		||||
 | 
			
		||||
[Install]
 | 
			
		||||
WantedBy=multi-user.target
 | 
			
		||||
@ -5,6 +5,8 @@
 | 
			
		||||
 | 
			
		||||
        <meta name="viewport" content="width=device-width, initial-scale=1">
 | 
			
		||||
 | 
			
		||||
        <link rel="shortcut icon" href="/static/favicon.ico">
 | 
			
		||||
 | 
			
		||||
        {% include "utils/style.jinja2" %}
 | 
			
		||||
    </head>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								package/share/ahriman/static/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								package/share/ahriman/static/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 5.7 KiB  | 
							
								
								
									
										3
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								setup.py
									
									
									
									
									
								
							@ -74,6 +74,9 @@ setup(
 | 
			
		||||
            "package/share/ahriman/build-status/package-actions-modals.jinja2",
 | 
			
		||||
            "package/share/ahriman/build-status/package-actions-script.jinja2",
 | 
			
		||||
        ]),
 | 
			
		||||
        ("share/ahriman/static", [
 | 
			
		||||
            "package/share/ahriman/static/favicon.ico",
 | 
			
		||||
        ]),
 | 
			
		||||
        ("share/ahriman/utils", [
 | 
			
		||||
            "package/share/ahriman/utils/bootstrap-scripts.jinja2",
 | 
			
		||||
            "package/share/ahriman/utils/style.jinja2",
 | 
			
		||||
 | 
			
		||||
@ -36,8 +36,8 @@ class Auth:
 | 
			
		||||
    :cvar ALLOWED_PATHS_GROUPS: URI paths prefixes which can be accessed without authorization, predefined
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    ALLOWED_PATHS = {"/", "/favicon.ico", "/index.html"}
 | 
			
		||||
    ALLOWED_PATHS_GROUPS = {"/user-api"}
 | 
			
		||||
    ALLOWED_PATHS = {"/", "/index.html"}
 | 
			
		||||
    ALLOWED_PATHS_GROUPS = {"/static", "/user-api"}
 | 
			
		||||
 | 
			
		||||
    def __init__(self, configuration: Configuration, provider: AuthSettings = AuthSettings.Disabled) -> None:
 | 
			
		||||
        """
 | 
			
		||||
@ -51,6 +51,7 @@ class Auth:
 | 
			
		||||
        self.allowed_paths_groups = set(configuration.getlist("auth", "allowed_paths_groups"))
 | 
			
		||||
        self.allowed_paths_groups.update(self.ALLOWED_PATHS_GROUPS)
 | 
			
		||||
        self.enabled = provider.is_enabled
 | 
			
		||||
        self.max_age = configuration.getint("auth", "max_age", fallback=7 * 24 * 3600)
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def load(cls: Type[Auth], configuration: Configuration) -> Auth:
 | 
			
		||||
 | 
			
		||||
@ -95,7 +95,7 @@ def setup_auth(application: web.Application, validator: Auth) -> web.Application
 | 
			
		||||
    """
 | 
			
		||||
    fernet_key = fernet.Fernet.generate_key()
 | 
			
		||||
    secret_key = base64.urlsafe_b64decode(fernet_key)
 | 
			
		||||
    storage = EncryptedCookieStorage(secret_key, cookie_name='API_SESSION')
 | 
			
		||||
    storage = EncryptedCookieStorage(secret_key, cookie_name="API_SESSION", max_age=validator.max_age)
 | 
			
		||||
    setup_session(application, storage)
 | 
			
		||||
 | 
			
		||||
    authorization_policy = AuthorizationPolicy(validator)
 | 
			
		||||
 | 
			
		||||
@ -18,6 +18,7 @@
 | 
			
		||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
#
 | 
			
		||||
from aiohttp.web import Application
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
 | 
			
		||||
from ahriman.web.views.index import IndexView
 | 
			
		||||
from ahriman.web.views.service.add import AddView
 | 
			
		||||
@ -31,7 +32,7 @@ from ahriman.web.views.user.login import LoginView
 | 
			
		||||
from ahriman.web.views.user.logout import LogoutView
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup_routes(application: Application) -> None:
 | 
			
		||||
def setup_routes(application: Application, static_path: Path) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    setup all defined routes
 | 
			
		||||
 | 
			
		||||
@ -64,10 +65,13 @@ def setup_routes(application: Application) -> None:
 | 
			
		||||
        POST /user-api/v1/logout               logout from service
 | 
			
		||||
 | 
			
		||||
    :param application: web application instance
 | 
			
		||||
    :param static_path: path to static files directory
 | 
			
		||||
    """
 | 
			
		||||
    application.router.add_get("/", IndexView, allow_head=True)
 | 
			
		||||
    application.router.add_get("/index.html", IndexView, allow_head=True)
 | 
			
		||||
 | 
			
		||||
    application.router.add_static("/static", static_path, follow_symlinks=True)
 | 
			
		||||
 | 
			
		||||
    application.router.add_post("/service-api/v1/add", AddView)
 | 
			
		||||
 | 
			
		||||
    application.router.add_post("/service-api/v1/remove", RemoveView)
 | 
			
		||||
 | 
			
		||||
@ -84,7 +84,7 @@ def setup_service(architecture: str, configuration: Configuration, spawner: Spaw
 | 
			
		||||
    application.middlewares.append(exception_handler(application.logger))
 | 
			
		||||
 | 
			
		||||
    application.logger.info("setup routes")
 | 
			
		||||
    setup_routes(application)
 | 
			
		||||
    setup_routes(application, configuration.getpath("web", "static_path"))
 | 
			
		||||
 | 
			
		||||
    application.logger.info("setup templates")
 | 
			
		||||
    aiohttp_jinja2.setup(application, loader=jinja2.FileSystemLoader(configuration.getpath("web", "templates")))
 | 
			
		||||
 | 
			
		||||
@ -62,6 +62,7 @@ def test_get_local_files(s3: S3, resource_path_root: Path) -> None:
 | 
			
		||||
        Path("web/templates/build-status/login-modal.jinja2"),
 | 
			
		||||
        Path("web/templates/build-status/package-actions-modals.jinja2"),
 | 
			
		||||
        Path("web/templates/build-status/package-actions-script.jinja2"),
 | 
			
		||||
        Path("web/templates/static/favicon.ico"),
 | 
			
		||||
        Path("web/templates/utils/bootstrap-scripts.jinja2"),
 | 
			
		||||
        Path("web/templates/utils/style.jinja2"),
 | 
			
		||||
        Path("web/templates/build-status.jinja2"),
 | 
			
		||||
 | 
			
		||||
@ -88,14 +88,11 @@ async def test_auth_handler_write(auth: Auth, mocker: MockerFixture) -> None:
 | 
			
		||||
        check_permission_mock.assert_called_with(aiohttp_request, UserAccess.Write, aiohttp_request.path)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_setup_auth(
 | 
			
		||||
        application_with_auth: web.Application,
 | 
			
		||||
        configuration: Configuration,
 | 
			
		||||
        mocker: MockerFixture) -> None:
 | 
			
		||||
def test_setup_auth(application_with_auth: web.Application, auth: Auth, mocker: MockerFixture) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    must setup authorization
 | 
			
		||||
    """
 | 
			
		||||
    aiohttp_security_setup_mock = mocker.patch("aiohttp_security.setup")
 | 
			
		||||
    application = setup_auth(application_with_auth, configuration)
 | 
			
		||||
    application = setup_auth(application_with_auth, auth)
 | 
			
		||||
    assert application.get("validator") is not None
 | 
			
		||||
    aiohttp_security_setup_mock.assert_called_once()
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,12 @@
 | 
			
		||||
from aiohttp import web
 | 
			
		||||
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.web.routes import setup_routes
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_setup_routes(application: web.Application) -> None:
 | 
			
		||||
def test_setup_routes(application: web.Application, configuration: Configuration) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    must generate non empty list of routes
 | 
			
		||||
    """
 | 
			
		||||
    setup_routes(application)
 | 
			
		||||
    setup_routes(application, configuration.getpath("web", "static_path"))
 | 
			
		||||
    assert application.router.routes()
 | 
			
		||||
 | 
			
		||||
@ -26,3 +26,11 @@ async def test_get_without_auth(client: TestClient) -> None:
 | 
			
		||||
    response = await client.get("/")
 | 
			
		||||
    assert response.status == 200
 | 
			
		||||
    assert await response.text()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def test_get_static(client: TestClient) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    must return static files
 | 
			
		||||
    """
 | 
			
		||||
    response = await client.get("/static/favicon.ico")
 | 
			
		||||
    assert response.status == 200
 | 
			
		||||
 | 
			
		||||
@ -59,4 +59,5 @@ secret_key =
 | 
			
		||||
 | 
			
		||||
[web]
 | 
			
		||||
host = 127.0.0.1
 | 
			
		||||
static_path = ../web/templates/static
 | 
			
		||||
templates = ../web/templates
 | 
			
		||||
		Reference in New Issue
	
	Block a user