4 use base qw( FS::svc_Common );
5 #use FS::Record qw( qsearch qsearchs );
10 FS::svc_cert - Object methods for svc_cert records
16 $record = new FS::svc_cert \%hash;
17 $record = new FS::svc_cert { 'column' => 'value' };
19 $error = $record->insert;
21 $error = $new_record->replace($old_record);
23 $error = $record->delete;
25 $error = $record->check;
29 An FS::svc_cert object represents a certificate. FS::svc_cert inherits from
30 FS::Record. The following fields are currently supported:
66 =item organization_unit
95 Creates a new certificate. To add the certificate to the database, see L<"insert">.
97 Note that this stores the hash reference, not a distinct copy of the hash it
98 points to. You can ask the object for a copy with the I<hash> method.
102 # the new method can be inherited from FS::Record, if a table method is defined
104 sub table { 'svc_cert'; }
107 my %dis = ( disable_default=>1, disable_fixed=>1, disable_inventory=>1, disable_select=>1 );
109 'name' => 'Certificate',
110 'name_plural' => 'Certificates',
111 'longname_plural' => 'Example services', #optional
112 'sorts' => 'svcnum', # optional sort field (or arrayref of sort fields, main first)
113 'display_weight' => 25,
114 'cancel_weight' => 65,
117 'privatekey' => { label=>'Private key', %dis, },
118 'csr' => { label=>'Certificate signing request', %dis, },
119 'certificate' => { label=>'Certificate', %dis, },
120 'cacert' => { label=>'Certificate authority chain', %dis, },
121 'common_name' => { label=>'Common name', %dis, },
122 'organization' => { label=>'Organization', %dis, },
123 'organization_unit' => { label=>'Organization Unit', %dis, },
124 'city' => { label=>'City', %dis, },
125 'state' => { label=>'State', %dis, },
126 'country' => { label=>'Country', %dis, },
127 'cert_contact' => { label=>'Contact email', %dis, },
129 #'another_field' => {
130 # 'label' => 'Description',
131 # 'def_label' => 'Description for service definitions',
133 # 'disable_default' => 1, #disable switches
134 # 'disable_fixed' => 1, #
135 # 'disable_inventory' => 1, #
138 # 'label' => 'Description',
139 # 'def_label' => 'Description for service defs',
140 # 'type' => 'select',
141 # 'select_table' => 'foreign_table',
142 # 'select_key' => 'key_field_in_table',
143 # 'select_label' => 'label_field_in_table',
152 Returns a meaningful identifier for this example
158 # $self->label_field; #or something more complicated if necessary
159 # check privatekey, check->privatekey, more?
160 return 'Certificate';
165 Adds this record to the database. If there is an error, returns the error,
166 otherwise returns false.
170 # the insert method can be inherited from FS::Record
174 Delete this record from the database.
178 # the delete method can be inherited from FS::Record
180 =item replace OLD_RECORD
182 Replaces the OLD_RECORD with this one in the database. If there is an error,
183 returns the error, otherwise returns false.
187 # the replace method can be inherited from FS::Record
191 Checks all fields to make sure this is a valid certificate. If there is
192 an error, returns the error, otherwise returns false. Called by the insert
197 # the check method should currently be supplied - FS::Record contains some
198 # data checking routines
204 $self->ut_numbern('svcnum')
205 || $self->ut_numbern('recnum')
206 || $self->ut_anything('privatekey') #XXX
207 || $self->ut_anything('csr') #XXX
208 || $self->ut_anything('certificate')#XXX
209 || $self->ut_anything('cacert') #XXX
210 || $self->ut_textn('common_name')
211 || $self->ut_textn('organization')
212 || $self->ut_textn('organization_unit')
213 || $self->ut_textn('city')
214 || $self->ut_textn('state')
215 || $self->ut_textn('country') #XXX char(2) or NULL
216 || $self->ut_textn('cert_contact')
218 return $error if $error;
223 =item generate_privatekey [ KEYSIZE ]
227 use IPC::Run qw( run );
230 sub generate_privatekey {
232 my $keysize = (@_ && $_[0]) ? shift : 2048;
233 run( [qw( openssl genrsa ), $keysize], '>pipe'=>\*OUT, '2>'=>'/dev/null' )
234 or die "error running openssl: $!";
236 my $privatekey = join('', <OUT>);
237 $self->privatekey($privatekey);
240 =item check_privatekey
244 sub check_privatekey {
246 my $in = $self->privatekey;
247 run( [qw( openssl rsa -check -noout)], '<'=>\$in, '>pipe'=>\*OUT, '2>'=>'/dev/null' )
248 ;# or die "error running openssl: $!";
251 return ($ok =~ /key ok/);
255 'CN' => 'common_name',
256 'O' => 'organization',
257 'OU' => 'organization_unit',
266 '/'. join('/', map { my $v = $self->get($subj{$_});
267 $v =~ s/([=\/])/\\$1/;
277 my $dir = $FS::UID::conf_dir. "/cache.". $FS::UID::datasrc; #XXX actual cache dir
278 my $fh = new File::Temp(
279 TEMPLATE => 'cert.'. '.XXXXXXXX',
281 ) or die "can't open temp file: $!\n";
282 print $fh $self->$field;
290 my $fh = $self->_file('privatekey');
292 run( [qw( openssl req -new -key ), $fh->filename, '-subj', $self->subj ],
293 '>pipe'=>\*OUT, '2>'=>'/dev/null'
295 or die "error running openssl: $!";
297 my $csr = join('', <OUT>);
305 sub generate_selfsigned {
310 my $key = $self->_file('privatekey');
311 my $csr = $self->_file('csr');
313 run( [qw( openssl req -x509 -nodes ),
315 '-key' => $key->filename,
316 '-in' => $csr->filename,
318 '>pipe'=>\*OUT, '2>'=>'/dev/null'
320 or die "error running openssl: $!";
322 my $csr = join('', <OUT>);
323 $self->certificate($csr);
326 #openssl x509 -in cert -noout -subject -issuer -dates -serial
327 #subject= /CN=cn.example.com/ST=AK/O=Tofuy/OU=Soybean dept./C=US/L=Tofutown
328 #issuer= /CN=cn.example.com/ST=AK/O=Tofuy/OU=Soybean dept./C=US/L=Tofutown
329 #notBefore=Nov 7 05:07:42 2010 GMT
330 #notAfter=Nov 6 05:07:42 2012 GMT
331 #serial=B1DBF1A799EF207B
333 sub check_certificate {
336 my $in = $self->certificate;
337 run( [qw( openssl x509 -noout -subject -issuer -dates -serial )],
339 '>pipe'=>\*OUT, '2>'=>'/dev/null'
341 or die "error running openssl: $!";
347 /^\s*(\w+)=\s*(.*)\s*$/ or next;
360 L<FS::Record>, schema.html from the base documentation.