From commits-return-23247-archive-asf-public=cust-asf.ponee.io@airavata.apache.org Wed Oct 14 01:15:49 2020 Return-Path: X-Original-To: archive-asf-public@cust-asf.ponee.io Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mxout1-he-de.apache.org (mxout1-he-de.apache.org [95.216.194.37]) by mx-eu-01.ponee.io (Postfix) with ESMTPS id 6CF3818060E for ; Wed, 14 Oct 2020 03:15:49 +0200 (CEST) Received: from mail.apache.org (mailroute1-lw-us.apache.org [207.244.88.153]) by mxout1-he-de.apache.org (ASF Mail Server at mxout1-he-de.apache.org) with SMTP id B228D6389F for ; Wed, 14 Oct 2020 01:15:48 +0000 (UTC) Received: (qmail 96569 invoked by uid 500); 14 Oct 2020 01:15:48 -0000 Mailing-List: contact commits-help@airavata.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@airavata.apache.org Delivered-To: mailing list commits@airavata.apache.org Received: (qmail 96560 invoked by uid 99); 14 Oct 2020 01:15:48 -0000 Received: from ec2-52-202-80-70.compute-1.amazonaws.com (HELO gitbox.apache.org) (52.202.80.70) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 14 Oct 2020 01:15:48 +0000 Received: by gitbox.apache.org (ASF Mail Server at gitbox.apache.org, from userid 33) id 7381581A7C; Wed, 14 Oct 2020 01:15:46 +0000 (UTC) Date: Wed, 14 Oct 2020 01:15:46 +0000 To: "commits@airavata.apache.org" Subject: [airavata-custos] branch master updated: custos-python-sdk-demo MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Message-ID: <160263814678.2206.3137526776431527934@gitbox.apache.org> From: smarru@apache.org X-Git-Host: gitbox.apache.org X-Git-Repo: airavata-custos X-Git-Refname: refs/heads/master X-Git-Reftype: branch X-Git-Oldrev: a16239330dea63f448b36f4369f3eac260039ac9 X-Git-Newrev: 2d341849dd8ea8a7c2efec6cc73b01dfd495352e X-Git-Rev: 2d341849dd8ea8a7c2efec6cc73b01dfd495352e X-Git-NotificationType: ref_changed_plus_diff X-Git-Multimail-Version: 1.5.dev Auto-Submitted: auto-generated This is an automated email from the ASF dual-hosted git repository. smarru pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/airavata-custos.git The following commit(s) were added to refs/heads/master by this push: new 2d34184 custos-python-sdk-demo 2d34184 is described below commit 2d341849dd8ea8a7c2efec6cc73b01dfd495352e Author: Suresh Marru AuthorDate: Tue Oct 13 21:15:22 2020 -0400 custos-python-sdk-demo --- custos-samples/configs/settings.ini | 8 + custos-samples/samples/__init__.py | 0 custos-samples/samples/group_management_samples.py | 58 ++++ .../samples/identity_management_samples.py | 40 +++ .../samples/secret_management_samples.py | 62 ++++ .../secure_resources_with_custos_simulation.py | 361 +++++++++++++++++++++ .../samples/sharing_management_samples.py | 89 +++++ custos-samples/samples/user_management_samples.py | 66 ++++ 8 files changed, 684 insertions(+) diff --git a/custos-samples/configs/settings.ini b/custos-samples/configs/settings.ini new file mode 100644 index 0000000..f6289bf --- /dev/null +++ b/custos-samples/configs/settings.ini @@ -0,0 +1,8 @@ +[CustosServer] +SERVER_HOST = custos.scigap.org +SERVER_SSL_PORT = 31499 +;CLIENT_ID = +;CLIENT_SEC = + +CLIENT_ID = +CLIENT_SEC = diff --git a/custos-samples/samples/__init__.py b/custos-samples/samples/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/custos-samples/samples/group_management_samples.py b/custos-samples/samples/group_management_samples.py new file mode 100644 index 0000000..a0082ee --- /dev/null +++ b/custos-samples/samples/group_management_samples.py @@ -0,0 +1,58 @@ +import os + +from custos.clients.group_management_client import GroupManagementClient + +from custos.transport.settings import CustosServerClientSettings +import custos.clients.utils.utilities as utl + +# load root directoty +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +# get settings file path (settings file path reside in configs folder under home directory) +settings_path = os.path.join(BASE_DIR, 'configs', "settings.ini") + +# read settings +custos_settings = CustosServerClientSettings(configuration_file_location=settings_path) + +# create custos user management client +group_management_client = GroupManagementClient(custos_settings) + +# obtain base 64 encoded token for tenant +b64_encoded_custos_token = utl.get_token(custos_settings=custos_settings) + + +def create_group(name, description, owner_id): + response = group_management_client.create_groups(token=b64_encoded_custos_token, name=name, + description=description, + owner_id=owner_id) + print(response) + return response + + +def add_user_to_group(username, group_id, membership_type): + response = group_management_client.add_user_to_group(token=b64_encoded_custos_token, + username=username, + group_id=group_id, + membership_type=membership_type) + print(response) + + +def add_child_group_to_parent_group(parent_group_id, child_group_id): + response = group_management_client.add_child_group(token=b64_encoded_custos_token, parent_group_id=parent_group_id, + child_group_id=child_group_id) + print(response) + + +def remove_child_group(parent_group_id, child_group_id): + response = group_management_client.add_child_group(token=b64_encoded_custos_token, parent_group_id=parent_group_id, + child_group_id=child_group_id) + print(response) + + +create_group("Group A", "Paren group", "TestUser4") +create_group("Group B", "Child group", "TestUser4") + +add_user_to_group("Testuser5", "602336d5-e193-41ac-bde6-eb36a73f687e", "Member") + +add_child_group_to_parent_group("8b0f8241-e995-496e-a4f5-bdbde4235215", "602336d5-e193-41ac-bde6-eb36a73f687e") +remove_child_group("8b0f8241-e995-496e-a4f5-bdbde4235215", "602336d5-e193-41ac-bde6-eb36a73f687e") diff --git a/custos-samples/samples/identity_management_samples.py b/custos-samples/samples/identity_management_samples.py new file mode 100644 index 0000000..d135f57 --- /dev/null +++ b/custos-samples/samples/identity_management_samples.py @@ -0,0 +1,40 @@ +import os + +from custos.clients.identity_management_client import IdentityManagementClient + +from custos.transport.settings import CustosServerClientSettings +import custos.clients.utils.utilities as utl + +# load APIServerClient with default configuration +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +settings_path = os.path.join(BASE_DIR, 'configs', "settings.ini") + +# logger.info(settings_path) +custos_settings = CustosServerClientSettings(configuration_file_location=settings_path) + +id_client = IdentityManagementClient(custos_settings) + +b64_encoded_custos_token = utl.get_token(custos_settings=custos_settings) + + +def login(username, password): + resp = id_client.authenticate(token=b64_encoded_custos_token, username=username, password=password) + print(resp) + + +def obtain_access_token_from_code(redirect_uri, code): + resp = id_client.token(token=b64_encoded_custos_token, redirect_uri=redirect_uri, code=code, + grant_type="authorization_code") + print(resp) + + +def get_oidc_configuration(client_id): + response = id_client.get_oidc_configuration(token=b64_encoded_custos_token, client_id=client_id) + print(response) + + +def logout(refresh_token): + response = id_client.end_user_session(token=b64_encoded_custos_token, refresh_token=refresh_token) + print(response) + diff --git a/custos-samples/samples/secret_management_samples.py b/custos-samples/samples/secret_management_samples.py new file mode 100644 index 0000000..229a6a4 --- /dev/null +++ b/custos-samples/samples/secret_management_samples.py @@ -0,0 +1,62 @@ +import os + +from custos.clients.resource_secret_management_client import ResourceSecretManagementClient + +from custos.transport.settings import CustosServerClientSettings +import custos.clients.utils.utilities as utl + +# load root directoty +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +# get settings file path (settings file path reside in configs folder under home directory) +settings_path = os.path.join(BASE_DIR, 'configs', "settings.ini") + +# read settings +custos_settings = CustosServerClientSettings(configuration_file_location=settings_path) + +# create custos user management client +resource_management_client = ResourceSecretManagementClient(custos_settings) + +# obtain base 64 encoded token for tenant +b64_encoded_custos_token = utl.get_token(custos_settings=custos_settings) + + +def generateSSH_key(owner_id, description): + response = resource_management_client.add_ssh_credential(token=b64_encoded_custos_token, + client_id=custos_settings.CUSTOS_CLIENT_ID, + owner_id=owner_id, + description=description) + print(response) + return response + + +def getSSHKey(ssh_credential_token): + response = resource_management_client.get_ssh_credential(token=b64_encoded_custos_token, + client_id=custos_settings.CUSTOS_CLIENT_ID, + ssh_credential_token=ssh_credential_token) + print(response) + return response + + +def addPasswordCredential(owner_id, description, password): + response = resource_management_client.add_password_credential(token=b64_encoded_custos_token, + client_id=custos_settings.CUSTOS_CLIENT_ID, + owner_id=owner_id, + description=description, + password=password) + print(response) + return response + + +def getPasswordCredential(password_credential_token): + response = resource_management_client.get_password_credential(token=b64_encoded_custos_token, + client_id=custos_settings.CUSTOS_CLIENT_ID, + password_credential_token=password_credential_token) + print(response) + return response + + +generateSSH_key("TestUser5", "My Gateway SSH Key") +addPasswordCredential("TestUser5", "My admin password", "asaxsxasxasx") +getSSHKey("655e8845-9afa-4251-bb0e-c1eb42bec2fc") +getPasswordCredential("a575726a-3e2a-41d1-ad08-06a97dbae903") diff --git a/custos-samples/samples/secure_resources_with_custos_simulation.py b/custos-samples/samples/secure_resources_with_custos_simulation.py new file mode 100644 index 0000000..7877a43 --- /dev/null +++ b/custos-samples/samples/secure_resources_with_custos_simulation.py @@ -0,0 +1,361 @@ +import os +import json + +from custos.clients.user_management_client import UserManagementClient +from custos.clients.group_management_client import GroupManagementClient +from custos.clients.resource_secret_management_client import ResourceSecretManagementClient +from custos.clients.sharing_management_client import SharingManagementClient + +from custos.transport.settings import CustosServerClientSettings +import custos.clients.utils.utilities as utl + +from google.protobuf.json_format import MessageToJson + +# load root directoty +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +# get settings file path (settings file path reside in configs folder under home directory) +settings_path = os.path.join(BASE_DIR, 'configs', "settings.ini") + +# read settings +custos_settings = CustosServerClientSettings(configuration_file_location=settings_path) + +# create custos user management client +user_management_client = UserManagementClient(custos_settings) + +# create custos group management client +group_management_client = GroupManagementClient(custos_settings) + +# create custos resource secret client +resource_secret_client = ResourceSecretManagementClient(custos_settings) + +# create sharing management client +sharing_management_client = SharingManagementClient(custos_settings) + +# obtain base 64 encoded token for tenant +b64_encoded_custos_token = utl.get_token(custos_settings=custos_settings) + +owner_id = "admin" + +created_groups = {} + +resource_ids = [] + + +def verifiy_admin_user(): + response = user_management_client.get_user(token=b64_encoded_custos_token, username=owner_id) + user_management_client.update_user_profile( + token=b64_encoded_custos_token, + username=response.username, + email=response.email, + first_name=response.first_name, + last_name=response.last_name) + + +def register_users(users): + for user in users: + print("Registering user: " + user['username']) + user_management_client.register_user(token=b64_encoded_custos_token, + username=user['username'], + first_name=user['first_name'], + last_name=user['last_name'], + password=user['password'], + email=user['email'], + is_temp_password=False) + + user_management_client.enable_user(token=b64_encoded_custos_token, username=user['username']) + + +def create_groups(groups): + for group in groups: + print("Creating group: " + group['name']) + grResponse = group_management_client.create_groups(token=b64_encoded_custos_token, + name=group['name'], + description=group['description'], + owner_id=group['owner_id']) + resp = MessageToJson(grResponse) + respData = json.loads(resp) + created_groups[respData['groups'][0]['name']] = respData['groups'][0]['id'] + + +def allocate_users_to_groups(user_group_mapping): + for usr_map in user_group_mapping: + group_id = created_groups[usr_map['group_name']] + print("Assigning user " + usr_map['username'] + " to group " + usr_map['group_name']) + group_management_client.add_user_to_group(token=b64_encoded_custos_token, + username=usr_map['username'], + group_id=group_id, + membership_type='Member' + ) + + +def allocate_child_group_to_parent_group(gr_gr_mapping): + for gr_map in gr_gr_mapping: + child_id = created_groups[gr_map['child_name']] + parent_id = created_groups[gr_map['parent_name']] + print("Assigning child group " + gr_map['child_name'] + " to parent group " + gr_map['parent_name']) + group_management_client.add_child_group(token=b64_encoded_custos_token, + parent_group_id=parent_id, + child_group_id=child_id) + + +def create_resource(): + response = resource_secret_client.add_ssh_credential( + token=b64_encoded_custos_token, + client_id=custos_settings.CUSTOS_CLIENT_ID, + owner_id='admin', + description='Testing SSH Key') + resource_ids.append(response.token) + + +def create_permissions(permissions): + for perm in permissions: + print("Creating permission " + perm['id']) + sharing_management_client.create_permission_type(token=b64_encoded_custos_token, + client_id=custos_settings.CUSTOS_CLIENT_ID, + id=perm['id'], + name=perm['name'], + description=perm['description']) + + +def create_entity_types(entity_types): + for type in entity_types: + print("Creating entity types " + type['id']) + sharing_management_client.create_entity_type(token=b64_encoded_custos_token, + client_id=custos_settings.CUSTOS_CLIENT_ID, + id=type['id'], + name=type['name'], + description=type['description']) + + +def register_resources(resources): + for resource in resources: + print("Register resources " + resource['id']) + sharing_management_client.create_entity(token=b64_encoded_custos_token, + client_id=custos_settings.CUSTOS_CLIENT_ID, + id=resource['id'], + name=resource['name'], + description=resource['description'], + owner_id=resource['user_id'], + type=resource['type'], + parent_id='') + + +def share_resource_with_user(sharings): + for shr in sharings: + print("Sharing entity " + shr['entity_id'] + " with user " + shr['user_id'] + " with permission " + shr[ + 'permission_type']) + sharing_management_client.share_entity_with_users(token=b64_encoded_custos_token, + client_id=custos_settings.CUSTOS_CLIENT_ID, + entity_id=shr['entity_id'], + permission_type=shr['permission_type'], + user_id=shr['user_id'] + ) + + +def share_resource_with_group(gr_sharings): + for shr in gr_sharings: + group_id = created_groups[shr['group_name']] + print("Sharing entity " + shr['entity_id'] + " with group " + shr['group_name'] + " with permission " + shr[ + 'permission_type']) + sharing_management_client.share_entity_with_groups(token=b64_encoded_custos_token, + client_id=custos_settings.CUSTOS_CLIENT_ID, + entity_id=shr['entity_id'], + permission_type=shr['permission_type'], + group_id=group_id) + + +def check_user_permissions(users): + for user in users: + access = sharing_management_client.user_has_access(token=b64_encoded_custos_token, + client_id=custos_settings.CUSTOS_CLIENT_ID, + entity_id=resource_ids[0], + permission_type="READ", + user_id=user['username']) + usr = user['username'] + print("Access for user " + usr + " : " + str(access)) + + +users = [ + { + 'username': 'UserA', + 'first_name': 'Aaron', + 'last_name': 'Bob', + 'password': '1234', + 'email': 'a@gmail.com' + }, + { + 'username': 'UserB', + 'first_name': 'Baron', + 'last_name': 'Bob', + 'password': '1234', + 'email': 'b@gmail.com' + }, + { + 'username': 'UserC', + 'first_name': 'Caron', + 'last_name': 'Bob', + 'password': '1234', + 'email': 'c@gmail.com' + }, + { + 'username': 'UserD', + 'first_name': 'Daron', + 'last_name': 'Bob', + 'password': '1234', + 'email': 'd@gmail.com' + }, + { + 'username': 'UserE', + 'first_name': 'Earon', + 'last_name': 'Bob', + 'password': '1234', + 'email': 'e@gmail.com' + }, + { + 'username': 'UserF', + 'first_name': 'Faron', + 'last_name': 'Bob', + 'password': '1234', + 'email': 'f@gmail.com' + } +] + +groups = [ + { + 'name': 'groupA', + 'description': 'Group L', + 'owner_id': 'admin' + }, + { + 'name': 'groupB', + 'description': 'Group B', + 'owner_id': 'admin' + }, + { + 'name': 'groupC', + 'description': 'Group C', + 'owner_id': 'admin' + } +] + +user_group_mapping = [ + { + 'group_name': 'groupA', + 'username': 'UserA' + }, + { + 'group_name': 'groupA', + 'username': 'UserB' + }, + { + 'group_name': 'groupB', + 'username': 'UserC' + }, + { + 'group_name': 'groupB', + 'username': 'UserD' + }, + { + 'group_name': 'groupC', + 'username': 'UserE' + }, + { + 'group_name': 'groupC', + 'username': 'UserF' + } +] + +child_gr_parent_gr_mapping = [ + { + "child_name": "groupB", + "parent_name": "groupA" + } +] + +permissions = [ + { + 'id': 'OWNER', + 'name': 'OWNER', + 'description': 'Owner permission' + }, + { + 'id': 'READ', + 'name': 'READ', + 'description': 'Read permission' + }, + { + 'id': 'WRITE', + 'name': 'WRITE', + 'description': 'WRITE permission' + } +] +entity_types = [ + { + 'id': 'SECRET', + 'name': 'SECRET', + 'description': 'SECRET Keys' + } +] + +verifiy_admin_user() + +# Register users +register_users(users) + +# Create groups +create_groups(groups) + +# Allocate users to groups +allocate_users_to_groups(user_group_mapping) + +# Allocate child groups to parent group +allocate_child_group_to_parent_group(child_gr_parent_gr_mapping) + +# Creare resource +create_resource() + +# Create permissions +create_permissions(permissions) + +# Create entity types +create_entity_types(entity_types) + +resources = [ + { + 'id': resource_ids[0], + 'name': 'SECRET', + 'description': 'Register SSH Key Id', + 'user_id': 'admin', + 'type': 'SECRET' + } +] + +sharings = [ + { + "entity_id": resource_ids[0], + "permission_type": "READ", + "type": "SECRET", + "user_id": "UserF" + } +] + +gr_sharings = [{ + + "entity_id": resource_ids[0], + "permission_type": "READ", + "type": "SSH_KEY", + "group_name": 'groupA' +}] + +# Register resources +register_resources(resources) + +# Share resource with users +share_resource_with_user(sharings) + +# Share resource with group +share_resource_with_group(gr_sharings) + +# Check user permissions +check_user_permissions(users) diff --git a/custos-samples/samples/sharing_management_samples.py b/custos-samples/samples/sharing_management_samples.py new file mode 100644 index 0000000..d94eeae --- /dev/null +++ b/custos-samples/samples/sharing_management_samples.py @@ -0,0 +1,89 @@ +import os + +from custos.clients.sharing_management_client import SharingManagementClient + +from custos.transport.settings import CustosServerClientSettings +import custos.clients.utils.utilities as utl + +# load root directoty +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +# get settings file path (settings file path reside in configs folder under home directory) +settings_path = os.path.join(BASE_DIR, 'configs', "settings.ini") + +# read settings +custos_settings = CustosServerClientSettings(configuration_file_location=settings_path) + +# create custos user management client +sharing_management_client = SharingManagementClient(custos_settings) + +# obtain base 64 encoded token for tenant +b64_encoded_custos_token = utl.get_token(custos_settings=custos_settings) + + +def create_permission_type(id, name, description): + response = sharing_management_client.create_permission_type(token=b64_encoded_custos_token, + client_id=custos_settings.CUSTOS_CLIENT_ID, + id=id, + name=name, + description=description) + print(response) + + +def create_entity_type(id, name, description): + response = sharing_management_client.create_entity_type(token=b64_encoded_custos_token, + client_id=custos_settings.CUSTOS_CLIENT_ID, + id=id, + name=name, + description=description) + print(response) + + +def create_entity(id, name, description, owner_id, type): + response = sharing_management_client.create_entity(token=b64_encoded_custos_token, + client_id=custos_settings.CUSTOS_CLIENT_ID, + id=id, + name=name, + description=description, + owner_id=owner_id, + type=type, + parent_id='') + print(response) + + +def share_entity_with_user(entity_id, permission_type, user_id): + response = sharing_management_client.share_entity_with_users(token=b64_encoded_custos_token, + client_id=custos_settings.CUSTOS_CLIENT_ID, + entity_id=entity_id, + permission_type=permission_type, + user_id=user_id) + print(response) + + +def share_entity_with_group(entity_id, permission_type, group_id): + response = sharing_management_client.share_entity_with_groups(token=b64_encoded_custos_token, + client_id=custos_settings.CUSTOS_CLIENT_ID, + entity_id=entity_id, + permission_type=permission_type, + group_id=group_id) + print(response) + + +def check_user_has_access(entity_id, permission_type, user_id): + response = sharing_management_client.user_has_access(token=b64_encoded_custos_token, + client_id=custos_settings.CUSTOS_CLIENT_ID, + entity_id=entity_id, + permission_type=permission_type, + user_id=user_id) + print(response) + + +# create_permission_type('RW', 'Read and Write', 'Read write permissions') +# create_entity_type("CRED_TOKEN", "Credential Token", "This is credential token") + +create_entity('655e8845-9afa-4251-bb0e-c1ebasasx42bec2fcASD', 'Password Token', 'This is password token', 'TestUser5', + 'CRED_TOKEN') +# share_entity_with_user('655e8845-9afa-4251-bb0e-c1eb42bec2fc', 'RW', 'TestUser4') +# share_entity_with_group('655e8845-9afa-4251-bb0e-c1eb42bec2fc', 'RW', '602336d5-e193-41ac-bde6-eb36a73f687e') +# +# check_user_has_access('655e8845-9afa-4251-bb0e-c1eb42bec2fc', 'RW', 'TestUser4') diff --git a/custos-samples/samples/user_management_samples.py b/custos-samples/samples/user_management_samples.py new file mode 100644 index 0000000..6b0b489 --- /dev/null +++ b/custos-samples/samples/user_management_samples.py @@ -0,0 +1,66 @@ +import os + +from custos.clients.user_management_client import UserManagementClient + +from custos.transport.settings import CustosServerClientSettings +import custos.clients.utils.utilities as utl + +# load root directoty +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +#get settings file path (settings file path reside in configs folder under home directory) +settings_path = os.path.join(BASE_DIR, 'configs', "settings.ini") + +# read settings +custos_settings = CustosServerClientSettings(configuration_file_location=settings_path) + +# create custos user management client +user_management_client = UserManagementClient(custos_settings) + +# obtain base 64 encoded token for tenant +b64_encoded_custos_token = utl.get_token(custos_settings=custos_settings) + + +def register_user(): + response = user_management_client.register_user(token=b64_encoded_custos_token, + username="TestUser5", + first_name="Watson", + last_name="Christe", + password="1234", + email="wat@gmail.com", + is_temp_password=False) + print(response) + + +def enable_user(): + response = user_management_client.enable_user(token=b64_encoded_custos_token, + username="TestUser5") + print(response) + + +def get_user(): + response = user_management_client.get_user(token=b64_encoded_custos_token, + username="TestUser5") + print(response) + + +def update_user(): + response = user_management_client.update_user_profile(token=b64_encoded_custos_token, + username="TestUser5", + first_name="Jimmy", + last_name="Jhon", + email="wat@gmail.com") + print(response) + + +def find_users(): + response = user_management_client.find_users(token=b64_encoded_custos_token, offset=0, limit=1, + username="TestUser5") + print(response) + + +register_user() +enable_user() +get_user() +update_user() +find_users()