예제 #1
0
파일: utils.py 프로젝트: pritunl/pritunl
def iter_hosts_dict(page=None):
    clients_collection = mongo.get_collection("clients")
    server_collection = mongo.get_collection("servers")

    response = server_collection.aggregate(
        [
            {"$project": {"hosts": True, "organizations": True}},
            {"$unwind": "$hosts"},
            {"$unwind": "$organizations"},
            {"$group": {"_id": "$hosts", "organizations": {"$addToSet": "$organizations"}}},
        ]
    )

    orgs = set()
    host_orgs = collections.defaultdict(list)
    for doc in response:
        orgs = orgs.union(doc["organizations"])
        host_orgs[doc["_id"]] = doc["organizations"]

    org_user_count = organization.get_user_count(orgs)

    for hst in iter_hosts(page=page):
        users_online = len(clients_collection.distinct("user_id", {"host_id": hst.id, "type": CERT_CLIENT}))

        user_count = 0
        for org_id in host_orgs[hst.id]:
            user_count += org_user_count.get(org_id, 0)

        hst.user_count = user_count
        hst.users_online = users_online

        yield hst.dict()
예제 #2
0
    def remove(self):
        user_collection = mongo.get_collection('users')
        server_collection = mongo.get_collection('servers')

        logger.debug('Remove org', 'organization',
            org_id=self.id,
        )

        server_ids = []

        for server in self.iter_servers():
            server_ids.append(server.id)
            if server.status == ONLINE:
                server.stop()

        server_collection.update({
            'organizations': self.id,
        }, {'$pull': {
            'organizations': self.id,
        }})

        mongo.MongoObject.remove(self)
        user_collection.remove({
            'org_id': self.id,
        })

        return server_ids
예제 #3
0
파일: key.py 프로젝트: pritunl/pritunl
def _find_doc(query, one_time=None, one_time_new=False):
    utils.rand_sleep()

    collection = mongo.get_collection("users_key_link")
    doc = collection.find_one(query)

    if one_time and doc and doc.get("one_time"):
        short_id = utils.generate_short_id()
        collection = mongo.get_collection("users_key_link")

        if one_time_new:
            set_doc = {"short_id": short_id}
        else:
            set_doc = {"one_time": "used"}

        response = collection.update(
            {"_id": doc["_id"], "short_id": doc["short_id"], "one_time": True}, {"$set": set_doc}
        )
        if not response["updatedExisting"]:
            return None

        if one_time_new:
            doc["short_id"] = short_id

    if not doc:
        time.sleep(settings.app.rate_limit_sleep)

    return doc
예제 #4
0
    def remove(self):
        user_collection = mongo.get_collection('users')
        user_audit_collection = mongo.get_collection('users_audit')
        user_net_link_collection = mongo.get_collection('users_net_link')
        server_collection = mongo.get_collection('servers')

        user_audit_collection.remove({
            'org_id': self.id,
        })

        user_net_link_collection.remove({
            'org_id': self.id,
        })

        server_ids = []

        for server in self.iter_servers():
            server_ids.append(server.id)
            if server.status == ONLINE:
                server.stop()

        server_collection.update({
            'organizations': self.id,
        }, {'$pull': {
            'organizations': self.id,
        }})

        mongo.MongoObject.remove(self)
        user_collection.remove({
            'org_id': self.id,
        })

        return server_ids
예제 #5
0
파일: dh_params.py 프로젝트: ijat/pritunl
def fill_dh_params():
    collection = mongo.get_collection('dh_params')
    queue_collection = mongo.get_collection('queue')

    new_dh_params = []
    dh_param_bits_pool = settings.app.dh_param_bits_pool
    dh_param_counts = utils.LeastCommonCounter()

    for dh_param_bits in dh_param_bits_pool:
        pool_count = collection.find({
            'dh_param_bits': dh_param_bits,
        }, {
            '_id': True
        }).count()

        dh_param_counts[dh_param_bits] = pool_count

        pool_count = queue_collection.find({
            'type': 'dh_params',
            'dh_param_bits': dh_param_bits,
        }, {
            '_id': True
        }).count()

        dh_param_counts[dh_param_bits] += pool_count

    for dh_param_bits, count in dh_param_counts.least_common():
        new_dh_params.append([dh_param_bits] * (
            settings.app.server_pool_size - count))

    for dh_param_bits in utils.roundrobin(*new_dh_params):
        que = queue.start('dh_params', dh_param_bits=dh_param_bits,
            priority=LOW)
예제 #6
0
파일: key.py 프로젝트: animefreak93/pritunl
def _find_doc(query, one_time=None, one_time_new=False):
    utils.rand_sleep()

    collection = mongo.get_collection('users_key_link')
    doc = collection.find_one(query)

    if one_time and doc and doc.get('one_time'):
        short_id = utils.generate_short_id()
        collection = mongo.get_collection('users_key_link')

        if one_time_new:
            set_doc = {
                'short_id': short_id,
            }
        else:
            set_doc = {
                'one_time': 'used',
            }

        response = collection.update({
            '_id': doc['_id'],
            'short_id': doc['short_id'],
            'one_time': True,
        }, {'$set': set_doc})
        if not response['updatedExisting']:
            return None

        if one_time_new:
            doc['short_id'] = short_id

    if not doc:
        time.sleep(settings.app.rate_limit_sleep)

    return doc
예제 #7
0
파일: user.py 프로젝트: fla3er/pritunl
def fill_user():
    collection = mongo.get_collection("users")
    org_collection = mongo.get_collection("organizations")
    queue_collection = mongo.get_collection("queue")

    orgs = {}
    orgs_count = utils.LeastCommonCounter()
    type_to_size = {CERT_CLIENT_POOL: settings.app.user_pool_size, CERT_SERVER_POOL: settings.app.server_user_pool_size}

    for org in organization.iter_orgs(type=None):
        orgs[org.id] = org
        orgs_count[org.id, CERT_CLIENT_POOL] = 0
        orgs_count[org.id, CERT_SERVER_POOL] = 0

    pools = collection.aggregate(
        [
            {"$match": {"type": {"$in": (CERT_CLIENT_POOL, CERT_SERVER_POOL)}}},
            {"$project": {"org_id": True, "type": True}},
            {"$group": {"_id": {"org_id": "$org_id", "type": "$type"}, "count": {"$sum": 1}}},
        ]
    )

    for pool in pools:
        orgs_count[pool["_id"]["org_id"], pool["_id"]["type"]] += pool["count"]

    pools = queue_collection.aggregate(
        [
            {"$match": {"type": "init_user_pooled", "user_doc.type": {"$in": (CERT_CLIENT_POOL, CERT_SERVER_POOL)}}},
            {"$project": {"user_doc.org_id": True, "user_doc.type": True}},
            {"$group": {"_id": {"org_id": "$user_doc.org_id", "type": "$user_doc.type"}, "count": {"$sum": 1}}},
        ]
    )

    for pool in pools:
        orgs_count[pool["_id"]["org_id"], pool["_id"]["type"]] += pool["count"]

    new_users = []

    for org_id_user_type, count in orgs_count.least_common():
        org_id, user_type = org_id_user_type
        pool_size = type_to_size[user_type]

        if count >= pool_size:
            break

        org = orgs.get(org_id)
        if not org:
            logger.warning("Pooler cannot find org from user_count", "pooler", org_id=org_id, user_type=user_type)
            continue
        new_users.append([(org, user_type)] * (pool_size - count))

    for org, user_type in utils.roundrobin(*new_users):
        org.new_user(type=user_type, block=False)
예제 #8
0
파일: messenger.py 프로젝트: afdnlw/pritunl
def publish(channels, message, extra=None, transaction=None):
    collection = mongo.get_collection('messages')
    doc = {
        'message': message,
    }

    if extra:
        for key, val in extra.items():
            doc[key] = val

    if transaction:
        collection = transaction.collection(collection.name_str)

    # ObjectId and timestamp must be set by server and ObjectId order must
    # match $natural order. Docs sent in order on client are not guaranteed
    # to match $natural order on server. Nonce is added to force an insert
    # from upsert to allow the use of $currentDate.
    # When using inserts manipulate=False must be set to prevent pymongo
    # from setting ObjectId locally.
    if isinstance(channels, str):
        doc['channel'] = channels

        collection.update({
            'nonce': bson.ObjectId(),
        }, {
            '$set': doc,
            '$currentDate': {
                'timestamp': True,
            },
        }, upsert=True)
    else:
        if transaction:
            for channel in channels:
                doc_copy = doc.copy()
                doc_copy['channel'] = channel

                tran_collection.bulk().find({
                    'nonce': bson.ObjectId(),
                }).upsert().update({
                    '$set': doc_copy,
                    '$currentDate': {
                        'timestamp': True,
                    },
                })
            tran_collection.bulk_execute()
        else:
            bulk = collection.initialize_ordered_bulk_op()
            for channel in channels:
                doc_copy = doc.copy()
                doc_copy['channel'] = channel

                bulk.find({
                    'nonce': bson.ObjectId(),
                }).upsert().update({
                    '$set': doc_copy,
                    '$currentDate': {
                        'timestamp': True,
                    },
                })
            bulk.execute()
예제 #9
0
파일: key.py 프로젝트: kknet/pritunl
def key_sync_get(org_id, user_id, server_id, key_hash):
    utils.rand_sleep()

    if not settings.local.sub_active:
        return utils.response('', status_code=480)

    auth_token = flask.request.headers.get('Auth-Token', None)
    auth_timestamp = flask.request.headers.get('Auth-Timestamp', None)
    auth_nonce = flask.request.headers.get('Auth-Nonce', None)
    auth_signature = flask.request.headers.get('Auth-Signature', None)
    if not auth_token or not auth_timestamp or not auth_nonce or \
            not auth_signature:
        return flask.abort(401)
    auth_nonce = auth_nonce[:32]

    try:
        if abs(int(auth_timestamp) - int(utils.time_now())) > \
                settings.app.auth_time_window:
            return flask.abort(401)
    except ValueError:
        return flask.abort(401)

    org = organization.get_by_id(org_id)
    if not org:
        return flask.abort(404)

    user = org.get_user(id=user_id)
    if not user:
        return flask.abort(404)
    elif not user.sync_secret:
        return flask.abort(404)

    auth_string = '&'.join([
        auth_token, auth_timestamp, auth_nonce, flask.request.method,
        flask.request.path] +
        ([flask.request.data] if flask.request.data else []))

    if len(auth_string) > AUTH_SIG_STRING_MAX_LEN:
        return flask.abort(401)

    auth_test_signature = base64.b64encode(hmac.new(
        user.sync_secret.encode(), auth_string,
        hashlib.sha256).digest())
    if auth_signature != auth_test_signature:
        return flask.abort(401)

    nonces_collection = mongo.get_collection('auth_nonces')
    try:
        nonces_collection.insert({
            'token': auth_token,
            'nonce': auth_nonce,
            'timestamp': utils.now(),
        }, w=0)
    except pymongo.errors.DuplicateKeyError:
        return flask.abort(401)

    key_conf = user.sync_conf(server_id, key_hash)
    if key_conf:
        return utils.response(key_conf['conf'])
    return utils.response('')
