All new accounts created on Gitlab now require administrator approval. If you invite any collaborators, please let Flux staff know so they can approve the accounts.

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;
my $silent = 0;
my $portal;
my $resend;
my %licenses = ();
#
# Configure variables
......@@ -53,6 +54,7 @@ my $TBOPS = "@TBOPSEMAIL@";
my $TBAUDIT = "@TBAUDITEMAIL@";
my $TBBASE = "@TBBASE@";
my $TBWWW = "@TBWWW@";
my $LICENSES = "$TB/sbin/manage_licenses";
#
# This script is setuid, so please do not run it as root. Hard to track
......@@ -233,12 +235,28 @@ if (exists($xmlparse->{'attribute'}->{"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.
#
foreach my $key (keys(%required)) {
fatal("Missing required attribute '$key'")
if (! exists($xmlparse->{'attribute'}->{"$key"}));
}
......@@ -386,6 +404,18 @@ if (!defined($newproj)) {
}
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.
#
......
......@@ -36,7 +36,8 @@ BIN_SCRIPTS = manage_profile manage_instance manage_dataset \
manage_images rtecheck checkprofile manage_extensions \
create_slivers searchip
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 \
APT_Aggregate.pm APT_Utility.pm APT_Rspec.pm
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 \
webmanage_images webrtecheck websearchip
APACHEHOOKS = apt_gitrepo.hook
WEB_SBIN_SCRIPTS= webportal_xmlrpc
WEB_SBIN_SCRIPTS= webportal_xmlrpc webmanage_licenses
LIBEXEC_SCRIPTS = $(WEB_BIN_SCRIPTS) $(WEB_SBIN_SCRIPTS)
USERLIBEXEC = rungenilib.proxy genilib-jail genilib-iocage gitrepo.proxy
......
This diff is collapsed.
......@@ -1071,5 +1071,20 @@ sub ValidUUID($)
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...
1;
......@@ -2989,6 +2989,26 @@ CREATE TABLE `lease_permissions` (
PRIMARY KEY (`lease_idx`,`permission_type`,`permission_idx`)
) 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`
--
......@@ -4479,6 +4499,20 @@ CREATE TABLE `project_leases` (
UNIQUE KEY `uuid` (`uuid`)
) 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`
--
......@@ -5246,6 +5280,22 @@ CREATE TABLE `user_features` (
PRIMARY KEY (`feature`,`uid_idx`)
) 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`
--
......
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
#
# Copyright (c) 2000-2016 University of Utah and the Flux Group.
# Copyright (c) 2000-2018 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -236,6 +236,7 @@ $EUID = 0;
#
DBQueryFatal("delete FROM last_reservation 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 project_stats where pid_idx='$pid_idx'");
DBQueryFatal("delete FROM group_stats where pid_idx='$pid_idx'");
......
......@@ -20,7 +20,8 @@ $(function () {
* name to the wrapper so we can find it later to
* 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?
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 ()
}
var template = _.template(detailsString);
$('#admin_content')
.html(template({"fields" : json.value}));
$('#project_content')
.html(template({"fields" : json.value,
"isleader" : window.ISLEADER,
"isadmin" : window.ISADMIN}));
// Format dates with moment before display.
$('#admin_table .format-date').each(function() {
$('#project_table .format-date').each(function() {
var date = $.trim($(this).html());
if (date != "") {
$(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);
});
$('#project_content .request-license').click(function(event) {
event.preventDefault();
RequestLicense(this);
});
}
var xmlthing = sup.CallServerMethod(null,
"show-project", "ProjectProfile",
......@@ -618,6 +628,31 @@ $(function ()
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);
});
......
......@@ -16,11 +16,11 @@ $(function ()
var fields = JSON.parse(_.unescape($('#form-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(errors);
renderForm(fields, errors,
renderForm(fields, errors, licenses,
window.APT_OPTIONS.joinproject,
window.APT_OPTIONS.ShowVerifyModal,
window.APT_OPTIONS.this_user,
......@@ -48,7 +48,7 @@ $(function ()
}
}
function renderForm(formfields, errors, joinproject, showVerify,
function renderForm(formfields, errors, licenses, joinproject, showVerify,
thisUser, promoting)
{
var buttonLabel = "Submit Request";
......@@ -68,7 +68,8 @@ $(function ()
});
var project_html = projectTemplate({
joinproject: joinproject,
formfields: formfields
formfields: formfields,
licenses: licenses,
});
var signup = signupTemplate({
button_label: buttonLabel,
......@@ -106,6 +107,11 @@ $(function ()
aptforms.DisableUnsavedWarning('#quickvm_signup_form');
});
}
// This activates the popover subsystem.
$('[data-toggle="popover"]').popover({
trigger: 'hover',
placement: 'auto',
});
}
$(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()
{
global $this_user;
global $ajax_args;
$result = array();
$licenses = $this_user->ProjectLicenses();
foreach ($licenses as $license) {
$blob = array("idx" => $license["license_idx"],
"name" => $license["license_name"],
"description_type" => $license["description_type"],
"description_text" => $license["description_text"],
"license_type" => $license["license_type"],
"license_text" => $license["license_text"]);
$result[] = $blob;
}
SPITAJAX_RESPONSE($result);
}
function Do_Accept()
{
global $this_user;
global $ajax_args;
$licenses = $this_user->ProjectLicenses();
$license = null;
if (!isset($ajax_args["idx"]) || $ajax_args["idx"] == "") {
SPITAJAX_ERROR(1, "Missing license idx");
return 1;
}
$idx = $ajax_args["idx"];
if (!is_numeric($idx)) {
SPITAJAX_ERROR(1, "License idx is not an integer");
return 1;
}
# Find the License so we know what kind it is.
foreach ($licenses as $tmp) {
if ($tmp["license_idx"] == $idx) {
$license = $tmp;
break;
}
}
if (!$license) {
SPITAJAX_ERROR(1, "Not a license that needs to be accepted.");
return 1;
}
$target = $license["pid"];
$project = Project::Lookup($target);
if (!$project) {
SPITAJAX_ERROR(1, "No such project");
return 1;
}
if (!$project->IsLeader($this_user)) {
SPITAJAX_ERROR(1, "Not allowed to accept/reject this license");
return 1;
}
$this_uid = $this_user->uid();
$webtask = WebTask::CreateAnonymous();
$retval = SUEXEC($this_uid, "nobody",
"webmanage_licenses -t " . $webtask->task_id() . " -- ".
" accept $idx $target ",
SUEXEC_ACTION_IGNORE);
$webtask->Refresh();
if ($retval != 0) {
if (!$webtask->exited() || $retval < 0) {
SUEXECERROR(SUEXEC_ACTION_CONTINUE);
SPITAJAX_ERROR(-1, "Internal error");
}
else {
# Need to pass exitcode through on this one.
SPITAJAX_ERROR($webtask->exitcode(), $webtask->output());
}
$webtask->Delete();
return;
}
$webtask->Delete();
SPITAJAX_RESPONSE(1);
}
function Do_Reject()
{
global $this_user;
global $ajax_args;
$licenses = $this_user->ProjectLicenses();
$license = null;
if (!isset($ajax_args["idx"]) || $ajax_args["idx"] == "") {
SPITAJAX_ERROR(1, "Missing license idx");
return 1;
}
$idx = $ajax_args["idx"];
if (!is_numeric($idx)) {
SPITAJAX_ERROR(1, "License idx is not an integer");
return 1;