Move OpenSSL-Query from omc-tools

Fixes #134

Co-authored-by: Matthias St. Pierre <matthias.st.pierre@ncp-e.com>

Reviewed-by: Matthias St. Pierre <Matthias.St.Pierre@ncp-e.com>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/tools/pull/170)
This commit is contained in:
Richard Levitte 2023-11-06 09:28:42 +01:00
parent cfc0cf920f
commit e31cc76864
9 changed files with 641 additions and 0 deletions

10
OpenSSL-Query/MANIFEST Normal file
View File

@ -0,0 +1,10 @@
lib/OpenSSL/Query.pm
lib/OpenSSL/Query/PersonREST.pm
lib/OpenSSL/Query/REST.pm
lib/OpenSSL/Query/ClaREST.pm
Makefile.PL
MANIFEST
META.yml
README.md
t/query.t
t/00-load.t

44
OpenSSL-Query/Makefile.PL Normal file
View File

@ -0,0 +1,44 @@
use 5.006;
use strict;
use warnings;
use inc::Module::Install;
name 'OpenSSL-Query';
module_name 'OpenSSL::Query';
version '1.2';
abstract '';
author q{Richard Levitte <levitte@openssl.org>};
license 'apache';
perl_version 5.006;
tests_recursive('t');
resources (
license => 'http://www.apache.org/licenses/LICENSE-2.0',
repository => 'https://github.com/openssl/tools.git',
bugtracker => 'https://github.com/openssl/tools/issues',
);
configure_requires (
'Module::Install' => 0,
);
build_requires (
'Test::More' => 0,
);
requires (
'Module::Load::Conditional' => 0,
'Class::Method::Modifiers' => 0,
'File::Spec' => 0,
'URI::Encode' => 0,
Moo => 0,
Carp => 0,
'LWP::UserAgent' => 0,
'LWP::Protocol::https' =>0,
);
install_as_site;
auto_install;
WriteAll;

74
OpenSSL-Query/README.md Normal file
View File

@ -0,0 +1,74 @@
OpenSSL::Query
==============
A module to query certain information about OpenSSL committers as well
as members of the OMC (OpenSSL Management Committee). These data are
usually interesting for other programs that need to verify identities,
whether a certain person holds a CLA, that sort of thing.
OpenSSL::Query is built to be able to handle several implementations
for access to the databases that hold the data. The default
implementation uses a RESTful API with JSON encoded responses,
OpenSSL::Query::REST.
Requirements
------------
OpenSSL::Query requires the following modules to build:
- Module::Install (debian package libmodule-install-perl)
OpenSSL::Query requires these extra modules to run:
- Class::Method::Modifiers (debian package libclass-method-modifiers-perl)
- Moo (debian package libmoo-perl)
- URI::Encode (debian package liburi-encode-perl)
- LWP::UserAgent
- LWP::Protocol::https
Any other module OpenSSL::Query depends on should be part of core
perl.
On Debian, you can use the following command to install the required packages:
sudo apt install libmodule-install-perl libclass-method-modifiers-perl libmoo-perl liburi-encode-perl
Installation
------------
perl Makefile.PL
make && sudo make install
Local installation
------------
For a local installation, you might want to consider using local::lib
(debian package liblocal-lib-perl). In that case, running Makefile.PL
is slightly different:
perl -Mlocal::lib Makefile.PL
Other than that, follow the instructions in "Installation" above.
To get the paths right permanently, you might want to consider adding
this in your `.bash_profile`, `.bashrc` och corresponding shell init
script:
eval "`perl -I$HOME/perl5/lib/perl5 -Mlocal::lib`"
Testing
-------
Testing is done like this:
make test
However, it requires the additional Perl module Dancer2 and
plackup (debian packages libdancer2-perl and libplack-perl)
and that a temporary query service is started as well.
This is part of QueryApp, and is started like this:
here=`pwd` # the directory OpenSSL-Query/
cd ../QueryApp # Or wherever you have it checked out
PERSONDB=./t/query_data/pdb.yaml CLADB=./t/query_data/cdb.txt \
PERL5LIB=./lib:$here/lib plackup bin/query.psgi

View File