예제 #10
0
파일: misc.py 프로젝트: Git-Host/pritunl
def sync_time():
    nounce = None
    doc = {}

    try:
        collection = mongo.get_collection('time_sync')

        nounce = ObjectId()
        collection.insert({
            'nounce': nounce,
        }, manipulate=False)

        mongo_time_start = datetime.datetime.utcnow()

        doc = collection.find_one({
            'nounce': nounce,
        })
        mongo_time = doc['_id'].generation_time.replace(tzinfo=None)

        settings.local.mongo_time = (mongo_time_start, mongo_time)

        collection.remove(doc['_id'])
    except:
        from pritunl import logger

        logger.exception('Failed to sync time',
            nounce=nounce,
            doc_id=doc.get('id'),
        )
        raise
예제 #11
0
    def _run_actions(self):
        has_bulk = mongo.has_bulk

        if has_bulk:
            collection_bulks = collections.defaultdict(
                lambda: collection.initialize_ordered_bulk_op())

        for action_set in self.action_sets:
            collection_name, bulk, actions, _, _ = action_set
            collection = mongo.get_collection(collection_name)

            if has_bulk:
                if bulk:
                    collection = collection_bulks[collection_name]
                elif actions == BULK_EXECUTE:
                    collection = collection_bulks.pop(collection_name)
                    collection.execute()
                    continue
            else:
                if bulk:
                    new_action = actions[0]
                    for action in actions[1:]:
                        if action[0] == 'upsert':
                            new_action[2] = {'upsert': True}
                        elif action[0] == 'update':
                            new_action[1].append(action[1][0])
                        elif action[0] == 'remove':
                            new_action[0] = 'remove'
                    actions = [new_action]
                elif actions == BULK_EXECUTE:
                    continue

            self._run_collection_actions(collection, actions)
예제 #12
0
파일: logger.py 프로젝트: pritunl/pritunl
def _logger_runner_thread():
    log_queue = logger.log_queue
    collection = mongo.get_collection('logs')

    while True:
        try:
            msg_docs = []
            while True:
                try:
                    msg = log_queue.popleft()
                    msg_docs.append({
                        'timestamp': utils.now(),
                        'message': msg,
                    })
                except IndexError:
                    break

            if msg_docs:
                yield
                collection.insert(msg_docs)
                event.Event(type=SYSTEM_LOG_UPDATED)

            yield interrupter_sleep(settings.app.log_db_delay)

        except GeneratorExit:
            raise
        except:
            logger.exception('Error in log runner thread', 'runners')
            time.sleep(0.5)
예제 #13
0
def sso_callback_get():
    state = flask.request.args.get('state')
    user = flask.request.args.get('user')

    tokens_collection = mongo.get_collection('sso_tokens')
    doc = tokens_collection.find_and_modify(query={
        '_id': state,
    }, remove=True)

    if not doc:
        return flask.abort(404)

    org_id = settings.app.sso_org
    if not org_id:
        return flask.abort(405)

    org = organization.get_by_id(org_id)
    if not org:
        return flask.abort(405)

    usr = org.find_user(name=user)
    if not usr:
        usr = org.new_user(name=user, email=user, type=CERT_CLIENT)

    key_link = org.create_user_key_link(usr.id)

    return flask.redirect(flask.request.url_root[:-1] + key_link['view_url'])
예제 #14
0
파일: task.py 프로젝트: Git-Host/pritunl
def check_thread():
    collection = mongo.get_collection('task')

    while True:
        try:
            cur_timestamp = utils.now()
            spec = {
                'ttl_timestamp': {'$lt': cur_timestamp},
            }

            for task_item in task.iter_tasks(spec):
                random_sleep()

                response = task.Task.collection.update({
                    '_id': task_item.id,
                    'ttl_timestamp': {'$lt': cur_timestamp},
                }, {'$unset': {
                    'runner_id': '',
                }})
                if response['updatedExisting']:
                    run_task(task_item)
        except:
            logger.exception('Error in task check thread', 'runners')

        yield interrupter_sleep(settings.mongo.task_ttl)
예제 #15
0
def validate_token(admin_id, token):
    coll = mongo.get_collection('auth_csrf_tokens')

    return bool(coll.find_one({
        '_id': token,
        'admin_id': admin_id,
    }))
예제 #16
0
def sso_request_get():
    state = uuid.uuid4().hex
    callback = flask.request.url_root + 'sso/callback'

    if not settings.local.sub_active:
        return flask.abort(405)

    resp = utils.request.post('https://auth.pritunl.com/request/google',
        json_data={
            'license': 'test',
            'callback': callback,
            'state': state,
        }, headers={
            'Content-Type': 'application/json',
        })

    if resp.status_code != 200:
        if resp.status_code == 401:
            return flask.abort(405)
        return flask.abort(500)

    tokens_collection = mongo.get_collection('sso_tokens')
    tokens_collection.insert({
        '_id': state,
        'timestamp': utils.now(),
        })

    data = resp.json()

    return flask.redirect(data['url'])
예제 #17
0
파일: key.py 프로젝트: pritunl/pritunl
def sso_duo_post():
    sso_mode = settings.app.sso
    token = utils.filter_str(flask.request.json.get("token")) or None
    passcode = utils.filter_str(flask.request.json.get("passcode")) or None

    if not token or not passcode:
        return utils.jsonify({"error": TOKEN_INVALID, "error_msg": TOKEN_INVALID_MSG}, 401)

    tokens_collection = mongo.get_collection("sso_tokens")
    doc = tokens_collection.find_one({"_id": token})
    if not doc or doc["_id"] != token:
        return utils.jsonify({"error": TOKEN_INVALID, "error_msg": TOKEN_INVALID_MSG}, 401)

    username = doc["username"]
    email = doc["email"]
    org_id = doc["org_id"]
    groups = doc["groups"]

    duo_auth = sso.Duo(
        username=username,
        factor=settings.app.sso_duo_mode,
        remote_ip=utils.get_remote_addr(),
        auth_type="Key",
        passcode=passcode,
    )
    valid = duo_auth.authenticate()
    if not valid:
        return utils.jsonify({"error": PASSCODE_INVALID, "error_msg": PASSCODE_INVALID_MSG}, 401)

    org = organization.get_by_id(org_id)
    if not org:
        return flask.abort(405)

    usr = org.find_user(name=username)
    if not usr:
        usr = org.new_user(
            name=username, email=email, type=CERT_CLIENT, auth_type=sso_mode, groups=list(groups) if groups else None
        )
        usr.audit_event("user_created", "User created with single sign-on", remote_addr=utils.get_remote_addr())

        event.Event(type=ORGS_UPDATED)
        event.Event(type=USERS_UPDATED, resource_id=org.id)
        event.Event(type=SERVERS_UPDATED)
    else:
        if usr.disabled:
            return flask.abort(403)

        if groups and groups - set(usr.groups or []):
            usr.groups = list(set(usr.groups or []) | groups)
            usr.commit("groups")

        if usr.auth_type != sso_mode:
            usr.auth_type = sso_mode
            usr.commit("auth_type")

    key_link = org.create_user_key_link(usr.id, one_time=True)

    usr.audit_event("user_profile", "User profile viewed from single sign-on", remote_addr=utils.get_remote_addr())

    return utils.jsonify({"redirect": utils.get_url_root() + key_link["view_url"]}, 200)
예제 #18
0
def _logger_runner_thread():
    log_queue = logger.log_queue
    collection = mongo.get_collection("logs")

    settings.local.logger_runner = True

    while True:
        try:
            msg_docs = []
            while True:
                try:
                    msg = log_queue.popleft()
                    msg_docs.append({"timestamp": utils.now(), "message": msg})
                except IndexError:
                    break

            if msg_docs:
                yield

                collection.insert(msg_docs)

            yield interrupter_sleep(settings.app.log_db_delay)

        except GeneratorExit:
            raise
        except:
            logger.exception("Error in log runner thread", "runners")
            time.sleep(0.5)
예제 #19
0
def subscribe(channels, cursor_id=None, timeout=None, yield_delay=None):
    collection = mongo.get_collection('messages')
    start_time = time.time()
    cursor_id = cursor_id or get_cursor_id(channels)
    while True:
        try:
            spec = {}

            if isinstance(channels, str):
                spec['channel'] = channels
            else:
                spec['channel'] = {'$in': channels}

            if cursor_id:
                spec['_id'] = {'$gt': cursor_id}

            yield

            if PYMONGO3:
                cursor = collection.find(spec,
                    cursor_type=pymongo.cursor.CursorType.TAILABLE_AWAIT).sort(
                    '$natural', pymongo.ASCENDING)
            else:
                cursor = collection.find(spec, tailable=True,
                    await_data=True).sort('$natural', pymongo.ASCENDING)

            yield

            while cursor.alive:
                for doc in cursor:
                    cursor_id = doc['_id']

                    yield

                    if doc.get('message') is not None:
                        doc.pop('nonce', None)
                        yield doc

                    if yield_delay:
                        time.sleep(yield_delay)

                        spec = spec.copy()
                        spec['_id'] = {'$gt': cursor_id}
                        cursor = collection.find(spec).sort(
                            '$natural', pymongo.ASCENDING)

                        for doc in cursor:
                            if doc.get('message') is not None:
                                doc.pop('nonce', None)
                                yield doc

                        return

                if timeout and time.time() - start_time >= timeout:
                    return

                yield

        except pymongo.errors.AutoReconnect:
            time.sleep(0.2)
