fce44ae9 Andy McCall 1 month ago ↶
1
2
3
use super :: format:: { short_id, signature_line, when} ;
4
use super :: open;
c6aa5bd9 Andy McCall 1 month ago ↶
5
use super :: json;
135212de Andy McCall 1 month ago ↶
6
use super :: ui;
fce44ae9 Andy McCall 1 month ago ↶
7
use anyhow:: Result;
8
use oxy_core:: hash:: ChangeHash;
83e0677b Andy McCall 1 month ago ↶
9
use oxy_core:: identity:: Identity;
28fc23d7 Andy McCall 1 month ago ↶
10
use oxy_core:: hash:: KeyId;
11
use oxy_core:: objects:: Hlc;
12
use oxy_core:: protocol:: SignedPolicy;
c6aa5bd9 Andy McCall 1 month ago ↶
13
use oxy_core:: repo:: { self , IdentityScope, PolicyUpdate, Repo, Rotation, SignatureStatus} ;
83e0677b Andy McCall 1 month ago ↶
14
use std:: path:: { Path, PathBuf} ;
fce44ae9 Andy McCall 1 month ago ↶
15
28fc23d7 Andy McCall 1 month ago ↶
16
const DAY_MS : u64 = 24 * 60 * 60 * 1000 ;
17
18
fn expiry_from_days ( days : u64 ) -> Option< u64 > {
19
( days > 0 ) .then ( | | Hlc:: now( ) .wall_ms + days * DAY_MS )
20
}
21
22
23
fn resolve_key ( repo : & Repo, prefix : & str ) -> Result< KeyId> {
24
if let Some ( k) = KeyId:: from_hex( prefix) {
25
return Ok ( k) ;
26
}
27
let mut candidates: Vec< KeyId> = Vec :: new( ) ;
28
if let Some ( ( _ , sp) ) = repo.current_policy ( ) ? {
29
candidates.extend ( sp.policy.identities.iter ( ) .map ( | i | i.hash ( ) ) ) ;
30
candidates.extend ( sp.policy.revoked.iter ( ) .copied ( ) ) ;
31
}
32
if let Some ( own) = repo.config.author_key {
33
candidates.push ( own) ;
34
if let Ok ( id) = repo.identity ( & own) {
35
candidates.extend ( id.delegates.iter ( ) .copied ( ) ) ;
36
}
37
}
38
candidates.sort ( ) ;
39
candidates.dedup ( ) ;
40
let matches: Vec< _ > = candidates.into_iter ( ) .filter ( | k | k.starts_with_hex ( prefix) ) .collect ( ) ;
41
match matches.len ( ) {
42
1 => Ok ( matches[ 0 ] ) ,
43
0 => anyhow:: bail! ( " no known key starts with {prefix:?}" ) ,
44
_ => anyhow:: bail! ( " {prefix:?} is ambiguous" ) ,
45
}
46
}
47
48
49
50
fn report_update ( u : & PolicyUpdate, out : Option< PathBuf> ) -> Result< ( ) > {
51
if u.applied {
52
println! ( " {} policy serial {} {} " , ui:: done( " Applied" ) , u.policy.serial, ui:: dim( " (push to publish it)" ) ) ;
53
return Ok ( ( ) ) ;
54
}
55
let path = out.unwrap_or_else ( | | PathBuf:: from( format! ( " policy-{} .proposal" , u.policy.serial) ) ) ;
56
std:: fs:: write( & path, u.signed ( ) .encode ( ) ) ? ;
57
println! (
58
" {} policy serial {} needs {} of {} root signatures; has {} . Wrote {} for another root to run {} " ,
59
ui:: caution( ) ,
60
u.policy.serial,
61
u.policy.root_threshold,
62
u.policy.roots.len ( ) ,
63
u.sigs.len ( ) ,
64
ui:: path( path.display ( ) ) ,
65
ui:: dim( format! ( " oxy policy sign {} " , path.display ( ) ) )
66
) ;
67
Ok ( ( ) )
68
}
69
70
fn report_rotation ( r : & Rotation, verb : & str ) -> Result< ( ) > {
71
println! ( " {} identity {} <{} >" , ui:: done( verb) , ui:: author( & r.identity.name) , r.identity.email) ;
72
println! ( " {} " , ui:: kv( " old key" , ui:: dim( r.old.to_hex ( ) ) ) ) ;
73
println! ( " {} " , ui:: kv( " new key" , r.new.to_hex ( ) ) ) ;
74
if ! r.identity.delegates.is_empty ( ) {
75
println! ( " {} " , ui:: kv( " delegates" , r.identity.delegates.iter ( ) .map ( | d | d.short ( 12 ) ) .collect:: < Vec< _ > > ( ) .join ( " " ) ) ) ;
76
}
77
if let Some ( e) = r.identity.expires {
78
println! ( " {} " , ui:: kv( " expires" , when ( e) ) ) ;
79
}
80
match & r.policy {
81
Some ( u) => report_update ( u, None ) ? ,
82
None => println! ( " {} {} " , ui:: note( ) , ui:: dim( " no policy names the old key; export the new identity for policy holders" ) ) ,
83
}
84
Ok ( ( ) )
85
}
86
87
pub fn create ( name : Option< String> , email : Option< String> , local : bool , expires : Option< u64 > ) -> Result< i32 > {
fce44ae9 Andy McCall 1 month ago ↶
88
let mut repo = open ( ) ? ;
89
let name = name.unwrap_or_else ( | | repo.config.author_name.clone ( ) ) ;
90
let email = email.unwrap_or_else ( | | repo.config.author_email.clone ( ) ) ;
91
let scope = if local { IdentityScope:: Repo } else { IdentityScope:: User } ;
28fc23d7 Andy McCall 1 month ago ↶
92
let ( key, _ ) = repo.create_identity_with ( & name, & email, scope, expires.and_then ( expiry_from_days) ) ? ;
94084087 Andy McCall 1 month ago ↶
93
println! ( " {} identity {} <{email} >" , ui:: done( " Created" ) , ui:: author( & name) ) ;
94
println! ( " {} " , ui:: kv( " key" , key) ) ;
95
println! ( " {} " , ui:: kv( " secret" , ui:: path( repo:: default_keys_dir( ) .display ( ) ) ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
96
match scope {
94084087 Andy McCall 1 month ago ↶
97
IdentityScope:: User => println! ( " {} " , ui:: kv( " scope" , format! ( " default author in {} " , ui:: path( repo.user_config_path ( ) .display ( ) ) ) ) ) ,
98
IdentityScope:: Repo => println! ( " {} " , ui:: kv( " scope" , " this repository's .oxy/config only" ) ) ,
fce44ae9 Andy McCall 1 month ago ↶
99
}
100
if ! repo.config.sign_changes {
94084087 Andy McCall 1 month ago ↶
101
println! ( " {} {} " , ui:: note( ) , ui:: dim( " changes will carry this author; run oxy config --user core.sign change to sign each commit" ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
102
}
103
Ok ( 0 )
104
}
105
106
pub fn show ( ) -> Result< i32 > {
107
let repo = open ( ) ? ;
108
let Some ( key) = repo.config.author_key else {
109
println! ( " No identity configured; run `oxy identity create`." ) ;
110
return Ok ( 0 ) ;
111
} ;
112
let id = repo.identity ( & key) ? ;
94084087 Andy McCall 1 month ago ↶
113
println! ( " {} {} <{} >" , ui:: section( " identity" ) , ui:: author( & id.name) , id.email) ;
114
println! ( " {} " , ui:: kv( " key" , key) ) ;
115
println! ( " {} " , ui:: kv( " created" , format! ( " {} {} " , when ( id.created) , ui:: dim( format! ( " ({} )" , ui:: ago( id.created) ) ) ) ) ) ;
28fc23d7 Andy McCall 1 month ago ↶
116
if let Some ( e) = id.expires {
117
let expired = id.is_expired_at ( Hlc:: now( ) .wall_ms) ;
118
println! ( " {} " , ui:: kv( " expires" , if expired { ui:: err( format! ( " {} EXPIRED" , when ( e) ) ) } else { when ( e) } ) ) ;
119
}
120
if let Some ( p) = id.previous {
121
println! ( " {} " , ui:: kv( " previous" , ui:: dim( p.short ( 12 ) ) ) ) ;
122
}
123
if ! id.delegates.is_empty ( ) {
124
println! ( " {} " , ui:: kv( " delegates" , id.delegates.iter ( ) .map ( | d | d.short ( 12 ) ) .collect:: < Vec< _ > > ( ) .join ( " " ) ) ) ;
125
}
126
if repo.is_revoked ( & key) ? {
127
println! ( " {} " , ui:: kv( " status" , ui:: err( format! ( " {} REVOKED" , ui:: glyph( " ✖" , " x" ) ) ) ) ) ;
128
}
94084087 Andy McCall 1 month ago ↶
129
println! ( " {} " , ui:: kv( " secret" , if repo.secret_key ( ) ? .is_some ( ) { ui:: ok( format! ( " {} available" , ui:: glyph( " ✔" , " *" ) ) ) } else { ui:: err( format! ( " {} not found in the key directory" , ui:: glyph( " ✖" , " x" ) ) ) } ) ) ;
130
println! ( " {} " , ui:: kv( " signing" , if repo.config.sign_changes { " every change (core.sign = change)" } else { " at push via seals (core.sign = seal)" } ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
131
Ok ( 0 )
132
}
133
c6aa5bd9 Andy McCall 1 month ago ↶
134
pub fn verify ( change : Option< & str > , as_json : bool ) -> Result< i32 > {
fce44ae9 Andy McCall 1 month ago ↶
135
let repo = open ( ) ? ;
136
let hashes: Vec< ChangeHash> = match change {
137
Some ( c) => vec! [ repo.resolve_change ( c) ? ] ,
138
None => repo.log ( ) ? .into_iter ( ) .filter ( | e | ! e.is_working_copy) .map ( | e | e.hash ) .collect ( ) ,
139
} ;
140
let mut bad = 0 ;
c6aa5bd9 Andy McCall 1 month ago ↶
141
if as_json {
142
let mut items = Vec :: new( ) ;
143
for h in & hashes {
144
let Ok ( c) = repo.store ( ) .get_change ( h) else { continue } ;
145
let ( status, key, name, revoked, reason) = match repo.authenticate_change ( h) ? {
146
SignatureStatus:: Unsigned => ( " unsigned" , None , None , false , None ) ,
147
SignatureStatus:: Valid { key, name, sealed, revoked, .. } => ( if sealed { " sealed" } else { " signed" } , Some ( key.to_hex ( ) ) , Some ( name) , revoked, None ) ,
148
SignatureStatus:: Invalid( why) => {
149
bad + = 1 ;
150
( " invalid" , None , None , false , Some ( why) )
151
}
152
} ;
153
items.push ( json:: obj( [
154
( " hash" , json:: s( & h.to_hex ( ) ) ) ,
155
( " change_id" , json:: s( & c.change_id.to_hex ( ) ) ) ,
156
( " status" , json:: s( status) ) ,
157
( " key" , json:: opt( key.as_deref ( ) , json:: s) ) ,
158
( " name" , json:: opt( name.as_deref ( ) , json:: s) ) ,
159
( " revoked" , json:: b( revoked) ) ,
160
( " reason" , json:: opt( reason.as_deref ( ) , json:: s) ) ,
161
] ) ) ;
162
}
163
println! ( " {} " , json:: arr( items) ) ;
164
return Ok ( if bad > 0 { 1 } else { 0 } ) ;
165
}
fce44ae9 Andy McCall 1 month ago ↶
166
for h in hashes {
2863456a Andy McCall 1 month ago ↶
167
let Ok ( c) = repo.store ( ) .get_change ( & h) else {
168
println! ( " {} {} " , ui:: hash( format! ( " #{} " , h.short ( 12 ) ) ) , ui:: dim( " not present (shallow)" ) ) ;
169
continue ;
170
} ;
fce44ae9 Andy McCall 1 month ago ↶
171
let line = signature_line ( & repo, & h) ? ;
135212de Andy McCall 1 month ago ↶
172
let styled = if line.starts_with ( " INVALID" ) {
fce44ae9 Andy McCall 1 month ago ↶
173
bad + = 1 ;
94084087 Andy McCall 1 month ago ↶
174
ui:: err( format! ( " {} {line} " , ui:: glyph( " ✖" , " x" ) ) )
135212de Andy McCall 1 month ago ↶
175
} else if line = = " unsigned" {
94084087 Andy McCall 1 month ago ↶
176
ui:: dim( format! ( " {} {line} " , ui:: glyph( " ·" , " -" ) ) )
135212de Andy McCall 1 month ago ↶
177
} else {
94084087 Andy McCall 1 month ago ↶
178
ui:: ok( format! ( " {} {line} " , ui:: glyph( " ✔" , " v" ) ) )
135212de Andy McCall 1 month ago ↶
179
} ;
180
println! ( " {} {} {styled} " , ui:: id( short_id ( & c) ) , ui:: hash( format! ( " #{} " , h.short ( 12 ) ) ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
181
}
182
Ok ( if bad > 0 { 1 } else { 0 } )
183
}
184
185
pub fn config ( key : & str , value : & str , user : bool ) -> Result< i32 > {
186
if user {
187
let path = repo:: default_user_config( ) ;
188
repo:: Config:: set_user( & path, key, value) ? ;
94084087 Andy McCall 1 month ago ↶
189
println! ( " {} {key} = {value} {} " , ui:: done( " Set" ) , ui:: dim( format! ( " (in {} )" , path.display ( ) ) ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
190
} else {
191
let mut repo = open ( ) ? ;
192
repo.config.set ( key, value) ? ;
193
repo.save_config ( ) ? ;
94084087 Andy McCall 1 month ago ↶
194
println! ( " {} {key} = {value} {} " , ui:: done( " Set" ) , ui:: dim( " (in this repository)" ) ) ;
fce44ae9 Andy McCall 1 month ago ↶
195
}
196
Ok ( 0 )
197
}
83e0677b Andy McCall 1 month ago ↶
198
199
pub fn export ( path : Option< PathBuf> ) -> Result< i32 > {
200
let repo = open ( ) ? ;
201
let Some ( key) = repo.config.author_key else {
202
anyhow:: bail! ( " no identity configured; run `oxy identity create`" ) ;
203
} ;
204
let id = repo.identity ( & key) ? ;
205
let path = path.unwrap_or_else ( | | PathBuf:: from( format! ( " {} .identity" , key.short ( 16 ) ) ) ) ;
206
std:: fs:: write( & path, id.encode ( ) ) ? ;
94084087 Andy McCall 1 month ago ↶
207
println! ( " {} identity {} <{} > ({} ) to {} " , ui:: done( " Wrote" ) , ui:: author( & id.name) , id.email, key.short ( 12 ) , ui:: path( path.display ( ) ) ) ;
83e0677b Andy McCall 1 month ago ↶
208
Ok ( 0 )
209
}
210
28fc23d7 Andy McCall 1 month ago ↶
211
pub fn rotate ( ) -> Result< i32 > {
212
let mut repo = open ( ) ? ;
213
let r = repo.rotate_identity ( ) ? ;
214
report_rotation ( & r, " Rotated" ) ? ;
215
Ok ( 0 )
216
}
217
218
pub fn renew ( expires : u64 ) -> Result< i32 > {
219
let mut repo = open ( ) ? ;
220
let r = repo.renew_identity ( expiry_from_days ( expires) ) ? ;
221
report_rotation ( & r, " Renewed" ) ? ;
222
Ok ( 0 )
223
}
224
225
pub fn revoke ( key : Option< & str > , reason : & str ) -> Result< i32 > {
226
let repo = open ( ) ? ;
227
let key = match key {
228
Some ( k) => resolve_key ( & repo, k) ? ,
229
None => repo.config.author_key.ok_or_else ( | | anyhow:: anyhow! ( " no identity configured" ) ) ? ,
230
} ;
231
let update = repo.revoke_identity ( & key, reason) ? ;
232
println! ( " {} key {} {} " , ui:: done( " Revoked" ) , ui:: hash( key.short ( 12 ) ) , if reason.is_empty ( ) { String :: new( ) } else { format! ( " : {reason} " ) } ) ;
233
match update {
234
Some ( u) => report_update ( & u, None ) ? ,
235
None => println! ( " {} {} " , ui:: note( ) , ui:: dim( " recorded locally; no policy named the key" ) ) ,
236
}
237
if repo.config.author_key = = Some ( key) {
238
println! ( " {} {} " , ui:: caution( ) , ui:: warn( " this was your configured key; run oxy identity create or rotate" ) ) ;
239
}
240
Ok ( 0 )
241
}
242
243
pub fn delegate_add ( identity_file : & Path) -> Result< i32 > {
244
let mut repo = open ( ) ? ;
245
let id = Identity:: decode( & std:: fs:: read( identity_file) ? ) ? ;
246
let r = repo.delegate_add ( id) ? ;
247
report_rotation ( & r, " Delegated" ) ? ;
248
Ok ( 0 )
249
}
250
251
pub fn delegate_remove ( key : & str ) -> Result< i32 > {
252
let mut repo = open ( ) ? ;
253
let k = resolve_key ( & repo, key) ? ;
254
let r = repo.delegate_remove ( & k) ? ;
255
report_rotation ( & r, " Undelegated" ) ? ;
256
Ok ( 0 )
257
}
258
259
pub fn delegate_list ( ) -> Result< i32 > {
260
let repo = open ( ) ? ;
261
let Some ( key) = repo.config.author_key else {
262
println! ( " No identity configured; run `oxy identity create`." ) ;
263
return Ok ( 0 ) ;
264
} ;
265
let id = repo.identity ( & key) ? ;
266
if id.delegates.is_empty ( ) {
267
println! ( " {} " , ui:: dim( " No delegates." ) ) ;
268
}
269
for d in & id.delegates {
270
let who = repo.identity ( d) .map ( | i | format! ( " {} <{} >" , i.name, i.email) ) .unwrap_or_else ( | _| " (unknown identity)" .into ( ) ) ;
271
println! ( " {} {} {} " , ui:: ok( ui:: glyph( " 🔑" , " *" ) ) , ui:: hash( d.short ( 12 ) ) , ui:: author( who) ) ;
272
}
273
Ok ( 0 )
274
}
275
276
pub fn policy_root_add ( identity_file : & Path, out : Option< PathBuf> ) -> Result< i32 > {
277
let repo = open ( ) ? ;
278
let id = Identity:: decode( & std:: fs:: read( identity_file) ? ) ? ;
279
let key = id.hash ( ) ;
280
let u = repo.policy_root_add ( id.clone ( ) ) ? ;
281
println! ( " {} root {} <{} > ({} )" , ui:: done( " Added" ) , ui:: author( & id.name) , id.email, key.short ( 12 ) ) ;
282
report_update ( & u, out) ? ;
283
Ok ( 0 )
284
}
285
286
pub fn policy_root_remove ( key : & str , out : Option< PathBuf> ) -> Result< i32 > {
287
let repo = open ( ) ? ;
288
let k = resolve_key ( & repo, key) ? ;
289
let u = repo.policy_root_remove ( & k) ? ;
290
println! ( " {} root {} " , ui:: done( " Removed" ) , ui:: hash( k.short ( 12 ) ) ) ;
291
report_update ( & u, out) ? ;
292
Ok ( 0 )
293
}
294
295
pub fn policy_threshold ( n : u32 , out : Option< PathBuf> ) -> Result< i32 > {
296
let repo = open ( ) ? ;
297
let u = repo.policy_set_threshold ( n) ? ;
298
println! ( " {} root threshold to {n} " , ui:: done( " Set" ) ) ;
299
report_update ( & u, out) ? ;
300
Ok ( 0 )
301
}
302
303
pub fn policy_sign ( proposal : & Path, out : Option< PathBuf> ) -> Result< i32 > {
304
let repo = open ( ) ? ;
305
let sp = SignedPolicy:: decode( & std:: fs:: read( proposal) ? ) ? ;
306
let u = repo.policy_sign ( sp) ? ;
307
println! ( " {} policy serial {} " , ui:: done( " Signed" ) , u.policy.serial) ;
308
report_update ( & u, Some ( out.unwrap_or_else ( | | proposal.to_path_buf ( ) ) ) ) ? ;
309
Ok ( 0 )
310
}
311
83e0677b Andy McCall 1 month ago ↶
312
pub fn policy_init ( ) -> Result< i32 > {
313
let repo = open ( ) ? ;
314
let sp = repo.policy_init ( ) ? ;
28fc23d7 Andy McCall 1 month ago ↶
315
println! ( " {} policy with root key {} " , ui:: done( " Created" ) , sp.policy.roots[ 0 ] .short ( 12 ) ) ;
316
report_update ( & sp, None ) ? ;
83e0677b Andy McCall 1 month ago ↶
317
Ok ( 0 )
318
}
319
c6aa5bd9 Andy McCall 1 month ago ↶
320
pub fn policy_show ( as_json : bool ) -> Result< i32 > {
83e0677b Andy McCall 1 month ago ↶
321
let repo = open ( ) ? ;
322
let Some ( ( r, sp) ) = repo.current_policy ( ) ? else {
c6aa5bd9 Andy McCall 1 month ago ↶
323
if as_json {
324
println! ( " null" ) ;
325
} else {
326
println! ( " No policy: this repository is unverified. Run `oxy policy init` to create one." ) ;
327
}
83e0677b Andy McCall 1 month ago ↶
328
return Ok ( 0 ) ;
329
} ;
330
let p = & sp.policy;
c6aa5bd9 Andy McCall 1 month ago ↶
331
if as_json {
332
let ident = | k : & oxy_core:: hash:: KeyId| {
333
let ( name, email) = p.identity ( k) .map ( | i | ( i.name.clone ( ) , i.email.clone ( ) ) ) .unwrap_or_default ( ) ;
334
json:: obj( [ ( " key" , json:: s( & k.to_hex ( ) ) ) , ( " name" , json:: s( & name) ) , ( " email" , json:: s( & email) ) ] )
335
} ;
336
println! (
337
" {} " ,
338
json:: obj( [
339
( " hash" , json:: s( & r.to_hex ( ) ) ) ,
340
( " serial" , json:: n( p.serial) ) ,
341
( " expires_ms" , json:: n( p.expires) ) ,
342
( " expired" , json:: b( p.is_expired ( oxy_core:: objects:: Hlc:: now( ) .wall_ms) ) ) ,
343
( " root_threshold" , json:: n( p.root_threshold) ) ,
344
( " roots" , json:: arr( p.roots.iter ( ) .map ( ident) ) ) ,
345
(
346
" rules" ,
347
json:: arr( p.rules.iter ( ) .map ( | rule | {
348
json:: obj( [
349
( " pattern" , json:: s( & rule.pattern) ) ,
350
( " keys" , json:: arr( rule.keys.iter ( ) .map ( ident) ) ) ,
351
( " threshold" , json:: n( rule.threshold) ) ,
352
( " require" , json:: s( rule.require.name ( ) ) ) ,
353
( " allow_conflicts" , json:: b( rule.allow_conflicts) ) ,
354
] )
355
} ) ) ,
356
) ,
357
( " revoked" , json:: arr( p.revoked.iter ( ) .map ( ident) ) ) ,
358
] )
359
) ;
360
return Ok ( 0 ) ;
361
}
83e0677b Andy McCall 1 month ago ↶
362
let name = | k : & oxy_core:: hash:: KeyId| p.identity ( k) .map ( | i | format! ( " {} <{} >" , i.name, i.email) ) .unwrap_or_else ( | | " (unknown identity)" .into ( ) ) ;
94084087 Andy McCall 1 month ago ↶
363
let expired = p.is_expired ( oxy_core:: objects:: Hlc:: now( ) .wall_ms) ;
364
println! ( " {} {} {} serial {} {} expires {} {} " , ui:: section( " policy" ) , ui:: hash( format! ( " #{} " , r.short ( 12 ) ) ) , ui:: sep( ) , p.serial, ui:: sep( ) , when ( p.expires) , if expired { ui:: err( format! ( " {} EXPIRED" , ui:: glyph( " ✖" , " x" ) ) ) } else { String :: new( ) } ) ;
365
println! ( " {} " , ui:: section( format! ( " roots {} " , ui:: dim( format! ( " (threshold {} )" , p.root_threshold) ) ) ) ) ;
83e0677b Andy McCall 1 month ago ↶
366
for k in & p.roots {
94084087 Andy McCall 1 month ago ↶
367
println! ( " {} {} {} " , ui:: ok( ui:: glyph( " 🔑" , " *" ) ) , ui:: hash( k.short ( 12 ) ) , ui:: author( name ( k) ) ) ;
83e0677b Andy McCall 1 month ago ↶
368
}
94084087 Andy McCall 1 month ago ↶
369
println! ( " {} " , ui:: section( " rules" ) ) ;
83e0677b Andy McCall 1 month ago ↶
370
for rule in & p.rules {
94084087 Andy McCall 1 month ago ↶
371
println! ( " {} {:12} {} " , ui:: ok( ui:: glyph( " ▸" , " >" ) ) , ui:: bookmark( & rule.pattern) , ui:: dim( format! ( " threshold {} , require {} , conflicts {} " , rule.threshold, rule.require.name ( ) , if rule.allow_conflicts { " allowed" } else { " refused" } ) ) ) ;
83e0677b Andy McCall 1 month ago ↶
372
for k in & rule.keys {
94084087 Andy McCall 1 month ago ↶
373
println! ( " {} {} " , ui:: hash( k.short ( 12 ) ) , ui:: author( name ( k) ) ) ;
83e0677b Andy McCall 1 month ago ↶
374
}
375
}
376
if ! p.revoked.is_empty ( ) {
94084087 Andy McCall 1 month ago ↶
377
println! ( " {} " , ui:: section( " revoked" ) ) ;
83e0677b Andy McCall 1 month ago ↶
378
for k in & p.revoked {
94084087 Andy McCall 1 month ago ↶
379
println! ( " {} {} {} " , ui:: err( ui:: glyph( " ✖" , " x" ) ) , ui:: hash( k.short ( 12 ) ) , ui:: author( name ( k) ) ) ;
83e0677b Andy McCall 1 month ago ↶
380
}
381
}
382
Ok ( 0 )
383
}
384
28fc23d7 Andy McCall 1 month ago ↶
385
pub fn policy_grant ( identity_file : & Path, pattern : Option< & str > , out : Option< PathBuf> ) -> Result< i32 > {
83e0677b Andy McCall 1 month ago ↶
386
let repo = open ( ) ? ;
387
let id = Identity:: decode( & std:: fs:: read( identity_file) ? ) ? ;
388
let key = id.hash ( ) ;
28fc23d7 Andy McCall 1 month ago ↶
389
let u = repo.policy_grant ( id.clone ( ) , pattern) ? ;
390
println! ( " {} {} <{} > ({} ) on {} " , ui:: done( " Granted" ) , ui:: author( & id.name) , id.email, key.short ( 12 ) , ui:: bookmark( pattern.unwrap_or ( " *" ) ) ) ;
391
report_update ( & u, out) ? ;
83e0677b Andy McCall 1 month ago ↶
392
Ok ( 0 )
393
}
394
28fc23d7 Andy McCall 1 month ago ↶
395
pub fn policy_revoke ( key : & str , out : Option< PathBuf> ) -> Result< i32 > {
83e0677b Andy McCall 1 month ago ↶
396
let repo = open ( ) ? ;
28fc23d7 Andy McCall 1 month ago ↶
397
if repo.current_policy ( ) ? .is_none ( ) {
83e0677b Andy McCall 1 month ago ↶
398
anyhow:: bail! ( " this repository has no policy" ) ;
28fc23d7 Andy McCall 1 month ago ↶
399
}
400
let full = resolve_key ( & repo, key) ? ;
401
let u = repo.policy_revoke ( & full) ? ;
402
println! ( " {} {} " , ui:: done( " Revoked" ) , ui:: hash( full.short ( 12 ) ) ) ;
403
report_update ( & u, out) ? ;
83e0677b Andy McCall 1 month ago ↶
404
Ok ( 0 )
405
}