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, users: I) -> Result<Ref<RemoteGroup>, ()>
520 where
521 I: IntoIterator<Item = Ref<RemoteUser>>,
522 {
523 let name = name.to_cstr();
524 let mut user_ptrs: Vec<_> = users.into_iter().map(|s| s.handle.as_ptr()).collect();
525
526 let value = unsafe {
527 BNRemoteCreateGroup(
528 self.handle.as_ptr(),
529 name.as_ptr(),
530 user_ptrs.as_mut_ptr(),
531 user_ptrs.len(),
532 )
533 };
534 NonNull::new(value)
535 .map(|handle| unsafe { RemoteGroup::ref_from_raw(handle) })
536 .ok_or(())
537 }
538
539 pub fn push_group<I>(&self, group: &RemoteGroup, extra_fields: I) -> Result<(), ()>
547 where
548 I: IntoIterator<Item = (String, String)>,
549 {
550 let (keys, values): (Vec<_>, Vec<_>) = extra_fields
551 .into_iter()
552 .map(|(k, v)| (k.to_cstr(), v.to_cstr()))
553 .unzip();
554 let mut keys_raw: Vec<_> = keys.iter().map(|s| s.as_ptr()).collect();
555 let mut values_raw: Vec<_> = values.iter().map(|s| s.as_ptr()).collect();
556
557 let success = unsafe {
558 BNRemotePushGroup(
559 self.handle.as_ptr(),
560 group.handle.as_ptr(),
561 keys_raw.as_mut_ptr(),
562 values_raw.as_mut_ptr(),
563 keys.len(),
564 )
565 };
566 success.then_some(()).ok_or(())
567 }
568
569 pub fn delete_group(&self, group: &RemoteGroup) -> Result<(), ()> {
577 let success = unsafe { BNRemoteDeleteGroup(self.handle.as_ptr(), group.handle.as_ptr()) };
578 success.then_some(()).ok_or(())
579 }
580
581 pub fn users(&self) -> Result<Array<RemoteUser>, ()> {
587 if !self.has_pulled_users() {
588 self.pull_users()?;
589 }
590 let mut count = 0;
591 let value = unsafe { BNRemoteGetUsers(self.handle.as_ptr(), &mut count) };
592 if value.is_null() {
593 return Err(());
594 }
595 Ok(unsafe { Array::new(value, count, ()) })
596 }
597
598 pub fn get_user_by_id(&self, id: &str) -> Result<Option<Ref<RemoteUser>>, ()> {
608 if !self.has_pulled_users() {
609 self.pull_users()?;
610 }
611 let id = id.to_cstr();
612 let value = unsafe { BNRemoteGetUserById(self.handle.as_ptr(), id.as_ptr()) };
613 Ok(NonNull::new(value).map(|handle| unsafe { RemoteUser::ref_from_raw(handle) }))
614 }
615
616 pub fn get_user_by_username(&self, username: &str) -> Result<Option<Ref<RemoteUser>>, ()> {
626 if !self.has_pulled_users() {
627 self.pull_users()?;
628 }
629 let username = username.to_cstr();
630 let value = unsafe { BNRemoteGetUserByUsername(self.handle.as_ptr(), username.as_ptr()) };
631 Ok(NonNull::new(value).map(|handle| unsafe { RemoteUser::ref_from_raw(handle) }))
632 }
633
634 pub fn current_user(&self) -> Result<Option<Ref<RemoteUser>>, ()> {
640 if !self.has_pulled_users() {
641 self.pull_users()?;
642 }
643 let value = unsafe { BNRemoteGetCurrentUser(self.handle.as_ptr()) };
644 Ok(NonNull::new(value).map(|handle| unsafe { RemoteUser::ref_from_raw(handle) }))
645 }
646
647 pub fn search_users(&self, prefix: &str) -> Result<(Array<BnString>, Array<BnString>), ()> {
653 let prefix = prefix.to_cstr();
654 let mut count = 0;
655 let mut user_ids = std::ptr::null_mut();
656 let mut usernames = std::ptr::null_mut();
657 let success = unsafe {
658 BNRemoteSearchUsers(
659 self.handle.as_ptr(),
660 prefix.as_ptr(),
661 &mut user_ids,
662 &mut usernames,
663 &mut count,
664 )
665 };
666
667 if !success {
668 return Err(());
669 }
670 assert!(!user_ids.is_null());
671 assert!(!usernames.is_null());
672 Ok(unsafe {
673 (
674 Array::new(user_ids, count, ()),
675 Array::new(usernames, count, ()),
676 )
677 })
678 }
679
680 pub fn pull_users(&self) -> Result<(), ()> {
685 self.pull_users_with_progress(NoProgressCallback)
686 }
687
688 pub fn pull_users_with_progress<P: ProgressCallback>(&self, mut progress: P) -> Result<(), ()> {
697 let success = unsafe {
698 BNRemotePullUsers(
699 self.handle.as_ptr(),
700 Some(P::cb_progress_callback),
701 &mut progress as *mut P as *mut c_void,
702 )
703 };
704 success.then_some(()).ok_or(())
705 }
706
707 pub fn create_user(
715 &self,
716 username: &str,
717 email: &str,
718 is_active: bool,
719 password: &str,
720 group_ids: &[u64],
721 user_permission_ids: &[u64],
722 ) -> Result<Ref<RemoteUser>, ()> {
723 let username = username.to_cstr();
724 let email = email.to_cstr();
725 let password = password.to_cstr();
726
727 let value = unsafe {
728 BNRemoteCreateUser(
729 self.handle.as_ptr(),
730 username.as_ptr(),
731 email.as_ptr(),
732 is_active,
733 password.as_ptr(),
734 group_ids.as_ptr(),
735 group_ids.len(),
736 user_permission_ids.as_ptr(),
737 user_permission_ids.len(),
738 )
739 };
740 NonNull::new(value)
741 .map(|handle| unsafe { RemoteUser::ref_from_raw(handle) })
742 .ok_or(())
743 }
744
745 pub fn push_user<I>(&self, user: &RemoteUser, extra_fields: I) -> Result<(), ()>
754 where
755 I: IntoIterator<Item = (String, String)>,
756 {
757 let (keys, values): (Vec<_>, Vec<_>) = extra_fields
758 .into_iter()
759 .map(|(k, v)| (k.to_cstr(), v.to_cstr()))
760 .unzip();
761 let mut keys_raw: Vec<_> = keys.iter().map(|s| s.as_ptr()).collect();
762 let mut values_raw: Vec<_> = values.iter().map(|s| s.as_ptr()).collect();
763 let success = unsafe {
764 BNRemotePushUser(
765 self.handle.as_ptr(),
766 user.handle.as_ptr(),
767 keys_raw.as_mut_ptr(),
768 values_raw.as_mut_ptr(),
769 keys_raw.len(),
770 )
771 };
772 success.then_some(()).ok_or(())
773 }
774
775 }
781
782impl PartialEq for Remote {
783 fn eq(&self, other: &Self) -> bool {
784 if !self.has_loaded_metadata() || other.has_loaded_metadata() {
786 self.address() == other.address()
787 } else if let Some((slf, oth)) = self.unique_id().ok().zip(other.unique_id().ok()) {
788 slf == oth
789 } else {
790 self.address() == other.address()
792 }
793 }
794}
795impl Eq for Remote {}
796
797impl ToOwned for Remote {
798 type Owned = Ref<Self>;
799
800 fn to_owned(&self) -> Self::Owned {
801 unsafe { RefCountable::inc_ref(self) }
802 }
803}
804
805unsafe impl RefCountable for Remote {
806 unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
807 Ref::new(Self {
808 handle: NonNull::new(BNNewRemoteReference(handle.handle.as_ptr())).unwrap(),
809 })
810 }
811
812 unsafe fn dec_ref(handle: &Self) {
813 BNFreeRemote(handle.handle.as_ptr());
814 }
815}
816
817impl CoreArrayProvider for Remote {
818 type Raw = *mut BNRemote;
819 type Context = ();
820 type Wrapped<'a> = Guard<'a, Self>;
821}
822
823unsafe impl CoreArrayProviderInner for Remote {
824 unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
825 BNFreeRemoteList(raw, count)
826 }
827
828 unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
829 let raw_ptr = NonNull::new(*raw).unwrap();
830 Guard::new(Self::from_raw(raw_ptr), context)
831 }
832}
833
834#[derive(Debug, Clone, PartialEq, Eq, Hash)]
835pub struct ConnectionOptions {
836 pub username: String,
837 pub password: Option<String>,
839 pub token: Option<String>,
843}
844
845impl ConnectionOptions {
846 pub fn new_with_token(username: String, token: String) -> Self {
847 Self {
848 username,
849 token: Some(token),
850 password: None,
851 }
852 }
853
854 pub fn new_with_password(username: String, password: String) -> Self {
855 Self {
856 username,
857 token: None,
858 password: Some(password),
859 }
860 }
861
862 pub fn with_token(self, token: String) -> Self {
863 Self {
864 token: Some(token),
865 ..self
866 }
867 }
868
869 pub fn with_password(self, token: String) -> Self {
870 Self {
871 token: Some(token),
872 ..self
873 }
874 }
875
876 pub fn from_enterprise() -> Result<Self, ()> {
877 let username = enterprise::server_username();
879 let token = enterprise::server_token();
880 Ok(Self::new_with_token(username, token))
881 }
882
883 pub fn from_secrets_provider(address: &str) -> Result<Self, ()> {
887 let secrets_provider_name = Settings::new().get_string("enterprise.secretsProvider");
888 let provider = CoreSecretsProvider::by_name(&secrets_provider_name).ok_or(())?;
889 let cred_data_str = provider.get_data(address);
890 if cred_data_str.is_empty() {
891 return Err(());
892 }
893 let cred_data: serde_json::Value = serde_json::from_str(&cred_data_str).map_err(|_| ())?;
894 let username = cred_data["username"].as_str().ok_or(())?;
895 let token = cred_data["token"].as_str().ok_or(())?;
896 Ok(Self::new_with_token(
897 username.to_string(),
898 token.to_string(),
899 ))
900 }
901
902 pub fn from_env_variables() -> Result<Self, VarError> {
903 let username = std::env::var("BN_ENTERPRISE_USERNAME")?;
904 let password = std::env::var("BN_ENTERPRISE_PASSWORD")?;
905 Ok(ConnectionOptions::new_with_password(username, password))
906 }
907}