예제 #20
0
파일: host.py 프로젝트: uguryildiz/pritunl
def _host_check_thread():
    collection = mongo.get_collection('hosts')

    while True:
        try:
            ttl_timestamp = {'$lt': utils.now() -
                datetime.timedelta(seconds=settings.app.host_ttl)}

            cursor = collection.find({
                'ping_timestamp': ttl_timestamp,
            }, {
                '_id': True,
            })

            for doc in cursor:
                response = collection.update({
                    '_id': doc['_id'],
                    'ping_timestamp': ttl_timestamp,
                }, {'$set': {
                    'status': OFFLINE,
                    'ping_timestamp': None,
                }})

                if response['updatedExisting']:
                    event.Event(type=HOSTS_UPDATED)
        except GeneratorExit:
            raise
        except:
            logger.exception('Error checking host status', 'runners')

        yield interrupter_sleep(settings.app.host_ttl)
예제 #21
0
파일: key.py 프로젝트: fla3er/pritunl
def sso_request_get():
    state = utils.rand_str(64)
    secret = utils.rand_str(64)
    callback = flask.request.url_root + 'sso/callback'

    if not settings.local.sub_active:
        return flask.abort(405)

    resp = utils.request.post(AUTH_SERVER + '/request/google',
        json_data={
            'license': settings.app.license,
            'callback': callback,
            'state': state,
            'secret': secret,
        }, headers={
            'Content-Type': 'application/json',
        })

    if resp.status_code != 200:
        if resp.status_code == 401:
            return flask.abort(405)
        return flask.abort(500)

    tokens_collection = mongo.get_collection('sso_tokens')
    tokens_collection.insert({
        '_id': state,
        'secret': secret,
        'timestamp': utils.now(),
    })

    data = resp.json()

    return flask.redirect(data['url'])
예제 #22
0
파일: utils.py 프로젝트: lisong521/pritunl
def get_user_count(type=CERT_CLIENT, org_ids=None):
    user_collection = mongo.get_collection('users')

    match_spec = {
        'type': type,
    }

    if org_ids:
        match_spec['org_id'] = {'$in': org_ids}

    response = user_collection.aggregate([
        {'$match': match_spec},
        {'$project': {
            '_id': True,
            'org_id': True,
        }},
        {'$group': {
            '_id': '$org_id',
            'count': {'$sum': 1},
        }},
    ])['result']

    org_user_count = {}
    for doc in response:
        org_user_count[doc['_id']] = doc['count']

    return org_user_count
예제 #23
0
파일: utils.py 프로젝트: 1990754123/pritunl
def link_servers(server_id, link_server_id, use_local_address=False):
    if server_id == link_server_id:
        raise TypeError('Server id must be different then link server id')

    collection = mongo.get_collection('servers')

    count = 0
    spec = {
        '_id': {'$in': [server_id, link_server_id]},
    }
    project = {
        '_id': True,
        'status': True,
        'hosts': True,
        'replica_count': True
    }

    hosts = set()
    for doc in collection.find(spec, project):
        if doc['status'] == ONLINE:
            raise ServerLinkOnlineError('Server must be offline to link')

        if doc['replica_count'] > 1:
            raise ServerLinkReplicaError('Server has replicas')

        hosts_set = set(doc['hosts'])
        if hosts & hosts_set:
            raise ServerLinkCommonHostError('Servers have a common host')
        hosts.update(hosts_set)

        count += 1
    if count != 2:
        raise ServerLinkError('Link server not found')

    tran = transaction.Transaction()
    collection = tran.collection('servers')

    collection.update({
        '_id': server_id,
        'links.server_id': {'$ne': link_server_id},
    }, {'$push': {
        'links': {
            'server_id': link_server_id,
            'user_id': None,
            'use_local_address': use_local_address,
        },
    }})

    collection.update({
        '_id': link_server_id,
        'links.server_id': {'$ne': server_id},
    }, {'$addToSet': {
        'links': {
            'server_id': server_id,
            'user_id': None,
            'use_local_address': use_local_address,
        },
    }})

    tran.commit()
예제 #24
0
파일: key.py 프로젝트: pritunl/pritunl
def user_linked_key_page_delete(short_code):
    utils.rand_sleep()

    collection = mongo.get_collection("users_key_link")
    collection.remove({"short_id": short_code})

    return utils.jsonify({})
예제 #25
0
파일: utils.py 프로젝트: Cesar456/pritunl
def unlink_servers(server_id, link_server_id):
    collection = mongo.get_collection('servers')

    spec = {
        '_id': {'$in': [server_id, link_server_id]},
    }
    project = {
        '_id': True,
        'status': True,
    }

    for doc in collection.find(spec, project):
        if doc['status'] == ONLINE:
            raise ServerLinkOnlineError('Server must be offline to unlink')

    tran = transaction.Transaction()
    collection = tran.collection('servers')

    collection.update({
        '_id': server_id,
    }, {'$pull': {
        'links': {'server_id': link_server_id},
    }})

    collection.update({
        '_id': link_server_id,
    }, {'$pull': {
        'links': {'server_id': server_id},
    }})

    tran.commit()
예제 #26
0
파일: transaction.py 프로젝트: ijat/pritunl
def _check_thread():
    collection = mongo.get_collection('transaction')

    while True:
        try:
            spec = {
                'ttl_timestamp': {'$lt': utils.now()},
            }

            for doc in collection.find(spec).sort('priority'):
                logger.info('Transaction timeout retrying...', 'runners',
                    doc=doc,
                )

                try:
                    tran = transaction.Transaction(doc=doc)
                    tran.run()
                except:
                    logger.exception('Failed to run transaction', 'runners',
                        transaction_id=doc['_id'],
                    )

            yield interrupter_sleep(settings.mongo.tran_ttl)
        except GeneratorExit:
            raise
        except:
            logger.exception('Error in transaction runner thread', 'runners')
            time.sleep(0.5)
예제 #27
0
파일: utils.py 프로젝트: pritunl/pritunl
def get_user_count(org_ids, type=CERT_CLIENT):
    user_collection = mongo.get_collection("users")
    org_user_count = {}

    for org_id in org_ids:
        org_user_count[org_id] = user_collection.find({"type": type, "org_id": org_id}, {"_id": True}).count()

    return org_user_count
예제 #28
0
파일: acme.py 프로젝트: pritunl/pritunl
def set_acme(token, authorization):
    coll = mongo.get_collection('acme_challenges')

    coll.insert({
        '_id': token,
        'authorization': authorization,
        'timestamp': utils.now(),
    })
예제 #29
0
파일: key.py 프로젝트: kknet/pritunl
def user_linked_key_page_delete_get(short_id):
    utils.rand_sleep()

    collection = mongo.get_collection('users_key_link')
    collection.remove({
        'short_id': short_id,
    })
    return utils.jsonify({})
예제 #30
0
파일: utils.py 프로젝트: Cesar456/pritunl
def get_server_page_total():
    org_collection = mongo.get_collection('servers')

    count = org_collection.find({}, {
        '_id': True,
    }).count()

    return int(math.floor(max(0, float(count - 1)) /
        settings.app.server_page_count))
예제 #31
0
def sso_duo_post():
    sso_mode = settings.app.sso
    token = utils.filter_str(flask.request.json.get('token')) or None
    passcode = utils.filter_str(flask.request.json.get('passcode')) or ''

    if sso_mode not in (DUO_AUTH, GOOGLE_DUO_AUTH, SLACK_DUO_AUTH,
            SAML_DUO_AUTH, SAML_OKTA_DUO_AUTH, SAML_ONELOGIN_DUO_AUTH,
            RADIUS_DUO_AUTH):
        return flask.abort(404)

    if not token:
        return utils.jsonify({
            'error': TOKEN_INVALID,
            'error_msg': TOKEN_INVALID_MSG,
        }, 401)

    tokens_collection = mongo.get_collection('sso_tokens')
    doc = tokens_collection.find_one({
        '_id': token,
    })
    if not doc or doc['_id'] != token or doc['type'] != DUO_AUTH:
        return utils.jsonify({
            'error': TOKEN_INVALID,
            'error_msg': TOKEN_INVALID_MSG,
        }, 401)

    username = doc['username']
    email = doc['email']
    org_id = doc['org_id']
    groups = set(doc['groups'] or [])

    if settings.app.sso_duo_mode == 'passcode':
        duo_auth = sso.Duo(
            username=username,
            factor=settings.app.sso_duo_mode,
            remote_ip=utils.get_remote_addr(),
            auth_type='Key',
            passcode=passcode,
        )
        valid = duo_auth.authenticate()
        if not valid:
            logger.error('Duo authentication not valid', 'sso',
                username=username,
            )
            return utils.jsonify({
                'error': PASSCODE_INVALID,
                'error_msg': PASSCODE_INVALID_MSG,
            }, 401)
    else:
        duo_auth = sso.Duo(
            username=username,
            factor=settings.app.sso_duo_mode,
            remote_ip=utils.get_remote_addr(),
            auth_type='Key',
        )
        valid = duo_auth.authenticate()
        if not valid:
            logger.error('Duo authentication not valid', 'sso',
                username=username,
            )
            return utils.jsonify({
                'error': DUO_FAILED,
                'error_msg': DUO_FAILED_MSG,
            }, 401)

    valid, org_id_new, groups2 = sso.plugin_sso_authenticate(
        sso_type='duo',
        user_name=username,
        user_email=email,
        remote_ip=utils.get_remote_addr(),
    )
    if valid:
        org_id = org_id_new or org_id
    else:
        logger.error('Duo plugin authentication not valid', 'sso',
            username=username,
        )
        return flask.abort(401)

    groups = groups | set(groups2 or [])

    usr = user.find_user_auth(name=username, auth_type=sso_mode)
    if not usr:
        org = organization.get_by_id(org_id)
        if not org:
            return flask.abort(405)

        usr = org.find_user(name=username)
    else:
        org = usr.org

    if not usr:
        usr = org.new_user(name=username, email=email, type=CERT_CLIENT,
            auth_type=sso_mode, groups=list(groups) if groups else None)
        usr.audit_event('user_created', 'User created with single sign-on',
            remote_addr=utils.get_remote_addr())

        event.Event(type=ORGS_UPDATED)
        event.Event(type=USERS_UPDATED, resource_id=org.id)
        event.Event(type=SERVERS_UPDATED)
    else:
        if usr.disabled:
            return flask.abort(403)

        if groups and groups - set(usr.groups or []):
            usr.groups = list(set(usr.groups or []) | groups)
            usr.commit('groups')

        if usr.auth_type != sso_mode:
            usr.auth_type = sso_mode
            usr.commit('auth_type')

    key_link = org.create_user_key_link(usr.id, one_time=True)

    usr.audit_event('user_profile',
        'User profile viewed from single sign-on',
        remote_addr=utils.get_remote_addr(),
    )

    return utils.jsonify({
        'redirect': utils.get_url_root() + key_link['view_url'],
    }, 200)
