diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..fc70b4a3e20e67ecc76c16c1389981ca0c154399
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+**__pycache__/
+**app/
\ No newline at end of file
diff --git a/api-server/.gitignore b/api-server/.gitignore
deleted file mode 100644
index d3b0ad4dd10f031d5fce4908fbe172ffdec54472..0000000000000000000000000000000000000000
--- a/api-server/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-__pycache__/
-app/
\ No newline at end of file
diff --git a/api-server/LocationStorage.py b/api-server/LocationStorage.py
deleted file mode 100644
index 6b5e5ca3ac7de031fea2ffcdc1d880e71e12ad4e..0000000000000000000000000000000000000000
--- a/api-server/LocationStorage.py
+++ /dev/null
@@ -1,29 +0,0 @@
-
-from pydantic import BaseModel
-
-from typing import Optional
-from typing import Dict
-from enum import Enum
-
-class LocationDataType(Enum):
-    DATASET: str = 'dataset'
-    STORAGETARGET: str = 'storage_target'
-
-class LocationData(BaseModel):
-    name: str
-    url: str
-    metadata: Optional[Dict[str, str]]
-
-
-class AbstractLocationDataStorageAdapter:
-    def getList(self, type: LocationDataType):
-        raise NotImplementedError()
-
-    def addNew(self, type: LocationDataType, data: LocationData):
-        raise NotImplementedError()
-
-    def getDetails(self, type: LocationDataType, id: str):
-        raise NotImplementedError()
-
-    def updateDetails(self, type:LocationDataType, id:str, data: LocationData):
-        raise NotImplementedError()
diff --git a/api-server/Dockerfile b/apiserver/Dockerfile
similarity index 61%
rename from api-server/Dockerfile
rename to apiserver/Dockerfile
index 9d8e06d9d3258f4c78c13c5531bf6be75fb7b75d..a97901c34850e2ee49023bbf92060270ac66aa37 100644
--- a/api-server/Dockerfile
+++ b/apiserver/Dockerfile
@@ -2,8 +2,12 @@ FROM tiangolo/uvicorn-gunicorn:python3.8
 
 LABEL maintainer="Christian Böttcher <c.boettcher@fz-juelich.de>"
 
+RUN mkdir -p /app/data
+
 RUN apt update && apt upgrade -y
 
 RUN pip install --no-cache-dir fastapi
 
-COPY ./api-server.py /app/main.py
\ No newline at end of file
+COPY ./main.py /app/
+COPY ./LocationStorage.py /app/
+COPY ./JsonFileStorageAdapter.py /app/
\ No newline at end of file
diff --git a/api-server/JsonFileStorageAdapter.py b/apiserver/JsonFileStorageAdapter.py
similarity index 62%
rename from api-server/JsonFileStorageAdapter.py
rename to apiserver/JsonFileStorageAdapter.py
index c3c8c50e30930aba30cd35336bddd2a34c31bf77..e65e62d8aac3bfe97456322bd3be0b0c506be0f7 100644
--- a/api-server/JsonFileStorageAdapter.py
+++ b/apiserver/JsonFileStorageAdapter.py
@@ -4,9 +4,19 @@ import uuid
 
 from LocationStorage import AbstractLocationDataStorageAdapter, LocationData, LocationDataType
 
+from typing import List
 
 DEFAULT_JSON_FILEPATH: str = "./app/data"
 
+class StoredData:
+    actualData: LocationData
+    users: List[str]
+
+
+# This stores LocationData via the StoredData Object as json files
+# These Jsonfiles then contain the actualData, as well as the users with permissions for this LocationData
+# all users have full permission to to anything with this dataobject, uncluding removing their own access (this might trigger a confirmation via the frontend, but this is not enforced via the api)
+# IMPORTANT: The adapter does not check for authentication or authorization, it should only be invoked if the permissions have been checked
 class JsonFileStorageAdapter(AbstractLocationDataStorageAdapter):
     data_dir: str
 
