Exemple #1
0
def register_shared_folder(shared_folder, records):
    # type: (shared_folder.SharedFolder, dict) -> bytes

    shared_folder_key = api.generate_aes_key()
    sf = {
        'shared_folder_uid': shared_folder.shared_folder_uid,
        'key_type': 1,
        'shared_folder_key': api.encrypt_aes(shared_folder_key, _USER_DATA_KEY),
        'name': api.encrypt_aes(shared_folder.name.encode('utf-8'), shared_folder_key),
        'is_account_folder': False,
        'manage_records': False,
        'manage_users': False,
        'default_manage_records': True,
        'default_manage_users': True,
        'default_can_edit': True,
        'default_can_share': True,
        'full_sync': True,
        'records': [{
            'record_uid': x[0],
            'record_key': api.encrypt_aes(x[1], shared_folder_key),
            'can_share': False,
            'can_edit': False
        } for x in records.items()],
        'users': [{
            'username': _USER_NAME,
            'manage_records': True,
            'manage_users': True
        }],
        'revision': 5
    }
    _SHARED_FOLDERS.append(sf)

    return shared_folder_key
Exemple #2
0
def register_record(record, key_type=None):
    # type: (record.Record, int or None) -> bytes
    data = {
        'title': record.title or '',
        'secret1': record.login or '',
        'secret2': record.password or '',
        'link': record.login_url or '',
        'notes': record.notes or '',
        'custom': record.custom_fields or '',
        'folder': record.folder or ''
    }

    extra = None
    udata = None
    if record.attachments:
        extra = {'files': record.attachments}
        udata = {'file_id': [x['id'] for x in record.attachments]}

    record_key = api.generate_aes_key() if key_type != 0 else _USER_DATA_KEY
    rec_object = {
        'record_uid': record.record_uid,
        'revision': record.revision if
        (0 < record.revision <= _REVISION) else _REVISION,
        'version': 2 if key_type != 0 else 1,
        'shared': key_type not in [0, 1],
        'data': api.encrypt_aes(json.dumps(data).encode('utf-8'), record_key),
    }
    if extra:
        rec_object['extra'] = api.encrypt_aes(
            json.dumps(extra).encode('utf-8'), record_key)
    if udata:
        rec_object['udata'] = udata

    _RECORDS.append(rec_object)

    meta_data = {
        'record_uid': record.record_uid,
        'owner': key_type in [0, 1],
        'can_share': key_type == 1,
        'can_edit': key_type == 1,
        'record_key_type': key_type
    }

    if key_type == 0:
        _RECORD_METADATA.append(meta_data)
    if key_type == 1:
        meta_data['record_key'] = utils.base64_url_encode(
            crypto.encrypt_aes_v1(record_key, _USER_DATA_KEY))
        _RECORD_METADATA.append(meta_data)
    elif key_type == 2:
        meta_data['record_key'] = utils.base64_url_encode(
            crypto.encrypt_rsa(record_key, _IMPORTED_PUBLIC_KEY))
        _RECORD_METADATA.append(meta_data)

    return record_key
Exemple #3
0
def register_team(team, key_type, sfs=None):
    # type: (team.Team, int, dict) -> bytes
    team_key = api.generate_aes_key()
    t = {
        'team_uid':
        team.team_uid,
        'name':
        team.name,
        'team_key_type':
        key_type,
        'team_key':
        api.encrypt_aes(team_key, _USER_DATA_KEY)
        if key_type == 1 else api.encrypt_rsa(team_key, _IMPORTED_PUBLIC_KEY),
        'team_private_key':
        api.encrypt_aes(_DER_PRIVATE_KEY, team_key),
        'restrict_edit':
        team.restrict_edit,
        'restrict_share':
        team.restrict_share,
        'restrict_view':
        team.restrict_view,
    }
    _TEAMS.append(t)

    if sfs:
        t['shared_folder_keys'] = [{
            'shared_folder_uid':
            x[0],
            'key_type':
            1,
            'shared_folder_key':
            api.encrypt_aes(x[1], team_key)
        } for x in sfs.items()]

        sf_uids = set()
        for uid in sfs:
            sf_uids.add(uid)
        for sf in _SHARED_FOLDERS:
            if sf['shared_folder_uid'] in sf_uids:
                if 'teams' not in sf:
                    sf['teams'] = []
                sf['teams'].append({
                    'team_uid': team.team_uid,
                    'name': team.name,
                    'manage_records': key_type == 1,
                    'manage_users': key_type == 1
                })

    return team_key
Exemple #4
0
def construct_update_rec_req(params, preexisting_record_hash, rec_to_update):
    """
    Build a rec_req for rec_to_import.

    Based on https://keeper.atlassian.net/wiki/spaces/KA/pages/13238307/record+update+-+deprecated
    and upload_attachment(params, attachments), which appears elsewhere in this file.

    We're not doing records_update yet, because it requires v3 and we don't do v3 yet.
    """
    # FIXME: This assert probably can be sorted out during code review.
    assert len(rec_to_update.folders) == 1, "What should we do with records that aren't in exactly one folder?"
    data = {
        'folder': rec_to_update.folders[0].get_folder_path(),
        'title': rec_to_update.title,
        'secret1': rec_to_update.login,
        'secret2': rec_to_update.password,
        'link': rec_to_update.login_url,
        # We add notes and custom a little later.
    }

    current_rec = params.record_cache[rec_to_update.uid]
    # This should always exist, because this is an import --update.
    preexisting_record = params.record_cache[rec_to_update.uid]

    preexisting_record_fields_str = preexisting_record['data_unencrypted'].decode('utf-8')
    preexisting_record_fields_dict = json.loads(preexisting_record_fields_str)

    # These fields need to be preserved - per our design.
    field_tuple = ('notes', 'custom')
    for field_name in field_tuple:
        # these need to be preserved
        if field_name in preexisting_record_fields_dict:
            data[field_name] = preexisting_record_fields_dict[field_name]

    unencrypted_key = preexisting_record['record_key_unencrypted']

    encrypted_data = api.encrypt_aes(json.dumps(data).encode('utf-8'), unencrypted_key)
    record_key = api.encrypt_aes(unencrypted_key, params.data_key)
    one_rec_req = {
        'record_uid': rec_to_update.uid,
        'record_key': record_key,
        'record_key_unencrypted': base64.urlsafe_b64encode(unencrypted_key).decode('utf-8'),
        'data': encrypted_data,
        'version': 2,
        'client_modified_time': api.current_milli_time(),
        'revision': current_rec['revision']
    }
    return one_rec_req
    def communicate_success(params, request):
        # type: (any, dict) -> dict
        if request['command'] == 'enterprise_allocate_ids':
            return enterprise_allocate_ids(params, request)

        rs = {'result': 'success', 'result_code': '', 'message': ''}
        if request['command'] == 'team_get_keys':
            rs['keys'] = [{
                'team_uid':
                x,
                'key':
                api.encrypt_aes(ent_env.team_key, vault_env.data_key),
                'type':
                1
            } for x in request['teams']]
            return rs
        if request['command'] == 'public_keys':
            rs['public_keys'] = [{
                'key_owner': x,
                'public_key': vault_env.encoded_public_key
            } for x in request['key_owners']]
            return rs

        cmd = TestEnterprise.expected_commands.pop(0)
        if cmd == request['command']:
            return rs
        if request['command'] == 'execute':
            request = request['requests'][0]
            if cmd == request['command']:
                return rs
        raise Exception()
    def test_role_create(self):
        param1 = TestCrossEnterpriseCommands.params1  # type: KeeperParams
        param2 = TestCrossEnterpriseCommands.params2  # type: KeeperParams

        ent1_parent_id = [x['node_id'] for x in param1.enterprise['nodes']][0]

        cmd = EnterpriseRoleCommand()
        ent2_role_id = cmd.get_enterprise_id(param2)
        dt = {"displayname": "Role"}
        encrypted_data = api.encrypt_aes(
            json.dumps(dt).encode('utf-8'),
            param2.enterprise['unencrypted_tree_key'])
        rq = {
            "command": "role_add",
            "role_id": ent2_role_id,
            "node_id": ent1_parent_id,
            "encrypted_data": encrypted_data,
            "visible_below": True,
            "new_user_inherit": False
        }
        failed = False
        try:
            api.communicate(param2, rq)
        except KeeperApiError as err:
            failed = True
            self.assertEqual(err.result_code, "bad_inputs_node_id")
        self.assertTrue(failed)