@ -0,0 +1,160 @@
#! /usr/bin/env perl
#
# Copyright 2017 The OpenSSL Project Authors. All Rights Reserved.
#
# Licensed under the OpenSSL license (the "License"). You may not use
# this file except in compliance with the License. You can obtain a copy
# in the file LICENSE in the source distribution or at
# https://www.openssl.org/source/license.html
use strict;
package OpenSSL::Query;
use Carp;
our %register_impl = ();
sub import {
my $class = shift;
my $regtype = undef;
my $regname = undef;
my $regprio = 999; # Bottom feeders
while (scalar @_ > 0) {
my $arg = shift;
if ($arg eq '-register-cla') {
$regtype = 'cla';
$regname = shift;
} elsif ($arg eq '-register-person') {
$regtype = 'person';
$regname = shift;
} elsif ($arg eq '-priority') {
$regprio = shift;
} else {
croak "Unknown argument $arg";
return;
}
}
if (!defined($regtype) || !defined($regname)) {
croak "No proper module registration";
}
$register_impl{$regtype}->{$regprio}->{$regname} = 1;
}
sub _new_type {
my $self = shift;
my $type = shift;
my @args = @_;
my @packages =
map { (sort keys %{$register_impl{$type}->{$_}}) }
sort keys %{$register_impl{$type}};
my @objs = ();
while (@packages) {
my $obj = (shift @packages)->new(@args);
push @objs, $obj if $obj;
}
croak "No implementation for $type queries" unless @objs;
return @objs;
}
sub new {
my $class = shift;
my @args = @_;
my $self = {};
bless $self, $class;
foreach (('person', 'cla')) {
$self->{$_} = [ $self->_new_type($_, @args) ];
}
return $self;
}
sub _perform {
my $self = shift;
my $type = shift;
my $sub = shift;
my @errors = ();
foreach (@{$self->{$type}}) {
my @result = eval { $sub->($_, @_); };
return wantarray ? @result : $result[0] unless $@;
push @errors, $@;
}
croak join("\n", @errors);
}
# Person methods
sub list_people {
my $self = shift;
$self->_perform('person',
sub { my $obj = shift;
return $obj->list_people(@_) },
@_);
}
sub find_person {
my $self = shift;
$self->_perform('person',
sub { my $obj = shift;
return wantarray
? ($obj->find_person(@_))
: $obj->find_person(@_); },
@_);
}
sub find_person_tag {
my $self = shift;
$self->_perform('person',
sub { my $obj = shift;
return $obj->find_person_tag(@_) },
@_);
}
sub is_member_of {
my $self = shift;
$self->_perform('person',
sub { my $obj = shift;
return $obj->is_member_of(@_) },
@_);
}
# Group methods
sub members_of {
my $self = shift;
$self->_perform('person',
sub { my $obj = shift;
return $obj->members_of(@_) },
@_);
}
# Cla methods
sub has_cla {
my $self = shift;
$self->_perform('cla',
sub { my $obj = shift;
return $obj->has_cla(@_) },
@_);
}
sub list_clas {
my $self = shift;
$self->_perform('cla',
sub { my $obj = shift;
return $obj->list_clas(@_) },
@_);
}
1;

View File

@ -0,0 +1,53 @@
#! /usr/bin/env perl
#
# Copyright 2017 The OpenSSL Project Authors. All Rights Reserved.
#
# Licensed under the OpenSSL license (the "License"). You may not use
# this file except in compliance with the License. You can obtain a copy
# in the file LICENSE in the source distribution or at
# https://www.openssl.org/source/license.html
use strict;
package OpenSSL::Query::ClaREST;
use Carp;
use Moo;
use OpenSSL::Query qw(-register-cla OpenSSL::Query::ClaREST -priority 1);
use HTTP::Status qw(:is);
use LWP::UserAgent;
use URI::Encode qw(uri_encode uri_decode);
use JSON::PP;
use Data::Dumper;
has base_url => ( is => 'ro', default => 'https://api.openssl.org' );
has _clahandler => ( is => 'ro', builder => 1 );
sub _build__clahandler {
my $ua = LWP::UserAgent->new( keep_alive => 1 );
$ua->env_proxy;
return $ua;
}
sub has_cla {
my $self = shift;
my $id = shift;
if ($id =~ m|<(\S+\@\S+)>|) { $id = $1; }
croak "Malformed input ID" unless $id =~ m|^\S+(\@\S+)$|;
my $ua = $self->_clahandler;
my $json = $ua->get($self->base_url . '/0/HasCLA/'
. uri_encode($id, {encode_reserved => 1}));
croak "Server error: ", $json->message if is_server_error($json->code);
return $json->code == 200;
}
sub list_clas {
my $self = shift;
my $ua = $self->_clahandler;
my $json = $ua->get($self->base_url . '/0/CLAs');
croak "Server error: ", $json->message if is_server_error($json->code);
return $json->code == 200;
}
1;

View File

