2015-07-12 20:36:46 +02:00
|
|
|
import logging
|
|
|
|
import json
|
|
|
|
import time
|
2019-03-21 02:22:22 +01:00
|
|
|
import binascii
|
2015-07-12 20:36:46 +02:00
|
|
|
|
2018-04-12 19:24:21 +02:00
|
|
|
import gevent
|
|
|
|
|
2017-04-01 22:31:38 +02:00
|
|
|
import util
|
version 0.2.0, new lib for bitcoin ecc, dont display or track notify errors, dont reload again within 1 sec, null peer ip fix, signingmoved to ContentManager, content.json include support, content.json multisig ready, content.json proper bitcoincore compatible signing, content.json include permissions, multithreaded publish, publish timeout 60s, no exception on invalid bitcoin address, testcase for new lib, bip32 based persite privatekey generation, multiuser ready, simple json database query command, websocket api fileGet, wrapper loading title stuck bugfix
2015-02-09 02:09:02 +01:00
|
|
|
from Crypt import CryptBitcoin
|
version 0.2.7, plugin system, multiuser plugin for zeroproxies, reworked imports, cookie parse, stats moved to plugin, usermanager class, dont generate site auth on listing, multiline notifications, allow server side prompt from user, update script keep plugins disabled status
2015-03-24 01:33:09 +01:00
|
|
|
from Plugin import PluginManager
|
2015-05-31 15:52:21 +02:00
|
|
|
from Config import config
|
2015-09-27 02:08:53 +02:00
|
|
|
from util import helper
|
2019-03-15 21:06:59 +01:00
|
|
|
from Debug import Debug
|
version 0.2.0, new lib for bitcoin ecc, dont display or track notify errors, dont reload again within 1 sec, null peer ip fix, signingmoved to ContentManager, content.json include support, content.json multisig ready, content.json proper bitcoincore compatible signing, content.json include permissions, multithreaded publish, publish timeout 60s, no exception on invalid bitcoin address, testcase for new lib, bip32 based persite privatekey generation, multiuser ready, simple json database query command, websocket api fileGet, wrapper loading title stuck bugfix
2015-02-09 02:09:02 +01:00
|
|
|
|
2015-11-17 12:48:03 +01:00
|
|
|
|
version 0.2.7, plugin system, multiuser plugin for zeroproxies, reworked imports, cookie parse, stats moved to plugin, usermanager class, dont generate site auth on listing, multiline notifications, allow server side prompt from user, update script keep plugins disabled status
2015-03-24 01:33:09 +01:00
|
|
|
@PluginManager.acceptPlugins
|
|
|
|
class User(object):
|
2015-06-17 23:34:54 +02:00
|
|
|
def __init__(self, master_address=None, master_seed=None, data={}):
|
|
|
|
if master_seed:
|
|
|
|
self.master_seed = master_seed
|
|
|
|
self.master_address = CryptBitcoin.privatekeyToAddress(self.master_seed)
|
|
|
|
elif master_address:
|
|
|
|
self.master_address = master_address
|
|
|
|
self.master_seed = data.get("master_seed")
|
|
|
|
else:
|
|
|
|
self.master_seed = CryptBitcoin.newSeed()
|
|
|
|
self.master_address = CryptBitcoin.privatekeyToAddress(self.master_seed)
|
|
|
|
self.sites = data.get("sites", {})
|
|
|
|
self.certs = data.get("certs", {})
|
2018-10-20 02:28:58 +02:00
|
|
|
self.settings = data.get("settings", {})
|
2018-04-12 19:25:13 +02:00
|
|
|
self.delayed_save_thread = None
|
2015-06-17 23:34:54 +02:00
|
|
|
|
|
|
|
self.log = logging.getLogger("User:%s" % self.master_address)
|
|
|
|
|
|
|
|
# Save to data/users.json
|
2017-04-01 22:31:38 +02:00
|
|
|
@util.Noparallel(queue=True, ignore_class=True)
|
2015-06-17 23:34:54 +02:00
|
|
|
def save(self):
|
2016-01-19 20:42:00 +01:00
|
|
|
s = time.time()
|
2015-06-17 23:34:54 +02:00
|
|
|
users = json.load(open("%s/users.json" % config.data_dir))
|
|
|
|
if self.master_address not in users:
|
|
|
|
users[self.master_address] = {} # Create if not exist
|
|
|
|
user_data = users[self.master_address]
|
2015-07-12 20:36:46 +02:00
|
|
|
if self.master_seed:
|
|
|
|
user_data["master_seed"] = self.master_seed
|
2015-06-17 23:34:54 +02:00
|
|
|
user_data["sites"] = self.sites
|
|
|
|
user_data["certs"] = self.certs
|
2018-10-20 02:28:58 +02:00
|
|
|
user_data["settings"] = self.settings
|
2019-03-15 23:56:16 +01:00
|
|
|
helper.atomicWrite("%s/users.json" % config.data_dir, json.dumps(users, indent=2, sort_keys=True).encode("utf8"))
|
2018-04-12 19:25:13 +02:00
|
|
|
self.log.debug("Saved in %.3fs" % (time.time() - s))
|
|
|
|
self.delayed_save_thread = None
|
|
|
|
|
|
|
|
def saveDelayed(self):
|
|
|
|
if not self.delayed_save_thread:
|
|
|
|
self.delayed_save_thread = gevent.spawn_later(5, self.save)
|
2015-06-17 23:34:54 +02:00
|
|
|
|
|
|
|
def getAddressAuthIndex(self, address):
|
2019-03-21 02:22:22 +01:00
|
|
|
return int(binascii.hexlify(address.encode()), 16)
|
2015-06-17 23:34:54 +02:00
|
|
|
|
2018-04-12 19:24:21 +02:00
|
|
|
@util.Noparallel()
|
|
|
|
def generateAuthAddress(self, address):
|
|
|
|
s = time.time()
|
|
|
|
address_id = self.getAddressAuthIndex(address) # Convert site address to int
|
|
|
|
auth_privatekey = CryptBitcoin.hdPrivatekey(self.master_seed, address_id)
|
|
|
|
self.sites[address] = {
|
|
|
|
"auth_address": CryptBitcoin.privatekeyToAddress(auth_privatekey),
|
|
|
|
"auth_privatekey": auth_privatekey
|
|
|
|
}
|
|
|
|
self.saveDelayed()
|
|
|
|
self.log.debug("Added new site: %s in %.3fs" % (address, time.time() - s))
|
|
|
|
return self.sites[address]
|
|
|
|
|
2015-06-17 23:34:54 +02:00
|
|
|
# Get user site data
|
|
|
|
# Return: {"auth_address": "xxx", "auth_privatekey": "xxx"}
|
|
|
|
def getSiteData(self, address, create=True):
|
|
|
|
if address not in self.sites: # Generate new BIP32 child key based on site address
|
2015-07-12 20:36:46 +02:00
|
|
|
if not create:
|
|
|
|
return {"auth_address": None, "auth_privatekey": None} # Dont create user yet
|
2018-04-12 19:24:21 +02:00
|
|
|
self.generateAuthAddress(address)
|
2015-06-17 23:34:54 +02:00
|
|
|
return self.sites[address]
|
|
|
|
|
2015-09-11 02:25:37 +02:00
|
|
|
def deleteSiteData(self, address):
|
|
|
|
if address in self.sites:
|
|
|
|
del(self.sites[address])
|
2018-04-12 19:25:13 +02:00
|
|
|
self.saveDelayed()
|
2015-09-11 02:25:37 +02:00
|
|
|
self.log.debug("Deleted site: %s" % address)
|
|
|
|
|
2018-10-20 02:28:58 +02:00
|
|
|
def setSiteSettings(self, address, settings):
|
2017-07-10 02:41:01 +02:00
|
|
|
site_data = self.getSiteData(address)
|
|
|
|
site_data["settings"] = settings
|
2018-04-12 19:25:13 +02:00
|
|
|
self.saveDelayed()
|
2017-07-10 02:41:01 +02:00
|
|
|
return site_data
|
|
|
|
|
2015-06-17 23:34:54 +02:00
|
|
|
# Get data for a new, unique site
|
|
|
|
# Return: [site_address, bip32_index, {"auth_address": "xxx", "auth_privatekey": "xxx", "privatekey": "xxx"}]
|
|
|
|
def getNewSiteData(self):
|
|
|
|
import random
|
2015-07-12 20:36:46 +02:00
|
|
|
bip32_index = random.randrange(2 ** 256) % 100000000
|
2015-06-17 23:34:54 +02:00
|
|
|
site_privatekey = CryptBitcoin.hdPrivatekey(self.master_seed, bip32_index)
|
|
|
|
site_address = CryptBitcoin.privatekeyToAddress(site_privatekey)
|
2015-07-12 20:36:46 +02:00
|
|
|
if site_address in self.sites:
|
|
|
|
raise Exception("Random error: site exist!")
|
2015-06-17 23:34:54 +02:00
|
|
|
# Save to sites
|
|
|
|
self.getSiteData(site_address)
|
|
|
|
self.sites[site_address]["privatekey"] = site_privatekey
|
2019-01-26 20:31:37 +01:00
|
|
|
self.save()
|
2015-06-17 23:34:54 +02:00
|
|
|
return site_address, bip32_index, self.sites[site_address]
|
|
|
|
|
|
|
|
# Get BIP32 address from site address
|
|
|
|
# Return: BIP32 auth address
|
|
|
|
def getAuthAddress(self, address, create=True):
|
|
|
|
cert = self.getCert(address)
|
|
|
|
if cert:
|
|
|
|
return cert["auth_address"]
|
|
|
|
else:
|
|
|
|
return self.getSiteData(address, create)["auth_address"]
|
|
|
|
|
|
|
|
def getAuthPrivatekey(self, address, create=True):
|
|
|
|
cert = self.getCert(address)
|
|
|
|
if cert:
|
|
|
|
return cert["auth_privatekey"]
|
|
|
|
else:
|
|
|
|
return self.getSiteData(address, create)["auth_privatekey"]
|
|
|
|
|
|
|
|
# Add cert for the user
|
|
|
|
def addCert(self, auth_address, domain, auth_type, auth_user_name, cert_sign):
|
2015-07-12 20:36:46 +02:00
|
|
|
# Find privatekey by auth address
|
2019-03-15 21:06:59 +01:00
|
|
|
auth_privatekey = [site["auth_privatekey"] for site in list(self.sites.values()) if site["auth_address"] == auth_address][0]
|
2015-06-17 23:34:54 +02:00
|
|
|
cert_node = {
|
|
|
|
"auth_address": auth_address,
|
|
|
|
"auth_privatekey": auth_privatekey,
|
|
|
|
"auth_type": auth_type,
|
|
|
|
"auth_user_name": auth_user_name,
|
|
|
|
"cert_sign": cert_sign
|
|
|
|
}
|
|
|
|
# Check if we have already cert for that domain and its not the same
|
|
|
|
if self.certs.get(domain) and self.certs[domain] != cert_node:
|
2016-04-09 19:49:12 +02:00
|
|
|
return False
|
2015-07-12 20:36:46 +02:00
|
|
|
elif self.certs.get(domain) == cert_node: # Same, not updated
|
2015-06-17 23:34:54 +02:00
|
|
|
return None
|
2015-07-12 20:36:46 +02:00
|
|
|
else: # Not exist yet, add
|
2015-06-17 23:34:54 +02:00
|
|
|
self.certs[domain] = cert_node
|
|
|
|
self.save()
|
|
|
|
return True
|
|
|
|
|
2016-04-09 19:49:12 +02:00
|
|
|
# Remove cert from user
|
|
|
|
def deleteCert(self, domain):
|
|
|
|
del self.certs[domain]
|
|
|
|
|
2016-01-19 20:42:00 +01:00
|
|
|
# Set active cert for a site
|
2015-06-17 23:34:54 +02:00
|
|
|
def setCert(self, address, domain):
|
|
|
|
site_data = self.getSiteData(address)
|
|
|
|
if domain:
|
|
|
|
site_data["cert"] = domain
|
|
|
|
else:
|
Version 0.3.5, Rev830, Full Tor mode support with hidden services, Onion stats in Sidebar, GeoDB download fix using Tor, Gray out disabled sites in Stats page, Tor hidden service status in stat page, Benchmark sha256, Skyts tracker out expodie in, 2 new tracker using ZeroNet protocol, Keep SSL cert option between restarts, SSL Certificate pinning support for connections, Site lock support for connections, Certificate pinned connections using implicit SSL, Flood protection whitelist support, Foreign keys support for DB layer, Not support for SQL query helper, 0 length file get bugfix, Pex onion address support, Faster port testing, Faster uPnP port opening, Need connections more often on owned sites, Delay ZeroHello startup message if port check or Tor manager not ready yet, Use lockfiles to avoid double start, Save original socket on proxy monkey patching to get ability to connect localhost directly, Handle atomic write errors, Broken gevent https workaround helper, Rsa crypt functions, Plugin to Bootstrap using ZeroNet protocol
2016-01-05 00:20:52 +01:00
|
|
|
if "cert" in site_data:
|
|
|
|
del site_data["cert"]
|
2018-04-12 19:25:13 +02:00
|
|
|
self.saveDelayed()
|
2015-06-17 23:34:54 +02:00
|
|
|
return site_data
|
|
|
|
|
|
|
|
# Get cert for the site address
|
2015-07-12 20:36:46 +02:00
|
|
|
# Return: { "auth_address":.., "auth_privatekey":.., "auth_type": "web", "auth_user_name": "nofish", "cert_sign":.. } or None
|
2015-06-17 23:34:54 +02:00
|
|
|
def getCert(self, address):
|
|
|
|
site_data = self.getSiteData(address, create=False)
|
2015-07-12 20:36:46 +02:00
|
|
|
if not site_data or "cert" not in site_data:
|
|
|
|
return None # Site dont have cert
|
2015-06-17 23:34:54 +02:00
|
|
|
return self.certs.get(site_data["cert"])
|
|
|
|
|
|
|
|
# Get cert user name for the site address
|
|
|
|
# Return: user@certprovider.bit or None
|
|
|
|
def getCertUserId(self, address):
|
|
|
|
site_data = self.getSiteData(address, create=False)
|
2015-07-12 20:36:46 +02:00
|
|
|
if not site_data or "cert" not in site_data:
|
|
|
|
return None # Site dont have cert
|
2015-06-17 23:34:54 +02:00
|
|
|
cert = self.certs.get(site_data["cert"])
|
|
|
|
if cert:
|
2015-07-12 20:36:46 +02:00
|
|
|
return cert["auth_user_name"] + "@" + site_data["cert"]
|