1
1
use async_trait:: async_trait;
2
2
use clap:: Args ;
3
3
use colorful:: Colorful ;
4
+ use serde:: Serialize ;
5
+ use std:: collections:: BTreeMap ;
6
+ use std:: fmt:: Display ;
4
7
5
8
use ockam:: identity:: Identifier ;
6
9
use ockam:: Context ;
7
10
use ockam_api:: authenticator:: direct:: Members ;
8
- use ockam_api:: fmt_ok ;
9
- use ockam_api:: nodes :: InMemoryNode ;
11
+ use ockam_api:: colors :: color_primary ;
12
+ use ockam_api:: { fmt_log , fmt_ok } ;
10
13
use ockam_multiaddr:: MultiAddr ;
11
14
12
- use crate :: project_member:: { create_authority_client , create_member_attributes, get_project } ;
15
+ use crate :: project_member:: { authority_client , create_member_attributes} ;
13
16
use crate :: shared_args:: { IdentityOpts , RetryOpts } ;
14
17
use crate :: { docs, Command , CommandGlobalOpts , Error } ;
15
18
16
19
const LONG_ABOUT : & str = include_str ! ( "./static/add/long_about.txt" ) ;
17
20
const AFTER_LONG_HELP : & str = include_str ! ( "./static/add/after_long_help.txt" ) ;
18
21
19
- /// This attribute in credential allows member to create a relay on the Project node, the name of the relay should be
20
- /// equal to the value of that attribute. If the value is `*` then any name is allowed
22
+ /// This credential attribute allows a member to establish a relay on the Project node.
23
+ /// The relay's name should match this attribute's value.
24
+ /// If the attribute's value is "*", it implies that any name is acceptable for the relay.
21
25
pub const OCKAM_RELAY_ATTRIBUTE : & str = "ockam-relay" ;
22
26
23
- /// Add members to a Project, as an authorized enroller directly
27
+ /// Add or update members of a Project, directly as an authorized enroller
24
28
#[ derive( Clone , Debug , Args ) ]
25
29
#[ command(
26
30
long_about = docs:: about( LONG_ABOUT ) ,
@@ -30,22 +34,29 @@ pub struct AddCommand {
30
34
#[ command( flatten) ]
31
35
identity_opts : IdentityOpts ,
32
36
33
- /// Which project add member to
37
+ /// The route to the Project to which a member should be added
34
38
#[ arg( long, short, value_name = "ROUTE_TO_PROJECT" ) ]
35
39
to : Option < MultiAddr > ,
36
40
41
+ /// The Identifier of the member to add
37
42
#[ arg( value_name = "IDENTIFIER" ) ]
38
43
member : Identifier ,
39
44
40
- /// Attributes in `key=value` format to be attached to the member. You can specify this option multiple times for multiple attributes
45
+ /// Attributes in `key=value` format to be attached to the member.
46
+ /// You can specify this option multiple times for multiple attributes
41
47
#[ arg( short, long = "attribute" , value_name = "ATTRIBUTE" ) ]
42
48
attributes : Vec < String > ,
43
49
44
- /// Name of the relay that the identity will be allowed to create. This name is transformed into attributes to prevent collisions when creating relay names. For example: `--relay foo` is shorthand for `--attribute ockam-relay=foo`
45
- #[ arg( long = "relay" , value_name = "ENROLLEE_ALLOWED_RELAY_NAME" ) ]
50
+ /// Name of the relay that the identity will be allowed to create.
51
+ /// This name is transformed into an attribute, and it's used to prevent collisions with other relays names.
52
+ /// E.g. `--relay foo` is a shorthand for `--attribute ockam-relay=foo`
53
+ #[ arg( long = "relay" , value_name = "ALLOWED_RELAY_NAME" ) ]
46
54
allowed_relay_name : Option < String > ,
47
55
48
- /// Add the enroller role. If you specify it, this flag is transformed into the attributes `--attribute ockam-role=enroller`. This role allows the Identity using the ticket to enroll other Identities into the Project, typically something that only admins can do
56
+ /// Set the enroller role for the member.
57
+ /// When this flag is set, it is transformed into the attribute `ockam-role=enroller`.
58
+ /// This role grants the Identity holding the ticket the ability to enroll other Identities
59
+ /// into the Project, which is a privilege usually reserved for administrators.
49
60
#[ arg( long = "enroller" ) ]
50
61
enroller : bool ,
51
62
@@ -62,36 +73,69 @@ impl Command for AddCommand {
62
73
}
63
74
64
75
async fn async_run ( self , ctx : & Context , opts : CommandGlobalOpts ) -> crate :: Result < ( ) > {
65
- let project = get_project ( & opts. state , & self . to ) . await ?;
76
+ let ( authority_node_client, project_name) =
77
+ authority_client ( ctx, & opts, & self . identity_opts , & self . to ) . await ?;
66
78
67
- let node = InMemoryNode :: start_with_project_name (
68
- ctx,
69
- & opts. state ,
70
- Some ( project. name ( ) . to_string ( ) ) ,
71
- )
72
- . await ?;
73
-
74
- let authority_node_client =
75
- create_authority_client ( & node, & opts. state , & self . identity_opts , & project) . await ?;
79
+ let attributes =
80
+ create_member_attributes ( & self . attributes , & self . allowed_relay_name , self . enroller ) ?;
76
81
77
82
authority_node_client
78
- . add_member (
79
- ctx,
80
- self . member . clone ( ) ,
81
- create_member_attributes (
82
- & self . attributes ,
83
- & self . allowed_relay_name ,
84
- self . enroller ,
85
- ) ?,
86
- )
83
+ . add_member ( ctx, self . member . clone ( ) , attributes. clone ( ) )
87
84
. await
88
85
. map_err ( Error :: Retry ) ?;
89
86
90
- opts. terminal . stdout ( ) . plain ( fmt_ok ! (
91
- "Identifier {} is now a Project member. It can get a credential and access Project resources, like portals of other members" ,
92
- self . member
93
- ) ) ;
87
+ let output = AddMemberOutput {
88
+ project : project_name,
89
+ identifier : self . member . clone ( ) ,
90
+ attributes,
91
+ } ;
92
+
93
+ opts. terminal
94
+ . stdout ( )
95
+ . plain ( output. to_string ( ) )
96
+ . json_obj ( & output) ?
97
+ . write_line ( ) ?;
98
+
99
+ Ok ( ( ) )
100
+ }
101
+ }
102
+
103
+ #[ derive( Serialize ) ]
104
+ struct AddMemberOutput {
105
+ project : String ,
106
+ identifier : Identifier ,
107
+ attributes : BTreeMap < String , String > ,
108
+ }
94
109
110
+ impl Display for AddMemberOutput {
111
+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
112
+ writeln ! (
113
+ f,
114
+ "{}" ,
115
+ fmt_ok!(
116
+ "Identifier {} is now a member of the Project {}" ,
117
+ color_primary( self . identifier. to_string( ) ) ,
118
+ color_primary( & self . project)
119
+ )
120
+ ) ?;
121
+ if !self . attributes . is_empty ( ) {
122
+ let attributes = self
123
+ . attributes
124
+ . iter ( )
125
+ . map ( |( k, v) | format ! ( "{}={}" , k, v) )
126
+ . collect :: < Vec < _ > > ( )
127
+ . join ( ", " ) ;
128
+ writeln ! (
129
+ f,
130
+ "{}" ,
131
+ fmt_log!( "With attributes: {}" , color_primary( attributes) )
132
+ ) ?;
133
+ }
134
+ writeln ! (
135
+ f,
136
+ "{}" ,
137
+ fmt_log!( "It can get a credential and access Project resources, like portals of other members" )
138
+ ) ?;
95
139
Ok ( ( ) )
96
140
}
97
141
}
0 commit comments