예제 #32
0
def sso_request_get():
    sso_mode = settings.app.sso

    if sso_mode not in (AZURE_AUTH, AZURE_DUO_AUTH, AZURE_YUBICO_AUTH,
            GOOGLE_AUTH, GOOGLE_DUO_AUTH, GOOGLE_YUBICO_AUTH,
            AUTHZERO_AUTH, AUTHZERO_DUO_AUTH, AUTHZERO_YUBICO_AUTH,
            SLACK_AUTH, SLACK_DUO_AUTH, SLACK_YUBICO_AUTH, SAML_AUTH,
            SAML_DUO_AUTH, SAML_YUBICO_AUTH, SAML_OKTA_AUTH,
            SAML_OKTA_DUO_AUTH, SAML_OKTA_YUBICO_AUTH, SAML_ONELOGIN_AUTH,
            SAML_ONELOGIN_DUO_AUTH, SAML_ONELOGIN_YUBICO_AUTH):
        return flask.abort(404)

    state = utils.rand_str(64)
    secret = utils.rand_str(64)
    callback = utils.get_url_root() + '/sso/callback'
    auth_server = AUTH_SERVER
    if settings.app.dedicated:
        auth_server = settings.app.dedicated

    if not settings.local.sub_active:
        logger.error('Subscription must be active for sso', 'sso')
        return flask.abort(405)

    if AZURE_AUTH in sso_mode:
        resp = requests.post(auth_server + '/v1/request/azure',
            headers={
                'Content-Type': 'application/json',
            },
            json={
                'license': settings.app.license,
                'callback': callback,
                'state': state,
                'secret': secret,
                'directory_id': settings.app.sso_azure_directory_id,
                'app_id': settings.app.sso_azure_app_id,
                'app_secret': settings.app.sso_azure_app_secret,
            },
        )

        if resp.status_code != 200:
            logger.error('Azure auth server error', 'sso',
                status_code=resp.status_code,
                content=resp.content,
            )

            if resp.status_code == 401:
                return flask.abort(405)

            return flask.abort(500)

        tokens_collection = mongo.get_collection('sso_tokens')
        tokens_collection.insert({
            '_id': state,
            'type': AZURE_AUTH,
            'secret': secret,
            'timestamp': utils.now(),
        })

        data = resp.json()

        return utils.redirect(data['url'])

    elif GOOGLE_AUTH in sso_mode:
        resp = requests.post(auth_server + '/v1/request/google',
            headers={
                'Content-Type': 'application/json',
            },
            json={
                'license': settings.app.license,
                'callback': callback,
                'state': state,
                'secret': secret,
            },
        )

        if resp.status_code != 200:
            logger.error('Google auth server error', 'sso',
                status_code=resp.status_code,
                content=resp.content,
            )

            if resp.status_code == 401:
                return flask.abort(405)

            return flask.abort(500)

        tokens_collection = mongo.get_collection('sso_tokens')
        tokens_collection.insert({
            '_id': state,
            'type': GOOGLE_AUTH,
            'secret': secret,
            'timestamp': utils.now(),
        })

        data = resp.json()

        return utils.redirect(data['url'])

    elif AUTHZERO_AUTH in sso_mode:
        resp = requests.post(auth_server + '/v1/request/authzero',
            headers={
                'Content-Type': 'application/json',
            },
            json={
                'license': settings.app.license,
                'callback': callback,
                'state': state,
                'secret': secret,
                'app_domain': settings.app.sso_authzero_domain,
                'app_id': settings.app.sso_authzero_app_id,
                'app_secret': settings.app.sso_authzero_app_secret,
            },
        )

        if resp.status_code != 200:
            logger.error('Auth0 auth server error', 'sso',
                status_code=resp.status_code,
                content=resp.content,
            )

            if resp.status_code == 401:
                return flask.abort(405)

            return flask.abort(500)

        tokens_collection = mongo.get_collection('sso_tokens')
        tokens_collection.insert({
            '_id': state,
            'type': AUTHZERO_AUTH,
            'secret': secret,
            'timestamp': utils.now(),
        })

        data = resp.json()

        return utils.redirect(data['url'])

    elif SLACK_AUTH in sso_mode:
        resp = requests.post(auth_server + '/v1/request/slack',
            headers={
                'Content-Type': 'application/json',
            },
            json={
                'license': settings.app.license,
                'callback': callback,
                'state': state,
                'secret': secret,
            },
        )

        if resp.status_code != 200:
            logger.error('Slack auth server error', 'sso',
                status_code=resp.status_code,
                content=resp.content,
            )

            if resp.status_code == 401:
                return flask.abort(405)

            return flask.abort(500)

        tokens_collection = mongo.get_collection('sso_tokens')
        tokens_collection.insert({
            '_id': state,
            'type': SLACK_AUTH,
            'secret': secret,
            'timestamp': utils.now(),
        })

        data = resp.json()

        return utils.redirect(data['url'])

    elif SAML_AUTH in sso_mode:
        resp = requests.post(auth_server + '/v1/request/saml',
            headers={
                'Content-Type': 'application/json',
            },
            json={
                'license': settings.app.license,
                'callback': callback,
                'state': state,
                'secret': secret,
                'sso_url': settings.app.sso_saml_url,
                'issuer_url': settings.app.sso_saml_issuer_url,
                'cert': settings.app.sso_saml_cert,
            },
        )

        if resp.status_code != 200:
            logger.error('Saml auth server error', 'sso',
                status_code=resp.status_code,
                content=resp.content,
            )

            if resp.status_code == 401:
                return flask.abort(405)

            return flask.abort(500)

        tokens_collection = mongo.get_collection('sso_tokens')
        tokens_collection.insert({
            '_id': state,
            'type': SAML_AUTH,
            'secret': secret,
            'timestamp': utils.now(),
        })

        return flask.Response(
            status=200,
            response=resp.content,
            content_type="text/html;charset=utf-8",
        )

    else:
        return flask.abort(404)
예제 #33
0
 def collection(cls):
     return mongo.get_collection('servers_output')
예제 #34
0
 def transaction_collection(cls):
     return mongo.get_collection('transaction')
예제 #35
0
    def _rollback_actions(self):
        for action_set in self.action_sets:
            collection_name, _, _, rollback_actions, _ = action_set
            collection = mongo.get_collection(collection_name)

            self._run_collection_actions(collection, rollback_actions)
예제 #36
0
def sso_yubico_post():
    sso_mode = settings.app.sso
    token = utils.filter_str(flask.request.json.get('token')) or None
    key = utils.filter_str(flask.request.json.get('key')) or None

    if sso_mode not in (GOOGLE_YUBICO_AUTH, SLACK_YUBICO_AUTH,
            SAML_YUBICO_AUTH, SAML_OKTA_YUBICO_AUTH,
            SAML_ONELOGIN_YUBICO_AUTH):
        return flask.abort(404)

    if not token or not key:
        return utils.jsonify({
            'error': TOKEN_INVALID,
            'error_msg': TOKEN_INVALID_MSG,
        }, 401)

    tokens_collection = mongo.get_collection('sso_tokens')
    doc = tokens_collection.find_one({
        '_id': token,
    })
    if not doc or doc['_id'] != token or doc['type'] != YUBICO_AUTH:
        return utils.jsonify({
            'error': TOKEN_INVALID,
            'error_msg': TOKEN_INVALID_MSG,
        }, 401)

    username = doc['username']
    email = doc['email']
    org_id = doc['org_id']
    groups = set(doc['groups'] or [])

    valid, yubico_id = sso.auth_yubico(key)
    if not valid:
        return utils.jsonify({
            'error': YUBIKEY_INVALID,
            'error_msg': YUBIKEY_INVALID_MSG,
        }, 401)

    usr = user.find_user_auth(name=username, auth_type=sso_mode)
    if not usr:
        org = organization.get_by_id(org_id)
        if not org:
            return flask.abort(405)

        usr = org.find_user(name=username)
    else:
        org = usr.org

    if not usr:
        usr = org.new_user(name=username, email=email, type=CERT_CLIENT,
            auth_type=sso_mode, yubico_id=yubico_id,
            groups=list(groups) if groups else None)
        usr.audit_event('user_created', 'User created with single sign-on',
            remote_addr=utils.get_remote_addr())

        event.Event(type=ORGS_UPDATED)
        event.Event(type=USERS_UPDATED, resource_id=org.id)
        event.Event(type=SERVERS_UPDATED)
    else:
        if yubico_id != usr.yubico_id:
            return utils.jsonify({
                'error': YUBIKEY_INVALID,
                'error_msg': YUBIKEY_INVALID_MSG,
            }, 401)

        if usr.disabled:
            return flask.abort(403)

        if groups and groups - set(usr.groups or []):
            usr.groups = list(set(usr.groups or []) | groups)
            usr.commit('groups')

        if usr.auth_type != sso_mode:
            usr.auth_type = sso_mode
            usr.commit('auth_type')

    key_link = org.create_user_key_link(usr.id, one_time=True)

    usr.audit_event('user_profile',
        'User profile viewed from single sign-on',
        remote_addr=utils.get_remote_addr(),
    )

    return utils.jsonify({
        'redirect': utils.get_url_root() + key_link['view_url'],
    }, 200)
예제 #37
0
파일: server.py 프로젝트: gergnz/pritunl
 def user_net_link_collection(cls):
     return mongo.get_collection('users_net_link')
예제 #38
0
 def host_collection(cls):
     return mongo.get_collection('hosts')
