Commit 13c85de6 authored by Leigh B Stoller's avatar Leigh B Stoller

Powder license support. Just project level, easy to add user level

licensing later.
parent 33b207d3
...@@ -44,6 +44,7 @@ my $impotent= 0; ...@@ -44,6 +44,7 @@ my $impotent= 0;
my $silent = 0; my $silent = 0;
my $portal; my $portal;
my $resend; my $resend;
my %licenses = ();
# #
# Configure variables # Configure variables
...@@ -53,6 +54,7 @@ my $TBOPS = "@TBOPSEMAIL@"; ...@@ -53,6 +54,7 @@ my $TBOPS = "@TBOPSEMAIL@";
my $TBAUDIT = "@TBAUDITEMAIL@"; my $TBAUDIT = "@TBAUDITEMAIL@";
my $TBBASE = "@TBBASE@"; my $TBBASE = "@TBBASE@";
my $TBWWW = "@TBWWW@"; my $TBWWW = "@TBWWW@";
my $LICENSES = "$TB/sbin/manage_licenses";
# #
# This script is setuid, so please do not run it as root. Hard to track # This script is setuid, so please do not run it as root. Hard to track
...@@ -233,12 +235,28 @@ if (exists($xmlparse->{'attribute'}->{"portal"})) { ...@@ -233,12 +235,28 @@ if (exists($xmlparse->{'attribute'}->{"portal"})) {
fatal("Bad portal: $portal"); fatal("Bad portal: $portal");
} }
} }
# Licenses. Save for later, but need to delete.
foreach my $key (keys(%{ $xmlparse->{'attribute'} })) {
if ($key =~ /^license_([-\w]+)$/) {
my $value = $xmlparse->{'attribute'}->{"$key"}->{'value'};
my $name = $1;
if (lc($value) eq "yes") {
system("$LICENSES show $name");
if ($?) {
fatal("Invalid license name: $name");
}
$licenses{$name} = $name;
print "requested license $name\n";
}
delete($xmlparse->{'attribute'}->{"$key"});
}
}
# #
# Make sure all the required arguments were provided. # Make sure all the required arguments were provided.
# #
foreach my $key (keys(%required)) { foreach my $key (keys(%required)) {
fatal("Missing required attribute '$key'") fatal("Missing required attribute '$key'")
if (! exists($xmlparse->{'attribute'}->{"$key"})); if (! exists($xmlparse->{'attribute'}->{"$key"}));
} }
...@@ -386,6 +404,18 @@ if (!defined($newproj)) { ...@@ -386,6 +404,18 @@ if (!defined($newproj)) {
} }
my $new_idx = $newproj->pid_idx(); my $new_idx = $newproj->pid_idx();
#
# Add any licenses.
#
if (keys(%licenses)) {
foreach my $name (keys(%licenses)) {
system("$LICENSES require $name $new_pid");
if ($?) {
fatal("Invalid license name: $name");
}
}
}
# #
# See if we are in an initial Emulab setup. If so, no email sent. # See if we are in an initial Emulab setup. If so, no email sent.
# #
......
...@@ -36,7 +36,8 @@ BIN_SCRIPTS = manage_profile manage_instance manage_dataset \ ...@@ -36,7 +36,8 @@ BIN_SCRIPTS = manage_profile manage_instance manage_dataset \
manage_images rtecheck checkprofile manage_extensions \ manage_images rtecheck checkprofile manage_extensions \
create_slivers searchip create_slivers searchip
SBIN_SCRIPTS = apt_daemon aptevent_daemon portal_xmlrpc apt_checkup \ SBIN_SCRIPTS = apt_daemon aptevent_daemon portal_xmlrpc apt_checkup \
portal_monitor apt_scheduler portal_resources portal_monitor apt_scheduler portal_resources \
manage_licenses
LIB_SCRIPTS = APT_Profile.pm APT_Instance.pm APT_Dataset.pm APT_Geni.pm \ LIB_SCRIPTS = APT_Profile.pm APT_Instance.pm APT_Dataset.pm APT_Geni.pm \
APT_Aggregate.pm APT_Utility.pm APT_Rspec.pm APT_Aggregate.pm APT_Utility.pm APT_Rspec.pm
WEB_BIN_SCRIPTS = webmanage_profile webmanage_instance webmanage_dataset \ WEB_BIN_SCRIPTS = webmanage_profile webmanage_instance webmanage_dataset \
...@@ -44,7 +45,7 @@ WEB_BIN_SCRIPTS = webmanage_profile webmanage_instance webmanage_dataset \ ...@@ -44,7 +45,7 @@ WEB_BIN_SCRIPTS = webmanage_profile webmanage_instance webmanage_dataset \
webrspec2genilib webmanage_reservations webmanage_gitrepo \ webrspec2genilib webmanage_reservations webmanage_gitrepo \
webmanage_images webrtecheck websearchip webmanage_images webrtecheck websearchip
APACHEHOOKS = apt_gitrepo.hook APACHEHOOKS = apt_gitrepo.hook
WEB_SBIN_SCRIPTS= webportal_xmlrpc WEB_SBIN_SCRIPTS= webportal_xmlrpc webmanage_licenses
LIBEXEC_SCRIPTS = $(WEB_BIN_SCRIPTS) $(WEB_SBIN_SCRIPTS) LIBEXEC_SCRIPTS = $(WEB_BIN_SCRIPTS) $(WEB_SBIN_SCRIPTS)
USERLIBEXEC = rungenilib.proxy genilib-jail genilib-iocage gitrepo.proxy USERLIBEXEC = rungenilib.proxy genilib-jail genilib-iocage gitrepo.proxy
......
This diff is collapsed.
...@@ -1071,5 +1071,20 @@ sub ValidUUID($) ...@@ -1071,5 +1071,20 @@ sub ValidUUID($)
return 0; return 0;
} }
sub ReadFile($)
{
my ($filename) = @_;
my $contents = "";
open(L, $filename)
or return undef;
while (<L>) {
$contents .= $_;
}
close(L);
return $contents;
}
# _Always_ make sure that this 1 is at the end of the file... # _Always_ make sure that this 1 is at the end of the file...
1; 1;
...@@ -2989,6 +2989,26 @@ CREATE TABLE `lease_permissions` ( ...@@ -2989,6 +2989,26 @@ CREATE TABLE `lease_permissions` (
PRIMARY KEY (`lease_idx`,`permission_type`,`permission_idx`) PRIMARY KEY (`lease_idx`,`permission_type`,`permission_idx`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1; ) ENGINE=MyISAM DEFAULT CHARSET=latin1;
--
-- Table structure for table `licenses`
--
DROP TABLE IF EXISTS `licenses`;
CREATE TABLE `licenses` (
`license_idx` int(11) NOT NULL auto_increment,
`license_name` varchar(48) NOT NULL default '',
`license_level` enum('project','user') NOT NULL default 'project',
`created` datetime default NULL,
`validfor` int(11) NOT NULL default '0',
`form_text` tinytext,
`license_text` text,
`license_type` enum('md','text','html') NOT NULL default 'md',
`description_text` text,
`description_type` enum('md','text','html') NOT NULL default 'md',
PRIMARY KEY (`license_idx`),
UNIQUE KEY `license_name` (`license_name`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
-- --
-- Table structure for table `linkdelays` -- Table structure for table `linkdelays`
-- --
...@@ -4479,6 +4499,20 @@ CREATE TABLE `project_leases` ( ...@@ -4479,6 +4499,20 @@ CREATE TABLE `project_leases` (
UNIQUE KEY `uuid` (`uuid`) UNIQUE KEY `uuid` (`uuid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1; ) ENGINE=MyISAM DEFAULT CHARSET=latin1;
--
-- Table structure for table `project_licenses`
--
DROP TABLE IF EXISTS `project_licenses`;
CREATE TABLE `project_licenses` (
`pid` varchar(48) NOT NULL default '',
`pid_idx` mediumint(8) unsigned NOT NULL default '0',
`license_idx` int(11) NOT NULL default '0',
`accepted` datetime default NULL,
`expiration` datetime default NULL,
PRIMARY KEY (`pid_idx`,`license_idx`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
-- --
-- Table structure for table `project_quotas` -- Table structure for table `project_quotas`
-- --
...@@ -5246,6 +5280,22 @@ CREATE TABLE `user_features` ( ...@@ -5246,6 +5280,22 @@ CREATE TABLE `user_features` (
PRIMARY KEY (`feature`,`uid_idx`) PRIMARY KEY (`feature`,`uid_idx`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1; ) ENGINE=MyISAM DEFAULT CHARSET=latin1;
--
-- Table structure for table `user_licenses`
--
DROP TABLE IF EXISTS `user_licenses`;
CREATE TABLE `user_licenses` (
`uid` varchar(48) NOT NULL default '',
`uid_idx` mediumint(8) unsigned NOT NULL default '0',
`license_idx` int(11) NOT NULL default '0',
`accepted` datetime default NULL,
`expiration` datetime default NULL,
PRIMARY KEY (`uid_idx`,`license_idx`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
-- --
-- Table structure for table `user_policies` -- Table structure for table `user_policies`
-- --
......
use strict;
use libdb;
sub DoUpdate($$$)
{
my ($dbhandle, $dbname, $version) = @_;
if (!DBTableExists("licenses")) {
DBQueryFatal("CREATE TABLE `licenses` ( ".
" `license_idx` int(11) NOT NULL auto_increment, ".
" `license_name` varchar(48) NOT NULL default '', ".
" `license_level` enum('project','user') ".
" NOT NULL default 'project', ".
" `created` datetime default NULL, ".
" `validfor` int(11) NOT NULL default '0', ".
" `form_text` tinytext, ".
" `license_text` text, ".
" `license_type` enum('md','text', 'html') ".
" NOT NULL default 'md', ".
" `description_text` text, ".
" `description_type` enum('md','text', 'html') ".
" NOT NULL default 'md', ".
" PRIMARY KEY (`license_idx`), ".
" UNIQUE KEY `license_name` (`license_name`) ".
") ENGINE=MyISAM DEFAULT CHARSET=latin1");
}
if (!DBTableExists("project_licenses")) {
DBQueryFatal("CREATE TABLE `project_licenses` ( ".
" `pid` varchar(48) NOT NULL default '', ".
" `pid_idx` mediumint(8) unsigned NOT NULL default '0', ".
" `license_idx` int(11) NOT NULL default '0', ".
" `accepted` datetime default NULL, ".
" `expiration` datetime default NULL, ".
" PRIMARY KEY (`pid_idx`,`license_idx`) ".
") ENGINE=MyISAM DEFAULT CHARSET=latin1");
}
if (!DBTableExists("user_licenses")) {
DBQueryFatal("CREATE TABLE `user_licenses` ( ".
" `uid` varchar(48) NOT NULL default '', ".
" `uid_idx` mediumint(8) unsigned NOT NULL default '0', ".
" `license_idx` int(11) NOT NULL default '0', ".
" `accepted` datetime default NULL, ".
" `expiration` datetime default NULL, ".
" PRIMARY KEY (`uid_idx`,`license_idx`) ".
") ENGINE=MyISAM DEFAULT CHARSET=latin1");
}
return 0;
}
1;
# Local Variables:
# mode:perl
# End:
#!/usr/bin/perl -wT #!/usr/bin/perl -wT
# #
# Copyright (c) 2000-2016 University of Utah and the Flux Group. # Copyright (c) 2000-2018 University of Utah and the Flux Group.
# #
# {{{EMULAB-LICENSE # {{{EMULAB-LICENSE
# #
...@@ -236,6 +236,7 @@ $EUID = 0; ...@@ -236,6 +236,7 @@ $EUID = 0;
# #
DBQueryFatal("delete FROM last_reservation where pid_idx='$pid_idx'"); DBQueryFatal("delete FROM last_reservation where pid_idx='$pid_idx'");
DBQueryFatal("delete FROM project_reservations where pid_idx='$pid_idx'"); DBQueryFatal("delete FROM project_reservations where pid_idx='$pid_idx'");
DBQueryFatal("delete FROM project_licenses where pid_idx='$pid_idx'");
DBQueryFatal("delete FROM nodetypeXpid_permissions where pid_idx='$pid_idx'"); DBQueryFatal("delete FROM nodetypeXpid_permissions where pid_idx='$pid_idx'");
DBQueryFatal("delete FROM project_stats where pid_idx='$pid_idx'"); DBQueryFatal("delete FROM project_stats where pid_idx='$pid_idx'");
DBQueryFatal("delete FROM group_stats where pid_idx='$pid_idx'"); DBQueryFatal("delete FROM group_stats where pid_idx='$pid_idx'");
......
...@@ -20,7 +20,8 @@ $(function () { ...@@ -20,7 +20,8 @@ $(function () {
* name to the wrapper so we can find it later to * name to the wrapper so we can find it later to
* add the error stuff. * add the error stuff.
*/ */
var wrapper = $("<div id='form-wrapper-' + key></div>"); var wrapper = $("<div id='form-wrapper-" + key + "'>" +
"</div>");
// How do I just move the item into the wrapper? // How do I just move the item into the wrapper?
wrapper.append($(item).clone()); wrapper.append($(item).clone());
......
$(function ()
{
'use strict';
var template_list = ["licenses", "oops-modal", "waitwait-modal"];
var templates = APT_OPTIONS.fetchTemplateList(template_list);
var licenses = [];
function initialize()
{
window.APT_OPTIONS.initialize(sup);
$('#main-body').html(templates["licenses"]);
$('#oops-div').html(templates["oops-modal"]);
$('#waitwait-div').html(templates["waitwait-modal"]);
GetLicenses();
}
function GetLicenses()
{
var callback = function(json) {
console.log("GetLicenses", json);
if (json.code) {
console.info("Could not list license: " + json.value);
return;
}
licenses = json.value;
if (! licenses.length) {
window.location.replace("landing.php");
return;
}
HandleLicense(licenses.shift());
};
var xmlthing = sup.CallServerMethod(null, "licenses", "List");
xmlthing.done(callback);
}
function HandleLicense(license)
{
console.info("HandleLicense", license);
// Clear for next license.
$('#description-panel .license').html("").addClass("hidden");
$('#license-panel .panel-body .license-text').html("");
// Reset handler for accept button.
$('#accept-license')
.off("click")
.click(function (event) {
event.preventDefault();
Accept(license);
});
$('#reject-license')
.off("click")
.click(function (event) {
event.preventDefault();
Reject(license);
});
if (license.description_text && license.description_text != "") {
var html;
if (license.description_type == "md") {
html = marked(license.description_text);
}
else if (license.description_type == "html") {
html = license.description_text;
}
else if (license.description_type == "text") {
html = "<pre>" + license.description_text + "</pre>";
}
$('#description-panel .license')
.html(html)
.removeClass("hidden");
}
var html;
if (license.license_type == "md") {
html = marked(license.license_text);
}
else if (license.license_type == "html") {
html = license.license_text;
}
else if (license.license_type == "text") {
html = "<pre>" + license.license_text + "</pre>";
}
$('#license-panel .panel-body .license-text').html(html);
}
function Accept(license)
{
console.info("Accept", license);
var callback = function(json) {
console.log(json);
if (json.code) {
console.info("Could not accept license: " + json.value);
return;
}
if (licenses.length) {
sup.ShowWaitWait("One moment please while we check to see if " +
"there are any more licenses to accept ...")
setTimeout(function () {
sup.HideWaitWait();
HandleLicense(licenses.shift());
}, 2000);
return;
}
window.location.replace("landing.php");
};
var xmlthing = sup.CallServerMethod(null,
"licenses", "Accept",
{"idx" : license.idx});
xmlthing.done(callback);
}
function Reject(license)
{
console.info("Reject", license);
var callback = function(json) {
console.log(json);
if (json.code) {
console.info("Could not reject license: " + json.value);
return;
}
if (licenses.length) {
sup.ShowWaitWait("One moment please while we check to see if " +
"there are any more licenses to accept ...")
setTimeout(function () {
sup.HideWaitWait();
HandleLicense(licenses.shift());
}, 2000);
return;
}
window.location.replace("landing.php");
};
var xmlthing = sup.CallServerMethod(null,
"licenses", "Reject",
{"idx" : license.idx});
xmlthing.done(callback);
}
$(document).ready(initialize);
});
...@@ -504,19 +504,29 @@ $(function () ...@@ -504,19 +504,29 @@ $(function ()
} }
var template = _.template(detailsString); var template = _.template(detailsString);
$('#admin_content') $('#project_content')
.html(template({"fields" : json.value})); .html(template({"fields" : json.value,
"isleader" : window.ISLEADER,
"isadmin" : window.ISADMIN}));
// Format dates with moment before display. // Format dates with moment before display.
$('#admin_table .format-date').each(function() { $('#project_table .format-date').each(function() {
var date = $.trim($(this).html()); var date = $.trim($(this).html());
if (date != "") { if (date != "") {
$(this).html(moment($(this).html()).format("ll")); $(this).html(moment($(this).html()).format("ll"));
} }
}); });
$('#admin_content .toggle').click(function() { $('#project_table [data-toggle="popover"]').popover({
trigger: 'hover',
placement: 'auto',
});
$('#project_content .toggle').click(function() {
Toggle(this); Toggle(this);
}); });
$('#project_content .request-license').click(function(event) {
event.preventDefault();
RequestLicense(this);
});
} }
var xmlthing = sup.CallServerMethod(null, var xmlthing = sup.CallServerMethod(null,
"show-project", "ProjectProfile", "show-project", "ProjectProfile",
...@@ -618,6 +628,31 @@ $(function () ...@@ -618,6 +628,31 @@ $(function ()
callback); callback);
} }
/*
* Request a license.
*/
function RequestLicense(target) {
var license_idx = $(target).data("license_idx");
var callback = function(json) {
if (json.code) {
sup.SpitOops("oops", json.value);
return;
}
// If this is the leader of the project, zap them to
// the license page. If an admin doing this, stay here.
if (window.ISLEADER) {
window.location.replace("licenses.php");
return;
}
$(target).closest('td').html("Acceptance pending");
};
sup.CallServerMethod(null, "licenses", "Request",
{"pid" : window.TARGET_PROJECT,
"idx" : license_idx},
callback);
}
$(document).ready(initialize); $(document).ready(initialize);
}); });
......
...@@ -16,11 +16,11 @@ $(function () ...@@ -16,11 +16,11 @@ $(function ()
var fields = JSON.parse(_.unescape($('#form-json')[0].textContent)); var fields = JSON.parse(_.unescape($('#form-json')[0].textContent));
var errors = JSON.parse(_.unescape($('#error-json')[0].textContent)); var errors = JSON.parse(_.unescape($('#error-json')[0].textContent));
var licenses = JSON.parse(_.unescape($('#licenses-json')[0].textContent));
console.info(fields); console.info(fields);
console.info(errors); console.info(errors);
renderForm(fields, errors, renderForm(fields, errors, licenses,
window.APT_OPTIONS.joinproject, window.APT_OPTIONS.joinproject,
window.APT_OPTIONS.ShowVerifyModal, window.APT_OPTIONS.ShowVerifyModal,
window.APT_OPTIONS.this_user, window.APT_OPTIONS.this_user,
...@@ -48,7 +48,7 @@ $(function () ...@@ -48,7 +48,7 @@ $(function ()
} }
} }
function renderForm(formfields, errors, joinproject, showVerify, function renderForm(formfields, errors, licenses, joinproject, showVerify,
thisUser, promoting) thisUser, promoting)
{ {
var buttonLabel = "Submit Request"; var buttonLabel = "Submit Request";
...@@ -68,7 +68,8 @@ $(function () ...@@ -68,7 +68,8 @@ $(function ()
}); });
var project_html = projectTemplate({ var project_html = projectTemplate({
joinproject: joinproject, joinproject: joinproject,
formfields: formfields formfields: formfields,
licenses: licenses,
}); });
var signup = signupTemplate({ var signup = signupTemplate({
button_label: buttonLabel, button_label: buttonLabel,
...@@ -106,6 +107,11 @@ $(function () ...@@ -106,6 +107,11 @@ $(function ()
aptforms.DisableUnsavedWarning('#quickvm_signup_form'); aptforms.DisableUnsavedWarning('#quickvm_signup_form');
}); });
} }
// This activates the popover subsystem.
$('[data-toggle="popover"]').popover({
trigger: 'hover',
placement: 'auto',
});
} }
$(document).ready(initialize); $(document).ready(initialize);
......
<?php
#
# Copyright (c) 2000-2018 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
# This file is part of the Emulab network testbed software.
#
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
#
# This file is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
# License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this file. If not, see <http://www.gnu.org/licenses/>.
#
# }}}
#
chdir("..");
include_once("webtask.php");
chdir("apt");
function Do_List()
{