@@ -29,10 +39,10 @@ class JsonFileStorageAdapter(AbstractLocationDataStorageAdapter):
             for f in allFiles:
                 with open(os.path.join(localpath, f)) as file:
                     data = json.load(file)
-                    retList.append({data['name'] : f})
+                    retList.append({data['actualData']['name'] : f})
             return retList
 
-    def addNew(self, type: LocationDataType, data: LocationData):
+    def addNew(self, type: LocationDataType, data: LocationData, usr: str):
         localpath = os.path.join(self.data_dir, type.value)
         if not (os.path.isdir(localpath)):
             # This type has apparently not yet been used at all, therefore we need to create its directory
@@ -41,8 +51,11 @@ class JsonFileStorageAdapter(AbstractLocationDataStorageAdapter):
         id = str(uuid.uuid4())
         while (os.path.exists(os.path.join(localpath, id))):
             id = str(uuid.uuid4())
+        toStore = StoredData()
+        toStore.users = [usr]
+        toStore.actualData = data
         with open(os.path.join(localpath, id), 'w') as json_file:
-            json.dump(data.__dict__, json_file)
+            json.dump(toStore.__dict__, json_file)
         return {id : data}
 
     def getDetails(self, type: LocationDataType, id: str):
@@ -52,13 +65,34 @@ class JsonFileStorageAdapter(AbstractLocationDataStorageAdapter):
             raise FileNotFoundError('The requested Object does not exist.')
         with open(fullpath) as file:
             data = json.load(file)
-        return data
+        return data['actualData']
 
-    def updateDetails(self, type:LocationDataType, id:str, data: LocationData):
+    def updateDetails(self, type:LocationDataType, id:str, data: LocationData, usr: str):
         localpath = os.path.join(self.data_dir, type.value)
         fullpath = os.path.join(localpath, id)
         if not os.path.isfile(fullpath):
             raise FileNotFoundError('The requested Object does not exist.')
+        
+        toStore = StoredData()
+
+        # get permissions from old file
+        with open(fullpath) as file:
+            data = json.load(file)
+            toStore.users = data['users']
+
+        toStore.actualData = data
         with open(fullpath, 'w') as file:
             json.dump(data.__dict__, file)
         return {id : data}