예제 #39
0
파일: key.py 프로젝트: ijat/pritunl
def sso_duo_post():
    sso_mode = settings.app.sso
    token = utils.filter_str(flask.request.json.get('token')) or None
    passcode = utils.filter_str(flask.request.json.get('passcode')) or None

    if not token or not passcode:
        return utils.jsonify({
            'error': TOKEN_INVALID,
            'error_msg': TOKEN_INVALID_MSG,
        }, 401)

    tokens_collection = mongo.get_collection('sso_tokens')
    doc = tokens_collection.find_one({
        '_id': token,
    })
    if not doc or doc['_id'] != token:
        return utils.jsonify({
            'error': TOKEN_INVALID,
            'error_msg': TOKEN_INVALID_MSG,
        }, 401)

    username = doc['username']
    email = doc['email']
    org_id = doc['org_id']
    groups = doc['groups']

    duo_auth = sso.Duo(
        username=username,
        factor=settings.app.sso_duo_mode,
        remote_ip=utils.get_remote_addr(),
        auth_type='Key',
        passcode=passcode,
    )
    valid = duo_auth.authenticate()
    if not valid:
        return utils.jsonify({
            'error': PASSCODE_INVALID,
            'error_msg': PASSCODE_INVALID_MSG,
        }, 401)

    org = organization.get_by_id(org_id)
    if not org:
        return flask.abort(405)

    usr = org.find_user(name=username)
    if not usr:
        usr = org.new_user(name=username, email=email, type=CERT_CLIENT,
            auth_type=sso_mode, groups=list(groups) if groups else None)
        usr.audit_event('user_created', 'User created with single sign-on',
            remote_addr=utils.get_remote_addr())

        event.Event(type=ORGS_UPDATED)
        event.Event(type=USERS_UPDATED, resource_id=org.id)
        event.Event(type=SERVERS_UPDATED)
    else:
        if usr.disabled:
            return flask.abort(403)

        if groups and groups - set(usr.groups or []):
            usr.groups = list(set(usr.groups or []) | groups)
            usr.commit('groups')

        if usr.auth_type != sso_mode:
            usr.auth_type = sso_mode
            usr.commit('auth_type')

    key_link = org.create_user_key_link(usr.id, one_time=True)

    usr.audit_event('user_profile',
        'User profile viewed from single sign-on',
        remote_addr=utils.get_remote_addr(),
    )

    return utils.jsonify({
        'redirect': utils.get_url_root() + key_link['view_url'],
    }, 200)
예제 #40
0
 def sso_push_cache_collection(cls):
     return mongo.get_collection('sso_push_cache')
예제 #41
0
 def sso_passcode_cache_collection(cls):
     return mongo.get_collection('sso_passcode_cache')
예제 #42
0
 def collection(cls):
     return mongo.get_collection('servers_ip_pool')
예제 #43
0
 def sso_client_cache_collection(cls):
     return mongo.get_collection('sso_client_cache')
예제 #44
0
 def limiter_collection(cls):
     return mongo.get_collection('auth_limiter')
예제 #45
0
def sso_callback_get():
    sso_mode = settings.app.sso

    if sso_mode not in (GOOGLE_AUTH, GOOGLE_DUO_AUTH, GOOGLE_YUBICO_AUTH,
            SLACK_AUTH, SLACK_DUO_AUTH, SLACK_YUBICO_AUTH, SAML_AUTH,
            SAML_DUO_AUTH, SAML_YUBICO_AUTH, SAML_OKTA_AUTH,
            SAML_OKTA_DUO_AUTH, SAML_OKTA_YUBICO_AUTH, SAML_ONELOGIN_AUTH,
            SAML_ONELOGIN_DUO_AUTH, SAML_ONELOGIN_YUBICO_AUTH):
        return flask.abort(405)

    state = flask.request.args.get('state')
    sig = flask.request.args.get('sig')

    tokens_collection = mongo.get_collection('sso_tokens')
    doc = tokens_collection.find_and_modify(query={
        '_id': state,
    }, remove=True)

    if not doc:
        return flask.abort(404)

    query = flask.request.query_string.split('&sig=')[0]
    test_sig = base64.urlsafe_b64encode(hmac.new(str(doc['secret']),
        query, hashlib.sha512).digest())
    if not utils.const_compare(sig, test_sig):
        return flask.abort(401)

    params = urlparse.parse_qs(query)

    if doc.get('type') == SAML_AUTH:
        username = params.get('username')[0]
        email = params.get('email', [None])[0]
        groups = set(params.get('groups') or [])
        org_name = params.get('org', [None])[0]

        if not username:
            return flask.abort(406)

        org_id = settings.app.sso_org
        if org_name:
            org = organization.get_by_name(org_name, fields=('_id'))
            if org:
                org_id = org.id

        valid, org_id_new, groups2 = sso.plugin_sso_authenticate(
            sso_type='saml',
            user_name=username,
            user_email=email,
            remote_ip=utils.get_remote_addr(),
            sso_org_names=[org_name],
        )
        if valid:
            org_id = org_id_new or org_id
        else:
            logger.error('Saml plugin authentication not valid', 'sso',
                username=username,
            )
            return flask.abort(401)

        groups = groups | set(groups2 or [])
    elif doc.get('type') == SLACK_AUTH:
        username = params.get('username')[0]
        email = None
        user_team = params.get('team')[0]
        org_names = params.get('orgs', [''])[0]
        org_names = org_names.split(',')

        valid = sso.verify_slack(username, user_team)
        if not valid:
            return flask.abort(401)

        org_id = settings.app.sso_org
        for org_name in org_names:
            org = organization.get_by_name(org_name, fields=('_id'))
            if org:
                org_id = org.id
                break

        valid, org_id_new, groups = sso.plugin_sso_authenticate(
            sso_type='slack',
            user_name=username,
            user_email=email,
            remote_ip=utils.get_remote_addr(),
            sso_org_names=org_names,
        )
        if valid:
            org_id = org_id_new or org_id
        else:
            logger.error('Slack plugin authentication not valid', 'sso',
                username=username,
            )
            return flask.abort(401)
        groups = set(groups or [])
    else:
        username = params.get('username')[0]
        email = username

        valid, google_groups = sso.verify_google(username)
        if not valid:
            return flask.abort(401)

        org_id = settings.app.sso_org

        valid, org_id_new, groups = sso.plugin_sso_authenticate(
            sso_type='google',
            user_name=username,
            user_email=email,
            remote_ip=utils.get_remote_addr(),
        )
        if valid:
            org_id = org_id_new or org_id
        else:
            logger.error('Google plugin authentication not valid', 'sso',
                username=username,
            )
            return flask.abort(401)
        groups = set(groups or [])

        if settings.app.sso_google_mode == 'groups':
            groups = groups | set(google_groups)
        else:
            for org_name in sorted(google_groups):
                org = organization.get_by_name(org_name, fields=('_id'))
                if org:
                    org_id = org.id
                    break

    if DUO_AUTH in sso_mode:
        token = utils.generate_secret()

        tokens_collection = mongo.get_collection('sso_tokens')
        tokens_collection.insert({
            '_id': token,
            'type': DUO_AUTH,
            'username': username,
            'email': email,
            'org_id': org_id,
            'groups': list(groups) if groups else None,
            'timestamp': utils.now(),
        })

        duo_page = static.StaticFile(settings.conf.www_path,
            'duo.html', cache=False, gzip=False)

        sso_duo_mode = settings.app.sso_duo_mode
        if sso_duo_mode == 'passcode':
            duo_mode = 'passcode'
        elif sso_duo_mode == 'phone':
            duo_mode = 'phone'
        else:
            duo_mode = 'push'

        body_class = duo_mode
        if settings.app.theme == 'dark':
            body_class += ' dark'

        duo_page.data = duo_page.data.replace('<%= body_class %>', body_class)
        duo_page.data = duo_page.data.replace('<%= token %>', token)
        duo_page.data = duo_page.data.replace('<%= duo_mode %>', duo_mode)

        return duo_page.get_response()

    if YUBICO_AUTH in sso_mode:
        token = utils.generate_secret()

        tokens_collection = mongo.get_collection('sso_tokens')
        tokens_collection.insert({
            '_id': token,
            'type': YUBICO_AUTH,
            'username': username,
            'email': email,
            'org_id': org_id,
            'groups': list(groups) if groups else None,
            'timestamp': utils.now(),
        })

        yubico_page = static.StaticFile(settings.conf.www_path,
            'yubico.html', cache=False, gzip=False)

        if settings.app.theme == 'dark':
            yubico_page.data = yubico_page.data.replace(
                '<body>', '<body class="dark">')
        yubico_page.data = yubico_page.data.replace('<%= token %>', token)

        return yubico_page.get_response()

    usr = user.find_user_auth(name=username, auth_type=sso_mode)
    if not usr:
        org = organization.get_by_id(org_id)
        if not org:
            return flask.abort(405)

        usr = org.find_user(name=username)
    else:
        org = usr.org

    if not usr:
        usr = org.new_user(name=username, email=email, type=CERT_CLIENT,
            auth_type=sso_mode, groups=list(groups) if groups else None)
        usr.audit_event('user_created', 'User created with single sign-on',
            remote_addr=utils.get_remote_addr())

        event.Event(type=ORGS_UPDATED)
        event.Event(type=USERS_UPDATED, resource_id=org.id)
        event.Event(type=SERVERS_UPDATED)
    else:
        if usr.disabled:
            return flask.abort(403)

        if groups and groups - set(usr.groups or []):
            usr.groups = list(set(usr.groups or []) | groups)
            usr.commit('groups')

        if usr.auth_type != sso_mode:
            usr.auth_type = sso_mode
            usr.commit('auth_type')

    key_link = org.create_user_key_link(usr.id, one_time=True)

    usr.audit_event('user_profile',
        'User profile viewed from single sign-on',
        remote_addr=utils.get_remote_addr(),
    )

    return utils.redirect(utils.get_url_root() + key_link['view_url'])
예제 #46
0
 def org_collection(cls):
     return mongo.get_collection('organizations')