@ -0,0 +1,125 @@
#! /usr/bin/env perl
#
# Copyright 2017 The OpenSSL Project Authors. All Rights Reserved.
#
# Licensed under the OpenSSL license (the "License"). You may not use
# this file except in compliance with the License. You can obtain a copy
# in the file LICENSE in the source distribution or at
# https://www.openssl.org/source/license.html
use strict;
package OpenSSL::Query::PersonREST;
use Carp;
use Moo;
use OpenSSL::Query qw(-register-person OpenSSL::Query::PersonREST -priority 1);
use LWP::UserAgent;
use HTTP::Status qw(:is);
use URI::Encode qw(uri_encode uri_decode);
use JSON::PP;
use Data::Dumper;
has base_url => ( is => 'ro', default => 'https://api.openssl.org' );
has _personhandler => ( is => 'lazy', builder => 1 );
sub _build__personhandler {
my $ua = LWP::UserAgent->new( keep_alive => 1 );
$ua->env_proxy;
return $ua;
}
sub list_people {
my $self = shift;
my $ua = $self->_personhandler;
my $json = $ua->get($self->base_url . '/0/People');
croak "Server error: ", $json->message if is_server_error($json->code);
return () unless $json->code == 200;
my $decoded = decode_json $json->decoded_content;
return @$decoded;
}
sub _id_encode {
my $id = shift;
return $id if ref($id) eq "";
croak "Malformed input ID" if ref($id) ne "HASH" || scalar keys %$id != 1;
my $tag = (keys %$id)[0];
return $tag . ':' . $id->{$tag};
}
sub find_person {
my $self = shift;
my $id = _id_encode(shift);
my $ua = $self->_personhandler;
my $json = $ua->get($self->base_url . '/0/Person/'
. uri_encode($id, {encode_reserved => 1}));
croak "Server error: ", $json->message if is_server_error($json->code);
return () unless $json->code == 200;
my $decoded = decode_json $json->decoded_content;
return wantarray ? %$decoded : scalar keys %$decoded > 0;
}
sub find_person_tag {
my $self = shift;
my $id = _id_encode(shift);
my $tag = shift;
my $ua = $self->_personhandler;
my $json = $ua->get($self->base_url
. '/0/Person/'
. uri_encode($id, {encode_reserved => 1})
. '/ValueOfTag/'
. uri_encode ($tag, {encode_reserved => 1}));
croak "Server error: ", $json->message if is_server_error($json->code);
return undef unless $json->code == 200;
my $decoded = decode_json $json->decoded_content;
return $decoded->[0];
}
sub is_member_of {
my $self = shift;
my $id = _id_encode(shift);
my $group = shift;
my $ua = $self->_personhandler;
my $json = $ua->get($self->base_url
. '/0/Person/'
. uri_encode($id, {encode_reserved => 1})
. '/IsMemberOf/'
. uri_encode ($group, {encode_reserved => 1}));
croak "Server error: ", $json->message if is_server_error($json->code);
return 0 unless $json->code == 200;
my $decoded = decode_json $json->decoded_content;
return $decoded->[0];
}
# Group methods
sub members_of {
my $self = shift;
my $group = shift;
my $ua = $self->_personhandler;
my $json = $ua->get($self->base_url
. '/0/Group/'
. uri_encode($group, {encode_reserved => 1})
. '/Members');
croak "Server error: ", $json->message if is_server_error($json->code);
return () unless $json->code == 200;
my $decoded = decode_json $json->decoded_content;
return @$decoded;
}
1;

View File

@ -0,0 +1,15 @@
#! /usr/bin/env perl
#
# Copyright 2017 The OpenSSL Project Authors. All Rights Reserved.
#
# Licensed under the OpenSSL license (the "License"). You may not use
# this file except in compliance with the License. You can obtain a copy
# in the file LICENSE in the source distribution or at
# https://www.openssl.org/source/license.html
use strict;
package OpenSSL::Query::REST;
use OpenSSL::Query::PersonREST;
use OpenSSL::Query::ClaREST;
1;

13
OpenSSL-Query/t/00-load.t Normal file
View File

@ -0,0 +1,13 @@
#!perl -T
use 5.006;
use strict;
use warnings;
use Test::More;
plan tests => 1;
BEGIN {
use_ok( 'OpenSSL::Query::REST' ) || print "Bail out!\n";
}
#note( "Testing OpenSSL::Query $OpenSSL::Query::VERSION, Perl $], $^X" );

147
OpenSSL-Query/t/query.t Normal file
View File

