1use super::{sync, GroupId, RemoteGroup, RemoteProject, RemoteUser};
2use binaryninjacore_sys::*;
3use std::env::VarError;
4use std::ffi::c_void;
5use std::ptr::NonNull;
6
7use crate::binary_view::BinaryView;
8use crate::database::Database;
9use crate::enterprise;
10use crate::progress::{NoProgressCallback, ProgressCallback};
11use crate::project::Project;
12use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
13use crate::secrets_provider::CoreSecretsProvider;
14use crate::settings::Settings;
15use crate::string::{BnString, IntoCStr};
16
17#[repr(transparent)]
18pub struct Remote {
19 pub(crate) handle: NonNull<BNRemote>,
20}
21
22impl Remote {
23 pub(crate) unsafe fn from_raw(handle: NonNull<BNRemote>) -> Self {
24 Self { handle }
25 }
26
27 pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNRemote>) -> Ref<Self> {
28 Ref::new(Self { handle })
29 }
30
31 pub fn new(name: &str, address: &str) -> Ref<Self> {
33 let name = name.to_cstr();
34 let address = address.to_cstr();
35 let result = unsafe { BNCollaborationCreateRemote(name.as_ptr(), address.as_ptr()) };
36 unsafe { Self::ref_from_raw(NonNull::new(result).unwrap()) }
37 }
38
39 pub fn get_for_local_database(database: &Database) -> Result<Option<Ref<Remote>>, ()> {
41 sync::get_remote_for_local_database(database)
42 }
43
44 pub fn get_for_binary_view(bv: &BinaryView) -> Result<Option<Ref<Remote>>, ()> {
46 sync::get_remote_for_binary_view(bv)
47 }
48
49 pub fn has_loaded_metadata(&self) -> bool {
51 unsafe { BNRemoteHasLoadedMetadata(self.handle.as_ptr()) }
52 }
53
54 pub fn unique_id(&self) -> Result<BnString, ()> {
56 if !self.has_loaded_metadata() {
57 self.load_metadata()?;
58 }
59 let result = unsafe { BNRemoteGetUniqueId(self.handle.as_ptr()) };
60 assert!(!result.is_null());
61 Ok(unsafe { BnString::from_raw(result) })
62 }
63
64 pub fn name(&self) -> String {
66 let result = unsafe { BNRemoteGetName(self.handle.as_ptr()) };
67 assert!(!result.is_null());
68 unsafe { BnString::into_string(result) }
69 }
70
71 pub fn address(&self) -> String {
73 let result = unsafe { BNRemoteGetAddress(self.handle.as_ptr()) };
74 assert!(!result.is_null());
75 unsafe { BnString::into_string(result) }
76 }
77
78 pub fn is_connected(&self) -> bool {
80 unsafe { BNRemoteIsConnected(self.handle.as_ptr()) }
81 }
82
83 pub fn username(&self) -> String {
85 let result = unsafe { BNRemoteGetUsername(self.handle.as_ptr()) };
86 assert!(!result.is_null());
87 unsafe { BnString::into_string(result) }
88 }
89
90 pub fn token(&self) -> String {
92 let result = unsafe { BNRemoteGetToken(self.handle.as_ptr()) };
93 assert!(!result.is_null());
94 unsafe { BnString::into_string(result) }
95 }
96
97 pub fn server_version(&self) -> Result<i32, ()> {
99 if !self.has_loaded_metadata() {
100 self.load_metadata()?;
101 }
102 Ok(unsafe { BNRemoteGetServerVersion(self.handle.as_ptr()) })
103 }
104
105 pub fn server_build_id(&self) -> Result<BnString, ()> {
107 if !self.has_loaded_metadata() {
108 self.load_metadata()?;
109 }
110 unsafe {
111 Ok(BnString::from_raw(BNRemoteGetServerBuildId(
112 self.handle.as_ptr(),
113 )))
114 }
115 }
116
117 pub fn auth_backends(&self) -> Result<(Array<BnString>, Array<BnString>), ()> {
120 if !self.has_loaded_metadata() {
121 self.load_metadata()?;
122 }
123
124 let mut backend_ids = std::ptr::null_mut();
125 let mut backend_names = std::ptr::null_mut();
126 let mut count = 0;
127 let success = unsafe {
128 BNRemoteGetAuthBackends(
129 self.handle.as_ptr(),
130 &mut backend_ids,
131 &mut backend_names,
132 &mut count,
133 )
134 };
135 success
136 .then(|| unsafe {
137 (
138 Array::new(backend_ids, count, ()),
139 Array::new(backend_names, count, ()),
140 )
141 })
142 .ok_or(())
143 }
144
145 pub fn is_admin(&self) -> Result<bool, ()> {
147 if !self.has_pulled_users() {
148 self.pull_users()?;
149 }
150 Ok(unsafe { BNRemoteIsAdmin(self.handle.as_ptr()) })
151 }
152
153 pub fn is_enterprise(&self) -> Result<bool, ()> {
155 if !self.has_loaded_metadata() {
156 self.load_metadata()?;
157 }
158 Ok(unsafe { BNRemoteIsEnterprise(self.handle.as_ptr()) })
159 }
160
161 pub fn load_metadata(&self) -> Result<(), ()> {
163 let success = unsafe { BNRemoteLoadMetadata(self.handle.as_ptr()) };
164 success.then_some(()).ok_or(())
165 }
166
167 pub fn request_authentication_token(&self, username: &str, password: &str) -> Option<String> {
169 let username = username.to_cstr();
170 let password = password.to_cstr();
171 let token = unsafe {
172 BNRemoteRequestAuthenticationToken(
173 self.handle.as_ptr(),
174 username.as_ptr(),
175 password.as_ptr(),
176 )
177 };
178 if token.is_null() {
179 None
180 } else {
181 Some(unsafe { BnString::into_string(token) })
182 }
183 }
184
185 pub fn connect(&self) -> Result<(), ()> {
193 if self.is_enterprise()? && enterprise::is_server_authenticated() {
194 self.connect_with_opts(ConnectionOptions::from_enterprise()?)
195 } else {
196 match ConnectionOptions::from_env_variables() {
198 Ok(connection_opts) => self.connect_with_opts(connection_opts),
199 Err(_) => {
200 let secrets_connection_opts =
202 ConnectionOptions::from_secrets_provider(&self.address())?;
203 self.connect_with_opts(secrets_connection_opts)
204 }
205 }
206 }
207 }
208
209 pub fn connect_with_opts(&self, options: ConnectionOptions) -> Result<(), ()> {
211 if !self.has_loaded_metadata() {
213 self.load_metadata()?;
214 }
215 let token = match options.token {
216 Some(token) => token,
217 None => {
218 let password = options
220 .password
221 .expect("No password or token for connection!");
222 let token = self.request_authentication_token(&options.username, &password);
223 token.unwrap().to_string()
225 }
226 };
227 let username = options.username.to_cstr();
228 let token = token.to_cstr();
229 let success =
230 unsafe { BNRemoteConnect(self.handle.as_ptr(), username.as_ptr(), token.as_ptr()) };
231 success.then_some(()).ok_or(())
232 }
233
234 pub fn disconnect(&self) -> Result<(), ()> {
240 let success = unsafe { BNRemoteDisconnect(self.handle.as_ptr()) };
241 success.then_some(()).ok_or(())
242 }
243
244 pub fn has_pulled_projects(&self) -> bool {
246 unsafe { BNRemoteHasPulledProjects(self.handle.as_ptr()) }
247 }
248
249 pub fn has_pulled_groups(&self) -> bool {
251 unsafe { BNRemoteHasPulledGroups(self.handle.as_ptr()) }
252 }
253
254 pub fn has_pulled_users(&self) -> bool {
256 unsafe { BNRemoteHasPulledUsers(self.handle.as_ptr()) }
257 }
258
259 pub fn projects(&self) -> Result<Array<RemoteProject>, ()> {
263 if !self.has_pulled_projects() {
264 self.pull_projects()?;
265 }
266
267 let mut count = 0;
268 let value = unsafe { BNRemoteGetProjects(self.handle.as_ptr(), &mut count) };
269 if value.is_null() {
270 return Err(());
271 }
272 Ok(unsafe { Array::new(value, count, ()) })
273 }
274
275 pub fn get_project_by_id(&self, id: &str) -> Result<Option<Ref<RemoteProject>>, ()> {
279 if !self.has_pulled_projects() {
280 self.pull_projects()?;
281 }
282
283 let id = id.to_cstr();
284 let value = unsafe { BNRemoteGetProjectById(self.handle.as_ptr(), id.as_ptr()) };
285 Ok(NonNull::new(value).map(|handle| unsafe { RemoteProject::ref_from_raw(handle) }))
286 }
287
288 pub fn get_project_by_name(&self, name: &str) -> Result<Option<Ref<RemoteProject>>, ()> {
292 if !self.has_pulled_projects() {
293 self.pull_projects()?;
294 }
295
296 let name = name.to_cstr();
297 let value = unsafe { BNRemoteGetProjectByName(self.handle.as_ptr(), name.as_ptr()) };
298 Ok(NonNull::new(value).map(|handle| unsafe { RemoteProject::ref_from_raw(handle) }))
299 }
300
301 pub fn pull_projects(&self) -> Result<(), ()> {
303 self.pull_projects_with_progress(NoProgressCallback)
304 }
305
306 pub fn pull_projects_with_progress<F: ProgressCallback>(
312 &self,
313 mut progress: F,
314 ) -> Result<(), ()> {
315 if !self.has_loaded_metadata() {
316 self.load_metadata()?;
317 }
318
319 let success = unsafe {
320 BNRemotePullProjects(
321 self.handle.as_ptr(),
322 Some(F::cb_progress_callback),
323 &mut progress as *mut F as *mut c_void,
324 )
325 };
326 success.then_some(()).ok_or(())
327 }
328
329 pub fn create_project(&self, name: &str, description: &str) -> Result<Ref<RemoteProject>, ()> {
336 if !self.has_pulled_projects() {
340 self.pull_projects()?;
341 }
342 let name = name.to_cstr();
343 let description = description.to_cstr();
344 let value = unsafe {
345 BNRemoteCreateProject(self.handle.as_ptr(), name.as_ptr(), description.as_ptr())
346 };
347 NonNull::new(value)
348 .map(|handle| unsafe { RemoteProject::ref_from_raw(handle) })
349 .ok_or(())
350 }
351
352 pub fn import_local_project(&self, project: &Project) -> Option<Ref<RemoteProject>> {
354 self.import_local_project_with_progress(project, NoProgressCallback)
355 }
356
357 pub fn import_local_project_with_progress<P: ProgressCallback>(
359 &self,
360 project: &Project,
361 mut progress: P,
362 ) -> Option<Ref<RemoteProject>> {
363 let value = unsafe {
364 BNRemoteImportLocalProject(
365 self.handle.as_ptr(),
366 project.handle.as_ptr(),
367 Some(P::cb_progress_callback),
368 &mut progress as *mut P as *mut c_void,
369 )
370 };
371 NonNull::new(value).map(|handle| unsafe { RemoteProject::ref_from_raw(handle) })
372 }
373
374 pub fn push_project<I>(&self, project: &RemoteProject, extra_fields: I) -> Result<(), ()>
381 where
382 I: IntoIterator<Item = (String, String)>,
383 {
384 let (keys, values): (Vec<_>, Vec<_>) = extra_fields
385 .into_iter()
386 .map(|(k, v)| (k.to_cstr(), v.to_cstr()))
387 .unzip();
388 let mut keys_raw = keys.iter().map(|s| s.as_ptr()).collect::<Vec<_>>();
389 let mut values_raw = values.iter().map(|s| s.as_ptr()).collect::<Vec<_>>();
390
391 let success = unsafe {
392 BNRemotePushProject(
393 self.handle.as_ptr(),
394 project.handle.as_ptr(),
395 keys_raw.as_mut_ptr(),
396 values_raw.as_mut_ptr(),
397 keys_raw.len(),
398 )
399 };
400 success.then_some(()).ok_or(())
401 }
402
403 pub fn delete_project(&self, project: &RemoteProject) -> Result<(), ()> {
405 let success =
406 unsafe { BNRemoteDeleteProject(self.handle.as_ptr(), project.handle.as_ptr()) };
407 success.then_some(()).ok_or(())
408 }
409
410 pub fn groups(&self) -> Result<Array<RemoteGroup>, ()> {
415 if !self.has_pulled_groups() {
416 self.pull_groups()?;
417 }
418
419 let mut count = 0;
420 let value = unsafe { BNRemoteGetGroups(self.handle.as_ptr(), &mut count) };
421 if value.is_null() {
422 return Err(());
423 }
424 Ok(unsafe { Array::new(value, count, ()) })
425 }
426
427 pub fn get_group_by_id(&self, id: GroupId) -> Result<Option<Ref<RemoteGroup>>, ()> {
432 if !self.has_pulled_groups() {
433 self.pull_groups()?;
434 }
435
436 let value = unsafe { BNRemoteGetGroupById(self.handle.as_ptr(), id.0) };
437 Ok(NonNull::new(value).map(|handle| unsafe { RemoteGroup::ref_from_raw(handle) }))
438 }
439
440 pub fn get_group_by_name(&self, name: &str) -> Result<Option<Ref<RemoteGroup>>, ()> {
445 if !self.has_pulled_groups() {
446 self.pull_groups()?;
447 }
448
449 let name = name.to_cstr();
450 let value = unsafe { BNRemoteGetGroupByName(self.handle.as_ptr(), name.as_ptr()) };
451
452 Ok(NonNull::new(value).map(|handle| unsafe { RemoteGroup::ref_from_raw(handle) }))
453 }
454
455 pub fn search_groups(&self, prefix: &str) -> Result<(Array<GroupId>, Array<BnString>), ()> {
461 let prefix = prefix.to_cstr();
462 let mut count = 0;
463 let mut group_ids = std::ptr::null_mut();
464 let mut group_names = std::ptr::null_mut();
465
466 let success = unsafe {
467 BNRemoteSearchGroups(
468 self.handle.as_ptr(),
469 prefix.as_ptr(),
470 &mut group_ids,
471 &mut group_names,
472 &mut count,
473 )
474 };
475 if !success {
476 return Err(());
477 }
478 Ok(unsafe {
479 (
480 Array::new(group_ids, count, ()),
481 Array::new(group_names, count, ()),
482 )
483 })
484 }
485
486 pub fn pull_groups(&self) -> Result<(), ()> {
489 self.pull_groups_with_progress(NoProgressCallback)
490 }
491
492 pub fn pull_groups_with_progress<F: ProgressCallback>(
499 &self,
500 mut progress: F,
501 ) -> Result<(), ()> {
502 let success = unsafe {
503 BNRemotePullGroups(
504 self.handle.as_ptr(),
505 Some(F::cb_progress_callback),
506 &mut progress as *mut F as *mut c_void,
507 )
508 };
509 success.then_some(()).ok_or(())
510 }
511
512 pub fn create_group<I>(&self, name: &str, usernames: I) -> Result<Ref<RemoteGroup>, ()>
520 where
521 I: IntoIterator<Item = String>,
522 {
523 let name = name.to_cstr();
524 let usernames: Vec<_> = usernames.into_iter().map(|s| s.to_cstr()).collect();
525 let mut username_ptrs: Vec<_> = usernames.iter().map(|s| s.as_ptr()).collect();
526
527 let value = unsafe {
528 BNRemoteCreateGroup(
529 self.handle.as_ptr(),
530 name.as_ptr(),
531 username_ptrs.as_mut_ptr(),
532 username_ptrs.len(),
533 )
534 };
535 NonNull::new(value)
536 .map(|handle| unsafe { RemoteGroup::ref_from_raw(handle) })
537 .ok_or(())
538 }
539
540 pub fn push_group<I>(&self, group: &RemoteGroup, extra_fields: I) -> Result<(), ()>
548 where
549 I: IntoIterator<Item = (String, String)>,
550 {
551 let (keys, values): (Vec<_>, Vec<_>) = extra_fields
552 .into_iter()
553 .map(|(k, v)| (k.to_cstr(), v.to_cstr()))
554 .unzip();
555 let mut keys_raw: Vec<_> = keys.iter().map(|s| s.as_ptr()).collect();
556 let mut values_raw: Vec<_> = values.iter().map(|s| s.as_ptr()).collect();
557
558 let success = unsafe {
559 BNRemotePushGroup(
560 self.handle.as_ptr(),
561 group.handle.as_ptr(),
562 keys_raw.as_mut_ptr(),
563 values_raw.as_mut_ptr(),
564 keys.len(),
565 )
566 };
567 success.then_some(()).ok_or(())
568 }
569
570 pub fn delete_group(&self, group: &RemoteGroup) -> Result<(), ()> {
578 let success = unsafe { BNRemoteDeleteGroup(self.handle.as_ptr(), group.handle.as_ptr()) };
579 success.then_some(()).ok_or(())
580 }
581
582 pub fn users(&self) -> Result<Array<RemoteUser>, ()> {
588 if !self.has_pulled_users() {
589 self.pull_users()?;
590 }
591 let mut count = 0;
592 let value = unsafe { BNRemoteGetUsers(self.handle.as_ptr(), &mut count) };
593 if value.is_null() {
594 return Err(());
595 }
596 Ok(unsafe { Array::new(value, count, ()) })
597 }
598
599 pub fn get_user_by_id(&self, id: &str) -> Result<Option<Ref<RemoteUser>>, ()> {
609 if !self.has_pulled_users() {
610 self.pull_users()?;
611 }
612 let id = id.to_cstr();
613 let value = unsafe { BNRemoteGetUserById(self.handle.as_ptr(), id.as_ptr()) };
614 Ok(NonNull::new(value).map(|handle| unsafe { RemoteUser::ref_from_raw(handle) }))
615 }
616
617 pub fn get_user_by_username(&self, username: &str) -> Result<Option<Ref<RemoteUser>>, ()> {
627 if !self.has_pulled_users() {
628 self.pull_users()?;
629 }
630 let username = username.to_cstr();
631 let value = unsafe { BNRemoteGetUserByUsername(self.handle.as_ptr(), username.as_ptr()) };
632 Ok(NonNull::new(value).map(|handle| unsafe { RemoteUser::ref_from_raw(handle) }))
633 }
634
635 pub fn current_user(&self) -> Result<Option<Ref<RemoteUser>>, ()> {
641 if !self.has_pulled_users() {
642 self.pull_users()?;
643 }
644 let value = unsafe { BNRemoteGetCurrentUser(self.handle.as_ptr()) };
645 Ok(NonNull::new(value).map(|handle| unsafe { RemoteUser::ref_from_raw(handle) }))
646 }
647
648 pub fn search_users(&self, prefix: &str) -> Result<(Array<BnString>, Array<BnString>), ()> {
654 let prefix = prefix.to_cstr();
655 let mut count = 0;
656 let mut user_ids = std::ptr::null_mut();
657 let mut usernames = std::ptr::null_mut();
658 let success = unsafe {
659 BNRemoteSearchUsers(
660 self.handle.as_ptr(),
661 prefix.as_ptr(),
662 &mut user_ids,
663 &mut usernames,
664 &mut count,
665 )
666 };
667
668 if !success {
669 return Err(());
670 }
671 assert!(!user_ids.is_null());
672 assert!(!usernames.is_null());
673 Ok(unsafe {
674 (
675 Array::new(user_ids, count, ()),
676 Array::new(usernames, count, ()),
677 )
678 })
679 }
680
681 pub fn pull_users(&self) -> Result<(), ()> {
686 self.pull_users_with_progress(NoProgressCallback)
687 }
688
689 pub fn pull_users_with_progress<P: ProgressCallback>(&self, mut progress: P) -> Result<(), ()> {
698 let success = unsafe {
699 BNRemotePullUsers(
700 self.handle.as_ptr(),
701 Some(P::cb_progress_callback),
702 &mut progress as *mut P as *mut c_void,
703 )
704 };
705 success.then_some(()).ok_or(())
706 }
707
708 pub fn create_user(
716 &self,
717 username: &str,
718 email: &str,
719 is_active: bool,
720 password: &str,
721 group_ids: &[u64],
722 user_permission_ids: &[u64],
723 ) -> Result<Ref<RemoteUser>, ()> {
724 let username = username.to_cstr();
725 let email = email.to_cstr();
726 let password = password.to_cstr();
727
728 let value = unsafe {
729 BNRemoteCreateUser(
730 self.handle.as_ptr(),
731 username.as_ptr(),
732 email.as_ptr(),
733 is_active,
734 password.as_ptr(),
735 group_ids.as_ptr(),
736 group_ids.len(),
737 user_permission_ids.as_ptr(),
738 user_permission_ids.len(),
739 )
740 };
741 NonNull::new(value)
742 .map(|handle| unsafe { RemoteUser::ref_from_raw(handle) })
743 .ok_or(())
744 }
745
746 pub fn push_user<I>(&self, user: &RemoteUser, extra_fields: I) -> Result<(), ()>
755 where
756 I: IntoIterator<Item = (String, String)>,
757 {
758 let (keys, values): (Vec<_>, Vec<_>) = extra_fields
759 .into_iter()
760 .map(|(k, v)| (k.to_cstr(), v.to_cstr()))
761 .unzip();
762 let mut keys_raw: Vec<_> = keys.iter().map(|s| s.as_ptr()).collect();
763 let mut values_raw: Vec<_> = values.iter().map(|s| s.as_ptr()).collect();
764 let success = unsafe {
765 BNRemotePushUser(
766 self.handle.as_ptr(),
767 user.handle.as_ptr(),
768 keys_raw.as_mut_ptr(),
769 values_raw.as_mut_ptr(),
770 keys_raw.len(),
771 )
772 };
773 success.then_some(()).ok_or(())
774 }
775
776 }
782
783impl PartialEq for Remote {
784 fn eq(&self, other: &Self) -> bool {
785 if !self.has_loaded_metadata() || other.has_loaded_metadata() {
787 self.address() == other.address()
788 } else if let Some((slf, oth)) = self.unique_id().ok().zip(other.unique_id().ok()) {
789 slf == oth
790 } else {
791 self.address() == other.address()
793 }
794 }
795}
796impl Eq for Remote {}
797
798impl ToOwned for Remote {
799 type Owned = Ref<Self>;
800
801 fn to_owned(&self) -> Self::Owned {
802 unsafe { RefCountable::inc_ref(self) }
803 }
804}
805
806unsafe impl RefCountable for Remote {
807 unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
808 Ref::new(Self {
809 handle: NonNull::new(BNNewRemoteReference(handle.handle.as_ptr())).unwrap(),
810 })
811 }
812
813 unsafe fn dec_ref(handle: &Self) {
814 BNFreeRemote(handle.handle.as_ptr());
815 }
816}
817
818impl CoreArrayProvider for Remote {
819 type Raw = *mut BNRemote;
820 type Context = ();
821 type Wrapped<'a> = Guard<'a, Self>;
822}
823
824unsafe impl CoreArrayProviderInner for Remote {
825 unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
826 BNFreeRemoteList(raw, count)
827 }
828
829 unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
830 let raw_ptr = NonNull::new(*raw).unwrap();
831 Guard::new(Self::from_raw(raw_ptr), context)
832 }
833}
834
835#[derive(Debug, Clone, PartialEq, Eq, Hash)]
836pub struct ConnectionOptions {
837 pub username: String,
838 pub password: Option<String>,
840 pub token: Option<String>,
844}
845
846impl ConnectionOptions {
847 pub fn new_with_token(username: String, token: String) -> Self {
848 Self {
849 username,
850 token: Some(token),
851 password: None,
852 }
853 }
854
855 pub fn new_with_password(username: String, password: String) -> Self {
856 Self {
857 username,
858 token: None,
859 password: Some(password),
860 }
861 }
862
863 pub fn with_token(self, token: String) -> Self {
864 Self {
865 token: Some(token),
866 ..self
867 }
868 }
869
870 pub fn with_password(self, token: String) -> Self {
871 Self {
872 token: Some(token),
873 ..self
874 }
875 }
876
877 pub fn from_enterprise() -> Result<Self, ()> {
878 let username = enterprise::server_username();
880 let token = enterprise::server_token();
881 Ok(Self::new_with_token(username, token))
882 }
883
884 pub fn from_secrets_provider(address: &str) -> Result<Self, ()> {
888 let secrets_provider_name = Settings::new().get_string("enterprise.secretsProvider");
889 let provider = CoreSecretsProvider::by_name(&secrets_provider_name).ok_or(())?;
890 let cred_data_str = provider.get_data(address);
891 if cred_data_str.is_empty() {
892 return Err(());
893 }
894 let cred_data: serde_json::Value = serde_json::from_str(&cred_data_str).map_err(|_| ())?;
895 let username = cred_data["username"].as_str().ok_or(())?;
896 let token = cred_data["token"].as_str().ok_or(())?;
897 Ok(Self::new_with_token(
898 username.to_string(),
899 token.to_string(),
900 ))
901 }
902
903 pub fn from_env_variables() -> Result<Self, VarError> {
904 let username = std::env::var("BN_ENTERPRISE_USERNAME")?;
905 let password = std::env::var("BN_ENTERPRISE_PASSWORD")?;
906 Ok(ConnectionOptions::new_with_password(username, password))
907 }
908}