#!/usr/bin/env perl -Ilib/ -w use strict; use Lemonldap::NG::Manager::Attributes; use Lemonldap::NG::Manager::Tree; use Lemonldap::NG::Manager::CTrees; use Lemonldap::NG::Manager; use Data::Dumper; use Regexp::Assemble; use JSON; use Getopt::Std; use Perl::Tidy; use IO::String; my ( $run, %opts ); getopts( 'f:', \%opts ); $run = Lemonldap::NG::Manager->run( { templateDir => 'site/templates/', configStorage => { confFile => 'site/lemonldap-ng.ini' } } ) if ( $opts{f} ); my $structFile = "site/static/struct.json"; my $confTreeFile = "site/static/js/conftree.js"; my $managerConstants = "lib/Lemonldap/NG/Manager/Constants.pm"; my $defaultValuesFile = "../lemonldap-ng-common/lib/Lemonldap/NG/Common/Conf/DefaultValues.pm"; my @simpleHashKeys; my @cnodesKeys; my %cnodesRe; $Data::Dumper::Sortkeys = sub { my ($hash) = @_; return [ ( defined $hash->{id} ? ('id') : () ), ( defined $hash->{title} ? ( 'title', ) : () ), ( grep { /^(?:id|title)$/ ? 0 : 1 } sort { return 1 if ( $a =~ /node/ and $b !~ /node/ ); return -1 if ( $b =~ /node/ ); lc($a) cmp lc($b); } keys %$hash ) ]; }; my $attributes = Lemonldap::NG::Manager::Attributes::attributes(); my $jsonEnc = JSON->new()->allow_nonref; my $mainTree; my @ignoreKeys; my $ignoreKeys; my $reIgnoreKeys = qr/^$/; $jsonEnc->canonical(1); # 1. confTree.js unless ( $opts{f} ) { $mainTree = Lemonldap::NG::Manager::CTrees::cTrees(); my $script = 'function templates(tpl,key){' . 'var ind;' . 'var scalarTemplate=function(r){' . 'return{' . '"id":tpl+"s/"+(ind++),' . '"title":r,' . '"get":tpl+"s/"+key+"/"+r' . '};' . '};' . 'switch(tpl){'; # To build confTree.js, each special node is scanned from # Lemonldap::NG::Manager::CTrees foreach my $node ( sort keys $mainTree ) { @cnodesKeys = (); my $jsonTree = []; &scanTree( $mainTree->{$node}, $jsonTree, '__KEY__' ); my $tmp = $jsonEnc->encode($jsonTree); $tmp =~ s!"__KEY__!tpl+"s/"+key+"/"+"!mg; $tmp =~ s/"(true|false)"/$1/sg; $tmp =~ s/:\s*"(\d+)"\s*(["\}])/:$1$2/sg; $script .= "case'$node':return$tmp;"; # Second step, Manager/Constants.pm file will contain datas issued from # this scan my $ra = Regexp::Assemble->new; # Build $oidcOPMetaDataNodeKeys, $samlSPMetaDataNodeKeys,... foreach my $r (@cnodesKeys) { $ra->add($r); } $cnodesRe{$node} = $ra->as_string; push @ignoreKeys, $node; } $script .= 'default:return [];}}'; open F, ">$confTreeFile" or die $!; print F $script; close F; my $ra = Regexp::Assemble->new; foreach my $re (@ignoreKeys) { $ra->add($re); } $ignoreKeys = $ra->as_string; $reIgnoreKeys = $ra->re; } # 2. struct.json $mainTree = Lemonldap::NG::Manager::Tree::tree(); my $jsonTree = []; &scanTree( $mainTree, $jsonTree ); open F, ">$structFile" || die $!; my $tmp = $jsonEnc->encode($jsonTree); $tmp =~ s/"(true|false)"/$1/sg; $tmp =~ s/:\s*"(\d+)"\s*(["\}])/:$1$2/sg; print F $tmp; close F; $tmp = undef; open F, ">$managerConstants" or die($!); my $exportedVars = '$' . join( 'Keys $', 'simpleHash', 'specialNode', sort keys %cnodesRe ) . 'Keys $specialNodeHash'; print F < [qw($exportedVars)] ); our \@EXPORT_OK = ( \@{ \$EXPORT_TAGS{'all'} } ); our \@EXPORT = ( \@{ \$EXPORT_TAGS{'all'} } ); our \$specialNodeHash = { virtualHosts => [qw(exportedHeaders locationRules post vhostOptions)], samlIDPMetaDataNodes => [qw(samlIDPMetaDataXML samlIDPMetaDataExportedAttributes samlIDPMetaDataOptions)], samlSPMetaDataNodes => [qw(samlSPMetaDataXML samlSPMetaDataExportedAttributes samlSPMetaDataOptions)], oidcOPMetaDataNodes => [qw(oidcOPMetaDataJSON oidcOPMetaDataJWKS oidcOPMetaDataOptions oidcOPMetaDataExportedVars)], oidcRPMetaDataNodes => [qw(oidcRPMetaDataOptions oidcRPMetaDataExportedVars)], }; EOF my $ra = Regexp::Assemble->new; foreach (@simpleHashKeys) { $ra->add($_); } print F "our \$simpleHashKeys = '" . $ra->as_string . "';\n" . "our \$specialNodeKeys = '${ignoreKeys}s';\n"; foreach ( sort keys %cnodesRe ) { print F "our \$${_}Keys = '$cnodesRe{$_}';\n"; } print F "\n1;\n"; close F; my $defaultValues = { map { defined $attributes->{$_}->{default} ? ( $_ => $attributes->{$_}->{default} ) : () } keys(%$attributes) }; my $defaultAttr = Dumper($defaultValues); $defaultAttr =~ s/^\$VAR1\s*=/sub defaultValues {\n return/; $defaultAttr = "# This file is generated by $0. Don't modify it by hand package Lemonldap::NG::Common::Conf::DefaultValues; our \$VERSION = '$Lemonldap::NG::Manager::Attributes::VERSION'; $defaultAttr} 1; "; my $dst; Perl::Tidy::perltidy( source => IO::String->new($defaultAttr), destination => \$dst ); open( F, "> $defaultValuesFile" ) or die($!); print F $dst; close F; exit; # keys: default select test type # sub scanTree { my ( $tree, $json, $prefix ) = splice @_; unless ( ref($tree) eq 'ARRAY' ) { die 'Not an array'; } $prefix //= ''; foreach my $leaf (@$tree) { my $jleaf = {}; # Grouped leaf if ( ref($leaf) and $leaf->{group} ) { die "'form' is required when using 'group'" unless ( $leaf->{form} ); push @$json, { id => "$prefix$leaf->{title}", title => $leaf->{title}, type => $leaf->{form}, get => $leaf->{group} }; } # Subnode elsif ( ref($leaf) ) { $jleaf->{title} = $jleaf->{id} = $leaf->{title}; $jleaf->{type} = $leaf->{form} if ( $leaf->{form} ); foreach my $n (qw(nodes nodes_cond)) { if ( $leaf->{$n} ) { $jleaf->{"_$n"} = []; scanTree( $leaf->{$n}, $jleaf->{"_$n"}, $prefix ); if ( $n eq 'nodes_cond' ) { foreach my $sn ( @{ $jleaf->{"_$n"} } ) { $sn->{show} = 'false'; } } } } $jleaf->{help} = $leaf->{help} if ( $leaf->{help} ); $jleaf->{_nodes_filter} = $leaf->{nodes_filter} if ( $leaf->{nodes_filter} ); push @$json, $jleaf; } # Leaf else { # Get data type and build tree # # Types : PerlModule bool boolOrExpr catAndAppList file hostname int # keyTextContainer lmAttrOrMacro longtext openidServerList pcre # rulesContainer samlAssertion samlAttributeContainer samlService # select text trool url virtualHostContainer word # password my $attr = $attributes->{$leaf} or die("Missing attribute $leaf"); $jleaf = { id => "$prefix$leaf", title => $leaf }; unless ( $attr->{type} ) { print STDERR "Fatal: no type: $leaf\n"; exit; } # TODO: change this $attr->{type} =~ s/^(?:url|word|pcre|lmAttrOrMacro|hostname|PerlModule)$/text/; $jleaf->{type} = $attr->{type} if ( $attr->{type} ne 'text' ); foreach my $w (qw(default select get template)) { $jleaf->{$w} = $attr->{$w} if ( defined $attr->{$w} ); } if ( $jleaf->{default} and ref( $jleaf->{default} ) ) { $jleaf->{default} = []; my $type = $attr->{type}; $type =~ s/Container//; foreach my $k ( sort keys( $attr->{default} ) ) { push @{ $jleaf->{default} }, { id => "$prefix$leaf/$k", title => $k, type => $type, data => $attr->{default}->{$k}, ( $type eq 'rule' ? ( re => $k ) : () ), }; } } if ($prefix) { push @cnodesKeys, $leaf; } if ( $attr->{type} =~ /^(?:catAndAppList|\w+Container)$/ ) { $jleaf->{cnodes} = $prefix . $leaf; unless ( $prefix or $leaf =~ $reIgnoreKeys ) { push @simpleHashKeys, $leaf; } if ( $opts{f} ) { my $js = getData( $prefix . $leaf ); } } else { if ( $opts{f} ) { my $file = $jleaf->{get} // $jleaf->{title}; my $js = getData($file); $jleaf->{get} = $file = $file . ".json"; open F, ">app/confs/$opts{f}/$file" or die $!; print F $js; close F; } if ( $prefix and !$jleaf->{get} ) { $jleaf->{get} = $prefix . $jleaf->{title}; } } push @$json, $jleaf; } } } sub getData { die $opts{f} unless $opts{f} =~ /^\d+$/; my $k = shift; my $q = "/confs/$opts{f}/$k"; return $run->( { HTTP_ACCEPT => 'application/json', PATH_INFO => $q, QUERY_STRING => '', REQUEST_URI => $q, REQUEST_METHOD => 'GET', } )->[2]->[0]; } 1;