Exemple #7
0
    def test_download_attachment_command(self):
        params = get_synced_params()
        cmd = record.RecordDownloadAttachmentCommand()

        records = [
            x for x in params.record_cache.values()
            if len(x['extra_unencrypted']) > 10
        ]
        rec = records[0]
        record_uid = rec['record_uid']
        extra = json.loads(rec['extra_unencrypted'].decode('utf-8'))
        attachments = {}
        for file in extra['files']:
            key = base64.urlsafe_b64decode(file['key'] + '==')
            body_encoded = api.encrypt_aes(os.urandom(file['size']), key)
            attachments['https://keepersecurity.com/files/' +
                        file['id']] = body_encoded

        def request_download(rq):
            self.assertEqual(rq['command'], 'request_download')
            return {
                'downloads': [{
                    'url': 'https://keepersecurity.com/files/' + x
                } for x in rq['file_ids']]
            }

        def request_http_get(url, **kwargs):
            attachment = mock.Mock()
            attachment.raw = io.BytesIO(
                base64.urlsafe_b64decode(attachments[url] + '=='))
            return attachment

        with mock.patch('requests.get', side_effect=request_http_get) as mock_get, \
                mock.patch('builtins.open', mock.mock_open()), mock.patch('os.path.abspath', return_value='/file_name') as mock_abspath:
            KeeperApiHelper.communicate_expect([request_download])
            cmd.execute(params, record=record_uid)
            self.assertTrue(KeeperApiHelper.is_expect_empty())
            mock_get.assert_called()
            mock_abspath.assert_called()
Exemple #8
0
def generate_data():
    r1 = record.Record()
    r1.record_uid = api.generate_record_uid()
    r1.folder = 'Old Folder'
    r1.title = 'Record 1'
    r1.login = '******'
    r1.password = '******'
    r1.login_url = 'https://keepersecurity.com/1'
    r1.set_field('field1', 'value1')
    r1.notes = 'note1'
    r1.attachments = [{
        'name':
        'Attachment 1',
        'key':
        base64.urlsafe_b64encode(
            api.generate_aes_key()).decode('utf-8').rstrip('='),
        'id':
        'ABCDEFGH',
        'size':
        1000
    }]
    r1.revision = 1
    r1_key = register_record(r1, 1)

    r2 = record.Record()
    r2.record_uid = api.generate_record_uid()
    r2.title = 'Record 2'
    r2.login = '******'
    r2.password = '******'
    r2.login_url = 'https://keepersecurity.com/2'
    r2.set_field('field2', 'value2')
    r2.notes = 'note2'
    r2.revision = 2
    r2_key = register_record(r2, 2)

    register_records_to_folder(None, [r1.record_uid, r2.record_uid])

    r3 = record.Record()
    r3.record_uid = api.generate_record_uid()
    r3.title = 'Record 3'
    r3.login = '******'
    r3.password = '******'
    r3.login_url = 'https://keepersecurity.com/3'
    r3.revision = 3
    r3_key = register_record(r3)

    sf1 = shared_folder.SharedFolder()
    sf1.shared_folder_uid = api.generate_record_uid()
    sf1.default_manage_records = False
    sf1.default_manage_users = False
    sf1.default_can_edit = False
    sf1.default_can_share = False
    sf1.name = 'Shared Folder 1'
    sf1_key = register_shared_folder(sf1, {r3.record_uid: r3_key})
    register_records_to_folder(sf1.shared_folder_uid, [r3.record_uid])
    _USER_FOLDER_SHARED_FOLDER.append(
        {'shared_folder_uid': sf1.shared_folder_uid})

    t1 = team.Team()
    t1.team_uid = api.generate_record_uid()
    t1.name = 'Team 1'
    t1.restrict_edit = True
    t1.restrict_share = True
    t1.restrict_view = False

    register_team(t1, 1, {sf1.shared_folder_uid: sf1_key})

    folder_key = api.generate_aes_key()
    _USER_FOLDERS.append({
        'folder_uid':
        api.generate_record_uid(),
        'key_type':
        1,
        'user_folder_key':
        api.encrypt_aes(folder_key, _USER_DATA_KEY),
        'revision':
        200,
        'type':
        'user_folder',
        'data':
        api.encrypt_aes(
            json.dumps({
                'name': 'User Folder 1'
            }).encode('utf-8'), folder_key)
    })
