Skip to content

Commit c3d09c9

Browse files
committed
add observing user directories
1 parent 1cdddd7 commit c3d09c9

File tree

11 files changed

+117
-26
lines changed

11 files changed

+117
-26
lines changed

core/addon.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,22 @@ async def async_setup_addon(
233233

234234
return True
235235

236+
async def read(self, key):
237+
"""read key/value pair from addon."""
238+
try:
239+
component = importlib.import_module(f"core.addons.{self.domain}")
240+
except ImportError as err:
241+
_LOGGER.error(f"Unable to import addon '{self.domain}': {err}")
242+
return False
243+
except Exception: # pylint: disable=broad-except
244+
_LOGGER.exception(f"Setup failed for {self.domain}: unknown error")
245+
return False
246+
247+
if hasattr(component, "read"):
248+
await component.read(self.core, key)
249+
else:
250+
_LOGGER.error(f"Unable to read key from addon '{self.domain}'")
251+
236252
async def async_process_images_in_addons(self, images) -> bool:
237253
"""Trigger image addons with images to process."""
238254
if self.type == AddonType.IMAGE:

core/addons/api/__init__.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -195,10 +195,12 @@ async def get(
195195
# -d remove exif data
196196

197197
result = Session.query(Photo).filter(Photo.uuid == entity_id).first()
198-
199-
file = os.path.join(result.directory, result.filename)
200-
if os.path.exists(os.path.join(file)):
201-
return web.FileResponse(path=file, status=200)
198+
if result:
199+
file = os.path.join(result.directory, result.filename)
200+
if os.path.exists(os.path.join(file)):
201+
return web.FileResponse(path=file, status=200)
202+
else:
203+
raise web.HTTPNotFound()
202204
else:
203205
raise web.HTTPNotFound()
204206

core/authentication/auth_database.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
import string
44
import uuid
55
from datetime import datetime, timedelta
6-
from typing import Optional, Tuple
6+
from typing import TYPE_CHECKING, Optional, Tuple
7+
8+
if TYPE_CHECKING:
9+
from core.core import ApplicationCore
710

811
import jwt
912
from passlib.hash import sha256_crypt
@@ -21,7 +24,7 @@
2124

2225

2326
class AuthDatabase:
24-
def __init__(self, data_dir: str):
27+
def __init__(self, core: "ApplicationCore", data_dir: str):
2528
Base.metadata.create_all(engine)
2629

2730
users = Session.query(User).all()
@@ -47,6 +50,8 @@ def __init__(self, data_dir: str):
4750
Session.add(user)
4851
Session.commit()
4952

53+
core.storage.create_user_home(user.id)
54+
5055
async def check_credentials(self, email: str, password: str) -> bool:
5156
"""Check if the given email is found, not disabled and matches with the hashed password."""
5257
user = (

core/authorization/__init__.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -172,10 +172,16 @@ async def delete_user_handler(self, request: web.Request) -> web.StreamResponse:
172172

173173
loggedInUser = await self.core.authentication.check_authorized(request)
174174
if loggedInUser != userId:
175-
_LOGGER.error(
176-
f"attempt to change a different user! Authenticated user: {loggedInUser}, user to change: {userId}"
177-
)
178-
raise web.HTTPForbidden
175+
# admin users can delete other users
176+
try:
177+
await self.core.authentication.check_permission(
178+
request, "admin.users:write"
179+
)
180+
except web.HTTPForbidden:
181+
_LOGGER.error(
182+
f"attempt to change a different user! Authenticated user: {loggedInUser}, user to change: {userId}"
183+
)
184+
raise web.HTTPForbidden
179185

180186
Session.query(User).filter(User.id == loggedInUser).update(
181187
{User.deleted_date: datetime.utcnow(), User.disabled: True},
@@ -235,13 +241,16 @@ async def create_user_handler(self, request: web.Request) -> web.StreamResponse:
235241
"lastname": new_user.lastname,
236242
}
237243

244+
# create user home
245+
self.core.storage.create_user_home(new_user.id)
246+
238247
return web.json_response(status=201, data=data)
239248

240249
_LOGGER.error(f"Could not find newly created user {email}")
241250
return web.json_response(status=500, data=data)
242251

243252
async def check_scope(self, scope: str):
253+
# TODO: check if requested scope has been granted by the user when creating the current token
244254
_LOGGER.warning(f"check_scope({scope})")
245255

246-
# TODO: raise if scope is missing
247256
# raise web.HTTPForbidden()

core/base.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
Session = scoped_session(session_factory)
1111

1212
Base = declarative_base()
13-
Base.metadata.create_all(engine)
1413

1514

1615
def generate_uuid():

core/core.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,26 @@
88
import sys
99
from logging.handlers import TimedRotatingFileHandler
1010
from time import monotonic
11-
from typing import (Any, Awaitable, Callable, Dict, Iterable, List, Optional,
12-
Sequence, Set, TypeVar)
11+
from typing import (
12+
Any,
13+
Awaitable,
14+
Callable,
15+
Dict,
16+
Iterable,
17+
List,
18+
Optional,
19+
Sequence,
20+
Set,
21+
TypeVar,
22+
)
1323

1424
from colorlog import ColoredFormatter
1525

1626
from core import loader
1727
from core.addon import Addon
1828
from core.authentication import Authentication, AuthenticationClient
1929
from core.authorization import Authorization
30+
from core.base import Base, engine
2031
from core.configs import Config
2132
from core.persistency.persistency import PersistencyManager
2233
from core.utils.timeout import TimeoutManager
@@ -296,15 +307,17 @@ async def async_block_till_done(self) -> None:
296307

297308
self.http = Webserver(self)
298309

310+
Base.metadata.create_all(engine)
311+
299312
# setup addons from config entries
300313
await self.async_set_up_addons()
301314

315+
await self.storage.start_directory_observing()
316+
_LOGGER.info("Observe user directories for file changes.")
317+
302318
await self.http.start()
303319
_LOGGER.info("Webserver should be up and running...")
304320

305-
await self.storage.start_directory_observing()
306-
_LOGGER.info("Observe user directories.")
307-
308321
while self._pending_tasks:
309322
pending = [task for task in self._pending_tasks if not task.done()]
310323
self._pending_tasks.clear()

core/persistency/dto/directory.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"""Directory representation for persistency."""
2+
import uuid
3+
from datetime import datetime
4+
5+
from sqlalchemy import Column, DateTime, ForeignKey, Integer, String
6+
7+
from ...base import Base
8+
9+
10+
def generate_uuid():
11+
return str(uuid.uuid4())
12+
13+
14+
class Directory(Base):
15+
"""Directory representation for user directories containing files."""
16+
17+
__tablename__ = "directories"
18+
19+
id = Column(String, name="uuid", primary_key=True, default=generate_uuid)
20+
directory = Column(String)
21+
owner = Column(Integer, ForeignKey("users.uuid"), nullable=False)
22+
added = Column(DateTime, default=datetime.utcnow)
23+
last_scanned = Column(DateTime)
24+
25+
def __init__(self, directory, owner):
26+
self.directory = directory
27+
self.owner = owner
28+
29+
def __repr__(self):
30+
return f"Directory({self.directory}, owner={self.owner})"

core/persistency/persistency.py

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -102,27 +102,42 @@ def process_load_queue(self, queue: Queue) -> None:
102102
time.sleep(1)
103103

104104
async def start_directory_observing(self) -> None:
105-
_LOGGER.debug("start_directory_observing")
105+
"""Start observing user directories for changes."""
106106
self.worker.start()
107107

108-
# TODO: add directories into database
109-
# webDir = os.path.join(self.core.config.data_dir, "web")
110-
# directory = Directory(webDir, 1)
111-
# Session.add(directory)
112-
# Session.commit()
113-
114108
directories = Session.query(Directory).all()
115109
for dir in directories:
116110
path = os.path.join(dir.directory)
117111
self.observer.schedule(self.event_handler, path, recursive=True)
118112
self.observer.start()
119113

120114
async def stop_directory_observing(self) -> None:
121-
_LOGGER.debug("stop_directory_observing")
115+
"""Stop observing user directories."""
122116
self.worker.stop()
123117
self.observer.stop()
124118
self.observer.join()
125119

120+
async def restart_directory_observing(self) -> None:
121+
"""stop and re-start directory observing."""
122+
await self.stop_directory_observing()
123+
await self.start_directory_observing()
124+
125+
def create_user_home(self, user_id: str) -> None:
126+
"""Create a directory for the user in the configured data directory."""
127+
# create user_home in filesystem
128+
path = os.path.join(self.config.data_dir, user_id)
129+
os.mkdir(path)
130+
131+
# add user_home to observing
132+
self.observer.schedule(self.event_handler, path, recursive=True)
133+
134+
# add user_home to database
135+
directory = Directory(path, user_id)
136+
Session.add(directory)
137+
Session.commit()
138+
139+
_LOGGER.info(f"user home for user {user_id} created.")
140+
126141
async def read_photos(self, user_id: int, offset: int = 0, limit: int = 10) -> List:
127142
return (
128143
Session.query(Photo)

core/webserver/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ async def start(self):
8080
_LOGGER.info(f"Webserver is listening on {site._host}:{site._port}")
8181

8282
async def init_auth(self):
83-
auth_database = AuthDatabase(self.core.config.data_dir)
83+
auth_database = AuthDatabase(self.core, self.core.config.data_dir)
8484

8585
# setup auth
8686
self.core.authorization = Authorization(self.core, self.app)

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ pyyaml>=5.3.1
1313
sqlalchemy>=1.3.20
1414
voluptuous>=0.12.0
1515
voluptuous-serialize>=2.4.0
16+
watchdog>=2.0.0
1617
wheel>=0.36.2

0 commit comments

Comments
 (0)