|
|
@ -7,20 +7,115 @@ import argparse
|
|
|
|
import sys
|
|
|
|
import sys
|
|
|
|
import yaml
|
|
|
|
import yaml
|
|
|
|
import uuid
|
|
|
|
import uuid
|
|
|
|
from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
|
|
import json
|
|
|
|
|
|
|
|
from http.server import CGIHTTPRequestHandler, HTTPServer
|
|
|
|
from os import environ as env
|
|
|
|
from os import environ as env
|
|
|
|
from os import path, makedirs
|
|
|
|
from os import path, makedirs, stat
|
|
|
|
from urllib.parse import urlparse, quote_plus
|
|
|
|
from urllib.parse import urlparse
|
|
|
|
|
|
|
|
|
|
|
|
import pwd
|
|
|
|
import pwd
|
|
|
|
import requests
|
|
|
|
import requests
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class OCMHandler(BaseHTTPRequestHandler):
|
|
|
|
class OCMHandler(CGIHTTPRequestHandler):
|
|
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
|
|
|
|
self.cgi_directories = ["/ocm"]
|
|
|
|
|
|
|
|
|
|
|
|
def do_GET(self):
|
|
|
|
def do_GET(self):
|
|
|
|
|
|
|
|
if self.path in ["/.well-known/ocm", "/ocm-provider"]:
|
|
|
|
|
|
|
|
self.send_response(200)
|
|
|
|
|
|
|
|
self.end_headers()
|
|
|
|
|
|
|
|
self.wfile.write(self.discovery())
|
|
|
|
|
|
|
|
elif self.path == "/ocm/webdav":
|
|
|
|
self.send_response(200)
|
|
|
|
self.send_response(200)
|
|
|
|
self.end_headers()
|
|
|
|
self.end_headers()
|
|
|
|
self.wfile.write(b"Hello, world!")
|
|
|
|
self.wfile.write(b"Hello World!")
|
|
|
|
|
|
|
|
elif self.path == "/ocm/webapp":
|
|
|
|
|
|
|
|
self.send_response(200)
|
|
|
|
|
|
|
|
self.end_headers()
|
|
|
|
|
|
|
|
self.wfile.write(b"Hello World!")
|
|
|
|
|
|
|
|
elif self.path == "/ocm/datatx":
|
|
|
|
|
|
|
|
self.send_response(200)
|
|
|
|
|
|
|
|
self.end_headers()
|
|
|
|
|
|
|
|
self.wfile.write(b"Hello World!")
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
self.send_response(404)
|
|
|
|
|
|
|
|
self.end_headers()
|
|
|
|
|
|
|
|
self.wfile.write(b"Not Found")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def do_POST(self):
|
|
|
|
|
|
|
|
if self.path == "/ocm/shares":
|
|
|
|
|
|
|
|
length = int(self.headers.get('content-length'))
|
|
|
|
|
|
|
|
payload = json.loads(self.rfile.read(length))
|
|
|
|
|
|
|
|
code, reply = self.validate_shares(payload)
|
|
|
|
|
|
|
|
self.send_response(code)
|
|
|
|
|
|
|
|
self.end_headers()
|
|
|
|
|
|
|
|
self.wfile.write(json.dumps(reply).encode("utf-8"))
|
|
|
|
|
|
|
|
elif self.path == "/ocm/webdav":
|
|
|
|
|
|
|
|
self.send_response(200)
|
|
|
|
|
|
|
|
self.end_headers()
|
|
|
|
|
|
|
|
self.wfile.write(b"{}")
|
|
|
|
|
|
|
|
elif self.path == "/ocm/webapp":
|
|
|
|
|
|
|
|
self.send_response(200)
|
|
|
|
|
|
|
|
self.end_headers()
|
|
|
|
|
|
|
|
self.wfile.write(b"{}")
|
|
|
|
|
|
|
|
elif self.path == "/ocm/datatx":
|
|
|
|
|
|
|
|
self.send_response(200)
|
|
|
|
|
|
|
|
self.end_headers
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
self.send_response(404)
|
|
|
|
|
|
|
|
self.end_headers()
|
|
|
|
|
|
|
|
self.wfile.write(b"Not Found")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def validate_shares(self, share) -> tuple[int, dict]:
|
|
|
|
|
|
|
|
# spec: https://cs3org.github.io/OCM-API/docs.html?branch=develop&repo=OCM-API&user=cs3org#/paths/~1shares/post return (201, {})
|
|
|
|
|
|
|
|
share_with = share.get("shareWith")
|
|
|
|
|
|
|
|
displayname = find_displayname(share_with)
|
|
|
|
|
|
|
|
if displayname is None:
|
|
|
|
|
|
|
|
return (400, {})
|
|
|
|
|
|
|
|
reply = {
|
|
|
|
|
|
|
|
"recipientDisplayName": displayname
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return (201, reply)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def discovery(self):
|
|
|
|
|
|
|
|
# spec: https://cs3org.github.io/OCM-API/docs.html?branch=develop&repo=OCM-API&user=cs3org#/paths/~1.well-known~1ocm/get
|
|
|
|
|
|
|
|
return json.dumps({
|
|
|
|
|
|
|
|
"enabled": True,
|
|
|
|
|
|
|
|
"apiVersion": "1.1.0",
|
|
|
|
|
|
|
|
"endPoint": "http://127.0.0.1:8080/ocm",
|
|
|
|
|
|
|
|
"provider": "ocm-cli",
|
|
|
|
|
|
|
|
"resourceTypes": [
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
"name": "file",
|
|
|
|
|
|
|
|
"shareTypes": [
|
|
|
|
|
|
|
|
"user"
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
"protocols": {
|
|
|
|
|
|
|
|
"webdav": "http://127.0.0.1:8080/ocm/webdav",
|
|
|
|
|
|
|
|
"datatx": "http://127.0.0.1:8080/ocm/datatx"
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
"name": "folder",
|
|
|
|
|
|
|
|
"shareTypes": [
|
|
|
|
|
|
|
|
"user"
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
"protocols": {
|
|
|
|
|
|
|
|
"webdav": "http://127.0.0.1:8080/ocm/webdav",
|
|
|
|
|
|
|
|
"datatx": "http://127.0.0.1:8080/ocm/datatx"
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
"capabilities": [
|
|
|
|
|
|
|
|
"/invite-accepted"
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
}).encode('utf-8')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def find_displayname(user: str) -> str:
|
|
|
|
|
|
|
|
return pwd.getpwnam(user).pw_gecos.strip(',')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class OCM:
|
|
|
|
class OCM:
|
|
|
@ -28,7 +123,6 @@ class OCM:
|
|
|
|
host: str
|
|
|
|
host: str
|
|
|
|
server: HTTPServer
|
|
|
|
server: HTTPServer
|
|
|
|
dir: str
|
|
|
|
dir: str
|
|
|
|
legacy_mode = False
|
|
|
|
|
|
|
|
config_dir: str
|
|
|
|
config_dir: str
|
|
|
|
config: dict
|
|
|
|
config: dict
|
|
|
|
config_file: str
|
|
|
|
config_file: str
|
|
|
@ -37,7 +131,7 @@ class OCM:
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, dir: str = env["PWD"]):
|
|
|
|
def __init__(self, dir: str = env["PWD"]):
|
|
|
|
self.user = env["USER"]
|
|
|
|
self.user = env["USER"]
|
|
|
|
self.fullname = pwd.getpwnam(self.user).pw_gecos.strip(',')
|
|
|
|
self.fullname = find_displayname(self.user)
|
|
|
|
self.dir = dir
|
|
|
|
self.dir = dir
|
|
|
|
self.config_dir = path.join(env["HOME"], ".config/ocm")
|
|
|
|
self.config_dir = path.join(env["HOME"], ".config/ocm")
|
|
|
|
self.config_file = path.join(self.config_dir, "config.yaml")
|
|
|
|
self.config_file = path.join(self.config_dir, "config.yaml")
|
|
|
@ -58,28 +152,29 @@ class OCM:
|
|
|
|
f.write(yaml.dump(self.config))
|
|
|
|
f.write(yaml.dump(self.config))
|
|
|
|
|
|
|
|
|
|
|
|
def get_share_payload(self, provider_id: str) -> dict:
|
|
|
|
def get_share_payload(self, provider_id: str) -> dict:
|
|
|
|
if self.legacy_mode:
|
|
|
|
name = self.config[self.user][provider_id]["name"],
|
|
|
|
|
|
|
|
name = name[0]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
owner = pwd.getpwuid(stat(name).st_uid).pw_name
|
|
|
|
return {
|
|
|
|
return {
|
|
|
|
"name": self.config[self.user][provider_id]["name"],
|
|
|
|
"shareWith": self.config[self.user][provider_id]["shareWith"],
|
|
|
|
"owner": self.user + '@localhost.local',
|
|
|
|
"name": name,
|
|
|
|
"permission":
|
|
|
|
"providerId": provider_id,
|
|
|
|
self.config[self.user][provider_id]["permission"],
|
|
|
|
"owner": owner,
|
|
|
|
|
|
|
|
"sender": self.user,
|
|
|
|
|
|
|
|
"senderDisplayName": self.fullname,
|
|
|
|
|
|
|
|
"shareType": self.config[self.user][provider_id]["shareType"],
|
|
|
|
|
|
|
|
"resourceType": self.config[self.user][provider_id]["resourceType"],
|
|
|
|
"protocol": {
|
|
|
|
"protocol": {
|
|
|
|
"name": "webdav",
|
|
|
|
"name": "webdav",
|
|
|
|
"options": {
|
|
|
|
"webdav": {
|
|
|
|
"sharedSecret":
|
|
|
|
"sharedSecret":
|
|
|
|
self.config[self.user][provider_id]["token"],
|
|
|
|
self.config[self.user][provider_id]["token"],
|
|
|
|
"permissions":
|
|
|
|
"permissions": self.config[self.user][provider_id]["permissions"],
|
|
|
|
'{http://open-cloud-mesh.org/ns}share-permissions'
|
|
|
|
"uri": f'/{self.user}/{provider_id}'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"providerId": provider_id,
|
|
|
|
|
|
|
|
"resourceType": "file",
|
|
|
|
|
|
|
|
# self.config[self.user][provider_id]["resourceType"],
|
|
|
|
|
|
|
|
"shareType": self.config[self.user][provider_id]["shareType"],
|
|
|
|
|
|
|
|
"shareWith": self.config[self.user][provider_id]["shareWith"],
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main(self):
|
|
|
|
def main(self):
|
|
|
|
parser = argparse.ArgumentParser()
|
|
|
|
parser = argparse.ArgumentParser()
|
|
|
@ -110,11 +205,17 @@ class OCM:
|
|
|
|
def receive(self):
|
|
|
|
def receive(self):
|
|
|
|
self.server.serve_forever()
|
|
|
|
self.server.serve_forever()
|
|
|
|
|
|
|
|
|
|
|
|
def create_share(self, receiver, resource_type, name, share_endpoint) -> None:
|
|
|
|
def create_share(
|
|
|
|
|
|
|
|
self,
|
|
|
|
|
|
|
|
receiver,
|
|
|
|
|
|
|
|
resource_type,
|
|
|
|
|
|
|
|
name,
|
|
|
|
|
|
|
|
share_endpoint
|
|
|
|
|
|
|
|
) -> None:
|
|
|
|
provider_id = uuid.uuid4().hex
|
|
|
|
provider_id = uuid.uuid4().hex
|
|
|
|
self.config[self.user][provider_id] = {
|
|
|
|
self.config[self.user][provider_id] = {
|
|
|
|
"name": name,
|
|
|
|
"name": name,
|
|
|
|
"permission": "read",
|
|
|
|
"permissions": ["read"],
|
|
|
|
"resourceType": resource_type,
|
|
|
|
"resourceType": resource_type,
|
|
|
|
"shareType": "user",
|
|
|
|
"shareType": "user",
|
|
|
|
"shareWith": receiver,
|
|
|
|
"shareWith": receiver,
|
|
|
@ -122,7 +223,6 @@ class OCM:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self.write_config()
|
|
|
|
self.write_config()
|
|
|
|
payload = self.get_share_payload(provider_id)
|
|
|
|
payload = self.get_share_payload(provider_id)
|
|
|
|
print(payload)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
headers = {
|
|
|
|
headers = {
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
'Content-Type': 'application/json',
|
|
|
@ -132,49 +232,31 @@ class OCM:
|
|
|
|
share_endpoint,
|
|
|
|
share_endpoint,
|
|
|
|
headers=headers,
|
|
|
|
headers=headers,
|
|
|
|
json=payload)
|
|
|
|
json=payload)
|
|
|
|
if share_req.status_code != 200:
|
|
|
|
if share_req.status_code != 201:
|
|
|
|
print("Error: " + str(share_req.status_code))
|
|
|
|
print("Error: " + str(share_req.status_code))
|
|
|
|
print(share_req.text)
|
|
|
|
print(share_req.text)
|
|
|
|
sys.exit(1)
|
|
|
|
sys.exit(1)
|
|
|
|
share_data = share_req.json()
|
|
|
|
share_data = share_req.json()
|
|
|
|
|
|
|
|
|
|
|
|
print(share_data)
|
|
|
|
print(share_data)
|
|
|
|
|
|
|
|
|
|
|
|
def share(self, file, directory, to):
|
|
|
|
def share(self, file, directory, to):
|
|
|
|
uparse = urlparse(to)
|
|
|
|
uparse = urlparse(to)
|
|
|
|
cloudid = f'{uparse.username}@{uparse.hostname}'
|
|
|
|
sharee = uparse.username
|
|
|
|
if not cloudid:
|
|
|
|
print("Sharing with " + sharee)
|
|
|
|
print("Username not specified")
|
|
|
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
print("Sharing with " + cloudid)
|
|
|
|
|
|
|
|
discovery_endpoint = uparse.scheme + "://" + \
|
|
|
|
discovery_endpoint = uparse.scheme + "://" + \
|
|
|
|
uparse.hostname + "/.well-known/ocm"
|
|
|
|
uparse.hostname + f":{uparse.port}/.well-known/ocm"
|
|
|
|
req = requests.get(discovery_endpoint)
|
|
|
|
|
|
|
|
if req.status_code != 200:
|
|
|
|
|
|
|
|
print("Could not find ocm-provider at " + discovery_endpoint)
|
|
|
|
|
|
|
|
print("Trying again with legacy endpoint")
|
|
|
|
|
|
|
|
# Fallback on legacy endpoint
|
|
|
|
|
|
|
|
discovery_endpoint = uparse.scheme + \
|
|
|
|
|
|
|
|
'://' + uparse.hostname + "/ocm-provider"
|
|
|
|
|
|
|
|
req = requests.get(discovery_endpoint)
|
|
|
|
req = requests.get(discovery_endpoint)
|
|
|
|
if req.status_code != 200:
|
|
|
|
if req.status_code != 200:
|
|
|
|
print("Could not find ocm-provider at " + discovery_endpoint)
|
|
|
|
print("Could not find ocm-provider at " + discovery_endpoint)
|
|
|
|
sys.exit(1)
|
|
|
|
sys.exit(1)
|
|
|
|
data = req.json()
|
|
|
|
data = req.json()
|
|
|
|
share_endpoint = data['endPoint'] + "/shares"
|
|
|
|
share_endpoint = data['endPoint'] + "/shares"
|
|
|
|
|
|
|
|
print(share_endpoint)
|
|
|
|
enabled = data['enabled']
|
|
|
|
enabled = data['enabled']
|
|
|
|
if not enabled:
|
|
|
|
if not enabled:
|
|
|
|
print("OCM is not enabled on this server")
|
|
|
|
print("OCM is not enabled on this server")
|
|
|
|
sys.exit(1)
|
|
|
|
sys.exit(1)
|
|
|
|
api_version = data['apiVersion']
|
|
|
|
|
|
|
|
resource_types = data['resourceTypes']
|
|
|
|
resource_types = data['resourceTypes']
|
|
|
|
if api_version == '1.0-proposal1':
|
|
|
|
|
|
|
|
self.legacy_mode = True
|
|
|
|
|
|
|
|
# provider = uparse.hostname
|
|
|
|
|
|
|
|
# else:
|
|
|
|
|
|
|
|
# provider = data['provider']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
file_or_folder = file
|
|
|
|
file_or_folder = file
|
|
|
|
resource_type = "file"
|
|
|
|
resource_type = "file"
|
|
|
|
if directory:
|
|
|
|
if directory:
|
|
|
@ -191,7 +273,7 @@ class OCM:
|
|
|
|
sys.exit(1)
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
|
|
self.create_share(
|
|
|
|
self.create_share(
|
|
|
|
cloudid, resource_type, file_or_folder, share_endpoint)
|
|
|
|
sharee, resource_type, file_or_folder, share_endpoint)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
if __name__ == "__main__":
|
|
|
|