Exemple #9
0
38JuMwHChTK29H9EOlqbOOuzYA1ENzL88hELpe+kl4RmpNS94BJDssikFFbjoiAV
fwIDAQAB
-----END PUBLIC KEY-----
'''

_SESSION_TOKEN = base64.urlsafe_b64encode(
    os.urandom(64)).decode('utf-8').strip('=')
_DEVICE_ID = os.urandom(64)

_2FA_ONE_TIME_TOKEN = '123456'
_2FA_DEVICE_TOKEN = base64.urlsafe_b64encode(
    os.urandom(32)).decode('utf-8').strip('=')

_IMPORTED_PRIVATE_KEY = RSA.importKey(_USER_PRIVATE_KEY, _PRIVATE_KEY_PASSWORD)
_DER_PRIVATE_KEY = _IMPORTED_PRIVATE_KEY.export_key(format='DER')
_ENCRYPTED_PRIVATE_KEY = api.encrypt_aes(_DER_PRIVATE_KEY, _USER_DATA_KEY)

_IMPORTED_PUBLIC_KEY = RSA.importKey(_USER_PUBLIC_KEY)
_ENCODED_PUBLIC_KEY = base64.urlsafe_b64encode(
    _IMPORTED_PUBLIC_KEY.export_key(format='DER')).decode('utf-8').strip('=')

_V2_DERIVED_KEY = rest_api.derive_key_v2('data_key', _USER_PASSWORD,
                                         _USER_SALT, _USER_ITERATIONS)
_dk = rest_api.encrypt_aes(_USER_DATA_KEY, _V2_DERIVED_KEY)
_ENCRYPTED_DATA_KEY = base64.urlsafe_b64encode(_dk).decode('utf-8').strip()

_V1_DERIVED_KEY = api.derive_key(_USER_PASSWORD, _USER_SALT, _USER_ITERATIONS)
_enc_iter = int.to_bytes(_USER_ITERATIONS,
                         length=3,
                         byteorder='big',
                         signed=False)
def get_enterprise_data(params, rq):
    # type: (KeeperParams, dict) -> dict

    encrypted_tree_key = api.encrypt_aes(
        _TREE_KEY, params.data_key) if _USE_DATA_KEY else api.encrypt_rsa(
            _TREE_KEY, _VAULT_ENV.public_key)
    tree_key_type = 1 if _USE_DATA_KEY else 2
    rs = {
        'result': 'success',
        'result_code': '',
        'message': '',
        'enterprise_name': 'Enterprise 1',
        'tree_key': encrypted_tree_key,
        'key_type_id': tree_key_type
    }
    includes = set(rq.get('include') or [])
    ent_id = _ENTERPRISE_ID << 32
    if 'nodes' in includes:
        rs['nodes'] = [{
            'node_id':
            _NODE1_ID,
            'encrypted_data':
            api.encrypt_aes(
                json.dumps({
                    'displayname': 'Root node'
                }).encode('utf-8'), _TREE_KEY)
        }, {
            'node_id':
            _NODE2_ID,
            'parent_id':
            _NODE1_ID,
            'encrypted_data':
            api.encrypt_aes(
                json.dumps({
                    'displayname': 'Sub node 1'
                }).encode('utf-8'), _TREE_KEY)
        }]
    if 'users' in includes:
        rs['users'] = [{
            'enterprise_user_id':
            _USER1_ID,
            'node_id':
            _NODE1_ID,
            'username':
            params.user,
            'encrypted_data':
            api.encrypt_aes(
                json.dumps({
                    'displayname': 'User 1'
                }).encode('utf-8'), _TREE_KEY),
            'status':
            'active',
            'lock':
            0
        }, {
            'enterprise_user_id':
            _USER2_ID,
            'node_id':
            _NODE2_ID,
            'username':
            _USER2_EMAIL,
            'encrypted_data':
            api.encrypt_aes(
                json.dumps({
                    'displayname': 'User 2'
                }).encode('utf-8'), _TREE_KEY),
            'status':
            'active',
            'lock':
            1
        }]
    if 'roles' in includes:
        rs['roles'] = [{
            'role_id':
            _ROLE1_ID,
            'node_id':
            _NODE1_ID,
            'encrypted_data':
            api.encrypt_aes(
                json.dumps({
                    'displayname': _ROLE1_NAME
                }).encode('utf-8'), _TREE_KEY),
            'visible_below':
            True,
            'new_user_inherit':
            True
        }]
    if 'role_users' in includes:
        rs['role_users'] = [{
            'role_id': ent_id + 301,
            'enterprise_user_id': _USER1_ID
        }]
    if 'teams' in includes:
        rs['teams'] = [{
            'team_uid': _TEAM1_UID,
            'name': _TEAM1_NAME,
            'node_id': _NODE1_ID,
            'restrict_sharing': False,
            'restrict_edit': False,
            'restrict_view': False,
        }, {
            'team_uid': _TEAM2_UID,
            'name': _TEAM2_NAME,
            'node_id': _NODE1_ID,
            'restrict_sharing': False,
            'restrict_edit': False,
            'restrict_view': False,
        }]
    if 'team_users' in includes:
        rs['team_users'] = [{
            'team_uid': _TEAM1_UID,
            'enterprise_user_id': _USER1_ID,
            'user_type': 1
        }, {
            'team_uid': _TEAM1_UID,
            'enterprise_user_id': _USER2_ID,
            'user_type': 1
        }]
    return rs
Exemple #11
0
def get_enterprise_data(params):
    # type: (KeeperParams) -> dict

    encrypted_tree_key = crypto.encrypt_aes_v1(_TREE_KEY, params.data_key) \
        if _USE_DATA_KEY else crypto.encrypt_rsa(_TREE_KEY, _VAULT_ENV.public_key)
    tree_key_type = 1 if _USE_DATA_KEY else 2
    rs = {
        'result': 'success',
        'result_code': '',
        'message': '',
        'enterprise_name': 'Enterprise 1',
        'tree_key': utils.base64_url_encode(encrypted_tree_key),
        'key_type_id': tree_key_type
    }

    rs['nodes'] = [
        {
            'node_id':  _NODE1_ID,
            'encrypted_data': api.encrypt_aes(json.dumps({}).encode('utf-8'), _TREE_KEY)
        },
        {
            'node_id': _NODE2_ID,
            'parent_id': _NODE1_ID,
            'encrypted_data': api.encrypt_aes(json.dumps({'displayname': 'Sub node 1'}).encode('utf-8'), _TREE_KEY)
        }
    ]
    rs['users'] = [
        {
            'enterprise_user_id':  _USER1_ID,
            'node_id': _NODE1_ID,
            'username': params.user,
            'encrypted_data': api.encrypt_aes(json.dumps({'displayname': 'User 1'}).encode('utf-8'), _TREE_KEY),
            'status': 'active',
            'lock': 0
        },
        {
            'enterprise_user_id':  _USER2_ID,
            'node_id': _NODE2_ID,
            'username': _USER2_EMAIL,
            'encrypted_data': api.encrypt_aes(json.dumps({'displayname': 'User 2'}).encode('utf-8'), _TREE_KEY),
            'status': 'active',
            'lock': 1
        }

    ]
    rs['roles'] = [
        {
            'role_id': _ROLE1_ID,
            'node_id': _NODE1_ID,
            'encrypted_data': api.encrypt_aes(json.dumps({'displayname': _ROLE1_NAME}).encode('utf-8'), _TREE_KEY),
            'visible_below': True,
            'new_user_inherit': True
        }
    ]
    rs['managed_nodes'] = [
        {
            'role_id': _ROLE1_ID,
            'managed_node_id': _NODE1_ID,
            'cascade_node_management': True,
        }
    ]
    rs['role_users'] = [
        {
            'role_id': _ROLE1_ID,
            'enterprise_user_id':  _USER1_ID
        }
    ]
    rs['teams'] = [
        {
            'team_uid': _TEAM1_UID,
            'name': _TEAM1_NAME,
            'node_id': _NODE1_ID,
            'restrict_sharing': False,
            'restrict_edit': False,
            'restrict_view': False,
        },
        {
            'team_uid': _TEAM2_UID,
            'name': _TEAM2_NAME,
            'node_id': _NODE1_ID,
            'restrict_sharing': False,
            'restrict_edit': False,
            'restrict_view': False,
        }
    ]
    rs['team_users'] = [
        {
            'team_uid': _TEAM1_UID,
            'enterprise_user_id': _USER1_ID,
            'user_type': 1
        },
        {
            'team_uid': _TEAM1_UID,
            'enterprise_user_id': _USER2_ID,
            'user_type': 1
        }
    ]
    return rs
Exemple #12
0
def construct_import_rec_req(params, preexisting_record_hash, rec_to_import):
    """Build a rec_req for rec_to_import."""
    r_hash = build_record_hash(record=rec_to_import, tokenize_gen=tokenize_full_import_record)

    if r_hash in preexisting_record_hash:
        # Nothing to do.  We already have this identical record.
        return None
    else:
        rec_to_import.uid = api.generate_record_uid()
        preexisting_record_hash[r_hash] = rec_to_import.uid
        record_key = os.urandom(32)
        rec_req = folder_pb2.RecordRequest()
        rec_req.recordUid = base64.urlsafe_b64decode(rec_to_import.uid + '==')
        rec_req.recordType = 0
        rec_req.encryptedRecordKey = base64.urlsafe_b64decode(api.encrypt_aes(record_key, params.data_key) + '==')
        rec_req.howLongAgo = 0
        rec_req.folderType = 1

        folder_uid = None
        if rec_to_import.folders:
            folder_uid = rec_to_import.folders[0].uid
        if folder_uid:
            if folder_uid in params.folder_cache:
                folder = params.folder_cache[folder_uid]
                if folder.type in {BaseFolderNode.SharedFolderType, BaseFolderNode.SharedFolderFolderType}:
                    rec_req.folderUid = base64.urlsafe_b64decode(folder.uid + '==')
                    rec_req.folderType = 2 if folder.type == BaseFolderNode.SharedFolderType else 3

                    sh_uid = folder.uid if folder.type == BaseFolderNode.SharedFolderType else folder.shared_folder_uid
                    sf = params.shared_folder_cache[sh_uid]
                    rec_req.encryptedRecordFolderKey = base64.urlsafe_b64decode(
                        api.encrypt_aes(record_key, sf['shared_folder_key_unencrypted']) + '=='
                    )
                else:
                    rec_req.folderType = 1
                    if folder.type != BaseFolderNode.RootFolderType:
                        rec_req.folderUid = base64.urlsafe_b64decode(folder.uid + '==')

        custom_fields = []
        totp = None
        if rec_to_import.custom_fields:
            for cf in rec_to_import.custom_fields:
                if cf == TWO_FACTOR_CODE:
                    totp = rec_to_import.custom_fields[cf]
                else:
                    custom_fields.append({
                        'name': cf,
                        'value': rec_to_import.custom_fields[cf]
                    })

        data = {
            'title': rec_to_import.title or '',
            'secret1': rec_to_import.login or '',
            'secret2': rec_to_import.password or '',
            'link': rec_to_import.login_url or '',
            'notes': rec_to_import.notes or '',
            'custom': custom_fields
        }
        rec_req.recordData = base64.urlsafe_b64decode(api.encrypt_aes(json.dumps(data).encode('utf-8'), record_key) + '==')
        if totp:
            extra = {
                'fields': [
                    {
                        'id': api.generate_record_uid(),
                        'field_type': 'totp',
                        'field_title': 'Two-Factor Code',
                        'type': 0,
                        'data': totp
                    }]
            }
            rec_req.extra = base64.urlsafe_b64decode(api.encrypt_aes(json.dumps(extra).encode('utf-8'), record_key) + '==')
        return rec_req
Exemple #13
0
def prepare_record_link(params, records):
    record_folders = {}  # type: [str, [str]]
    record_links = []
    for rec in records:
        if rec.uid:
            if rec.uid in params.record_cache:
                if rec.uid in record_folders:
                    folder_ids = record_folders[rec.uid]
                else:
                    folder_ids = list(find_folders(params, rec.uid))
                    record_folders[rec.uid] = folder_ids
                record = params.record_cache[rec.uid]
                for fol in rec.folders or [None]:
                    folder_uid = fol.uid if fol else ''
                    if folder_uid and folder_uid not in params.folder_cache:
                        continue
                    if folder_uid in folder_ids:
                        continue
                    if len(folder_ids) > 0:
                        folder_ids.append(folder_uid)
                        src_folder = params.folder_cache[folder_ids[0]]
                        dst_folder = params.folder_cache[
                            folder_uid] if folder_uid in params.folder_cache else params.root_folder
                        req = {
                            'command':
                            'move',
                            'to_type':
                            dst_folder.type
                            if dst_folder.type != BaseFolderNode.RootFolderType
                            else BaseFolderNode.UserFolderType,
                            'link':
                            True,
                            'move': [],
                            'transition_keys': []
                        }
                        if dst_folder.type != BaseFolderNode.RootFolderType:
                            req['to_uid'] = dst_folder.uid
                        mo = {
                            'type':
                            'record',
                            'uid':
                            rec.uid,
                            'from_type':
                            src_folder.type
                            if src_folder.type != BaseFolderNode.RootFolderType
                            else BaseFolderNode.UserFolderType,
                            'cascade':
                            True
                        }
                        if src_folder.type != BaseFolderNode.RootFolderType:
                            mo['from_uid'] = src_folder.uid
                        req['move'].append(mo)

                        transition_key = None
                        record_key = record['record_key_unencrypted']
                        if src_folder.type in {
                                BaseFolderNode.SharedFolderType,
                                BaseFolderNode.SharedFolderFolderType
                        }:
                            if dst_folder.type in {
                                    BaseFolderNode.SharedFolderType,
                                    BaseFolderNode.SharedFolderFolderType
                            }:
                                ssf_uid = src_folder.uid if src_folder.type == BaseFolderNode.SharedFolderType else \
                                    src_folder.shared_folder_uid
                                dsf_uid = dst_folder.uid if dst_folder.type == BaseFolderNode.SharedFolderType else \
                                    dst_folder.shared_folder_uid
                                if ssf_uid != dsf_uid:
                                    shf = params.shared_folder_cache[dsf_uid]
                                    transition_key = api.encrypt_aes(
                                        record_key,
                                        shf['shared_folder_key_unencrypted'])
                            else:
                                transition_key = api.encrypt_aes(
                                    record_key, params.data_key)
                        else:
                            if dst_folder.type in {
                                    BaseFolderNode.SharedFolderType,
                                    BaseFolderNode.SharedFolderFolderType
                            }:
                                dsf_uid = dst_folder.uid if dst_folder.type == BaseFolderNode.SharedFolderType else \
                                    dst_folder.shared_folder_uid
                                shf = params.shared_folder_cache[dsf_uid]
                                transition_key = api.encrypt_aes(
                                    record_key,
                                    shf['shared_folder_key_unencrypted'])
                        if transition_key is not None:
                            req['transition_keys'].append({
                                'uid': rec.uid,
                                'key': transition_key
                            })
                        record_links.append(req)
    return record_links
Exemple #14
0
def prepare_folder_permission(params,
                              folders):  # type: (KeeperParams, list) -> list
    shared_folder_lookup = {}
    for shared_folder_uid in params.shared_folder_cache:
        path = get_folder_path(params, shared_folder_uid)
        if path:
            shared_folder_lookup[path] = shared_folder_uid

    email_pattern = re.compile(EMAIL_PATTERN)
    emails = set()
    teams = set()
    for fol in folders:
        if fol.permissions:
            for perm in fol.permissions:
                if perm.uid:
                    teams.add(perm.uid)
                    if perm.name:
                        teams.add(perm.name)
                elif perm.name:
                    match = email_pattern.match(perm.name)
                    if match:
                        if perm.name != params.user:
                            emails.add(perm.name.lower())
                    else:
                        teams.add(perm.name)

    if len(emails) > 0:
        api.load_user_public_keys(params, list(emails))

    if len(teams) > 0:
        api.load_available_teams(params)
        team_uids = set()
        for t in teams:
            team_uid = next(
                (x.get('team_uid')
                 for x in params.available_team_cache if x.get('team_uid') == t
                 or x.get('team_name').casefold() == t.casefold()), None)
            if team_uid:
                team_uids.add(team_uid)
        if len(team_uids) > 0:
            api.load_team_keys(params, list(team_uids))

    folder_permissions = []
    for fol in folders:
        shared_folder_uid = shared_folder_lookup.get(fol.path)
        if not shared_folder_uid:
            continue
        shared_folder = params.shared_folder_cache.get(shared_folder_uid)
        if not shared_folder:
            continue
        shared_folder_key = shared_folder.get('shared_folder_key_unencrypted')
        if not shared_folder_key:
            continue

        if fol.permissions:
            add_users = []
            add_teams = []
            for perm in fol.permissions:
                try:
                    if perm.uid:
                        if perm.uid in params.key_cache:
                            team_key = params.key_cache[perm.uid]
                            rq = {
                                'team_uid': perm.uid,
                                'manage_users': perm.manage_users,
                                'manage_records': perm.manage_records,
                            }
                            if type(team_key) == bytes:
                                rq['shared_folder_key'] = api.encrypt_aes(
                                    shared_folder_key, team_key)
                            else:
                                rq['shared_folder_key'] = api.encrypt_rsa(
                                    shared_folder_key, team_key)
                            add_teams.append(rq)
                            continue

                    if perm.name:
                        name = perm.name.casefold()
                        if name in params.key_cache:
                            rsa_key = params.key_cache[name]
                            rq = {
                                'username':
                                name,
                                'manage_users':
                                perm.manage_users,
                                'manage_records':
                                perm.manage_records,
                                'shared_folder_key':
                                api.encrypt_rsa(shared_folder_key, rsa_key)
                            }
                            add_users.append(rq)
                            continue

                        team_uid = next(
                            (x.get('team_uid')
                             for x in params.available_team_cache
                             if x.get('team_name').casefold() == name), None)
                        if team_uid in params.key_cache:
                            team_key = params.key_cache[team_uid]
                            rq = {
                                'team_uid': team_uid,
                                'manage_users': perm.manage_users,
                                'manage_records': perm.manage_records,
                            }
                            if type(team_key) == bytes:
                                rq['shared_folder_key'] = api.encrypt_aes(
                                    shared_folder_key, team_key)
                            else:
                                rq['shared_folder_key'] = api.encrypt_rsa(
                                    shared_folder_key, team_key)
                            add_teams.append(rq)
                            continue

                except Exception as e:
                    logging.debug(e)

            if add_teams or add_users:
                request = {
                    'command': 'shared_folder_update',
                    'operation': 'update',
                    'pt': 'Commander',
                    'shared_folder_uid': shared_folder_uid,
                    'force_update': True,
                    'add_teams': add_teams,
                    'add_users': add_users,
                }
                folder_permissions.append(request)

    return folder_permissions
Exemple #15
0
def prepare_folder_add(params, folders, records):
    folder_hash = {}
    for f_uid in params.folder_cache:
        fol = params.folder_cache[f_uid]
        h = hashlib.md5()
        hs = '{0}|{1}'.format((fol.name or '').lower(), fol.parent_uid or '')
        h.update(hs.encode())
        shared_folder_key = None
        if fol.type in {
                BaseFolderNode.SharedFolderType,
                BaseFolderNode.SharedFolderFolderType
        }:
            sf_uid = fol.shared_folder_uid if fol.type == BaseFolderNode.SharedFolderFolderType else fol.uid
            if sf_uid in params.shared_folder_cache:
                shared_folder_key = params.shared_folder_cache[sf_uid][
                    'shared_folder_key_unencrypted']
        folder_hash[h.hexdigest()] = f_uid, fol.type, shared_folder_key

    folder_add = []  # type: [folder_pb2.FolderRequest]
    if folders:
        for fol in folders:
            skip_folder = False
            parent_uid = ''
            comps = list(path_components(fol.path))
            for i in range(len(comps)):
                comp = comps[i]
                h = hashlib.md5()
                hs = '{0}|{1}'.format(comp.lower(), parent_uid)
                h.update(hs.encode())
                digest = h.hexdigest()

                is_last = False
                if i == len(comps) - 1:
                    is_last = True

                if digest not in folder_hash:
                    folder_uid = api.generate_record_uid()
                    folder_type = 'shared_folder' if is_last else 'user_folder'

                    fol_req = folder_pb2.FolderRequest()
                    fol_req.folderUid = base64.urlsafe_b64decode(folder_uid +
                                                                 '==')
                    fol_req.folderType = 2 if folder_type == 'shared_folder' else 1

                    if parent_uid:
                        fol_req.parentFolderUid = base64.urlsafe_b64decode(
                            parent_uid + '==')

                    folder_key = os.urandom(32)
                    fol_req.encryptedFolderKey = base64.urlsafe_b64decode(
                        api.encrypt_aes(folder_key, params.data_key) + '==')

                    data = {'name': comp}
                    fol_req.folderData = base64.urlsafe_b64decode(
                        api.encrypt_aes(
                            json.dumps(data).encode('utf-8'), folder_key) +
                        '==')

                    if folder_type == 'shared_folder':
                        fol_req.sharedFolderFields.encryptedFolderName = base64.urlsafe_b64decode(
                            api.encrypt_aes(comp.encode('utf-8'), folder_key) +
                            '==')
                        fol_req.sharedFolderFields.manageUsers = fol.manage_users
                        fol_req.sharedFolderFields.manageRecords = fol.manage_records
                        fol_req.sharedFolderFields.canEdit = fol.can_edit
                        fol_req.sharedFolderFields.canShare = fol.can_share

                    folder_add.append(fol_req)
                    folder_hash[
                        digest] = folder_uid, folder_type, folder_key if folder_type == 'shared_folder' else None
                else:
                    folder_uid, folder_type, folder_key = folder_hash[digest]
                    if is_last:
                        skip_folder = folder_type != 'shared_folder'
                    else:
                        skip_folder = folder_type != 'user_folder'

                parent_uid = folder_uid
                if skip_folder:
                    break

    if records:
        for rec in records:
            if rec.folders:
                for fol in rec.folders:
                    parent_uid = ''
                    parent_shared_folder_uid = None
                    parent_shared_folder_key = None
                    parent_type = ''
                    for is_domain in [True, False]:
                        path = fol.domain if is_domain else fol.path
                        if not path:
                            continue

                        comps = list(path_components(path))
                        for i in range(len(comps)):
                            comp = comps[i]
                            h = hashlib.md5()
                            hs = '{0}|{1}'.format(comp.lower(), parent_uid)
                            h.update(hs.encode())
                            digest = h.hexdigest()

                            if digest not in folder_hash:
                                is_shared = False
                                if i == len(comps) - 1:
                                    is_shared = is_domain

                                folder_uid = api.generate_record_uid()
                                if not parent_type or parent_type == 'user_folder':
                                    folder_type = 'shared_folder' if is_shared else 'user_folder'
                                else:
                                    folder_type = 'shared_folder_folder'

                                fol_req = folder_pb2.FolderRequest()
                                fol_req.folderUid = base64.urlsafe_b64decode(
                                    folder_uid + '==')
                                fol_req.folderType = 2 if folder_type == 'shared_folder' else 3 if folder_type == 'shared_folder_folder' else 1

                                if parent_uid:
                                    fol_req.parentFolderUid = base64.urlsafe_b64decode(
                                        parent_uid + '==')
                                    if folder_type == 'shared_folder_folder' and parent_uid == parent_shared_folder_uid:
                                        fol_req.parentFolderUid = b''

                                folder_key = os.urandom(32)
                                if folder_type == 'shared_folder_folder':
                                    fol_req.encryptedFolderKey = base64.urlsafe_b64decode(
                                        api.encrypt_aes(
                                            folder_key,
                                            parent_shared_folder_key
                                            or params.data_key) + '==')
                                else:
                                    fol_req.encryptedFolderKey = base64.urlsafe_b64decode(
                                        api.encrypt_aes(
                                            folder_key, params.data_key) +
                                        '==')

                                data = {'name': comp}
                                fol_req.folderData = base64.urlsafe_b64decode(
                                    api.encrypt_aes(
                                        json.dumps(data).encode('utf-8'),
                                        folder_key) + '==')

                                if folder_type == 'shared_folder':
                                    fol_req.sharedFolderFields.encryptedFolderName = base64.urlsafe_b64decode(
                                        api.encrypt_aes(
                                            comp.encode('utf-8'), folder_key) +
                                        '==')

                                    parent_shared_folder_key = folder_key
                                    parent_shared_folder_uid = folder_uid

                                elif folder_type == 'shared_folder_folder':
                                    if parent_shared_folder_uid:
                                        fol_req.sharedFolderFolderFields.sharedFolderUid = base64.urlsafe_b64decode(
                                            parent_shared_folder_uid + '==')

                                folder_add.append(fol_req)
                                folder_hash[
                                    digest] = folder_uid, folder_type, parent_shared_folder_key
                            else:
                                folder_uid, folder_type, parent_shared_folder_key = folder_hash[
                                    digest]

                            if folder_type == 'shared_folder':
                                parent_shared_folder_uid = folder_uid

                            parent_uid = folder_uid
                            parent_type = folder_type

                    fol.uid = parent_uid

    return folder_add
Exemple #16
0
def prepare_record_add(params, records):
    record_hash = {}
    for r_uid in params.record_cache:
        rec = api.get_record(params, r_uid)
        h = hashlib.sha256()
        for token in tokenize_record(rec):
            h.update(token.encode())
        record_hash[h.hexdigest()] = r_uid

    record_adds = []
    for rec in records:
        h = hashlib.sha256()
        for token in tokenize_import_record(rec):
            h.update(token.encode())
        r_hash = h.hexdigest()
        if r_hash in record_hash:
            rec.uid = record_hash[r_hash]
        else:
            rec.uid = api.generate_record_uid()
            record_hash[r_hash] = rec.uid
            record_key = os.urandom(32)
            rec_req = folder_pb2.RecordRequest()
            rec_req.recordUid = base64.urlsafe_b64decode(rec.uid + '==')
            rec_req.recordType = 0
            rec_req.encryptedRecordKey = base64.urlsafe_b64decode(
                api.encrypt_aes(record_key, params.data_key) + '==')
            rec_req.howLongAgo = 0
            rec_req.folderType = 1

            folder_uid = None
            if rec.folders:
                folder_uid = rec.folders[0].uid
            if folder_uid:
                if folder_uid in params.folder_cache:
                    folder = params.folder_cache[folder_uid]
                    if folder.type in {
                            BaseFolderNode.SharedFolderType,
                            BaseFolderNode.SharedFolderFolderType
                    }:
                        rec_req.folderUid = base64.urlsafe_b64decode(
                            folder.uid + '==')
                        rec_req.folderType = 2 if folder.type == BaseFolderNode.SharedFolderType else 3

                        sh_uid = folder.uid if folder.type == BaseFolderNode.SharedFolderType else folder.shared_folder_uid
                        sf = params.shared_folder_cache[sh_uid]
                        rec_req.encryptedRecordFolderKey = base64.urlsafe_b64decode(
                            api.encrypt_aes(
                                record_key,
                                sf['shared_folder_key_unencrypted']) + '==')
                    else:
                        rec_req.folderType = 1
                        if folder.type != BaseFolderNode.RootFolderType:
                            rec_req.folderUid = base64.urlsafe_b64decode(
                                folder.uid + '==')

            custom_fields = []
            totp = None
            if rec.custom_fields:
                for cf in rec.custom_fields:
                    if cf == TWO_FACTOR_CODE:
                        totp = rec.custom_fields[cf]
                    else:
                        custom_fields.append({
                            'name': cf,
                            'value': rec.custom_fields[cf]
                        })

            data = {
                'title': rec.title or '',
                'secret1': rec.login or '',
                'secret2': rec.password or '',
                'link': rec.login_url or '',
                'notes': rec.notes or '',
                'custom': custom_fields
            }
            rec_req.recordData = base64.urlsafe_b64decode(
                api.encrypt_aes(json.dumps(data).encode('utf-8'), record_key) +
                '==')
            if totp:
                extra = {
                    'fields': [{
                        'id': api.generate_record_uid(),
                        'field_type': 'totp',
                        'field_title': 'Two-Factor Code',
                        'type': 0,
                        'data': totp
                    }]
                }
                rec_req.extra = base64.urlsafe_b64decode(
                    api.encrypt_aes(
                        json.dumps(extra).encode('utf-8'), record_key) + '==')
            record_adds.append(rec_req)

    return record_adds
Exemple #17
0
def upload_attachment(params, attachments):
    '''
    :param attachments:
    :type attachments: [(str, ImportAttachment)]
    '''

    print('Uploading attachments:')
    while len(attachments) > 0:
        chunk = attachments[:90]
        attachments = attachments[90:]

        uploads = None

        file_no = 0
        file_size = 0
        for _, att in chunk:
            file_no += 1
            file_size += att.size or 0

        #TODO check storage subscription
        rq = {'command': 'request_upload', 'file_count': file_no}
        try:
            rs = api.communicate(params, rq)
            if rs['result'] == 'success':
                uploads = rs['file_uploads']
        except Exception as e:
            logging.error(e)
            return

        uploaded = {}
        for record_id, atta in chunk:
            if not uploads:
                break

            try:
                upload = uploads.pop()
                buffer = io.BytesIO()
                cipher = None
                key = atta.key
                if not key:
                    key = os.urandom(32)
                    iv = os.urandom(16)
                    cipher = AES.new(key, AES.MODE_CBC, iv)
                    buffer.write(iv)
                with atta.open() as s:
                    finished = False
                    while not finished:
                        chunk = s.read(10240)
                        if len(chunk) > 0:
                            if cipher is not None:
                                if len(chunk) < 10240:
                                    finished = True
                                    chunk = api.pad_binary(chunk)
                                chunk = cipher.encrypt(chunk)
                            buffer.write(chunk)
                        else:
                            finished = True
                size = buffer.tell() - 16
                if size > 0:
                    buffer.seek(0, io.SEEK_SET)
                    files = {
                        upload['file_parameter']:
                        (atta.name, buffer, 'application/octet-stream')
                    }
                    print('{0} ... '.format(atta.name), end='', flush=True)
                    response = requests.post(upload['url'],
                                             files=files,
                                             data=upload['parameters'])
                    if response.status_code == upload['success_status_code']:
                        if record_id not in uploaded:
                            uploaded[record_id] = []
                        uploaded[record_id].append({
                            'key':
                            base64.urlsafe_b64encode(key).decode().rstrip('='),
                            'name':
                            atta.name,
                            'file_id':
                            upload['file_id'],
                            'size':
                            size
                        })
                        print('Done')
                    else:
                        print('Failed')

            except Exception as e:
                logging.warning(e)

        if len(uploaded) > 0:
            rq = {
                'command': 'record_update',
                'pt': 'Commander',
                'device_id': 'Commander',
                'client_time': api.current_milli_time(),
                'update_records': []
            }
            for record_id in uploaded:
                if record_id in params.record_cache:
                    rec = params.record_cache[record_id]
                    extra = json.loads(rec['extra_unencrypted'].decode(
                        'utf-8')) if 'extra' in rec else {}
                    files = extra.get('files')
                    if files is None:
                        files = []
                        extra['files'] = files
                    udata = rec['udata'] if 'udata' in rec else {}
                    file_ids = udata.get('file_ids')
                    if file_ids is None:
                        file_ids = []
                        udata['file_ids'] = file_ids
                    for atta in uploaded[record_id]:
                        file_ids.append(atta['file_id'])
                        files.append({
                            'id': atta['file_id'],
                            'name': atta['name'],
                            'size': atta['size'],
                            'key': atta['key']
                        })

                    ru = {
                        'record_uid':
                        record_id,
                        'version':
                        2,
                        'client_modified_time':
                        api.current_milli_time(),
                        'extra':
                        api.encrypt_aes(
                            json.dumps(extra).encode('utf-8'),
                            rec['record_key_unencrypted']),
                        'udata':
                        udata,
                        'revision':
                        rec['revision']
                    }
                    api.resolve_record_access_path(params, record_id, path=ru)
                    rq['update_records'].append(ru)
            try:
                rs = api.communicate(params, rq)
                if rs['result'] == 'success':
                    api.sync_down(params)
            except Exception as e:
                logging.debug(e)
Exemple #18
0
def prepare_folder_permission(params, folders):
    shared_folder_lookup = {}
    for shared_folder_uid in params.shared_folder_cache:
        path = get_folder_path(params, shared_folder_uid)
        if path:
            shared_folder_lookup[path] = shared_folder_uid

    email_pattern = re.compile(EMAIL_PATTERN)
    # public keys
    emails = {}
    for fol in folders:
        if fol.permissions:
            for perm in fol.permissions:
                if not perm.uid:
                    match = email_pattern.match(perm.name)
                    if match:
                        if perm.name not in emails:
                            if perm.name != params.user:
                                emails[perm.name.lower()] = None

    if emails:
        request = {'command': "public_keys", 'key_owners': list(emails.keys())}
        try:
            rs = api.communicate(params, request)
            if 'public_keys' in rs:
                for pk in rs['public_keys']:
                    if 'public_key' in pk:
                        emails[pk['key_owner']] = pk['public_key']
        except Exception as e:
            logging.debug(e)

    folder_permissions = []
    for fol in folders:
        shared_folder_uid = shared_folder_lookup.get(fol.path)
        if not shared_folder_uid:
            continue
        shared_folder = params.shared_folder_cache.get(shared_folder_uid)
        if not shared_folder:
            continue
        shared_folder_key = shared_folder.get('shared_folder_key_unencrypted')
        if not shared_folder_key:
            continue
        if fol.permissions:
            add_users = []
            add_teams = []
            for perm in fol.permissions:
                is_team = False
                if perm.uid and params.team_cache:
                    is_team = perm.uid in params.team_cache
                else:
                    match = email_pattern.match(perm.name)
                    if not match:
                        is_team = True
                    if is_team:
                        perm.uid = None
                        for team_uid in params.team_cache:
                            team = params.team_cache[team_uid]
                            if team['name'].lower() == perm.name.lower():
                                perm.uid = team_uid
                                break
                if is_team:
                    team = params.team_cache.get(perm.uid)
                    if team:
                        found = False
                        if 'teams' in shared_folder:
                            for team in shared_folder['teams']:
                                if team['team_uid'] == team['team_uid']:
                                    found = True
                                    break
                        if not found:
                            add_teams.append({
                                'team_uid':
                                team['team_uid'],
                                'manage_users':
                                perm.manage_users,
                                'manage_records':
                                perm.manage_records,
                                'shared_folder_key':
                                api.encrypt_aes(shared_folder_key,
                                                team['team_key_unencrypted']),
                            })
                elif perm.name:
                    email = perm.name.lower()
                    found = False
                    if 'users' in shared_folder:
                        for user in shared_folder['users']:
                            if user['username'] == email:
                                found = True
                                break
                    if not found:
                        if email == params.user.lower():
                            add_users.append({
                                'username':
                                email,
                                'manage_users':
                                perm.manage_users,
                                'manage_records':
                                perm.manage_records,
                                'shared_folder_key':
                                api.encrypt_aes(shared_folder_key,
                                                params.data_key)
                            })
                        elif email in emails:
                            public_key = emails[email]
                            if public_key:
                                try:
                                    rsa_key = RSA.importKey(
                                        base64.urlsafe_b64decode(public_key +
                                                                 '=='))
                                    add_users.append({
                                        'username':
                                        email,
                                        'manage_users':
                                        perm.manage_users,
                                        'manage_records':
                                        perm.manage_records,
                                        'shared_folder_key':
                                        api.encrypt_rsa(
                                            shared_folder_key, rsa_key)
                                    })
                                except Exception as e:
                                    logging.debug(e)

            if add_teams or add_users:
                request = {
                    'command': 'shared_folder_update',
                    'operation': 'update',
                    'pt': 'Commander',
                    'shared_folder_uid': shared_folder_uid,
                    'force_update': True,
                    'add_teams': add_teams,
                    'add_users': add_users,
                }
                folder_permissions.append(request)

    return folder_permissions
Exemple #19
0
def prepare_record_add(params, records):
    record_hash = {}
    for r_uid in params.record_cache:
        rec = api.get_record(params, r_uid)
        h = hashlib.md5()
        hs = '{0}|{1}|{2}'.format(rec.title or '', rec.login or '',
                                  rec.password or '')
        h.update(hs.encode())
        record_hash[h.hexdigest()] = r_uid

    record_adds = []
    for rec in records:
        h = hashlib.md5()
        hs = '{0}|{1}|{2}'.format(rec.title or '', rec.login or '',
                                  rec.password or '')
        h.update(hs.encode())
        rec_hash = h.hexdigest()

        record_uid = record_hash.get(rec_hash)
        if record_uid is None:
            record_key = os.urandom(32)
            record_uid = api.generate_record_uid()
            req = {
                'command': 'record_add',
                'record_uid': record_uid,
                'record_type': 'password',
                'record_key': api.encrypt_aes(record_key, params.data_key),
                'how_long_ago': 0,
                'folder_type': 'user_folder'
            }
            folder_uid = None
            if rec.folders:
                if len(rec.folders) > 0:
                    folder_uid = rec.folders[0].uid
            if folder_uid:
                if folder_uid in params.folder_cache:
                    folder = params.folder_cache[folder_uid]
                    if folder.type in {
                            BaseFolderNode.SharedFolderType,
                            BaseFolderNode.SharedFolderFolderType
                    }:
                        req['folder_uid'] = folder.uid
                        req['folder_type'] = 'shared_folder' if folder.type == BaseFolderNode.SharedFolderType else 'shared_folder_folder'

                        sh_uid = folder.uid if folder.type == BaseFolderNode.SharedFolderType else folder.shared_folder_uid
                        sf = params.shared_folder_cache[sh_uid]
                        req['folder_key'] = api.encrypt_aes(
                            record_key, sf['shared_folder_key_unencrypted'])
                        if 'key_type' not in sf:
                            if 'teams' in sf:
                                for team in sf['teams']:
                                    req['team_uid'] = team['team_uid']
                                    if team['manage_records']:
                                        break
                    else:
                        req['folder_type'] = 'user_folder'
                        if folder.type != BaseFolderNode.RootFolderType:
                            req['folder_uid'] = folder.uid

            custom_fields = []
            if rec.custom_fields:
                for cf in rec.custom_fields:
                    custom_fields.append({
                        'name': cf,
                        'value': rec.custom_fields[cf]
                    })

            data = {
                'title': rec.title or '',
                'secret1': rec.login or '',
                'secret2': rec.password or '',
                'link': rec.login_url or '',
                'notes': rec.notes or '',
                'custom': custom_fields
            }
            req['data'] = api.encrypt_aes(
                json.dumps(data).encode('utf-8'), record_key)
            record_adds.append(req)

        rec.uid = record_uid

    return record_adds
Exemple #20
0
def prepare_folder_add(params, records):
    folder_hash = {}
    for f_uid in params.folder_cache:
        fol = params.folder_cache[f_uid]
        h = hashlib.md5()
        hs = '{0}|{1}'.format((fol.name or '').lower(), fol.parent_uid or '')
        h.update(hs.encode())
        shared_folder_key = None
        if fol.type in {
                BaseFolderNode.SharedFolderType,
                BaseFolderNode.SharedFolderFolderType
        }:
            sf_uid = fol.shared_folder_uid if fol.type == BaseFolderNode.SharedFolderFolderType else fol.uid
            if sf_uid in params.shared_folder_cache:
                shared_folder_key = params.shared_folder_cache[sf_uid][
                    'shared_folder_key_unencrypted']
        folder_hash[h.hexdigest()] = f_uid, fol.type, shared_folder_key

    folder_add = []

    for rec in records:
        if rec.folders:
            for fol in rec.folders:
                parent_uid = ''
                parent_shared_folder_uid = None
                parent_shared_folder_key = None
                parent_type = BaseFolderNode.RootFolderType
                for is_domain in [True, False]:
                    path = fol.domain if is_domain else fol.path
                    if not path:
                        continue

                    comps = list(path_components(path))
                    for i in range(len(comps)):
                        comp = comps[i]
                        h = hashlib.md5()
                        hs = '{0}|{1}'.format(comp.lower(), parent_uid)
                        h.update(hs.encode())
                        digest = h.hexdigest()
                        if digest not in folder_hash:
                            is_shared = False
                            if i == len(comps) - 1:
                                is_shared = is_domain
                            folder_uid = api.generate_record_uid()
                            request = {
                                'command': 'folder_add',
                                'folder_uid': folder_uid
                            }
                            if parent_type in {
                                    BaseFolderNode.UserFolderType,
                                    BaseFolderNode.RootFolderType
                            }:
                                folder_type = 'shared_folder' if is_shared else 'user_folder'
                            else:
                                folder_type = 'shared_folder_folder'
                            request['folder_type'] = folder_type

                            encryption_key = params.data_key
                            if request[
                                    'folder_type'] == 'shared_folder_folder' and parent_shared_folder_uid and parent_shared_folder_key:
                                encryption_key = parent_shared_folder_key
                                request[
                                    'shared_folder_uid'] = parent_shared_folder_uid

                            folder_key = os.urandom(32)
                            request['key'] = api.encrypt_aes(
                                folder_key, encryption_key)
                            if parent_type not in {
                                    BaseFolderNode.RootFolderType,
                                    BaseFolderNode.SharedFolderType
                            }:
                                request['parent_uid'] = parent_uid

                            if request['folder_type'] == 'shared_folder':
                                request['name'] = api.encrypt_aes(
                                    comp.encode('utf-8'), folder_key)
                                parent_shared_folder_key = folder_key

                            data = {'name': comp}
                            request['data'] = api.encrypt_aes(
                                json.dumps(data).encode('utf-8'), folder_key)
                            folder_add.append(request)
                            parent_uid = folder_uid
                            parent_type = folder_type
                            folder_hash[
                                digest] = parent_uid, parent_type, parent_shared_folder_key
                        else:
                            parent_uid, parent_type, parent_shared_folder_key = folder_hash[
                                digest]

                        if parent_type == BaseFolderNode.SharedFolderType:
                            parent_shared_folder_uid = parent_uid
                fol.uid = parent_uid

    return folder_add
Exemple #21
0
def prepare_shared_folder_add(params, folders):
    folder_hash = {}
    for f_uid in params.folder_cache:
        fol = params.folder_cache[f_uid]
        h = hashlib.md5()
        hs = '{0}|{1}'.format((fol.name or '').lower(), fol.parent_uid or '')
        h.update(hs.encode())
        shared_folder_key = None
        if fol.type in {
                BaseFolderNode.SharedFolderType,
                BaseFolderNode.SharedFolderFolderType
        }:
            sf_uid = fol.shared_folder_uid if fol.type == BaseFolderNode.SharedFolderFolderType else fol.uid
            if sf_uid in params.shared_folder_cache:
                shared_folder_key = params.shared_folder_cache[sf_uid][
                    'shared_folder_key_unencrypted']
        folder_hash[h.hexdigest()] = f_uid, fol.type, shared_folder_key

    # public keys
    emails = {}
    for fol in folders:
        if fol.permissions:
            for perm in fol.permissions:
                if perm.name not in emails:
                    _, email = parseaddr(perm.name)
                    if email:
                        if email != params.user:
                            emails[email.lower()] = None
    if emails:
        request = {'command': "public_keys", 'key_owners': list(emails.keys())}
        try:
            rs = api.communicate(params, request)
            if 'public_keys' in rs:
                for pk in rs['public_keys']:
                    if 'public_key' in pk:
                        emails[pk['key_owner']] = pk['public_key']
        except Exception as e:
            logging.debug(e)

    shared_folder_add = []
    for fol in folders:
        skip_folder = False
        parent_uid = ''
        parent_type = ''
        parent_key = None
        comps = list(path_components(fol.path))
        for i in range(len(comps)):
            comp = comps[i]
            h = hashlib.md5()
            hs = '{0}|{1}'.format(comp.lower(), parent_uid)
            h.update(hs.encode())
            digest = h.hexdigest()

            is_last = False
            if i == len(comps) - 1:
                is_last = True
            if digest not in folder_hash:
                folder_uid = api.generate_record_uid()
                request = {'command': 'folder_add', 'folder_uid': folder_uid}
                folder_type = 'shared_folder' if is_last else 'user_folder'
                request['folder_type'] = folder_type

                encryption_key = params.data_key
                folder_key = os.urandom(32)
                request['key'] = api.encrypt_aes(folder_key, encryption_key)
                if parent_uid:
                    request['parent_uid'] = parent_uid
                if folder_type == 'shared_folder':
                    request['name'] = api.encrypt_aes(comp.encode('utf-8'),
                                                      folder_key)

                data = {'name': comp}
                request['data'] = api.encrypt_aes(
                    json.dumps(data).encode('utf-8'), folder_key)

                shared_folder_add.append(request)
                parent_uid = folder_uid
                parent_type = folder_type
                parent_key = folder_key
                folder_hash[
                    digest] = folder_uid, folder_type, folder_key if folder_type == 'shared_folder' else None
            else:
                parent_uid, parent_type, parent_key = folder_hash[digest]
                if is_last:
                    skip_folder = parent_type != 'shared_folder'
                else:
                    skip_folder = parent_type != 'user_folder'
            if skip_folder:
                break

        if not skip_folder and parent_type == 'shared_folder':
            request = {
                'command': 'shared_folder_update',
                'operation': 'update',
                'pt': 'Commander',
                'shared_folder_uid': parent_uid,
                'force_update': True,
                'default_manage_users': fol.manage_users,
                'default_manage_records': fol.manage_records,
                'default_can_edit': fol.can_edit,
                'default_can_share': fol.can_share
            }
            if fol.permissions:
                for perm in fol.permissions:
                    is_team = False
                    if perm.uid and params.team_cache:
                        is_team = perm.uid in params.team_cache
                    else:
                        _, email = parseaddr(perm.name)
                        if not email:
                            is_team = True
                        if is_team:
                            perm.uid = None
                            for team in params.team_cache:
                                if team['name'].lower() == perm.name.lower():
                                    perm.uid = team['team_uid']
                                    break
                    if is_team:
                        if perm.uid and perm.uid in params.team_cache:
                            if 'add_teams' not in request:
                                request['add_teams'] = []
                            team = params.team_cache[perm.uid]
                            request['add_teams'].append({
                                'team_uid':
                                perm.uid,
                                'manage_users':
                                perm.manage_users,
                                'manage_records':
                                perm.manage_records,
                                'shared_folder_key':
                                api.encrypt_aes(parent_key, team['team_key'])
                            })
                    else:
                        if 'add_users' not in request:
                            request['add_users'] = []
                        email = perm.name.lower()
                        if email == params.user.lower():
                            request['add_users'].append({
                                'username':
                                email,
                                'manage_users':
                                perm.manage_users,
                                'manage_records':
                                perm.manage_records,
                                'shared_folder_key':
                                api.encrypt_aes(parent_key, params.data_key)
                            })
                        elif email in emails:
                            public_key = emails[email]
                            if public_key:
                                try:
                                    rsa_key = RSA.importKey(
                                        base64.urlsafe_b64decode(public_key +
                                                                 '=='))
                                    request['add_users'].append({
                                        'username':
                                        email,
                                        'manage_users':
                                        perm.manage_users,
                                        'manage_records':
                                        perm.manage_records,
                                        'shared_folder_key':
                                        api.encrypt_rsa(parent_key, rsa_key)
                                    })
                                except:
                                    pass
            shared_folder_add.append(request)

    return shared_folder_add