Skip to content

Commit a371239

Browse files
committed
Add support for read-write API keys and MCP tools for finalizing reviews
1 parent 7b21eb5 commit a371239

File tree

15 files changed

+487
-149
lines changed

15 files changed

+487
-149
lines changed

assets/vue/ApiKeys.vue

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
<span class="real">{{ key.apiKey }}</span>
4949
</span>
5050
</td>
51-
<td>read-only</td>
51+
<td>{{ key.type }}</td>
5252
<td>{{ key.description }}</td>
5353
<td>{{ key.expires }}</td>
5454
<td class="text-center">
@@ -80,6 +80,13 @@
8080
<label for="api-key-description" class="col-form-label">Description</label>
8181
<input v-model="apiKeyDescription" class="form-control" id="api-key-description" />
8282
</div>
83+
<div class="mb-3">
84+
<label for="api-key-type" class="col-form-label">Type</label>
85+
<select v-model="apiKeyType" class="form-select" id="api-key-type">
86+
<option value="read-only">Read-Only</option>
87+
<option value="read-write">Read-Write</option>
88+
</select>
89+
</div>
8390
<div class="mb-3">
8491
<label for="api-key-expires" class="col-form-label">Expires</label>
8592
<input v-model="apiKeyExpires" type="datetime-local" class="form-control" id="api-key-expires" />
@@ -117,6 +124,7 @@ export default {
117124
addApiKeyUrl: '/api_keys',
118125
apiKeys: null,
119126
apiKeyDescription: 'User API Key',
127+
apiKeyType: 'read-only',
120128
apiKeyExpires: new moment().add(365, 'days').format('YYYY-MM-DDTHH:mm'),
121129
lastCopied: null,
122130
refreshUrl: '/api_keys/meta'
@@ -132,7 +140,8 @@ export default {
132140
},
133141
async addApiKey() {
134142
const ua = new UserAgent({baseURL: window.location.href});
135-
await ua.post(this.addApiKeyUrl, {form: {description: this.apiKeyDescription, expires: this.apiKeyExpires}});
143+
const form = {description: this.apiKeyDescription, type: this.apiKeyType, expires: this.apiKeyExpires};
144+
await ua.post(this.addApiKeyUrl, {form});
136145
this.doApiRefresh();
137146
},
138147
async deleteApiKey(key) {
@@ -147,6 +156,7 @@ export default {
147156
id: key.id,
148157
apiKey: key.api_key,
149158
description: key.description,
159+
type: key.write_access ? 'read-write' : 'read-only',
150160
expires: moment(key.expires_epoch * 1000).fromNow(),
151161
removeUrl: `/api_keys/${key.id}`
152162
});

assets/vue/ReportMetadata.vue

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,10 @@
188188
<i class="fas fa-user"></i>
189189
</th>
190190
<th class="fit text-start noleftpad" scope="row">Reviewing User:</th>
191-
<td>{{ reviewingUser }}</td>
191+
<td>
192+
{{ reviewingUser }}
193+
<span v-if="pkgAiAssisted" class="ai-assisted-badge">(with AI Assistant <i class="fas fa-robot"></i>)</span>
194+
</td>
192195
</tr>
193196
</tbody>
194197
</table>
@@ -367,6 +370,7 @@ export default {
367370
hasSpdxReport: false,
368371
history: [],
369372
notice: null,
373+
pkgAiAssisted: false,
370374
pkgChecksum: null,
371375
pkgEmbargoed: false,
372376
pkgFiles: [],
@@ -439,6 +443,7 @@ export default {
439443
this.pkgUrl = data.package_url;
440444
this.pkgVersion = data.package_version;
441445
this.pkgEmbargoed = data.embargoed;
446+
this.pkgAiAssisted = data.ai_assisted;
442447
443448
this.pkgChecksum = data.package_checksum;
444449
this.checkoutUrl = `/reviews/file_view/${this.pkgId}`;

cpanfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@ requires 'Try::Tiny';
1818
requires 'YAML::XS';
1919
requires 'JSON::Validator';
2020
requires 'Digest::SHA1';
21-
requires 'MCP', '>= 0.06';
21+
requires 'MCP', '>= 0.07';

lib/Cavil/Controller/API.pm

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
package Cavil::Controller::API;
1717
use Mojo::Base 'Mojolicious::Controller', -signatures;
1818

19+
use Mojo::JSON qw(true false);
20+
1921
sub identify ($self) {
2022
my $name = $self->stash('name');
2123
my $checksum = $self->stash('checksum');
@@ -64,7 +66,14 @@ sub status ($self) {
6466
sub whoami ($self) {
6567
my $user = $self->current_user;
6668
my $id = $self->users->id_for_login($user);
67-
$self->render(json => {id => $id, user => $user});
69+
$self->render(
70+
json => {
71+
id => $id,
72+
user => $user,
73+
roles => $self->current_user_roles,
74+
write_access => $self->current_user_has_write_access ? true : false
75+
}
76+
);
6877
}
6978

7079
1;

lib/Cavil/Controller/APIKeys.pm

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@ use Mojo::Base 'Mojolicious::Controller', -signatures;
1919
sub create ($self) {
2020
my $validation = $self->validation;
2121
$validation->optional('description');
22+
$validation->required('type')->in(qw(read-only read-write));
2223
$validation->required('expires')->like(qr/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/);
2324
return $self->reply->json_validation_error if $validation->has_error;
2425

2526
my $owner = $self->users->id_for_login($self->current_user);
2627
my $api_key = $self->api_keys->create(
2728
owner => $owner,
2829
description => $validation->param('description'),
30+
type => $validation->param('type'),
2931
expires => $validation->param('expires')
3032
);
3133

lib/Cavil/Controller/Auth/APIKey.pm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ sub check ($self) {
2222
my $token = $1;
2323

2424
$self->_denied and return undef unless defined(my $user = $self->api_keys->find_by_key($token));
25-
$self->stash('api.user' => $user);
25+
$self->stash('cavil.api.user' => $user->{login}, 'cavil.api.write_access' => $user->{write_access});
2626

2727
return 1;
2828
}

lib/Cavil/Controller/Reviewer.pm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ sub review_package ($self) {
151151
die "Unknown state";
152152
}
153153
$pkg->{review_timestamp} = 1;
154+
$pkg->{ai_assisted} = 0;
154155

155156
$self->packages->update($pkg);
156157

lib/Cavil/Model/APIKeys.pm

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@ use Mojo::Base -base, -signatures;
44
has 'pg';
55

66
sub create ($self, %args) {
7-
my %data = (owner => $args{owner}, description => $args{description} // '', expires => $args{expires});
7+
my %data = (
8+
owner => $args{owner},
9+
description => $args{description} // '',
10+
write_access => $args{type} eq 'read-write' ? 1 : 0,
11+
expires => $args{expires}
12+
);
813
return $self->pg->db->insert('api_keys', \%data, {returning => '*'})->hash;
914
}
1015

@@ -13,7 +18,7 @@ sub find_by_key ($self, $key) {
1318
'SELECT * FROM api_keys ak JOIN bot_users bu ON ak.owner = bu.id
1419
WHERE ak.api_key = ? AND expires > NOW()', $key
1520
)->hash;
16-
return $user->{login};
21+
return {login => $user->{login}, write_access => $user->{write_access}};
1722
}
1823

1924
sub list ($self, $owner) {

lib/Cavil/Model/Packages.pm

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -582,8 +582,10 @@ sub unpacked ($self, $id) {
582582
}
583583

584584
sub update ($self, $pkg) {
585-
my %updates = map { exists $pkg->{$_} ? ($_ => $pkg->{$_}) : () }
586-
(qw(created checksum priority state obsolete result notice reviewed reviewing_user external_link embargoed));
585+
my %updates = map { exists $pkg->{$_} ? ($_ => $pkg->{$_}) : () } (
586+
qw(created checksum priority state obsolete result notice reviewed reviewing_user external_link),
587+
qw(embargoed ai_assisted)
588+
);
587589
$updates{reviewed} = \'now()' if $pkg->{review_timestamp};
588590
return $self->pg->db->update('bot_packages', \%updates, {id => $pkg->{id}});
589591
}

lib/Cavil/Model/Users.pm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ sub remove_role ($self, $id, $role) {
5757
}
5858

5959
sub roles ($self, $user) {
60-
return $self->pg->db->query('select roles from bot_users where login = ?', $user)->arrays->flatten->to_array;
60+
return $self->pg->db->query('select roles from bot_users where login = ?', $user)->arrays->flatten->sort->to_array;
6161
}
6262

6363
1;

0 commit comments

Comments
 (0)