예제 #47
0
def key_sync_get(org_id, user_id, server_id, key_hash):
    if not settings.user.conf_sync:
        return utils.jsonify({})

    if not settings.local.sub_active:
        return utils.jsonify({}, status_code=480)

    utils.rand_sleep()

    auth_token = flask.request.headers.get('Auth-Token', None)
    auth_timestamp = flask.request.headers.get('Auth-Timestamp', None)
    auth_nonce = flask.request.headers.get('Auth-Nonce', None)
    auth_signature = flask.request.headers.get('Auth-Signature', None)
    if not auth_token or not auth_timestamp or not auth_nonce or \
            not auth_signature:
        return flask.abort(406)
    auth_nonce = auth_nonce[:32]

    try:
        if abs(int(auth_timestamp) - int(utils.time_now())) > \
                settings.app.auth_time_window:
            return flask.abort(408)
    except ValueError:
        return flask.abort(405)

    org = organization.get_by_id(org_id)
    if not org:
        return flask.abort(404)

    usr = org.get_user(id=user_id)
    if not usr:
        return flask.abort(404)
    elif not usr.sync_secret:
        return flask.abort(410)

    if auth_token != usr.sync_token:
        return flask.abort(410)

    if usr.disabled:
        return flask.abort(403)

    auth_string = '&'.join([
        usr.sync_token, auth_timestamp, auth_nonce, flask.request.method,
        flask.request.path])

    if len(auth_string) > AUTH_SIG_STRING_MAX_LEN:
        return flask.abort(413)

    auth_test_signature = base64.b64encode(hmac.new(
        usr.sync_secret.encode(), auth_string,
        hashlib.sha512).digest())
    if not utils.const_compare(auth_signature, auth_test_signature):
        return flask.abort(401)

    nonces_collection = mongo.get_collection('auth_nonces')
    try:
        nonces_collection.insert({
            'token': auth_token,
            'nonce': auth_nonce,
            'timestamp': utils.now(),
        })
    except pymongo.errors.DuplicateKeyError:
        return flask.abort(409)

    key_conf = usr.sync_conf(server_id, key_hash)
    if key_conf:
        usr.audit_event('user_profile',
            'User profile synced from pritunl client',
            remote_addr=utils.get_remote_addr(),
        )

        sync_signature = base64.b64encode(hmac.new(
            usr.sync_secret.encode(), key_conf['conf'],
            hashlib.sha512).digest())

        return utils.jsonify({
            'signature': sync_signature,
            'conf': key_conf['conf'],
        })

    return utils.jsonify({})
예제 #48
0
 def clients_collection(cls):
     return mongo.get_collection('clients')
예제 #49
0
def link_state_put():
    if settings.app.demo_mode:
        return utils.demo_blocked()

    auth_token = flask.request.headers.get('Auth-Token', None)
    auth_timestamp = flask.request.headers.get('Auth-Timestamp', None)
    auth_nonce = flask.request.headers.get('Auth-Nonce', None)
    auth_signature = flask.request.headers.get('Auth-Signature', None)
    if not auth_token or not auth_timestamp or not auth_nonce or \
            not auth_signature:
        return flask.abort(406)
    auth_nonce = auth_nonce[:32]

    try:
        if abs(int(auth_timestamp) - int(utils.time_now())) > \
                settings.app.auth_time_window:
            return flask.abort(408)
    except ValueError:
        return flask.abort(405)

    host = link.get_host(utils.ObjectId(auth_token))
    if not host:
        return flask.abort(404)

    auth_string = '&'.join([
        auth_token,
        auth_timestamp,
        auth_nonce,
        flask.request.method,
        flask.request.path,
    ])

    if len(auth_string) > AUTH_SIG_STRING_MAX_LEN:
        return flask.abort(413)

    auth_test_signature = base64.b64encode(
        hmac.new(host.secret.encode(), auth_string.encode(),
                 hashlib.sha512).digest()).decode()
    if not utils.const_compare(auth_signature, auth_test_signature):
        return flask.abort(401)

    nonces_collection = mongo.get_collection('auth_nonces')
    try:
        nonces_collection.insert({
            'token': auth_token,
            'nonce': auth_nonce,
            'timestamp': utils.now(),
        })
    except pymongo.errors.DuplicateKeyError:
        return flask.abort(409)

    host.load_link()

    host.version = flask.request.json.get('version')
    host.public_address = flask.request.json.get('public_address')
    host.local_address = flask.request.json.get('local_address')
    host.address6 = flask.request.json.get('address6')

    state, active = host.get_state()
    if active:
        host.location.status = flask.request.json.get('status') or None
        host.location.commit('status')

    data = json.dumps(state, default=lambda x: str(x))
    data += (16 - len(data) % 16) * '\x00'

    iv = os.urandom(16)
    key = hashlib.sha256(host.secret.encode()).digest()
    cipher = Cipher(algorithms.AES(key),
                    modes.CBC(iv),
                    backend=default_backend()).encryptor()
    enc_data = base64.b64encode(
        cipher.update(data.encode()) + cipher.finalize())

    enc_signature = base64.b64encode(
        hmac.new(host.secret.encode(), enc_data,
                 hashlib.sha512).digest()).decode()

    resp = flask.Response(response=enc_data, mimetype='application/base64')
    resp.headers.add('Cache-Control', 'no-cache, no-store, must-revalidate')
    resp.headers.add('Pragma', 'no-cache')
    resp.headers.add('Expires', 0)
    resp.headers.add('Cipher-IV', base64.b64encode(iv))
    resp.headers.add('Cipher-Signature', enc_signature)

    return resp
예제 #50
0
 def server_collection(cls):
     return mongo.get_collection('servers')
예제 #51
0
 def dh_params_collection(cls):
     return mongo.get_collection('dh_params')
예제 #52
0
 def users_ip_collection(cls):
     return mongo.get_collection('users_ip')
예제 #53
0
    def _run_post_actions(self):
        for action_set in self.action_sets:
            collection_name, _, _, _, post_actions = action_set
            collection = mongo.get_collection(collection_name)

            self._run_collection_actions(collection, post_actions)
예제 #54
0
def update():
    license = settings.app.license
    collection = mongo.get_collection('settings')

    if not settings.app.id:
        settings.app.id = utils.random_name()
        settings.commit()

    if not license:
        settings.local.sub_active = False
        settings.local.sub_status = None
        settings.local.sub_plan = None
        settings.local.sub_quantity = None
        settings.local.sub_amount = None
        settings.local.sub_period_end = None
        settings.local.sub_trial_end = None
        settings.local.sub_cancel_at_period_end = None
        settings.local.sub_url_key = None
    else:
        for i in xrange(2):
            try:
                response = requests.get(
                    'https://app.pritunl.com/subscription',
                    json={
                        'id': settings.app.id,
                        'license': license,
                        'version': settings.local.version_int,
                    },
                    timeout=max(settings.app.http_request_timeout, 10),
                )

                # License key invalid
                if response.status_code == 470:
                    logger.warning('License key is invalid', 'subscription')
                    update_license(None)
                    update()
                    return False

                if response.status_code == 473:
                    raise ValueError(('Version %r not recognized by ' +
                        'subscription server') % settings.local.version_int)

                data = response.json()

                settings.local.sub_active = data['active']
                settings.local.sub_status = data['status']
                settings.local.sub_plan = data['plan']
                settings.local.sub_quantity = data['quantity']
                settings.local.sub_amount = data['amount']
                settings.local.sub_period_end = data['period_end']
                settings.local.sub_trial_end = data['trial_end']
                settings.local.sub_cancel_at_period_end = data[
                    'cancel_at_period_end']
                settings.local.sub_url_key = data.get('url_key')
                settings.local.sub_styles[data['plan']] = data['styles']
            except:
                if i < 1:
                    logger.exception('Failed to check subscription status',
                        'subscription, retrying...')
                    time.sleep(1)
                    continue
                logger.exception('Failed to check subscription status',
                    'subscription')
                settings.local.sub_active = False
                settings.local.sub_status = None
                settings.local.sub_plan = None
                settings.local.sub_quantity = None
                settings.local.sub_amount = None
                settings.local.sub_period_end = None
                settings.local.sub_trial_end = None
                settings.local.sub_cancel_at_period_end = None
                settings.local.sub_url_key = None
            break

    if settings.app.license_plan != settings.local.sub_plan and \
            settings.local.sub_plan:
        settings.app.license_plan = settings.local.sub_plan
        settings.commit()

    response = collection.update({
        '_id': 'subscription',
        '$or': [
            {'active': {'$ne': settings.local.sub_active}},
            {'plan': {'$ne': settings.local.sub_plan}},
        ],
    }, {'$set': {
        'active': settings.local.sub_active,
        'plan': settings.local.sub_plan,
    }})
    if response['updatedExisting']:
        if settings.local.sub_active:
            if settings.local.sub_plan == 'premium':
                event.Event(type=SUBSCRIPTION_PREMIUM_ACTIVE)
            elif settings.local.sub_plan == 'enterprise':
                event.Event(type=SUBSCRIPTION_ENTERPRISE_ACTIVE)
            else:
                event.Event(type=SUBSCRIPTION_NONE_INACTIVE)
        else:
            if settings.local.sub_plan == 'premium':
                event.Event(type=SUBSCRIPTION_PREMIUM_INACTIVE)
            elif settings.local.sub_plan == 'enterprise':
                event.Event(type=SUBSCRIPTION_ENTERPRISE_INACTIVE)
            else:
                event.Event(type=SUBSCRIPTION_NONE_INACTIVE)

    return True
