diff options
| author | Josh Ferrell <josh@vector35.com> | 2026-04-14 16:22:40 -0400 |
|---|---|---|
| committer | Josh Ferrell <josh@vector35.com> | 2026-05-05 14:46:15 -0400 |
| commit | af50b06082044fcbbf730deda985d0133ca98b88 (patch) | |
| tree | 6b1ae5a4678d8a39a88af4de2cd150663e1a9c8e | |
| parent | e4db4f0704ea6e6b381e9322a336661dc23c3b5f (diff) | |
Switch collaboration permission checks to using User objects
| -rw-r--r-- | binaryninjaapi.h | 17 | ||||
| -rw-r--r-- | binaryninjacore.h | 14 | ||||
| -rw-r--r-- | collaboration.cpp | 56 | ||||
| -rw-r--r-- | python/collaboration/group.py | 43 | ||||
| -rw-r--r-- | python/collaboration/project.py | 26 | ||||
| -rw-r--r-- | python/collaboration/remote.py | 12 | ||||
| -rw-r--r-- | rust/src/collaboration/group.rs | 44 | ||||
| -rw-r--r-- | rust/src/collaboration/project.rs | 28 | ||||
| -rw-r--r-- | rust/src/collaboration/remote.rs | 13 |
9 files changed, 125 insertions, 128 deletions
diff --git a/binaryninjaapi.h b/binaryninjaapi.h index ac528db8..1dd3bc0f 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -5780,7 +5780,7 @@ namespace BinaryNinja { void PerformDefineRelocation(Architecture* arch, BNRelocationInfo& info, Ref<Symbol> sym, uint64_t reloc); /*! OnAfterSnapshotDataApplied is called when loading a view from a database, after snapshot data has been applied to it. - + \note This method **may** be overridden by custom BinaryViews. \warning This method **must not** be called directly. @@ -22511,9 +22511,9 @@ namespace BinaryNinja::Collaboration uint64_t GetId(); std::string GetName(); void SetName(const std::string& name); - void SetUsernames(const std::vector<std::string>& usernames); - bool ContainsUser(const std::string& username); - + std::vector<Ref<CollabUser>> GetUsers(); + void SetUsers(const std::vector<Ref<CollabUser>>& users); + bool ContainsUser(Ref<CollabUser> user); }; /*! @@ -22694,10 +22694,11 @@ namespace BinaryNinja::Collaboration /*! Create a new group on the remote (and pull it) \param name Group name + \param users List of users in group \return Reference to the created group \throws RemoteException If there is an error in any request or if the remote is not connected */ - Ref<CollabGroup> CreateGroup(const std::string& name, const std::vector<std::string>& usernames); + Ref<CollabGroup> CreateGroup(const std::string& name, const std::vector<Ref<CollabUser>>& users = {}); /*! @@ -23145,9 +23146,9 @@ namespace BinaryNinja::Collaboration Ref<CollabPermission> CreateUserPermission(const std::string& userId, BNCollaborationPermissionLevel level, ProgressFunction progress = {}); void PushPermission(Ref<CollabPermission> permission, const std::vector<std::pair<std::string, std::string>>& extraFields = {}); void DeletePermission(Ref<CollabPermission> permission); - bool CanUserView(const std::string& username); - bool CanUserEdit(const std::string& username); - bool CanUserAdmin(const std::string& username); + bool CanUserView(Ref<CollabUser> user); + bool CanUserEdit(Ref<CollabUser> user); + bool CanUserAdmin(Ref<CollabUser> user); }; class AnalysisMergeConflict : public CoreRefCountObject<BNAnalysisMergeConflict, BNNewAnalysisMergeConflictReference, BNFreeAnalysisMergeConflict> diff --git a/binaryninjacore.h b/binaryninjacore.h index c5dcc7d4..f28f3c42 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -8607,7 +8607,7 @@ extern "C" BINARYNINJACOREAPI BNCollaborationGroup* BNRemoteGetGroupByName(BNRemote* remote, const char* name); BINARYNINJACOREAPI bool BNRemoteSearchGroups(BNRemote* remote, const char* prefix, uint64_t** groupIds, char*** groupNames, size_t* count); BINARYNINJACOREAPI bool BNRemotePullGroups(BNRemote* remote, BNProgressFunction progress, void* progressContext); - BINARYNINJACOREAPI BNCollaborationGroup* BNRemoteCreateGroup(BNRemote* remote, const char* name, const char** usernames, size_t usernameCount); + BINARYNINJACOREAPI BNCollaborationGroup* BNRemoteCreateGroup(BNRemote* remote, const char* name, BNCollaborationUser** users, size_t userCount); BINARYNINJACOREAPI bool BNRemotePushGroup(BNRemote* remote, BNCollaborationGroup* group, const char** extraFieldKeys, const char** extraFieldValues, size_t extraFieldCount); BINARYNINJACOREAPI bool BNRemoteDeleteGroup(BNRemote* remote, BNCollaborationGroup* group); BINARYNINJACOREAPI BNCollaborationUser** BNRemoteGetUsers(BNRemote* remote, size_t* count); @@ -8631,9 +8631,9 @@ extern "C" BINARYNINJACOREAPI uint64_t BNCollaborationGroupGetId(BNCollaborationGroup* group); BINARYNINJACOREAPI char* BNCollaborationGroupGetName(BNCollaborationGroup* group); BINARYNINJACOREAPI void BNCollaborationGroupSetName(BNCollaborationGroup* group, const char* name); - BINARYNINJACOREAPI bool BNCollaborationGroupGetUsers(BNCollaborationGroup* group, char*** userIds, char*** usernames, size_t* count); - BINARYNINJACOREAPI bool BNCollaborationGroupSetUsernames(BNCollaborationGroup* group, const char** names, size_t count); - BINARYNINJACOREAPI bool BNCollaborationGroupContainsUser(BNCollaborationGroup* group, const char* username); + BINARYNINJACOREAPI BNCollaborationUser** BNCollaborationGroupGetUsers(BNCollaborationGroup* group, size_t* count); + BINARYNINJACOREAPI bool BNCollaborationGroupSetUsers(BNCollaborationGroup* group, BNCollaborationUser** users, size_t count); + BINARYNINJACOREAPI bool BNCollaborationGroupContainsUser(BNCollaborationGroup* group, BNCollaborationUser* user); // CollabUser BINARYNINJACOREAPI BNCollaborationUser* BNNewCollaborationUserReference(BNCollaborationUser* user); @@ -8696,9 +8696,9 @@ extern "C" BINARYNINJACOREAPI BNCollaborationPermission* BNRemoteProjectCreateUserPermission(BNRemoteProject* project, const char* userId, BNCollaborationPermissionLevel level, BNProgressFunction progress, void* progressContext); BINARYNINJACOREAPI bool BNRemoteProjectPushPermission(BNRemoteProject* project, BNCollaborationPermission* permission, const char** extraFieldKeys, const char** extraFieldValues, size_t extraFieldCount); BINARYNINJACOREAPI bool BNRemoteProjectDeletePermission(BNRemoteProject* project, BNCollaborationPermission* permission); - BINARYNINJACOREAPI bool BNRemoteProjectCanUserView(BNRemoteProject* project, const char* username); - BINARYNINJACOREAPI bool BNRemoteProjectCanUserEdit(BNRemoteProject* project, const char* username); - BINARYNINJACOREAPI bool BNRemoteProjectCanUserAdmin(BNRemoteProject* project, const char* username); + BINARYNINJACOREAPI bool BNRemoteProjectCanUserView(BNRemoteProject* project, BNCollaborationUser* user); + BINARYNINJACOREAPI bool BNRemoteProjectCanUserEdit(BNRemoteProject* project, BNCollaborationUser* user); + BINARYNINJACOREAPI bool BNRemoteProjectCanUserAdmin(BNRemoteProject* project, BNCollaborationUser* user); // RemoteFile BINARYNINJACOREAPI BNRemoteFile* BNNewRemoteFileReference(BNRemoteFile* file); diff --git a/collaboration.cpp b/collaboration.cpp index 14274164..15000cda 100644 --- a/collaboration.cpp +++ b/collaboration.cpp @@ -878,16 +878,16 @@ void Remote::PullGroups(ProgressFunction progress) } -Ref<CollabGroup> Remote::CreateGroup(const std::string& name, const std::vector<std::string>& usernames) +Ref<CollabGroup> Remote::CreateGroup(const std::string& name, const std::vector<Ref<CollabUser>>& users) { - const char** cstrNames = new const char*[usernames.size()]; - for (size_t i = 0; i < usernames.size(); i++) + BNCollaborationUser** cUsers = new BNCollaborationUser*[users.size()]; + for (size_t i = 0; i < users.size(); i++) { - cstrNames[i] = usernames[i].c_str(); + cUsers[i] = users[i]->m_object; } - BNCollaborationGroup* group = BNRemoteCreateGroup(m_object, name.c_str(), cstrNames, usernames.size()); - delete[] cstrNames; + BNCollaborationGroup* group = BNRemoteCreateGroup(m_object, name.c_str(), cUsers, users.size()); + delete[] cUsers; if (!group) return nullptr; return new CollabGroup(group); @@ -1076,22 +1076,38 @@ void CollabGroup::SetName(const std::string& name) } -void CollabGroup::SetUsernames(const std::vector<std::string>& usernames) +std::vector<Ref<CollabUser>> CollabGroup::GetUsers() { - const char** cNames = new const char*[usernames.size()]; - for (size_t i = 0; i < usernames.size(); i++) + size_t count = 0; + BNCollaborationUser** users = BNCollaborationGroupGetUsers(m_object, &count); + std::vector<Ref<CollabUser>> out; + out.reserve(count); + for (size_t i = 0; i < count; i++) + { + out.push_back(new CollabUser(BNNewCollaborationUserReference(users[i]))); + } + BNFreeCollaborationUserList(users, count); + return out; +} + + +void CollabGroup::SetUsers(const std::vector<Ref<CollabUser>>& users) +{ + size_t count = users.size(); + BNCollaborationUser** cUsers = new BNCollaborationUser*[count]; + for (size_t i = 0; i < count; i++) { - cNames[i] = usernames[i].c_str(); + cUsers[i] = users[i]->m_object; } - BNCollaborationGroupSetUsernames(m_object, cNames, usernames.size()); - delete[] cNames; + BNCollaborationGroupSetUsers(m_object, cUsers, count); + delete[] cUsers; } -bool CollabGroup::ContainsUser(const std::string& username) +bool CollabGroup::ContainsUser(Ref<CollabUser> user) { - return BNCollaborationGroupContainsUser(m_object, username.c_str()); + return BNCollaborationGroupContainsUser(m_object, user->m_object); } @@ -1566,21 +1582,21 @@ void RemoteProject::DeletePermission(Ref<CollabPermission> permission) } -bool RemoteProject::CanUserView(const std::string& username) +bool RemoteProject::CanUserView(Ref<CollabUser> user) { - return BNRemoteProjectCanUserView(m_object, username.c_str()); + return BNRemoteProjectCanUserView(m_object, user->m_object); } -bool RemoteProject::CanUserEdit(const std::string& username) +bool RemoteProject::CanUserEdit(Ref<CollabUser> user) { - return BNRemoteProjectCanUserEdit(m_object, username.c_str()); + return BNRemoteProjectCanUserEdit(m_object, user->m_object); } -bool RemoteProject::CanUserAdmin(const std::string& username) +bool RemoteProject::CanUserAdmin(Ref<CollabUser> user) { - return BNRemoteProjectCanUserAdmin(m_object, username.c_str()); + return BNRemoteProjectCanUserAdmin(m_object, user->m_object); } diff --git a/python/collaboration/group.py b/python/collaboration/group.py index d1fbd9d0..967e52f1 100644 --- a/python/collaboration/group.py +++ b/python/collaboration/group.py @@ -2,7 +2,7 @@ import ctypes from typing import List, Tuple from .. import _binaryninjacore as core -from . import remote, util +from . import remote, user, util class Group: @@ -70,43 +70,40 @@ class Group: core.BNCollaborationGroupSetName(self._handle, name) @property - def users(self) -> List[Tuple[str, str]]: + def users(self) -> List[user.User]: """ Get list of users in the group - :return: List of (userid, username) pairs + :return: List of users """ count = ctypes.c_size_t() - user_ids = ctypes.POINTER(ctypes.c_char_p)() - usernames = ctypes.POINTER(ctypes.c_char_p)() - if not core.BNCollaborationGroupGetUsers(self._handle, user_ids, usernames, count): + result = core.BNCollaborationGroupGetUsers(self._handle, count) + if not result: raise RuntimeError(util._last_error()) - result = [] + out = [] for i in range(count.value): - result.append((core.pyNativeStr(user_ids[i]), core.pyNativeStr(usernames[i]))) - core.BNFreeStringList(user_ids, count.value) - core.BNFreeStringList(usernames, count.value) - return result + out.append(user.User(result[i])) + return out @users.setter - def users(self, usernames: List[str]): + def users(self, users: List[user.User]): """ - Set the list of users in a group by their usernames. + Set the list of users in a group. You will need to push the group to update the Remote. - :param usernames: Usernames of new group members + :param users: New group members """ - array = (ctypes.c_char_p * len(usernames))() - for i in range(len(usernames)): - array[i] = core.cstr(usernames[i]) - if not core.BNCollaborationGroupSetUsernames(self._handle, array, len(usernames)): + array = ctypes.POINTER(core.BNCollaborationUserHandle)() + for i in range(len(users)): + array[i] = users[i]._handle + if not core.BNCollaborationGroupSetUsers(self._handle, array, len(users)): raise RuntimeError(util._last_error()) - def contains_user(self, username: str) -> bool: + def contains_user(self, user: user.User) -> bool: """ - Test if a group has a user with the given username + Test if a group contains a user - :param username: Username of user to check membership - :return: If the user is in the group + :param user: User to check membership of + :return: If the group contains the user """ - return core.BNCollaborationGroupContainsUser(self._handle, username) + return core.BNCollaborationGroupContainsUser(self._handle, user._handle) diff --git a/python/collaboration/project.py b/python/collaboration/project.py index a143e6d9..f78e488e 100644 --- a/python/collaboration/project.py +++ b/python/collaboration/project.py @@ -11,7 +11,7 @@ from ..binaryview import BinaryView, ProgressFuncType from ..database import Database from ..filemetadata import FileMetadata from ..project import Project -from . import databasesync, file, folder, permission, remote, util +from . import databasesync, file, folder, permission, remote, user, util def _nop(*args, **kwargs): @@ -713,35 +713,35 @@ class RemoteProject: if not core.BNRemoteProjectDeletePermission(self._handle, permission._handle): raise RuntimeError(util._last_error()) - def can_user_view(self, username: str) -> bool: + def can_user_view(self, user: user.User) -> bool: """ Determine if a user is in any of the view/edit/admin groups - :param username: Username of user to check - :return: True if they are in any of those groups + :param user: User to check + :return: True if the user has view permission (either directly or from a group) :raises: RuntimeError if there was an error """ - return core.BNRemoteProjectCanUserView(self._handle, username) + return core.BNRemoteProjectCanUserView(self._handle, user._handle) - def can_user_edit(self, username: str) -> bool: + def can_user_edit(self, user: user.User) -> bool: """ Determine if a user is in any of the edit/admin groups - :param username: Username of user to check - :return: True if they are in any of those groups + :param user: User to check + :return: True if the user has edit permission (either directly or from a group) :raises: RuntimeError if there was an error """ - return core.BNRemoteProjectCanUserEdit(self._handle, username) + return core.BNRemoteProjectCanUserEdit(self._handle, user._handle) - def can_user_admin(self, username: str) -> bool: + def can_user_admin(self, user: user.User) -> bool: """ Determine if a user is in the admin group - :param username: Username of user to check - :return: True if they are in any of those groups + :param user: User to check + :return: True if the user has admin permission (either directly or from a group) :raises: RuntimeError if there was an error """ - return core.BNRemoteProjectCanUserAdmin(self._handle, username) + return core.BNRemoteProjectCanUserAdmin(self._handle, user._handle) def upload_new_file( self, diff --git a/python/collaboration/remote.py b/python/collaboration/remote.py index 811fd33b..1cbdb424 100644 --- a/python/collaboration/remote.py +++ b/python/collaboration/remote.py @@ -520,22 +520,22 @@ class Remote: if not core.BNRemotePullGroups(self._handle, util.wrap_progress(progress), None): raise RuntimeError(util._last_error()) - def create_group(self, name: str, usernames: List[str]) -> 'group.Group': + def create_group(self, name: str, users: List[user.User]) -> 'group.Group': """ Create a new group on the remote (and pull it) .. note:: This function is only available to accounts with admin status on the Remote :param name: Group name - :param usernames: List of usernames of users in the group + :param users: List of users in the group :return: Reference to the created group :raises: RuntimeError if there was an error """ - c_usernames = (ctypes.c_char_p * len(usernames))() - for (i, username) in enumerate(usernames): - c_usernames[i] = core.cstr(username) + c_users = (core.BNCollaborationUserHandle * len(users))() + for (i, member) in enumerate(users): + c_users[i] = member._handle - value = core.BNRemoteCreateGroup(self._handle, name, c_usernames, len(usernames)) + value = core.BNRemoteCreateGroup(self._handle, name, c_users, len(users)) if value is None: raise RuntimeError(util._last_error()) return group.Group(value) diff --git a/rust/src/collaboration/group.rs b/rust/src/collaboration/group.rs index 01f92169..e3ee82d0 100644 --- a/rust/src/collaboration/group.rs +++ b/rust/src/collaboration/group.rs @@ -1,4 +1,5 @@ use super::Remote; +use crate::collaboration::RemoteUser; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; use crate::string::{BnString, IntoCStr}; use binaryninjacore_sys::*; @@ -55,55 +56,40 @@ impl RemoteGroup { } /// Get list of users in the group - pub fn users(&self) -> Result<(Array<BnString>, Array<BnString>), ()> { - let mut usernames = std::ptr::null_mut(); - let mut user_ids = std::ptr::null_mut(); + pub fn users(&self) -> Result<Array<RemoteUser>, ()> { let mut count = 0; // TODO: This should only fail if collaboration is not supported. // TODO: Because you should not have a RemoteGroup at that point we can ignore? - let success = unsafe { - BNCollaborationGroupGetUsers( - self.handle.as_ptr(), - &mut user_ids, - &mut usernames, - &mut count, - ) - }; - success - .then(|| unsafe { - let ids = Array::new(user_ids, count, ()); - let users = Array::new(usernames, count, ()); - (ids, users) - }) + let result = unsafe { BNCollaborationGroupGetUsers(self.handle.as_ptr(), &mut count) }; + (!result.is_null()) + .then(|| unsafe { Array::new(result, count, ()) }) .ok_or(()) } // TODO: Are any permissions required to the set the remote group users? - /// Set the list of users in a group by their usernames. + /// Set the list of users in a group. /// You will need to push the group to update the Remote. - pub fn set_users<I>(&self, usernames: I) -> Result<(), ()> + pub fn set_users<I>(&self, users: I) -> Result<(), ()> where - I: IntoIterator<Item = String>, + I: IntoIterator<Item = Ref<RemoteUser>>, { - let usernames: Vec<_> = usernames.into_iter().map(|u| u.to_cstr()).collect(); - let mut usernames_raw: Vec<_> = usernames.iter().map(|s| s.as_ptr()).collect(); + let mut users_raw: Vec<_> = users.into_iter().map(|s| s.handle.as_ptr()).collect(); // TODO: This should only fail if collaboration is not supported. // TODO: Because you should not have a RemoteGroup at that point we can ignore? // TODO: Do you need any permissions to do this? let success = unsafe { - BNCollaborationGroupSetUsernames( + BNCollaborationGroupSetUsers( self.handle.as_ptr(), - usernames_raw.as_mut_ptr(), - usernames_raw.len(), + users_raw.as_mut_ptr(), + users_raw.len(), ) }; success.then_some(()).ok_or(()) } - /// Test if a group has a user with the given username - pub fn contains_user(&self, username: &str) -> bool { - let username = username.to_cstr(); - unsafe { BNCollaborationGroupContainsUser(self.handle.as_ptr(), username.as_ptr()) } + /// Test if a group contains a user + pub fn contains_user(&self, user: Ref<RemoteUser>) -> bool { + unsafe { BNCollaborationGroupContainsUser(self.handle.as_ptr(), user.handle.as_ptr()) } } } diff --git a/rust/src/collaboration/project.rs b/rust/src/collaboration/project.rs index cfcc1db4..3a24ea8b 100644 --- a/rust/src/collaboration/project.rs +++ b/rust/src/collaboration/project.rs @@ -12,6 +12,7 @@ use super::{ }; use crate::binary_view::{BinaryView, BinaryViewExt}; +use crate::collaboration::RemoteUser; use crate::database::Database; use crate::file_metadata::FileMetadata; use crate::progress::{NoProgressCallback, ProgressCallback}; @@ -761,34 +762,31 @@ impl RemoteProject { success.then_some(()).ok_or(()) } - /// Determine if a user is in any of the view/edit/admin groups. + /// Determine if a user has view permission (either directly or from a group) /// /// # Arguments /// - /// * `username` - Username of user to check - pub fn can_user_view(&self, username: &str) -> bool { - let username = username.to_cstr(); - unsafe { BNRemoteProjectCanUserView(self.handle.as_ptr(), username.as_ptr()) } + /// * `user` - User to check + pub fn can_user_view(&self, user: Ref<RemoteUser>) -> bool { + unsafe { BNRemoteProjectCanUserView(self.handle.as_ptr(), user.handle.as_ptr()) } } - /// Determine if a user is in any of the edit/admin groups. + /// Determine if a user has edit permission (either directly or from a group) /// /// # Arguments /// - /// * `username` - Username of user to check - pub fn can_user_edit(&self, username: &str) -> bool { - let username = username.to_cstr(); - unsafe { BNRemoteProjectCanUserEdit(self.handle.as_ptr(), username.as_ptr()) } + /// * `user` - User to check + pub fn can_user_edit(&self, user: Ref<RemoteUser>) -> bool { + unsafe { BNRemoteProjectCanUserEdit(self.handle.as_ptr(), user.handle.as_ptr()) } } - /// Determine if a user is in the admin group. + /// Determine if a user has admin permission (either directly or from a group) /// /// # Arguments /// - /// * `username` - Username of user to check - pub fn can_user_admin(&self, username: &str) -> bool { - let username = username.to_cstr(); - unsafe { BNRemoteProjectCanUserAdmin(self.handle.as_ptr(), username.as_ptr()) } + /// * `user` - User to check + pub fn can_user_admin(&self, user: Ref<RemoteUser>) -> bool { + unsafe { BNRemoteProjectCanUserAdmin(self.handle.as_ptr(), user.handle.as_ptr()) } } /// Get the default directory path for a remote Project. This is based off diff --git a/rust/src/collaboration/remote.rs b/rust/src/collaboration/remote.rs index 03c808bc..22e65245 100644 --- a/rust/src/collaboration/remote.rs +++ b/rust/src/collaboration/remote.rs @@ -515,21 +515,20 @@ impl Remote { /// # Arguments /// /// * `name` - Group name - /// * `usernames` - List of usernames of users in the group - pub fn create_group<I>(&self, name: &str, usernames: I) -> Result<Ref<RemoteGroup>, ()> + /// * `users` - List of users in the group + pub fn create_group<I>(&self, name: &str, users: I) -> Result<Ref<RemoteGroup>, ()> where - I: IntoIterator<Item = String>, + I: IntoIterator<Item = Ref<RemoteUser>>, { let name = name.to_cstr(); - let usernames: Vec<_> = usernames.into_iter().map(|s| s.to_cstr()).collect(); - let mut username_ptrs: Vec<_> = usernames.iter().map(|s| s.as_ptr()).collect(); + let mut user_ptrs: Vec<_> = users.into_iter().map(|s| s.handle.as_ptr()).collect(); let value = unsafe { BNRemoteCreateGroup( self.handle.as_ptr(), name.as_ptr(), - username_ptrs.as_mut_ptr(), - username_ptrs.len(), + user_ptrs.as_mut_ptr(), + user_ptrs.len(), ) }; NonNull::new(value) |