+
+    def getOwner(self, type: LocationDataType(), id: str):
+        raise NotImplementedError()
+
+    def checkPerm(self, type: LocationDataType, id: str, usr: str):
+        raise NotImplementedError()
+
+    def addPerm(self, type: LocationDataType, id: str, authUsr: str, newUser: str):
+        raise NotImplementedError()
+
+    def rmPerm(self, type: LocationDataType, id: str, usr: str, rmUser: str):
+        raise NotImplementedError()
\ No newline at end of file
diff --git a/apiserver/LocationStorage.py b/apiserver/LocationStorage.py
new file mode 100644
index 0000000000000000000000000000000000000000..d80c07117f2fd2b60cf9f7fed5772b47b88373b4
--- /dev/null
+++ b/apiserver/LocationStorage.py
@@ -0,0 +1,60 @@
+
+from pydantic import BaseModel
+
+from typing import Optional
+from typing import Dict
+from enum import Enum
+
+class LocationDataType(Enum):
+    DATASET: str = 'dataset'
+    STORAGETARGET: str = 'storage_target'
+
+class LocationData(BaseModel):
+    name: str
+    url: str
+    metadata: Optional[Dict[str, str]]
+
+#
+'''
+This is an abstract storage adapter for storing information about datasets, storage targets and similar things.
+It can easily be expanded to also store other data (that has roughly similar metadata), just by expanding the LocationDataType Enum.
+
+In general, all data is public. This means, that the adapter does not do any permission checking, except when explicitly asked via the checkPerm function.
+The caller therefore has to manually decide when to check for permissions, and not call any function unless it is already authorized (or does not need any authorization).
+
+The usr: str (the user id) that is required for several functions, is a unique and immutable string, that identifies the user. This can be a verified email or a user name. 
+The management of authentication etc. is done by the caller, this adapter assumes that the user id fulfills the criteria.
+Permissions are stored as a list of user ids, and every id is authorized for full access.
+'''
+class AbstractLocationDataStorageAdapter:
+    # get a list of all LocationData Elements with the provided type, as pairs of {name : id}
+    def getList(self, type: LocationDataType):
+        raise NotImplementedError()
+
+    # add a new element of the provided type, assign and return the id and the new data as {id : LocationData}
+    def addNew(self, type: LocationDataType, data: LocationData, usr: str):
+        raise NotImplementedError()
+
+    # return the LocationData of the requested object (identified by id and type)
+    def getDetails(self, type: LocationDataType, id: str):
+        raise NotImplementedError()
+
+    # change the details of the requested object, return {id : newData}
+    def updateDetails(self, type:LocationDataType, id:str, data: LocationData, usr: str):
+        raise NotImplementedError()
+
+    # return the owner of the requested object; if multiple owners are set, return them is a list
+    def getOwner(self, type: LocationDataType, id: str):
+        raise NotImplementedError()
+
+    # check if the given user has permission to change the given object
+    def checkPerm(self, type: LocationDataType, id: str, usr: str):
+        raise NotImplementedError()
+
+    # add user to file perm
+    def addPerm(self, type: LocationDataType, id: str, usr: str):
+        raise NotImplementedError()
+
+    # remove user from file perm
+    def rmPerm(self, type: LocationDataType, id: str, usr: str):
+        raise NotImplementedError()
diff --git a/api-server/README.md b/apiserver/README.md
similarity index 95%
rename from api-server/README.md
rename to apiserver/README.md
index c8f39fa553bb8ddcc21fd7afa09228eab9b58a0e..90a74be54980c019e4645fa53338790331956fad 100644
--- a/api-server/README.md
+++ b/apiserver/README.md
@@ -23,7 +23,7 @@ pip install uvicorn[standard]
 
 To start the server, run
 ```bash
-uvicorn api-server:app --reload
+uvicorn main:app --reload
 ```
 
 Withour any other options, this starts your server on `<localhost:8000>`.
@@ -43,7 +43,7 @@ docker built -t datacatalog-apiserver .
 ```
 while in the same directory as the Dockerfile.
 
- `datacatalog-apiserver` is a local tag to identify the built docker image.
+`datacatalog-apiserver` is a local tag to identify the built docker image.
 
 ### Running the docker image
 
diff --git a/apiserver/__init__.py b/apiserver/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/api-server/api-server.py b/apiserver/main.py
similarity index 100%
rename from api-server/api-server.py
rename to apiserver/main.py
diff --git a/apiserver/requirements.txt b/apiserver/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..fe7d70a70123621c6d4561a9f48e2b3a784429af
--- /dev/null
+++ b/apiserver/requirements.txt
@@ -0,0 +1,4 @@
+fastapi==0.63.0
+pytest==6.2.4
+requests==2.25.1
+uvicorn==0.13.4
\ No newline at end of file
diff --git a/apiserver_tests/__init__.py b/apiserver_tests/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/apiserver_tests/test_responsiveness.py b/apiserver_tests/test_responsiveness.py
new file mode 100644
index 0000000000000000000000000000000000000000..db58b1685dbd3826de184ce640495ae1fdbb0e9d
--- /dev/null
+++ b/apiserver_tests/test_responsiveness.py
@@ -0,0 +1,19 @@
+# These Tests only check if every api path that should work is responding to requests, the functionality is not yet checked
+# Therefore this only detects grievous errors in the request handling.
+
+from fastapi.testclient import TestClient
+
+from apiserver.LocationStorage import LocationDataType
+from apiserver import main
+
+
+client = TestClient(main.app)
+
+def test_root():
+    rsp = client.get('/')
+    assert rsp.status_code >= 200 and rsp.status_code < 300 # any 200 response is fine, as a get to the root should not return any error
+
+def test_types():
+    for location_type in LocationDataType:
+        rsp = client.get('/' + location_type.value)
+        assert rsp.status_code >= 200 and rsp.status_code < 300 # any 200 response is fine, as a get to the datatypes should not return any error
\ No newline at end of file