예제 #55
0
def sso_callback_get():
    sso_mode = settings.app.sso

    if sso_mode not in (AZURE_AUTH, AZURE_DUO_AUTH, AZURE_YUBICO_AUTH,
            GOOGLE_AUTH, GOOGLE_DUO_AUTH, GOOGLE_YUBICO_AUTH,
            AUTHZERO_AUTH, AUTHZERO_DUO_AUTH, AUTHZERO_YUBICO_AUTH,
            SLACK_AUTH, SLACK_DUO_AUTH, SLACK_YUBICO_AUTH, SAML_AUTH,
            SAML_DUO_AUTH, SAML_YUBICO_AUTH, SAML_OKTA_AUTH,
            SAML_OKTA_DUO_AUTH, SAML_OKTA_YUBICO_AUTH, SAML_ONELOGIN_AUTH,
            SAML_ONELOGIN_DUO_AUTH, SAML_ONELOGIN_YUBICO_AUTH):
        return flask.abort(405)

    remote_addr = utils.get_remote_addr()
    state = flask.request.args.get('state')
    sig = flask.request.args.get('sig')

    tokens_collection = mongo.get_collection('sso_tokens')
    doc = tokens_collection.find_and_modify(query={
        '_id': state,
    }, remove=True)

    if not doc:
        return flask.abort(404)

    query = flask.request.query_string.split('&sig=')[0]
    test_sig = base64.urlsafe_b64encode(hmac.new(str(doc['secret']),
        query, hashlib.sha512).digest())
    if not utils.const_compare(sig, test_sig):
        journal.entry(
            journal.SSO_AUTH_FAILURE,
            state=state,
            remote_address=remote_addr,
            reason=journal.SSO_AUTH_REASON_INVALID_CALLBACK,
            reason_long='Signature mismatch',
        )
        return flask.abort(401)

    params = urlparse.parse_qs(query)

    if doc.get('type') == SAML_AUTH:
        username = params.get('username')[0]
        email = params.get('email', [None])[0]

        org_names = []
        if params.get('org'):
            org_names_param = params.get('org')[0]
            if ';' in org_names_param:
                org_names = org_names_param.split(';')
            else:
                org_names = org_names_param.split(',')
            org_names = [x for x in org_names if x]
        org_names = sorted(org_names)

        groups = []
        if params.get('groups'):
            groups_param = params.get('groups')[0]
            if ';' in groups_param:
                groups = groups_param.split(';')
            else:
                groups = groups_param.split(',')
            groups = [x for x in groups if x]
        groups = set(groups)

        if not username:
            return flask.abort(406)

        org_id = settings.app.sso_org
        if org_names:
            not_found = False
            for org_name in org_names:
                org = organization.get_by_name(
                    utils.filter_unicode(org_name),
                    fields=('_id'),
                )
                if org:
                    not_found = False
                    org_id = org.id
                    break
                else:
                    not_found = True

            if not_found:
                logger.warning('Supplied org names do not exists',
                    'sso',
                    sso_type=doc.get('type'),
                    user_name=username,
                    user_email=email,
                    org_names=org_names,
                )

        valid, org_id_new, groups2 = sso.plugin_sso_authenticate(
            sso_type='saml',
            user_name=username,
            user_email=email,
            remote_ip=remote_addr,
            sso_org_names=org_names,
        )
        if valid:
            org_id = org_id_new or org_id
        else:
            logger.error('Saml plugin authentication not valid', 'sso',
                username=username,
            )

            journal.entry(
                journal.SSO_AUTH_FAILURE,
                user_name=username,
                remote_address=remote_addr,
                reason=journal.SSO_AUTH_REASON_PLUGIN_FAILED,
                reason_long='Saml plugin authentication failed',
            )

            return flask.abort(401)

        groups = groups | set(groups2 or [])
    elif doc.get('type') == SLACK_AUTH:
        username = params.get('username')[0]
        email = None
        user_team = params.get('team')[0]
        org_names = params.get('orgs', [''])[0]
        org_names = sorted(org_names.split(','))

        if user_team != settings.app.sso_match[0]:
            journal.entry(
                journal.SSO_AUTH_FAILURE,
                user_name=username,
                remote_address=remote_addr,
                reason=journal.SSO_AUTH_REASON_SLACK_FAILED,
                reason_long='Slack team not valid',
            )

            return flask.abort(401)

        not_found = False
        org_id = settings.app.sso_org
        for org_name in org_names:
            org = organization.get_by_name(
                utils.filter_unicode(org_name),
                fields=('_id'),
            )
            if org:
                not_found = False
                org_id = org.id
                break
            else:
                not_found = True

        if not_found:
            logger.warning('Supplied org names do not exists',
                'sso',
                sso_type=doc.get('type'),
                user_name=username,
                user_email=email,
                org_names=org_names,
            )

        valid, org_id_new, groups = sso.plugin_sso_authenticate(
            sso_type='slack',
            user_name=username,
            user_email=email,
            remote_ip=remote_addr,
            sso_org_names=org_names,
        )
        if valid:
            org_id = org_id_new or org_id
        else:
            logger.error('Slack plugin authentication not valid', 'sso',
                username=username,
            )

            journal.entry(
                journal.SSO_AUTH_FAILURE,
                user_name=username,
                remote_address=remote_addr,
                reason=journal.SSO_AUTH_REASON_PLUGIN_FAILED,
                reason_long='Slack plugin authentication failed',
            )

            return flask.abort(401)
        groups = set(groups or [])
    elif doc.get('type') == GOOGLE_AUTH:
        username = params.get('username')[0]
        email = username

        valid, google_groups = sso.verify_google(username)
        if not valid:
            journal.entry(
                journal.SSO_AUTH_FAILURE,
                user_name=username,
                remote_address=remote_addr,
                reason=journal.SSO_AUTH_REASON_GOOGLE_FAILED,
                reason_long='Google authentication failed',
            )

            return flask.abort(401)

        org_id = settings.app.sso_org

        valid, org_id_new, groups = sso.plugin_sso_authenticate(
            sso_type='google',
            user_name=username,
            user_email=email,
            remote_ip=remote_addr,
        )
        if valid:
            org_id = org_id_new or org_id
        else:
            logger.error('Google plugin authentication not valid', 'sso',
                username=username,
            )

            journal.entry(
                journal.SSO_AUTH_FAILURE,
                user_name=username,
                remote_address=remote_addr,
                reason=journal.SSO_AUTH_REASON_PLUGIN_FAILED,
                reason_long='Google plugin authentication failed',
            )

            return flask.abort(401)
        groups = set(groups or [])

        if settings.app.sso_google_mode == 'groups':
            groups = groups | set(google_groups)
        else:
            not_found = False
            google_groups = sorted(google_groups)
            for org_name in google_groups:
                org = organization.get_by_name(
                    utils.filter_unicode(org_name),
                    fields=('_id'),
                )
                if org:
                    not_found = False
                    org_id = org.id
                    break
                else:
                    not_found = True

            if not_found:
                logger.warning('Supplied org names do not exists',
                    'sso',
                    sso_type=doc.get('type'),
                    user_name=username,
                    user_email=email,
                    org_names=google_groups,
                )
    elif doc.get('type') == AZURE_AUTH:
        username = params.get('username')[0]
        email = None

        tenant, username = username.split('/', 2)
        if tenant != settings.app.sso_azure_directory_id:
            logger.error('Azure directory ID mismatch', 'sso',
                username=username,
            )

            journal.entry(
                journal.SSO_AUTH_FAILURE,
                user_name=username,
                azure_tenant=tenant,
                remote_address=remote_addr,
                reason=journal.SSO_AUTH_REASON_AZURE_FAILED,
                reason_long='Azure directory ID mismatch',
            )

            return flask.abort(401)

        valid, azure_groups = sso.verify_azure(username)
        if not valid:
            journal.entry(
                journal.SSO_AUTH_FAILURE,
                user_name=username,
                remote_address=remote_addr,
                reason=journal.SSO_AUTH_REASON_AZURE_FAILED,
                reason_long='Azure authentication failed',
            )

            return flask.abort(401)

        org_id = settings.app.sso_org

        valid, org_id_new, groups = sso.plugin_sso_authenticate(
            sso_type='azure',
            user_name=username,
            user_email=email,
            remote_ip=remote_addr,
        )
        if valid:
            org_id = org_id_new or org_id
        else:
            logger.error('Azure plugin authentication not valid', 'sso',
                username=username,
            )

            journal.entry(
                journal.SSO_AUTH_FAILURE,
                user_name=username,
                remote_address=remote_addr,
                reason=journal.SSO_AUTH_REASON_PLUGIN_FAILED,
                reason_long='Azure plugin authentication failed',
            )

            return flask.abort(401)
        groups = set(groups or [])

        if settings.app.sso_azure_mode == 'groups':
            groups = groups | set(azure_groups)
        else:
            not_found = False
            azure_groups = sorted(azure_groups)
            for org_name in azure_groups:
                org = organization.get_by_name(
                    utils.filter_unicode(org_name),
                    fields=('_id'),
                )
                if org:
                    not_found = False
                    org_id = org.id
                    break
                else:
                    not_found = True

            if not_found:
                logger.warning('Supplied org names do not exists',
                    'sso',
                    sso_type=doc.get('type'),
                    user_name=username,
                    user_email=email,
                    org_names=azure_groups,
                )
    elif doc.get('type') == AUTHZERO_AUTH:
        username = params.get('username')[0]
        email = None

        valid, authzero_groups = sso.verify_authzero(username)
        if not valid:
            journal.entry(
                journal.SSO_AUTH_FAILURE,
                user_name=username,
                remote_address=remote_addr,
                reason=journal.SSO_AUTH_REASON_AUTHZERO_FAILED,
                reason_long='Auth0 authentication failed',
            )

            return flask.abort(401)

        org_id = settings.app.sso_org

        valid, org_id_new, groups = sso.plugin_sso_authenticate(
            sso_type='authzero',
            user_name=username,
            user_email=email,
            remote_ip=remote_addr,
        )
        if valid:
            org_id = org_id_new or org_id
        else:
            logger.error('Auth0 plugin authentication not valid', 'sso',
                username=username,
            )

            journal.entry(
                journal.SSO_AUTH_FAILURE,
                user_name=username,
                remote_address=remote_addr,
                reason=journal.SSO_AUTH_REASON_PLUGIN_FAILED,
                reason_long='Auth0 plugin authentication failed',
            )

            return flask.abort(401)
        groups = set(groups or [])

        if settings.app.sso_authzero_mode == 'groups':
            groups = groups | set(authzero_groups)
        else:
            not_found = False
            authzero_groups = sorted(authzero_groups)
            for org_name in authzero_groups:
                org = organization.get_by_name(
                    utils.filter_unicode(org_name),
                    fields=('_id'),
                )
                if org:
                    not_found = False
                    org_id = org.id
                    break
                else:
                    not_found = True

            if not_found:
                logger.warning('Supplied org names do not exists',
                    'sso',
                    sso_type=doc.get('type'),
                    user_name=username,
                    user_email=email,
                    org_names=authzero_groups,
                )
    else:
        logger.error('Unknown sso type', 'sso',
            sso_type=doc.get('type'),
        )
        return flask.abort(401)

    if DUO_AUTH in sso_mode:
        token = utils.generate_secret()

        tokens_collection = mongo.get_collection('sso_tokens')
        tokens_collection.insert({
            '_id': token,
            'type': DUO_AUTH,
            'username': username,
            'email': email,
            'org_id': org_id,
            'groups': list(groups) if groups else None,
            'timestamp': utils.now(),
        })

        duo_page = static.StaticFile(settings.conf.www_path,
            'duo.html', cache=False, gzip=False)

        sso_duo_mode = settings.app.sso_duo_mode
        if sso_duo_mode == 'passcode':
            duo_mode = 'passcode'
        elif sso_duo_mode == 'phone':
            duo_mode = 'phone'
        else:
            duo_mode = 'push'

        body_class = duo_mode
        if settings.app.theme == 'dark':
            body_class += ' dark'

        duo_page.data = duo_page.data.replace('<%= body_class %>', body_class)
        duo_page.data = duo_page.data.replace('<%= token %>', token)
        duo_page.data = duo_page.data.replace('<%= duo_mode %>', duo_mode)

        return duo_page.get_response()

    if YUBICO_AUTH in sso_mode:
        token = utils.generate_secret()

        tokens_collection = mongo.get_collection('sso_tokens')
        tokens_collection.insert({
            '_id': token,
            'type': YUBICO_AUTH,
            'username': username,
            'email': email,
            'org_id': org_id,
            'groups': list(groups) if groups else None,
            'timestamp': utils.now(),
        })

        yubico_page = static.StaticFile(settings.conf.www_path,
            'yubico.html', cache=False, gzip=False)

        if settings.app.theme == 'dark':
            yubico_page.data = yubico_page.data.replace(
                '<body>', '<body class="dark">')
        yubico_page.data = yubico_page.data.replace('<%= token %>', token)

        return yubico_page.get_response()

    return _validate_user(username, email, sso_mode, org_id, groups,
        remote_addr, http_redirect=True)