@ -0,0 +1,147 @@
#! /usr/bin/env perl
# This means that 'dance' at the end of query.psgi will not start a built in
# service, but will simply return a coderef. This is useful to run this with
# diverse dispatchers as well as tests.
BEGIN { $ENV{DANCER_APPHANDLER} = 'PSGI';}
use strict;
use warnings;
use Test::More;
use OpenSSL::Query::REST;
use Data::Dumper;
plan tests => 16;
SKIP: {
my $query;
skip "No object for further operations", 8
unless ok( $query = eval { OpenSSL::Query->new(base_url => $ENV{BASE_URL}
// "http://localhost:5000") },
'Creating a OpenSSL::Query object' );
# print STDERR Dumper($query);
subtest 'Request of identity list' => sub {
plan tests => 1;
my @res = $query->list_people();
ok( scalar @res > 0, 'We got a list' );
note( Dumper( [ @res ] ) );
};
subtest 'Request of person data for Ray Bradbury' => sub {
plan tests => 2;
my $res1 = $query->find_person( 'Ray Bradbury' );
ok( $res1, 'Ray Bradbury is present' );
note( $res1 );
my %res2 = $query->find_person( 'Ray Bradbury' );
ok(scalar keys %res2 > 1, "Got Ray Bradbury's data" );
note( Dumper( { %res2 } ) );
};
subtest 'Request of person data for Ray Bradbury as full name' => sub {
plan tests => 2;
my $res1 = $query->find_person( { fullname => 'Ray Bradbury' } );
ok( $res1, 'Ray Bradbury is present' );
note( $res1 );
my %res2 = $query->find_person( 'Ray Bradbury' );
ok(scalar keys %res2 > 1, "Got Ray Bradbury's data" );
note( Dumper( { %res2 } ) );
};
subtest 'Request of membership in specific group for Ray Bradbury' => sub {
plan tests => 1;
my $res = $query->is_member_of( 'Ray Bradbury', 'scifi' );
ok( $res, "Ray Bradbury is member of scifi since ".( $res ? $res : "(unknown)" ) );
note( $res );
};
subtest 'Request of membership in specific group for Ray Bradbury as full name' => sub {
plan tests => 1;
my $res = $query->is_member_of( { fullname => 'Ray Bradbury' }, 'scifi' );
ok( $res, "Ray Bradbury is member of scifi since ".( $res ? $res : "(unknown)" ) );
note( $res );
};
subtest 'Request of "author" tag value for Ray Bradbury' => sub {
plan tests => 1;
my $res = $query->find_person_tag( 'Ray Bradbury', 'author' );
ok( $res, "The 'author' tag for Ray Bradbury is ".( $res ? $res : "(unknown)" ) );
note( Dumper $res );
};
subtest 'Request of "author" tag value for Ray Bradbury as full name' => sub {
plan tests => 1;
my $res = $query->find_person_tag( { fullname => 'Ray Bradbury' }, 'author' );
ok( $res, "The 'author' tag for Ray Bradbury is ".( $res ? $res : "(unknown)" ) );
note( Dumper $res );
};
subtest 'Request of CLA status for Ray Bradbury' => sub {
plan tests => 1;
my $res = $query->has_cla( 'ray@Ourplace.com' );
ok( $res, 'Ray Bradbury has CLA as ray@Ourplace.com' );
note( $res );
};
subtest 'Request of membership in the group "writers"' => sub {
plan tests => 1;
my @res = $query->members_of( 'writers' );
ok( @res, 'Finding members of "writers"' );
note( Dumper @res );
};
subtest 'Request of person data for Jay Luser' => sub {
plan tests => 2;
my $res1 = $query->find_person( 'Jay Luser' );
ok( !$res1, 'Jay Luser is not present' );
note( $res1 );
my %res2 = $query->find_person( 'Jay Luser' );
ok( !%res2, "Failed getting Jay Luser's data" );
};
subtest 'Request of membership in specific group for Jay Luser' => sub {
plan tests => 1;
my $res = $query->is_member_of( 'Jay Luser', 'scifi' );
ok( !$res, 'Jay Luser is not member of scifi' );
note( $res );
};
subtest 'Request of "author" tag value for Jay Luser' => sub {
plan tests => 1;
my $res = $query->find_person_tag( 'Jay Luser', 'author' );
ok( !$res, "No 'author' tag for Jay Luser" );
note( $res );
};
subtest 'Request of CLA status for Jay Luser' => sub {
plan tests => 1;
my $res = $query->has_cla( 'jluser@ourplace.com' );
ok( !$res, 'Jay Luser has no CLA' );
note( $res );
};
subtest 'Request of membership in the group "couchpotatoes"' => sub {
plan tests => 1;
my @res = $query->members_of( 'couchpotatoes' );
ok( !@res, 'No members in "couchpotatoes"' );
note( @res );
};
subtest 'Request all existing CLAs' => sub {
plan tests => 1;
my @res = $query->list_clas();
ok( @res, 'We got CLAs' );
note( @res );
};
}
1;