예제 #56
0
 def otp_cache_collection(cls):
     return mongo.get_collection('otp_cache')
예제 #57
0
def sso_duo_post():
    remote_addr = utils.get_remote_addr()
    sso_mode = settings.app.sso
    token = utils.filter_str(flask.request.json.get('token')) or None
    passcode = utils.filter_str(flask.request.json.get('passcode')) or ''

    if sso_mode not in (DUO_AUTH, AZURE_DUO_AUTH, GOOGLE_DUO_AUTH,
            SLACK_DUO_AUTH, SAML_DUO_AUTH, SAML_OKTA_DUO_AUTH,
            SAML_ONELOGIN_DUO_AUTH, RADIUS_DUO_AUTH):
        return flask.abort(404)

    if not token:
        return utils.jsonify({
            'error': TOKEN_INVALID,
            'error_msg': TOKEN_INVALID_MSG,
        }, 401)

    tokens_collection = mongo.get_collection('sso_tokens')
    doc = tokens_collection.find_and_modify(query={
        '_id': token,
    }, remove=True)
    if not doc or doc['_id'] != token or doc['type'] != DUO_AUTH:
        journal.entry(
            journal.SSO_AUTH_FAILURE,
            remote_address=remote_addr,
            reason=journal.SSO_AUTH_REASON_INVALID_TOKEN,
            reason_long='Invalid Duo authentication token',
        )

        return utils.jsonify({
            'error': TOKEN_INVALID,
            'error_msg': TOKEN_INVALID_MSG,
        }, 401)

    username = doc['username']
    email = doc['email']
    org_id = doc['org_id']
    groups = set(doc['groups'] or [])

    if settings.app.sso_duo_mode == 'passcode':
        duo_auth = sso.Duo(
            username=username,
            factor=settings.app.sso_duo_mode,
            remote_ip=remote_addr,
            auth_type='Key',
            passcode=passcode,
        )
        valid = duo_auth.authenticate()
        if not valid:
            logger.warning('Duo authentication not valid', 'sso',
                username=username,
            )

            journal.entry(
                journal.SSO_AUTH_FAILURE,
                username=username,
                remote_address=remote_addr,
                reason=journal.SSO_AUTH_REASON_DUO_FAILED,
                reason_long='Duo passcode authentication failed',
            )

            return utils.jsonify({
                'error': PASSCODE_INVALID,
                'error_msg': PASSCODE_INVALID_MSG,
            }, 401)
    else:
        duo_auth = sso.Duo(
            username=username,
            factor=settings.app.sso_duo_mode,
            remote_ip=remote_addr,
            auth_type='Key',
        )
        valid = duo_auth.authenticate()
        if not valid:
            logger.warning('Duo authentication not valid', 'sso',
                username=username,
            )

            journal.entry(
                journal.SSO_AUTH_FAILURE,
                remote_address=remote_addr,
                reason=journal.SSO_AUTH_REASON_DUO_FAILED,
                reason_long='Duo authentication failed',
            )

            return utils.jsonify({
                'error': DUO_FAILED,
                'error_msg': DUO_FAILED_MSG,
            }, 401)

    valid, org_id_new, groups2 = sso.plugin_sso_authenticate(
        sso_type='duo',
        user_name=username,
        user_email=email,
        remote_ip=remote_addr,
    )
    if valid:
        org_id = org_id_new or org_id
    else:
        logger.warning('Duo plugin authentication not valid', 'sso',
            username=username,
        )

        journal.entry(
            journal.SSO_AUTH_FAILURE,
            user_name=username,
            remote_address=remote_addr,
            reason=journal.SSO_AUTH_REASON_PLUGIN_FAILED,
            reason_long='Duo plugin authentication failed',
        )

        return flask.abort(401)

    groups = groups | set(groups2 or [])

    return _validate_user(username, email, sso_mode, org_id, groups,
        remote_addr)
예제 #58
0
 def collection(cls):
     return mongo.get_collection('users')
예제 #59
0
파일: user.py 프로젝트: pichuang/pritunl
def user_get(org_id, user_id=None, page=None):
    org = organization.get_by_id(org_id)
    if user_id:
        return utils.jsonify(org.get_user(user_id).dict())

    page = flask.request.args.get('page', None)
    page = int(page) if page else page
    search = flask.request.args.get('search', None)
    limit = int(flask.request.args.get('limit', settings.user.page_count))
    otp_auth = False
    dns_mapping = False
    server_count = 0
    servers = []

    for svr in org.iter_servers(fields=('name', 'otp_auth', 'dns_mapping')):
        servers.append(svr)
        server_count += 1
        if svr.otp_auth:
            otp_auth = True
        if svr.dns_mapping:
            dns_mapping = True

    users = []
    users_id = []
    users_data = {}
    users_servers = {}
    fields = (
        'organization',
        'organization_name',
        'name',
        'email',
        'pin',
        'type',
        'auth_type',
        'otp_secret',
        'disabled',
        'bypass_secondary',
        'dns_servers',
        'dns_suffix',
    )
    for usr in org.iter_users(page=page,
                              search=search,
                              search_limit=limit,
                              fields=fields):
        users_id.append(usr.id)

        user_dict = usr.dict()
        user_dict['gravatar'] = settings.user.gravatar
        user_dict['audit'] = settings.app.auditing == ALL
        user_dict['status'] = False
        user_dict['sso'] = settings.app.sso
        user_dict['otp_auth'] = otp_auth
        if dns_mapping:
            user_dict['dns_mapping'] = (
                '%s.%s.vpn' % (usr.name.split('@')[0], org.name)).lower()
        else:
            user_dict['dns_mapping'] = None
        user_dict['network_links'] = []

        users_data[usr.id] = user_dict
        users_servers[usr.id] = {}

        server_data = []
        for svr in servers:
            data = {
                'id': svr.id,
                'name': svr.name,
                'status': False,
                'server_id': svr.id,
                'device_name': None,
                'platform': None,
                'real_address': None,
                'virt_address': None,
                'virt_address6': None,
                'connected_since': None
            }
            server_data.append(data)
            users_servers[usr.id][svr.id] = data

        user_dict['servers'] = sorted(server_data, key=lambda x: x['name'])

        users.append(user_dict)

    clients_collection = mongo.get_collection('clients')
    for doc in clients_collection.find({
            'user_id': {
                '$in': users_id
            },
    }):
        server_data = users_servers[doc['user_id']].get(doc['server_id'])
        if not server_data:
            continue

        users_data[doc['user_id']]['status'] = True

        if server_data['status']:
            server_data = {
                'name': server_data['name'],
            }
            append = True
        else:
            append = False

        virt_address6 = doc.get('virt_address6')
        if virt_address6:
            server_data['virt_address6'] = virt_address6.split('/')[0]

        server_data['id'] = doc['_id']
        server_data['status'] = True
        server_data['server_id'] = server_data['id']
        server_data['device_name'] = doc['device_name']
        server_data['platform'] = doc['platform']
        server_data['real_address'] = doc['real_address']
        server_data['virt_address'] = doc['virt_address'].split('/')[0]
        server_data['connected_since'] = doc['connected_since']

        if append:
            svrs = users_data[doc['user_id']]['servers']
            svrs.append(server_data)
            users_data[doc['user_id']]['servers'] = sorted(
                svrs, key=lambda x: x['name'])

    net_link_collection = mongo.get_collection('users_net_link')
    for doc in net_link_collection.find({
            'user_id': {
                '$in': users_id
            },
    }):
        users_data[doc['user_id']]['network_links'].append(doc['network'])

    ip_addrs_iter = server.multi_get_ip_addr(org_id, users_id)
    for user_id, server_id, addr, addr6 in ip_addrs_iter:
        server_data = users_servers[user_id].get(server_id)
        if server_data:
            if not server_data['virt_address']:
                server_data['virt_address'] = addr
            if not server_data['virt_address6']:
                server_data['virt_address6'] = addr6

    if page is not None:
        return utils.jsonify({
            'page': page,
            'page_total': org.page_total,
            'server_count': server_count,
            'users': users,
        })
    elif search is not None:
        return utils.jsonify({
            'search':
            search,
            'search_more':
            limit < org.last_search_count,
            'search_limit':
            limit,
            'search_count':
            org.last_search_count,
            'search_time':
            round((time.time() - flask.g.start), 4),
            'server_count':
            server_count,
            'users':
            users,
        })
    else:
        return utils.jsonify(users)
예제 #60
0
 def routes_collection(cls):
     return mongo.get_collection('routes_reserve')