diff --git a/composer.json b/composer.json index 8d8c008cb4ddd308241835b6880b1bbf260b3ee2..391d7de4984c1c6598578cc7a218ae10a41faef8 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,7 @@ { "require": { - "twig/twig": "^3.0" + "twig/twig": "^3.0", + "spomky-labs/otphp": "^10.0", + "chillerlan/php-qrcode": "^4.3" } } diff --git a/database/edugain_schema.sql b/database/edugain_schema.sql index 47f951cf6048246711e11fdf77632eae2b47b667..9d88b3fd9799c619b1507c723b8d54a2acd38e96 100644 --- a/database/edugain_schema.sql +++ b/database/edugain_schema.sql @@ -1,3 +1,9 @@ +-- MySQL dump 10.14 Distrib 5.5.68-MariaDB, for Linux (x86_64) +-- +-- Host: localhost Database: edugain_twoln +-- ------------------------------------------------------ +-- Server version 5.5.68-MariaDB + /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; @@ -25,7 +31,7 @@ CREATE TABLE `attribute` ( `description` longtext COLLATE utf8_unicode_ci, PRIMARY KEY (`id`), UNIQUE KEY `UNIQ_FA7AEFFB5E237E06` (`name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=48 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -41,10 +47,11 @@ CREATE TABLE `cert` ( `sha1` char(255) COLLATE utf8_unicode_ci NOT NULL, `sha256` char(255) COLLATE utf8_unicode_ci NOT NULL, `code` char(20) COLLATE utf8_unicode_ci DEFAULT NULL, + `createtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `sha1` (`sha1`), KEY `sha256` (`sha256`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +) ENGINE=MyISAM AUTO_INCREMENT=250 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -97,7 +104,7 @@ CREATE TABLE `entities` ( PRIMARY KEY (`id`), UNIQUE KEY `entityid` (`entityid`), KEY `regauth` (`regauth`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +) ENGINE=MyISAM AUTO_INCREMENT=39362 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -124,12 +131,12 @@ CREATE TABLE `entities_status` ( `sirtfi_idp_cnt` smallint(6) DEFAULT NULL, `sirtfi_sp_cnt` smallint(6) DEFAULT NULL, `sirtfi_cnt` smallint(6) DEFAULT NULL, - `sirtfi2_idp_cnt` smallint(6) DEFAULT NULL, - `sirtfi2_sp_cnt` smallint(6) DEFAULT NULL, - `sirtfi2_cnt` smallint(6) DEFAULT NULL, `hfd_cnt` smallint(6) DEFAULT NULL, `coco_cnt` smallint(6) DEFAULT NULL, - `rands_cnt` smallint(6) DEFAULT NULL + `rands_cnt` smallint(6) DEFAULT NULL, + `sirtfi2_idp_cnt` smallint(6) DEFAULT NULL, + `sirtfi2_sp_cnt` smallint(6) DEFAULT NULL, + `sirtfi2_cnt` smallint(6) DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; @@ -181,7 +188,7 @@ CREATE TABLE `entity_contactperson` ( `subtypeschema` char(128) COLLATE utf8_unicode_ci DEFAULT NULL, `subtype` char(64) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`id`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +) ENGINE=MyISAM AUTO_INCREMENT=105013 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -393,7 +400,7 @@ CREATE TABLE `entity_roles` ( PRIMARY KEY (`id`), UNIQUE KEY `entity_id` (`entity_id`,`roledesc`), KEY `roledesc` (`roledesc`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +) ENGINE=MyISAM AUTO_INCREMENT=57738 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -457,7 +464,7 @@ CREATE TABLE `entityattributes_dict` ( `short` char(30) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `name` (`name`,`value`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +) ENGINE=MyISAM AUTO_INCREMENT=205 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -472,7 +479,7 @@ CREATE TABLE `entitywarnings_dict` ( `message` char(255) COLLATE utf8_unicode_ci NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `message` (`message`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +) ENGINE=MyISAM AUTO_INCREMENT=61 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -577,6 +584,7 @@ CREATE TABLE `federation_status` ( `last_expire_notification` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', `new_profile_status` tinyint(1) DEFAULT NULL, `new_profile_modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', + `sign_cert_sha1` char(255) COLLATE utf8_unicode_ci DEFAULT NULL, UNIQUE KEY `code` (`code`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; @@ -701,7 +709,7 @@ CREATE TABLE `person` ( `role` char(15) COLLATE utf8_unicode_ci DEFAULT NULL, `code` char(20) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`id`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +) ENGINE=MyISAM AUTO_INCREMENT=1991 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -796,7 +804,7 @@ CREATE TABLE `security_contact` ( /*!50001 SET character_set_results = utf8 */; /*!50001 SET collation_connection = utf8_general_ci */; /*!50001 CREATE ALGORITHM=UNDEFINED */ -/*!50013 SQL SECURITY DEFINER */ +/*!50013 DEFINER=`root`@`localhost` SQL SECURITY DEFINER */ /*!50001 VIEW `federation_v` AS select `federation`.`code` AS `code`,`mds_data`.`reg_auth` AS `reg_auth`,`mds_data`.`metadata_url` AS `metadata_url`,`mds_data`.`status` AS `status`,`federation`.`fed_id` AS `fed_id`,`federation`.`europe` AS `europe`,`federation`.`contact_email` AS `contact_email`,`federation`.`url` AS `url`,`federation`.`policy_english` AS `policy_english`,`federation`.`policy_hardcopy` AS `policy_hardcopy`,`federation`.`policy_date` AS `policy_date`,`federation`.`membership_date` AS `membership_date`,`federation`.`production_date` AS `production_date`,`federation`.`name` AS `name`,`federation`.`policy_version` AS `policy_version`,`federation_status`.`valid_until` AS `valid_until`,`federation_status`.`last_notification` AS `last_notification`,`federation_status`.`feed_problem` AS `feed_problem`,`federation_status`.`new_profile_status` AS `new_profile_status`,`federation_status`.`new_profile_modified` AS `new_profile_modified` from ((`federation` left join `mds_data` on((`mds_data`.`code` = `federation`.`code`))) left join `federation_status` on((`federation`.`code` = `federation_status`.`code`))) */; /*!50001 SET character_set_client = @saved_cs_client */; /*!50001 SET character_set_results = @saved_cs_results */; @@ -810,3 +818,5 @@ CREATE TABLE `security_contact` ( /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2023-07-25 21:24:58 diff --git a/edugain.config/admin_template.php b/edugain.config/admin_template.php new file mode 100644 index 0000000000000000000000000000000000000000..91120cb6a3cdcc059d8fe896128f4bdb8ce2058e --- /dev/null +++ b/edugain.config/admin_template.php @@ -0,0 +1,26 @@ +<?php +$admins= [ +'twoln'=>[ + 'hash' => '$2y$10$BSxcjTwsiycJKIlrpcUnmuLYwO15Zove3ota/FLPXYkUtyZNi60jC', + 'mail'=>'twoln@umk.pl', +// 'mail'=>'tw2529@gmail.com', + 'groups'=>[EDUGAIN_OT, EDUGAIN_SEC], + 'admin' => true, +], +'mgw'=>[ + 'hash'=>'$2y$10$CH259054ypVfnmUDcU14BuisV4/lnr.4EP.kjPTEzW6YjQ1xo4DpC', + 'mail'=>'mgw@umk.pl', + 'admin' => true, +], +'zbyszek'=>[ + 'hash'=>'$2y$10$blPXmNWOmkgqsrszyroTXOUXSqaG5jz5Mn8GgVzaaXquyE0dyWoO6', + 'mail'=>'zbigniew.oltuszyk@man.poznan.pl', + 'admin' => true, +], + 'secretariat'=> [ + 'hash' => '$2y$10$Rlikn5XeMwwWqPlwPdCSHexkOzaLSyoYMs.P/TOQM9Haly8ZrgRE2', + 'admin' => false, + ] +]; + + diff --git a/edugain.config/database_policy_template.php b/edugain.config/database_policy_template.php new file mode 100644 index 0000000000000000000000000000000000000000..82be5b917c84bb239be4fff62b621efa956944b1 --- /dev/null +++ b/edugain.config/database_policy_template.php @@ -0,0 +1,3 @@ +<?php +define('USER', 'policy'); +define('PASSWORD', 'xxxxxx'); diff --git a/edugain.config/database_ro_template.php b/edugain.config/database_ro_template.php new file mode 100644 index 0000000000000000000000000000000000000000..f44ac2fbe6023ef929a700be45ee304003af509f --- /dev/null +++ b/edugain.config/database_ro_template.php @@ -0,0 +1,3 @@ +<?php +define('USER', 'edugain_ro'); +define('PASSWORD', 'xxxxxx'); diff --git a/edugain.config/database_rw_template.php b/edugain.config/database_rw_template.php new file mode 100644 index 0000000000000000000000000000000000000000..16817fe0e3bb719b13d7e3b4f2d741b244fb8bbe --- /dev/null +++ b/edugain.config/database_rw_template.php @@ -0,0 +1,3 @@ +<?php +define('USER', 'edugain'); +define('PASSWORD', 'xxxxxx'); diff --git a/external_scripts/dbhost/cert.py b/external_scripts/dbhost/cert.py new file mode 100755 index 0000000000000000000000000000000000000000..ab6e4eb760511d8c9fbc0cf99be36c177a427bdb --- /dev/null +++ b/external_scripts/dbhost/cert.py @@ -0,0 +1,131 @@ +#!/usr/bin/python +""" +This script will add a federation signing certificate to the database. +One required argument is the identifier of the federation. The certificate +to be added must be in the PEM format must be named upstreamCert_IDENIFIER.cer +and must be in the Certs subdirecory of your current directory. + +The database access details are below as "db, host, user", we suggest that +it is safest to use localhost as host and have the "user" access right only +from the localhost. The script will prompt for password of the user (again +for security reasons this password is not stored in the script). + +If a given federation already has a certificate in the database, the script +will ask if we want to remove or keep the other certificate. It is possible +to have more certificates than one and they will all be tried during the +validation process. This feature is meant for smooth transition form one +signing key to another. The old key should be removed after the transition +has been finalised. + +At the moment there is no script for removing the certificate, this needs +to be done manually accessing the database. Looking at the createtime field +may be helpful in selecting the right certificate to remove. +""" +import sys,re,ssl,base64,urllib2 +import MySQLdb +import os.path +import getpass +from M2Crypto import X509 +from OpenSSL import crypto + +import sys + +db = 'edugain' +host = 'localhost' +user = 'cert' +passwd = getpass.getpass() +selectcodes = "select code from federation" +select = "select code from federation where code='%s'" +exists = "select id from cert where code='%s'" +delete = "delete from cert where id='%s'" +insert = "insert into cert (code,data,sha1, sha256) values (%(code)s, %(cert)s, %(sha1)s, %(sha256)s)" +update = "update federation_status set feed_etag=null, feed_last_modified='000-00-00 00:00:00' where code='%(code)s'" + +mys = MySQLdb.connect(host, user, passwd, db) +mys.autocommit(True) +curs = mys.cursor() +curd = mys.cursor() +codes = [] + +def yn_choice(msg, default='y'): + choices = 'Y/n' if default.lower() in ('y', 'yes') else 'y/N' + choice = raw_input("%s (%s) " % (msg, choices)) + values = ('y', 'yes', '') if choices == 'Y/n' else ('y', 'yes') + return choice.strip().lower() in values + +if len(sys.argv) == 1: + print selectcodes + sys.exit(0) + cnt = curs.execute(selectcodes) + if cnt>0: + nextrow = curs.fetchone() + while nextrow: + code = None + row = nextrow + nextrow = curs.fetchone() + codes.append(row[0]) +else: + for i in range(1, len(sys.argv)): + codes.append(sys.argv[i]) +for code in codes: + try: + cnt = curs.execute(select % code) + if cnt==1: + nextrow = curs.fetchone() + while nextrow: + code = None + row = nextrow + nextrow = curs.fetchone() + if row is None: + continue + if row[0]: + code = row[0] + print 'looking for ', code, 'Certs/upstreamCert_'+code+'.cer' + if code and (os.path.isfile('Certs/upstreamCert_'+code+'.cer')): + f = open ('Certs/upstreamCert_'+code+'.cer', 'r') + cert = f.read() + cert_der = ssl.PEM_cert_to_DER_cert(cert) + x509 = crypto.load_certificate(crypto.FILETYPE_PEM, cert) + fpsha256 = x509.digest('sha256').decode().replace(':', '') + fpsha1 = x509.digest('sha1').decode().replace(':', '') + #x509 = X509.load_cert_string(cert, X509.FORMAT_PEM) + #fpsha1 = x509.get_fingerprint('sha1') + #fpsha256 = x509.get_fingerprint('sha256') + c_is = curd.execute(exists % code) + if c_is == 2: + print 'It is the next certificate for ' + code + \ + '\nWe can not register more than two certificates per federation!' + continue + if c_is: + choice = yn_choice('There is a certificate registered for ' + + code + + "\nDo you want to delete this certificate? ") + if choice: + c_nrow = curd.fetchone() + while c_nrow: + c_id = None + c_row = c_nrow + c_nrow = curd.fetchone() + if c_row is None: + continue + if c_row[0]: + print(delete % c_row[0]) + curd.execute(delete % c_row[0]) + else: + choice = yn_choice('Do you want to register this certificate and ' + + 'have temporary two certificates for ' + code + '?') + if not choice: + print code + " skipped, nothing happened" + continue + cnt = curs.execute(insert, {'code': code, 'cert': base64.b64encode(cert_der), 'sha1': fpsha1, 'sha256': fpsha256}) + print 'certificate for', code, 'added' + print(update % {'code': code}) + curs.execute(update % {'code': code}) + else: + print "Certificate not found, nothing happened" + else: + print 'No such federation: '+code + except IndexError: + print 'brak', code + + diff --git a/lib/API.php b/lib/API.php index b6dd1dd1fb5f3f993b3ff0536ad95a7eafc946dc..502c62428399f49769ac38a609a1f42f2b97a3f1 100644 --- a/lib/API.php +++ b/lib/API.php @@ -428,7 +428,7 @@ class API { 'required' => FALSE, 'default' => '0', 'values' => '0, 1, 2, 3, 4', - 'description' => '0 - show all; 1 - only production feds; 2 - all members; 3 - only candidates; 4 - only voting-only)', + 'description' => '0 - show all; 1 - only production feds; 2 - all members; 3 - only candidates; 4 - only voting-only', ], $this->addStdArgument('format'), ], @@ -454,14 +454,14 @@ class API { private function action_list_feds() { $this->description([ 'header' => "List federation details (code, name, contact email, registration authority string, status).", - 'supportedFormats' => ['json', 'print_r'], + 'supportedFormats' => ['json', 'csv', 'print_r'], 'arguments' => [ [ 'arg' => 'opt', 'required' => FALSE, 'default' => '0', 'values' => '0, 1, 2, 3, 4', - 'description' => '0 - show all; 1 - only production feds; 2 - all members; 3 - only candidates; 4 - only voting-only)'], + 'description' => '0 - show all; 1 - only production feds; 2 - all members; 3 - only candidates; 4 - only voting-only'], $this->addStdArgument('format'), ], 'returns' => [ @@ -487,6 +487,17 @@ class API { $edugain = new eduGAIN($this->opts['opt']); $F = $edugain->FEDS; ksort($F, SORT_STRING | SORT_FLAG_CASE); + + + if ($this->format == 'csv') { + $out = ''; + foreach ($F as $fed => $A) { + $ln = $fed . "\t" . implode("\t", $A) . "\n"; + $out .= $ln; + } + return($out); + } + foreach ($F as $fed => $A) { $out[$fed] = ['name' => $A['name'], 'email' => $A['contact_email'], @@ -1378,7 +1389,6 @@ Click action names for a full description.</p>'; 'returns' => ['description' => 'Arrays of structured data - one entry per link type and test time.'], 'examples' => [['opt' => [], 'title' => 'Status list from the last test'],], ]); - if ($this->opts['help'] == 1) { return ""; } diff --git a/lib/Map_source/worldHigh.svg b/lib/Map_source/worldHigh.svg index 950b31d2156ba08b587ba68012740313238e1544..f909ae70c5b3221f3da67070f2c0dbeac363b5a8 100644 --- a/lib/Map_source/worldHigh.svg +++ b/lib/Map_source/worldHigh.svg @@ -1,7 +1,14 @@ <?xml version="1.0" encoding="utf-8"?> <!-- (c) ammap.com | SVG map of World High --> -<svg xmlns="http://www.w3.org/2000/svg" xmlns:amcharts="http://amcharts.com/ammap" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"> - <defs> +<svg + xmlns="http://www.w3.org/2000/svg" + xmlns:amcharts="http://amcharts.com/ammap" + xmlns:xlink="http://www.w3.org/1999/xlink" + version="1.1" + width="1009" + height="500"> + + <defs> <style type="text/css"> .land { @@ -277,4 +284,4 @@ <path id="ZM" title="Zambia" class="land" style="" d="M568.089,489.016L568.099,489.091L568.176,489.233L568.257,489.335L568.264,489.485L568.302,489.625L568.42,489.659L568.517,489.668L568.607,489.568L568.729,489.57L568.863,489.634L568.911,489.795L569.016,490.011L569.185,490.159L569.299,490.302L569.26,490.562L569.188,490.799L569.418,491.034L569.717,491.257L569.798,491.356L569.822,491.688L569.868,491.8L570.071,492.076L570.17,492.259L570.163,492.365L569.618,492.912L569.445,492.965L569.281,492.996L569.135,493.108L569.047,493.225L569.079,493.287L569.137,493.474L569.264,493.77L569.379,493.977L569.283,494.236L569.067,494.676L568.967,494.715L568.949,495.048L569.015,495.17L569.123,495.266L569.167,495.492L569.17,495.804L569.158,496.055L569.022,496.694L569.268,497.253L569.352,497.314L569.693,497.318L569.751,497.365L569.67,497.524L569.522,497.688L569.431,497.771L568.997,497.962L568.374,498.173L568.244,498.375L568.161,498.67L568.231,498.843L568.313,498.942L568.287,499.199L568.232,499.471L568.251,499.685L568.223,499.874L568.141,499.967L568.032,500.252L567.898,500.539L567.792,500.67L567.636,500.807L567.389,500.921L567.393,500.979L567.673,501.111L567.745,501.203L567.771,501.266L567.711,501.326L567.654,501.411L567.783,501.499L567.941,501.573L568.09,501.764L568.223,502.031L568.261,502.125L568.292,502.162L568.34,502.166L568.433,502.126L568.604,501.98L568.729,501.928L568.88,502.136L568.277,502.342L567.961,502.45L567.06,502.758L566.273,503.027L566.068,503.078L565.66,503.209L565.457,503.29L564.746,503.525L564.449,503.642L564.21,503.759L563.623,503.933L563.068,504.097L562.462,504.267L561.784,504.456L561.403,504.593L561.146,504.711L560.545,504.951L560.517,505.009L560.525,505.172L560.602,505.508L560.752,505.813L560.878,505.989L560.96,506.441L561.006,506.838L560.598,506.839L559.88,506.841L559.135,506.842L558.455,506.994L557.896,507.225L557.229,507.586L557.012,507.728L556.844,507.837L556.738,507.978L556.686,508.286L556.686,508.762L556.618,509.104L556.416,509.419L556.416,509.418L555.403,509.799L554.741,510.11L554.091,510.479L553.598,510.958L553.262,511.547L552.704,512.277L552.136,512.91L551.533,513.584L550.857,513.828L550.289,513.773L549.604,513.5L549.06,513.448L548.657,513.618L548.285,513.565L547.943,513.291L547.657,513.191L547.425,513.265L547.129,513.252L546.588,513.102L546.12,512.635L545.867,512.443L545.672,512.369L545.112,512.294L543.827,512.187L543.693,512.211L543.159,512.303L542.492,512.418L541.918,512.533L541.317,512.653L540.758,512.167L540.125,511.616L539.468,511.001L538.975,510.52L538.731,510.243L538.298,509.877L537.986,509.698L537.865,509.608L537.555,508.636L537.386,507.744L537.386,507.073L537.385,506.144L537.385,505.216L537.385,504.29L537.384,503.365L537.384,502.441L537.384,501.52L537.383,500.599L537.383,499.679L537.383,499.232L538.03,499.232L538.764,499.232L539.531,499.232L540.365,499.232L541.199,499.232L542.033,499.232L542.615,499.232L542.768,499.225L542.952,499.197L542.965,499.106L542.725,498.655L542.737,498.495L542.801,498.189L542.9,497.925L543.031,497.578L543.045,497.374L542.94,496.709L542.95,496.339L542.98,495.954L543.01,495.59L542.974,495.336L543.018,495.198L543.097,495L543.138,494.776L543.187,494.68L543.171,494.591L543.127,494.425L543.083,494.053L543.022,493.536L542.961,493.164L543.063,493.185L543.275,493.22L543.378,493.402L543.438,493.602L543.581,493.613L543.953,493.731L544.082,493.896L544.167,494.253L544.116,494.436L543.996,494.583L544.116,494.713L544.365,494.8L544.511,494.774L544.931,494.531L545.099,494.488L545.318,494.44L545.516,494.378L546.075,494.267L546.38,494.218L546.552,494.131L546.672,494.131L546.758,494.201L546.681,494.453L546.655,494.679L546.762,495.102L546.842,495.299L547.022,495.443L547.153,495.518L547.298,495.671L547.599,495.645L548.261,495.861L548.462,495.961L548.741,496.061L548.939,496.098L549.622,496.174L549.874,496.225L550.342,496.293L550.716,496.305L550.981,496.274L551.167,496.212L551.281,496.144L551.333,496.085L551.41,495.874L551.549,495.416L551.604,495.279L551.742,495.216L551.921,495.175L552.026,495.248L552.143,495.756L552.664,496.215L552.842,496.601L552.972,496.931L553.085,497.021L553.283,497.134L553.599,497.174L553.881,497.186L554.475,497.424L554.947,497.613L555.285,497.749L555.44,497.852L555.548,498.021L555.612,498.152L555.716,498.492L555.827,498.761L556.008,498.812L556.17,498.833L556.33,499.017L556.451,499.178L556.691,499.572L556.868,499.844L556.926,500.109L557.127,500.286L557.401,500.361L557.654,500.371L557.8,500.292L558.159,500.154L558.439,499.997L558.643,499.942L558.764,499.976L558.856,500.085L558.905,500.298L558.917,500.417L559.116,500.53L559.263,500.485L559.32,500.354L559.323,500.289L559.322,499.709L559.322,499.207L559.321,498.735L559.321,498.16L559.32,497.659L559.32,497.247L559.319,496.817L559.191,496.841L559.03,496.939L558.659,496.951L558.514,497.024L558.469,497.135L558.497,497.279L558.505,497.475L558.45,497.567L558.288,497.604L558.052,497.528L557.625,497.43L557.269,497.369L557.013,497.109L556.667,496.717L556.44,496.52L555.894,496.111L555.801,496.029L555.635,495.838L555.493,495.514L555.423,495.299L555.357,495.139L555.284,494.901L555.416,494.535L555.601,493.839L555.735,493.338L555.809,492.967L556.074,492.589L556.093,492.251L555.986,491.818L556.013,491.579L556.031,490.972L556.046,490.462L556.05,490.214L555.977,489.783L555.798,489.306L555.404,488.642L555.406,488.5L555.64,488.343L556.011,488.069L556.193,487.906L556.411,487.675L556.508,487.557L556.722,487.261L556.857,487.02L556.904,486.709L556.802,486.413L557.01,486.355L557.693,486.251L558.445,486.136L559.238,486.014L560.038,485.892L560.813,485.773L561.516,485.666L562.002,485.591L562.074,485.794L562.226,486.132L562.397,486.38L562.612,486.598L562.794,486.73L562.915,486.77L563.685,486.756L563.961,486.888L564.202,487.056L564.262,487.316L564.42,487.479L564.591,487.607L564.665,487.623L564.791,487.592L564.997,487.589L565.188,487.645L565.279,487.702L565.288,487.92L565.346,488.017L565.606,488.055L565.871,488.072L566.127,488.22L566.403,488.246L566.723,488.307L566.875,488.466L567.214,488.629L567.63,488.775L567.93,488.94z"/> <path id="ZW" title="Zimbabwe" class="land" style="" d="M563.508,526.797L563.254,526.625L562.907,526.513L562.465,526.461L561.892,526.482L561.186,526.577L560.429,526.462L559.62,526.14L558.949,526.026L558.147,526.166L558.111,526.169L557.973,526.06L557.753,525.825L557.388,525.783L557.289,525.729L557.207,525.641L557.153,525.529L557.132,525.404L557.192,525.018L557.159,524.975L557.062,524.928L556.861,524.882L556.38,524.707L555.774,524.537L554.791,524.351L554.409,524.303L554.32,524.246L554.21,524.104L554.021,523.661L553.844,523.369L553.421,522.918L553.354,522.779L553.375,522.421L553.406,522.133L553.451,521.889L553.431,521.66L553.425,521.376L553.438,521.186L553.381,521.104L553.228,521.046L552.791,521.02L552.263,521.031L552.246,520.743L552.195,520.297L552.097,520.041L551.975,519.908L551.732,519.769L551.241,519.579L550.572,519.29L550,518.862L549.345,518.331L549.14,518.239L548.898,517.741L548.53,516.889L548.554,516.606L548.498,516.467L548.14,516.05L548.061,515.833L547.998,515.614L547.429,515.004L547.235,514.738L547.087,514.396L546.94,514.123L546.816,514.013L546.654,513.828L546.542,513.616L546.49,513.458L546.533,513.247L546.588,513.102L547.129,513.252L547.425,513.265L547.657,513.191L547.943,513.291L548.285,513.565L548.657,513.618L549.06,513.448L549.604,513.5L550.289,513.773L550.857,513.828L551.533,513.584L552.136,512.91L552.704,512.277L553.262,511.547L553.598,510.958L554.091,510.479L554.741,510.11L555.403,509.799L556.416,509.418L556.416,509.419L556.618,509.104L556.686,508.762L556.686,508.286L556.738,507.978L556.844,507.837L557.012,507.728L557.229,507.586L557.896,507.225L558.455,506.994L559.135,506.842L559.88,506.841L560.598,506.839L561.006,506.838L561.012,507.295L561.043,507.81L561.123,507.859L561.663,507.87L562.529,507.907L563.364,507.941L563.897,508.315L564.076,508.395L564.63,508.495L565.338,509.118L566.19,509.176L566.776,509.371L567.292,509.585L567.589,509.842L567.781,509.9L568.041,509.919L568.167,509.943L568.14,510.128L567.966,510.442L567.989,510.892L568.228,511.516L568.26,512.06L568.187,513.019L568.189,513.95L568.214,514.283L568.253,514.503L568.304,514.624L568.294,514.762L568.152,515.154L568.038,515.566L568.034,515.731L567.99,515.848L567.905,515.951L567.533,516.142L567.47,516.26L567.471,516.474L567.518,516.654L567.658,516.72L567.826,516.822L567.892,516.956L567.893,517.098L567.839,517.361L567.689,517.796L567.839,518.298L568.006,518.624L568.237,519L568.333,519.233L568.328,519.4L568.293,519.563L567.948,520.253L567.699,520.683L567.395,521.143L566.993,521.432L566.889,521.571L566.848,521.729L566.862,522.075L566.844,522.438L566.5,522.995L566.713,523.475L566.665,523.52L566.549,523.588L566.054,524.129L565.553,524.678L565.187,525.079L564.771,525.535L564.305,526.047L563.906,526.486z"/> </g> -</svg> \ No newline at end of file +</svg> diff --git a/lib/config-template.php b/lib/config-template.php index 3991236decd054dbe6d356f356c7740fd4acdfac..8a47e403376782b1f3c9e6d03326fae7754583aa 100644 --- a/lib/config-template.php +++ b/lib/config-template.php @@ -1,22 +1,56 @@ <?php // site settings // -// the directory holding user access config files -define('eduGAIN_config','path_to_the_config_directory'); +// the directory holding user access config files (the trailig slash in important) +define('eduGAIN_config','path_to_the_config_directory/'); // the root URL for the site define('ROOT_URL','https://technical.edugain.org'); // the log file define('LOG_FILE','/var/log/edugain/technical_site.log'); +// the OTP server URL if you define this as null OTP will not be used +//define('OTP_SERVER', null); +define('OTP_SERVER', 'XXXX/otp_server.php'); +define('OTP_DATABASE', 'otp'); // database details (user details are taken form appropraiate configs in eduGAIN_config directory) define('DB_HOST','localhost'); -define('DB_DATABASE','edugain_twoln'); +define('DB_DATABASE','edugain'); // Validator locations define('VALIDATOR_SCRIPTS', '/srv/edugain.config/validator_scripts'); define('VALIDATOR_SCHEMAS_LOG', '/srv/edugain.config/validator_scripts/logs'); define('VALIDATOR_TMP', '/srv/edugain.config/mds/tmp'); +// details of simpleSAMLphp for management login + +define('SSP_PATH_TO_AUTOLOADER','/var/www/html/simplesamlphp/lib/_autoload.php'); +// set EDUGAIN_OT, EDUGAIN_SEC, EDUGAIN_SUPP to the value assinged in eduTEAMS +define('EDUGAIN_OT', 'eduGAIN-OT'); +define('EDUGAIN_SEC', 'eduGAIN-Secretariat'); +define('EDUGAIN_SUPP', 'eduGAIN-Support'); + +// if you set LOCAL_AUTH you will enable the option to use local accounts, not only federated +define('LOCAL_AUTH', false); + + +// system settings +define('COCO_ALWAYS',1); // set this to 0 if CoCo stuff is to be shown only when CoCo EC is selected +// the VERSION constant can be used to give templates/js/ files a version-dependent +// name and avoid problems with cacheing. +define('VERSION','_t'); +// this is the duration that the federation news history will show +define('BACK_TIME_FED', '18 months'); +// this is the duration that the eneities news history will show +define('BACK_TIME_ENTITIES', '14 days'); +// the log level use 3 or less for normal production +define('LOG_LEVEL',5); +// number of seconds to the expire time that causes the warning icon being shown +define('VALIDITY_WARNING_THRESHOLD', 388800); +// number of seconds to the expire time that causes the alert icon being shown +define('VALIDITY_ALERT_THRESHOLD', 10800); + +// The settings below can probably stay as they are +// // local URLs (mainly for the menu items) define('API_URL', ROOT_URL.'/api'); define('PARTICIPATING_FEDERATIONS_URL', ROOT_URL.'/status'); @@ -56,16 +90,6 @@ define('WIKI_URL','https://wiki.edugain.org/'); define('WIKI_ADD_IDP_URL','https://wiki.geant.org/display/eduGAIN/How+to+set+up+an+Identity+Provider+for+eduGAIN'); define('WIKI_ADD_SERVICE_URL','https://wiki.geant.org/display/eduGAIN/How+to+Join+eduGAIN+as+Service+Provider'); -// system settings -define('COCO_ALWAYS',1); // set this to 0 if CoCo stuff is to be shown only when CoCo EC is selected -define('VERSION','_t'); -define('BACK_TIME_FED', '18 months'); -define('BACK_TIME_ENTITIES', '14 days'); -define('LOG_LEVEL',5); -define('VALIDITY_WARNING_THRESHOLD', 388800); -define('VALIDITY_ALERT_THRESHOLD', 10800); - - // TWIG directories - do not modify unless you know what you are doing define('CACHE_LOCATION', '../var/cache/'); define('PAGE_LOGIC_LOCATION', '../page-logic/'); diff --git a/lib/eduGAIN_entity.php b/lib/eduGAIN_entity.php index 717933e3710fb4c281ec1748c5873fa0b2f665a8..557c9d67562f109edfe7df11db33a4e409b5fba9 100644 --- a/lib/eduGAIN_entity.php +++ b/lib/eduGAIN_entity.php @@ -74,18 +74,6 @@ class eduGAIN_entity extends eduGAIN { $this->globalStats = $out; } - private function generateNamespaces() { - $out = ''; - $q = "SELECT * FROM feed_namespaces"; - $result = $this->databaseQuery($q); - while ($obj = $result->fetch_assoc()) { - $nskey = $obj['nskey']; - $nsvalue = $obj['nsvalue']; - $out .= $nskey . '="' . $nsvalue . '" '; - } - return $out; - } - /** * Show entity metadata XML code */ @@ -99,13 +87,11 @@ class eduGAIN_entity extends eduGAIN { if (!is_numeric($entity_row_id)) { exit; } - $ns = $this->generateNamespaces(); $q = "SELECT entity FROM entities WHERE id=$entity_row_id"; $result = $this->databaseQuery($q); if ($obj = $result->fetch_assoc()) { $entityText = base64_decode($obj['entity']); - $entity = preg_replace('/md:EntityDescriptor /','md:EntityDescriptor ' . $ns, $entityText); - return($entity); + return($entityText); } else { die("entity search problem"); } @@ -981,7 +967,7 @@ class eduGAIN_entity extends eduGAIN { $c_span = ' colspan=2'; $out .= "<tr class='$row_class'><td style='text-align:right; width: 3em; padding-right:8px' rowspan=$j>ROW_NUMBER_REPLACE</td><td class='entity-role'>" . $V['type'] . "</td><td $c_span>" . $V['val'] . "</td><td rowspan=$j style='width:5em; vertical-align: top; padding:3px;'>$eccs $clash</td><td rowspan=$j class='$validatorClass'><a class='entity_links' id='link_" . $entity["id"] . "' href='javascript:show_details(" . - $entity["id"] . ",NEXT_REPLACE,PREV_REPLACE)'>Entity details</a></td> + $entity["id"] . ",NEXT_REPLACE,PREV_REPLACE)'>Entity details</a></td> </tr>\n"; foreach ($TR as $V) { diff --git a/lib/eduGAIN_manage.php b/lib/eduGAIN_manage.php new file mode 100644 index 0000000000000000000000000000000000000000..4ae0cb346c899dfa80e5f2de6602a1920d101a5f --- /dev/null +++ b/lib/eduGAIN_manage.php @@ -0,0 +1,358 @@ +<?php + +setlocale(LC_ALL, 'en_GB.UTF8'); +date_default_timezone_set('UTC'); + + +class eduGAIN_manage extends eduGAIN { + + public function __construct($production_only = 0) { + parent::__construct($production_only); + $this->load_federation_map_codes(); + $this->load_federation_country_names(); + $this->load_federation_search_strings(); + } + + private static $Colors = [ + 1 => "#00899F", + 4 => "#B5D000", + 6 => "#004359", + ]; + private $Color; + + private $args = []; + private $changedFields = []; + public $code = ''; + private $updateMaps = true; + + public function load_colors() { + $this->Color = []; + $this->Color['va'] = '#c0c0c0'; + $this->Color['im'] = '#c0c0c0'; + $this->Color['gg'] = '#c0c0c0'; + $this->Color['je'] = '#c0c0c0'; + foreach ($this->FEDS as $fed => $F) { + preg_match('/([A-Z]*)-?/', $F['code'], $m); + $country = strtolower($m[1]); + $this->Color[$country] = empty($this->Colors[$F['status']]) ? 'none' : $this->Colors[$F['status']]; + } + } + + + private function sanitizePost($postInput) { + $output = []; + $code = $postInput['code']; + if (!preg_match('/^[A-Z]+-?[A-Z]*$/', $code)) { + exit; + } + $this->code = $code; + + $output['code'] = $code; + $this->changedFields = json_decode($this->cleanInput($postInput["changed_fields"], false)); + foreach ([ + 'europe', + 'policy_english', + 'tsg_delegate_0_3', + 'tsg_deputy_0_3', + 'tsg_delegate_1_3', + 'tsg_deputy_1_3', + ] as $key) { + if (isset($postInput[$key])) { + $output[$key] = 1; + } else { + $output[$key] = 0; + } + } + + foreach ( ['url', 'metadata_url', 'reg_auth', 'security_contact_url_value',] as $key) { + if(!empty($postInput[$key])) { + $output[$key] = filter_var($this->cleanInput($postInput[$key]), FILTER_SANITIZE_URL); + } + } + + for ($i = 0; $i < 6; $i++) { + foreach (['policy', 'registration'] as $type) { + if (isset($postInput[$type . '_' . $i])) { + $output[$type . '_' . $i] = filter_var($this->cleanInput($postInput[$type . '_' . $i]), FILTER_SANITIZE_URL); + } + } + } + + foreach ( ['status', 'fed_status', 'policy_version', 'policy_hardcopy'] as $key) { + if(isset($postInput[$key]) && $postInput[$key] !== "") { + $output[$key] = filter_var($postInput[$key], FILTER_SANITIZE_NUMBER_INT); + } + } + + foreach ( ['contact_email', 'security_contact_mail_value', 'tsg_delegate_0_2', 'tsg_delegate_1_2', 'tsg_deputy_0_2', 'tsg_deputy_1_2'] as $key) { + if(!empty($postInput[$key])) { + $output[$key] = filter_var($postInput[$key], FILTER_SANITIZE_EMAIL); + } + } + + if (!empty($postInput[$key]) && preg_match('/^\+?[\d ]+$/', $postInput['security_contact_phone_value'])) { + $output['security_contact_phone_value'] = $postInput['security_contact_phone_value']; + } + + foreach ( ['policy_date', 'membership_date', 'production_date'] as $key) { + if (!empty($postInput[$key]) && preg_match('/^\d\d\d\d-\d\d-\d\d$/', $postInput[$key])) { + $output[$key] = $postInput[$key]; + } + } + + foreach ( ['name', 'tsg_delegate_0_0', 'tsg_delegate_0_1', 'tsg_delegate_1_0', 'tsg_delegate_1_1', 'tsg_deputy_0_0', 'tsg_deputy_0_1', 'tsg_deputy_1_0', 'tsg_deputy_1_1'] as $key) { + if(!empty($postInput[$key])) { + $output[$key] = $this->cleanInput($postInput[$key]); + } + } + + for ($i = 0; $i < 5; $i++) { + $key = 'country_code_' . $i; + if(isset($postInput[$key]) && ($postInput[$key] === "0" || $postInput[$key] === "1" || preg_match('/^[a-z][a-z]$/', $postInput[$key]))) { + if ($postInput[$key] === "0" || $postInput[$key] === "1" || isset(Countries::names[strtoupper($postInput[$key])])) { + $output[$key] = $postInput[$key]; + } + } + } + $this->args = $output; + Utils::debug(4, $this->changedFields, "sanitizePost: Changed fields: ", "\n"); + } + + private function handleDeclaration() { + if (isset($_FILES['pol_file']['error']) && ($_FILES['pol_file']['error'] === UPLOAD_ERR_OK)) { + if (isset($_FILES['pol_file']['tmp_name'])) { + if ("eduGAIN_Declaration_" . $this->code . "_" . $this->args['policy_version'] . ".pdf" != $_FILES["pol_file"]["name"]) { + echo("wrong file name: " . $_FILES["pol_file"]["name"] . ", upload skipped"); + } else { + if (file_exists("../../Declarations/" . $_FILES["pol_file"]["name"])) { + echo("file " . $_FILES["pol_file"]["name"] . " already exists - upload skipped"); + } else { + move_uploaded_file($_FILES["pol_file"]["tmp_name"], "../../Declarations/" . $_FILES["pol_file"]["name"]); + echo("file " . $_FILES["pol_file"]["name"] . " uploaded"); + } + } + } + } + } + + private function uploadDeclaration() { + if (isset($_FILES['pol_file']['error']) && ($_FILES['pol_file']['error'] === UPLOAD_ERR_OK)) { + if (isset($_FILES['pol_file']['tmp_name'])) { + $declarationFileName = "eduGAIN_Declaration_" . $this->code . "_" . $this->args['policy_version'] . ".pdf"; + move_uploaded_file($_FILES["pol_file"]["tmp_name"], "../../Declarations/" . $declarationFileName); + echo("file " . $_FILES["pol_file"]["name"] . " uploaded"); + } + } + } + + private function updateFederationStatus($superadmin) { + + } + + private function updateUniqueRecord($table, $fields) { + Utils::debug(4, $fields, "updateUniqueRecord: Fields:", "\n"); + Utils::debug(4, $this->changedFields, "updateUniqueRecord: Changed fields:", "\n"); + $update_map = 0; + $noUpdates = true; + $U = []; + $V = []; + $FL = []; + foreach ($fields as $f) { + if (!isset($this->args[$f])) { + continue; + } + $FL[] = $f; + $v = $this->args[$f]; + $val = is_numeric($v) ? $v : "'$v'"; + $V[] = $val; + if (in_array($f, $this->changedFields)) { + $U[] = "$f=$val"; + $noUpdates = false; + } + } + if ($noUpdates) { + Utils::debug(4, "updateUniqueRecord: noupdates\n"); + return false; + } + $q = "INSERT INTO $table (" . join(',', $FL) . ") VALUES (" . join(',', $V) . ")" . + " ON DUPLICATE KEY UPDATE " . join(',', $U); + $this->databaseQuery($q); + return true; + } + + private function updateMdsData($superadmin) { + if ($superadmin !== 1) { + if ($this->args['status'] > 4 || in_array('code', $this->changedFields) || + in_array('metadata_url', $this->changedFields) || in_array('reg_auth', $this->changedFields)) { + Utils::debug(4, "updateMdsData - updates not allowed without superadmin rights\n"); + return; + } + } + Utils::debug(4, $this->args['status'], "MDS Status:", "\n"); + $f = ['code', 'metadata_url', 'reg_auth', 'status']; + $this->updateUniqueRecord('mds_data', $f); + if ($this->args['status'] != $this->args['fed_status'] && $this->updateMaps) { + $this->updateMaps = false; + eduGAIN_map::make_maps(); + eduGAIN_map::make_maps(1); + } + } + + private function updateFederationTable() { + $f = ['fed_id', 'code', 'europe', 'contact_email', 'url', 'policy_english', + 'policy_hardcopy', 'policy_date', 'membership_date', 'production_date', 'policy_version', + 'name']; + $this->updateUniqueRecord('federation', $f); + } + + private function updateSecurityContacts() { + $noUpdates = true; + $table = 'security_contact'; + $fields = ['mail', 'url', 'phone']; + $inserts = []; + foreach ($fields as $field) { + $field_value = 'security_contact_'.$field.'_value'; + Utils::debug(4, $field, "updateSecurityContacts: testing: ", "\n"); + if (in_array($field_value, $this->changedFields)) { + $noUpdates = false; + } + if (!empty($this->args[$field_value])) { + Utils::debug(4, $field, "updateSecurityContacts: updating: ", "\n"); + $q = "INSERT INTO $table (code, type, value) VALUES ('$this->code', '$field', '" . $this->args[$field_value] . "')"; + $inserts[] = $q; + } + } + $this->runUpdates($table, $noUpdates, $inserts); + } + + private function updatePersonTable() { + $Person = []; + $noUpdates = true; + foreach ([ + 'tsg_delegate_0_0', + 'tsg_delegate_0_1', + 'tsg_delegate_0_2', + 'tsg_delegate_0_3', + 'tsg_deputy_0_0', + 'tsg_deputy_0_1', + 'tsg_deputy_0_2', + 'tsg_deputy_0_3', + 'tsg_delegate_1_0', + 'tsg_delegate_1_1', + 'tsg_delegate_1_2', + 'tsg_delegate_1_3', + 'tsg_deputy_1_0', + 'tsg_deputy_1_1', + 'tsg_deputy_1_2', + 'tsg_deputy_1_3', + ] as $key) { + $m = explode('_', $key); + $function = $m[1]; + $functionIndex = $m[2]; + $functionField = $m[3]; + $v = $this->args[$key]; + $v = is_numeric($v) ? $v : "'$v'"; + $Person[$function][$functionIndex][$functionField] = $v; + if (in_array($key, $this->changedFields)) { + $noUpdates = false; + } + } + if ($noUpdates) { + return; + } + $table = 'person'; + $q = "DELETE FROM $table WHERE code = '$this->code'"; + $this->databaseQuery($q); + foreach ($Person as $function => $PR) { + foreach ($PR as $p) { + // we assume that the person must have a last-name + if ($p[1] == "''") { + continue; + } + $q = "INSERT INTO $table (given_name, last_name, email, show_email, code, role) VALUES (" . join(',', $p) . ",'$this->code','tsg_$function')"; + $this->databaseQuery($q); + } + } + + } + + private function updatePolicyTable() { + $table = 'policy'; + $noUpdates = true; + $inserts = []; + for ($i = 0; $i < 6; $i++) { + foreach (['policy', 'registration'] as $type) { + if (isset($this->args[$type . '_' . $i]) && $this->args[$type . '_' . $i]) { + $q = "INSERT INTO $table (code,type,url) VALUES ('$this->code','$type','" . $this->args[$type . '_' . $i] . "')"; + $inserts[] = $q; + if (in_array($type . '_' . $i, $this->changedFields)) { + $noUpdates = false; + } + } + } + } + $this->runUpdates($table, $noUpdates, $inserts); + } + + private function runUpdates($table, $noUpdates, $inserts) { + if ($noUpdates) { + return; + } + $q = "DELETE FROM $table WHERE code = '$this->code'"; + $this->databaseQuery($q); + foreach ($inserts as $q) { + $this->databaseQuery($q); + } + } + + private function updateRegions() { + $table = 'regions'; + $noUpdates = true; + $inserts = []; + for ($i = 0; $i < 5; $i++) { + $key = 'country_code_' . $i; + if (!isset($this->args[$key])) { + continue; + } + if ($this->args[$key] !== "0" && in_array($key, $this->changedFields)) { + $noUpdates = false; + } + } + if ($noUpdates) { + return; + } + + for ($i = 0; $i < 5; $i++) { + $key = 'country_code_' . $i; + $countryCode = $this->args[$key]; + if (empty($countryCode) || $countryCode === "1") { + continue; + } + $inserts[]= "INSERT INTO $table (code,country_code,priority) VALUES('$this->code','$countryCode',$i)"; + } + $this->runUpdates($table, $noUpdates, $inserts); + + if ($this->updateMaps) { + $this->updateMaps = false; + eduGAIN_map::make_maps(); + eduGAIN_map::make_maps(1); + } + } + + public function update_EduGAINTables($A, $superadmin) { + if (empty($A['code'])) { + return; + } + $this->sanitizePost($A); + $this->uploadDeclaration(); + $this->updateMdsData($superadmin); + $this->updateSecurityContacts(); + $this->updateFederationTable(); + $this->updatePersonTable(); + $this->updatePolicyTable(); + $this->load_colors(); + $this->updateRegions(); + } + +} + diff --git a/lib/eduGAIN_manage_gui.php b/lib/eduGAIN_manage_gui.php new file mode 100644 index 0000000000000000000000000000000000000000..3674c246046ead1fec4fc781acc195f0950ca429 --- /dev/null +++ b/lib/eduGAIN_manage_gui.php @@ -0,0 +1,185 @@ +<?php + +/** + * Description of eduGAIN_manage_gui + * + * @author twoln + */ +setlocale(LC_ALL, 'en_GB.UTF8'); + +class eduGAIN_manage_gui { + public function __construct($role) { + $this->role = $role; + } + + public static function pageGraphics() { + print '<head> +<link rel="stylesheet" type="text/css" href="css/master.css"> +<link rel="stylesheet" href="third-party/theme/jquery-ui.css"> +<script src="third-party/jquery.js"></script> +<script src="third-party/jquery-ui.js"></script> +</head> +<body> + <div id="inner"> +<header> + <div style="position: relative; height: 115px; width: 100%; background: #003f5f"> + <img id="geant_logo_t" src="images/geant_logo1.png" style="position: absolute; left: 20px; bottom: 0px;"> + <img src="images/edugain_logo1.png" style="position:absolute; right: 40px; bottom: 0px;"> + </div> + </header>'; + } + + public function row($label, $type, $id, $sub_id = '', $critical = 0, $hidden = 0, $nonempty_level = 10) { + $class = ''; + $additionalClass = ''; + $ro = ''; + $disabled = ''; + $statusDisabled = ''; + $out = ''; + $outArray; + + switch ($critical) { + case 2: // OT only + $class = 'critical ot'; + if ($this->role != 'OT') { + $ro = ' readonly'; + $disabled = ' disabled'; + } + break; + case 1: // requires OTP + $class = 'critical'; + $ro = ''; + break; + default: + break; + } + + + + if ($nonempty_level < 10) { + if ($class != '') { + $class = ' class="nonempty_level_'.$nonempty_level.' '.$class.'"'; + } else { + $class = ' class="nonempty_level_'.$nonempty_level.'"'; + } + } else { + if ($class != '') { + $class = ' class="'.$class.'"'; + } + } + + if ($hidden == 1) { + $additionalClass = ' class="additional"'; + } + $inp_size = 100; + $h_style = 'font-weight: bold;'; + if ($sub_id !== '') { + if ($sub_id !== -1) + $id = $id . '_' . $sub_id; + $h_style = 'font-style: italic;'; + $inp_size = 80; + } + $out .= '<tr' . $additionalClass . '><td style="vertical-align: top; ' . $h_style . '">' . $label . '</td><td>'; + if ($type == 'I') { + $out .= '<input type="text" size=' . $inp_size . $class . $ro . ' name="' . $id . '" id="' . $id . '">'; + } + if ($type == 'R') { + $out .= '<input type="radio" name="' . $id . '"' . $class . ' id="' . $id . '">'; + } + if ($type == 'C') { + $out .= '<input type="checkbox" name="' . $id . '"' . $class . $disabled . ' id="' . $id . '">'; + } + if ($type == 'AREA') { + $out .= '<select name="' . $id . '"' . $class . $ro . ' id="' . $id . '"> + <option value="0">select one</option><option value="1">remove</option>'; + foreach (Countries::names as $code => $name) { + $out .= '<option value="' . strtolower($code) . '">' . $name . '</option>'; + } + $out .= '</select>'; + } + if ($type == 'ST') { + $out .= '<select name="' . $id . '"' . $class . $statusDisabled . ' id="' . $id . '"> +<option value="0" id="status_none" class="critical" selected>none</option> +<option value="1" class="critical">candidate</option> +<option value="4" class="critical">voting only</option> +<option value="5" class="critical, ot">suspended</option> +<option value="6" class="critical, ot">paricipant</option> +</select>'; + } + if ($type == "SIRTFI") { + $out .= '<table>'; + $out .= $this->row('E-mail', 'I', $id . '_mail_value', -1, 1); + $out .= $this->row('preferred', 'C', $id . '_mail_preferred', -1, 1); + $out .= $this->row('WWW', 'I', $id . '_url_value', -1, 1); + $out .= $this->row('preferred', 'C', $id . '_url_preferred', -1, 1); + $out .= $this->row('Tel', 'I', $id . '_phone_value', -1, 1); + $out .= $this->row('preferred', 'C', $id . '_phone_preferred', -1, 1); + $out .= '</table>'; + } + if ($type == "PERS") { + $out .= '<table>'; + $out .= $this->row('Given name', 'I', $id . '_0', 0); + $out .= $this->row('Last name', 'I', $id . '_0', 1); + $out .= $this->row('E-mail', 'I', $id . '_0', 2); + $out .= $this->row('Public e-mail', 'C', $id . '_0', 3); + $out .= $this->row('Given name', 'I', $id . '_1', 0); + $out .= $this->row('Last name', 'I', $id . '_1', 1); + $out .= $this->row('E-mail', 'I', $id . '_1', 2); + $out .= $this->row('Public e-mail', 'C', $id . '_1', 3); + $out .= '</table>'; + } + if ($type == "EDECL") { + $out .= '<table>'; + $out .= $this->row('Policy version', 'I', 'policy_version', -1, 0, 0, 1); + $out .= $this->row('Policy date', 'I', 'policy_date', -1, 0, 0, 1); + $out .= $this->row('Signed policy', 'SPOL', 'signed_policy', -1); +// $out .= $this->row('Hardcopy', 'I', 'policy_hardcopy', -1); + $out .= $this->row('Membership date', 'I', 'membership_date', -1, 0, 0, 4); + $out .= $this->row('Production date', 'I', 'production_date', -1, 2, 0, 6); + $out .= '</table>'; + } + if ($type == "POL") { + $out .= '<div><input type="text" class="nonempty_level_4" size=' . $inp_size . ' name="' . $id . '_0" id="' . $id . '_0"></div>'; + $out .= '<div class="additional ' . $id . '"><input type="text" size=' . $inp_size . ' name="' . $id . '_1" id="' . $id . '_1"></div>'; + $out .= '<div class="additional ' . $id . '"><input type="text" size=' . $inp_size . ' name="' . $id . '_2" id="' . $id . '_2"></div>'; + $out .= '<div class="additional ' . $id . '"><input type="text" size=' . $inp_size . ' name="' . $id . '_3" id="' . $id . '_3"></div>'; + $out .= '<div class="additional ' . $id . '"><input type="text" size=' . $inp_size . ' name="' . $id . '_4" id="' . $id . '_4"></div>'; + $out .= '<div class="additional ' . $id . '"><input type="text" size=' . $inp_size . ' name="' . $id . '_5" id="' . $id . '_5"></div>'; + $out .= '<button id="' . $id . '" class="next_button ui-button ui-corner-all ui-widget">Add another</button>'; + } + + if ($type == "AREAS") { + $out .= '<table>'; + $out .= $this->row('', 'AREA', $id, 0); + $out .= $this->row('', 'AREA', $id, 1, 0, 1); + $out .= $this->row('', 'AREA', $id, 2, 0, 1); + $out .= $this->row('', 'AREA', $id, 3, 0, 1); + $out .= $this->row('', 'AREA', $id, 4, 0, 1); + $out .= '<tr><td></td><td><button id="next_country_button" class="ui-button ui-corner-all ui-widget" id="' . $id . '">Add another</button></td></tr>'; + $out .= '</table>'; + } + + if ($type == "CERT") { + + $out .= '<div class="additional ' . $id . '"><div id="' . $id . '_0"></div></div>'; + $out .= '<div class="additional ' . $id . '"><div id="' . $id . '_1"></div></div>'; + $out .= '<div class="additional ' . $id . '"><div id="' . $id . '_2"></div></div>'; + $out .= '<div class="additional ' . $id . '"><div id="' . $id . '_3"></div></div>'; + } + + if ($type == "SPOL") { + $out .= '<div class="additional ' . $id . '"><div id="' . $id . '_0"></div></div>'; + $out .= '<div class="additional ' . $id . '"><div id="' . $id . '_1"></div></div>'; + $out .= '<div class="additional ' . $id . '"><div id="' . $id . '_2"></div></div>'; + $out .= '<div class="additional ' . $id . '"><div id="' . $id . '_3"></div></div>'; + $out .= '<div style="display:none" id="pol_div"><input type="file" name="pol_file" id="pol_file"></div>'; + $out .= '<div style="display:none" id="declaration_div"><input type="text" size="80" id="declaration_file" readonly></div>'; + } + + $out .= '</td></tr>' . "\n"; + return $out; + } + + private $role = ''; + +} diff --git a/lib/eduTeams.php b/lib/eduTeams.php new file mode 100644 index 0000000000000000000000000000000000000000..6deee1d22dd4c6edf977bb1195535a4711a56872 --- /dev/null +++ b/lib/eduTeams.php @@ -0,0 +1,64 @@ +<?php + +use Exception; + +include_once SSP_PATH_TO_AUTOLOADER; + +class eduTeams { + + /** + * initialise ourselves, and simpleSAMLphp + */ + public function __construct() { + $this->auth = new \SimpleSAML\Auth\Simple('default-sp'); + $this->session = \SimpleSAML\Session::getSessionFromRequest(); + } + /** + * finds out whether the user is already authenticated. Does not trigger an authentication if not. + * + * @return boolean auth state + */ + public function isAuthenticated() { + $status = $this->auth->isAuthenticated(); + $this->session->cleanup(); + return $status; + } + + /** + * authenticates a user. + * + * @return void + * @throws Exception + */ + + public function authenticate() { + $this->auth->requireAuth(); + $this->session->cleanup(); + Utils::debug(3,"authenticate via eduTEAMS done\n"); + } + + public function getUser() { + $attributes = $this->auth->getAttributes(); + $this->session->cleanup(); + if (!isset($attributes['urn:oid:1.3.6.1.4.1.5923.1.1.1.13'][0])) { + $failtext = "FATAL ERROR: we did not receive a unique user identifier from the authentication source!"; + echo $failtext; + throw new Exception($failtext); + } + $user = $attributes['urn:oid:1.3.6.1.4.1.5923.1.1.1.13'][0]; + $email = $attributes['urn:oid:0.9.2342.19200300.100.1.3'][0]; + $groups = $attributes['urn:oid:1.3.6.1.4.1.5923.1.1.1.7']; + $G = []; + foreach ( $groups as $group) { + $m = []; + if (preg_match('/^urn:geant:eduteams.org:service:eduteams:group:eduGAIN:(.*)#eduteams\.org$/', $group, $m)) { + $G[] = $m[1]; + } + } + return(['user' => $user, 'groups' => $G, 'email' => $email]); + } + public $session; + public $auth; +} + + diff --git a/lib/localAuth.php b/lib/localAuth.php new file mode 100644 index 0000000000000000000000000000000000000000..8bd6d86c500622149fa587ccb8e9169c6bab359a --- /dev/null +++ b/lib/localAuth.php @@ -0,0 +1,53 @@ +<?php +session_start(); + +use Exception; + +class localAuth { + + public function isAuthenticated() { + if (isset($_SESSION['auth_ok']) && $_SESSION['auth_ok'] == 'authenticated') { + return true; + } else { + return false; + } + + } + + public function authenticate() { + Utils::debug(3,"Authenticating locally\n"); + require_once(eduGAIN_config . "admin.php"); + if (isset($_POST['user'])) { + if (array_key_exists($_POST['user'], $admins)) { + Utils::debug(3,"local AUTH testing\n"); + if (isset($_POST['passwd']) && password_verify($_POST['passwd'], $admins[$_POST['user']]['hash'])) { + Utils::debug(3,"local AUTH testing1\n"); + + $_SESSION['auth_ok'] = 'authenticated'; + $user = [ + 'user' => $admins[$_POST['useśr']]['mail'], + 'groups' => $admins[$_POST['user']]['groups'], + 'email' => $admins[$_POST['user']]['mail'] + ]; + $_SESSION['user'] = $user; + Utils::debug(3,"local AUTH successful\n"); + header("Location: ".ROOT_URL."/authenticate"); + exit; + } + } + Utils::debug(3,"local AUTH failed\n"); + header("Location: ".ROOT_URL."/authenticate?retry"); + exit; + } + } + + public function getUser() { + require_once(eduGAIN_config . "admin.php"); + $user = $_SESSION['user']; + $email = $admins[$user['user']]['mail']; + return(['user' => $user, 'groups' => $user['groups'], 'email' => $email]); + } + +} + + diff --git a/lib/validatorClass.php b/lib/validatorClass.php index 842973a41ddb2f151aceac261700026612cca834..bd5abb3caf229f14a9a844ff64e2ca53237e3507 100644 --- a/lib/validatorClass.php +++ b/lib/validatorClass.php @@ -92,6 +92,7 @@ class Validator { } fputs($ff, "-----END CERTIFICATE-----\n"); fclose($ff); + $this->edugain->FEDS[$fed_id]['certificate'][$n]['certfile'] = $certfilename; } } $ff = fopen($this->params['filename'].'-certinfo.json', 'w'); @@ -279,6 +280,7 @@ class Validator { public function checkmetadata() { $params = $this->params; $schres = 0; + $certidx = -1; if ( ($params['validate'] || $params['entities']) && ($params['url'] != "") ) { $ret = $this->get_data(); if ($ret[0] == 304) { @@ -325,8 +327,11 @@ class Validator { if ($params['edugain'] || $params['gobetween']) $fromdb = 1; else $fromdb = 0; system($this->validatordir."/aggregator.py ".$params['filename']." $schres $fromdb >".$params['filename']."-aggregator.log 2>&1",$aggres); + $certidx = trim(file_get_contents($params['filename']."-aggregator.log")); + if ($certidx == '') { + $certidx = 0; + } system($this->validatordir."/mda.sh --verbose ".$params['filename'].".xml main > ".$params['filename']."-mda.log 2>&1", $res); - $certidx = 0; $searchfor = "Unable to validate signature"; $matches = array(); $handle = @fopen($params['filename']."-mda.log", "r"); @@ -343,7 +348,10 @@ class Validator { if (count($matches)) { system($this->validatordir."/aggregator.py ".$params['filename']." $schres $fromdb 1 >".$params['filename']."-aggregator2.log 2>&1",$aggres); system($this->validatordir."/mda.sh --verbose ".$params['filename']."_1.xml main > ".$params['filename']."-mda.log 2>&1", $res); - $certidx = 1; + $certidx = trim(file_get_contents($params['filename']."-aggregator2.log")); + if ($certidx == '') { + $certidx = 1; + } } system($this->validatordir."/pyff.sh ".$params['filename'].'>'.$params['filename']."-pyff.log 2>&1", $pyffres); } else $res = $schres; diff --git a/page-logic/authenticate.php b/page-logic/authenticate.php new file mode 100644 index 0000000000000000000000000000000000000000..b7c64bf802b117e5c2f2d54a8ba620a556645cf5 --- /dev/null +++ b/page-logic/authenticate.php @@ -0,0 +1,94 @@ +<?php +/* + * This takes care of the first step of authentication - either federated or via local username/passwrd + * when authentication is done the user is redirected to "manage" + */ +session_start(); + + +function runAuth($auth_type) { + if ($auth_type == 'none') { + return; + } + if ($auth_type == 'local') { + $auth = new \localAuth(); + } else { + $auth = new \eduTeams(); + } + if (!$auth->isAuthenticated()) { + Utils::debug(3,"AUTH required\n"); + $auth->authenticate(); + Utils::debug(3,"AUTH successful\n"); + exit; + } + $user = $auth->getUser(); + if (in_array(EDUGAIN_OT, $user['groups'])) { + $admin = true; + } elseif (in_array(EDUGAIN_SEC, $user['groups'])) { + $secretariat = true; + } else { + print("Sorry, no proper authorization"); + Utils::debug(2, $user, "Unauthorised access:", "\n"); + exit; + } + $_SESSION['auth_ok'] = 'authenticated'; + $_SESSION['user'] = $user; + header("Location: ".ROOT_URL."/manage"); + exit; + +} + +$authType = 'none'; +if(isset($_POST['user'])) { +$authType = 'local'; +//exit; +} elseif(isset($_GET['eduteams'])) { + $authType = 'eduteams'; +} + + +// if already authenticated send to "manage" +if (isset($_SESSION) && isset($_SESSION['auth_ok']) && isset($_SESSION['user']) && $_SESSION['auth_ok'] == 'authenticated') { + Utils::debug(3,"authenticate: AUTH not needed\n"); + header("Location: ".ROOT_URL."/manage"); + exit; +} + +runAuth($authType); + +$admin = 0; +$secretariat = 0; + +// first test if is SSP authenticated and if not, authenticate and return back to the start of this script + + +eduGAIN_manage_gui::pageGraphics(); + ?> +<div style="margin-left: 2em; margin-top:4ex"> + <h2> +<?php +if (isset($_GET['retry'])) { + print ('Incorrect credentials entered, please retry'); +} else { + print('Authentication required for accessing the federations management inteface'); +} +?> +</h2> +<p> +<div style="width: 20em;padding: 1ex; font-size:30px; font-weight: bold; background:#f1893b; text-align: center"> + <a href="<?php echo ROOT_URL ?>/authenticate?eduteams">Login via eduGAIN</a> +</div> +<?php if (LOCAL_AUTH === true) { ?> +<p> +or use the form below to login with a local account<p> +<form action="<?php echo ROOT_URL ?>/authenticate" method = POST> +<table> +<tr><td>Username:</td><td><input name="user" type="text"></td></tr> +<tr><td>Password:</td><td><input name="passwd" type="password"></td></tr> +<tr><td> </td><td><input type="submit" value="Submit"></td></tr> +</table> +</form> +<?php } ?> +</div> +</div> + diff --git a/page-logic/manage.php b/page-logic/manage.php new file mode 100644 index 0000000000000000000000000000000000000000..1526c2cb5797016b8f0021d25c3a00c63ad81006 --- /dev/null +++ b/page-logic/manage.php @@ -0,0 +1,242 @@ +<?php + use OTPHP\TOTP; + +//Set the cookie lifetime of the session +ini_set( "session.cookie_lifetime", 3600 ); +session_start(); +Utils::debug(3,"Starting MANAGE\n"); + + +class Management { + public function __construct() { + $this->otpCode = filter_var($_POST['second_factor'], FILTER_SANITIZE_NUMBER_INT); + require(eduGAIN_config . "admin.php"); + if (isset($_SESSION['user'])) { + $this->user = $_SESSION['user']; + $this->groups = $this->user['groups']; + } + $this->setUserRole(); + if (isset($_POST['critical_save']) && $_POST['critical_save'] == 1) { + $this->criticalSave = 1; + } + } + + public function setMFA() { + Utils::debug(3,$_SESSION['mfa_authenticated'], "MFA auth: ", "\n"); + if (!isset($_SESSION['mfa_authenticated']) || $_SESSION['mfa_authenticated'] != 1) { + if ($this->verifyOtp($this->user, $this->otpCode) == 3) { + $this->authOtp(); + } + } + } + + public function verifyMFASetup() { + + if (!isset($_SESSION['mfa_authenticated']) || $_SESSION['mfa_authenticated'] != 1) { + $this->verifyOtp($this->user); + } + } + + public function setPostVar() { + if (LOCAL_AUTH == true && OTP_SERVER == null && $this->admin == 1) { + $this->superadmin = 1; + $this->post_var = $_POST; + return; + } + if ($this->otpCode != '') { + $ooR = $this->verifyOtp($this->user, $this->otpCode); + if ($ooR === 1) { + if ($this->admin == 1) { + $this->superadmin = 1; + } + $this->post_var = $_SESSION['last_post']; + unset($_SESSION['last_post']); + } else { + print "Verification failed"; + unset($_SESSION['last_post']); + exit; + } + } else { + $this->post_var = $_POST; + } + } + + public function checkCriticalSave() { + if ($this->criticalSave) { + if ($this->admin == 0 && $this->secretariat == 0) { + eduGAIN_manage_gui::pageGraphics(); + print("Not authorized to perform this operation"); + Utils::debug(4, "Not authorized for critical save\n"); + exit; + } + $this->authOtp(); + } + } + + private function authOtp() { + if (is_null(OTP_SERVER)) { + return(1); + } + $_SESSION['last_post'] = $_POST; + header("Location: ".ROOT_URL."/otp_code"); + exit; + } + + private function verifyOtp($user, $otpCode = '') { + if (is_null(OTP_SERVER)) { + return(1); + } + if ($this->otp_succeeded === 1 && $otpCode !== '') { + return(1); + } + $this->otp_succeeded = 0; + $url = OTP_SERVER.'?user='.$user['email'].'&otp='.$otpCode; + Utils::debug(3,$url, "OTP server link: ", "\n"); + $res = json_decode(file_get_contents($url)); + Utils::debug(3,$res, "Result form OTP: ", "\n"); + if (!isset($res)) { + print("Sorry there was a problem with OTP authentication"); + exit; + } + switch ($res) { + case -1: // user not found, generate code + header("Location: ".ROOT_URL."/otp_start"); + exit; + case 0: // user found but no OTP match + $_SESSION['otp_set'] = 1; + if ($otpCode !== '') { + eduGAIN_manage_gui::pageGraphics(); + print '<div style="margin-left: 2em; margin-top:4ex">'; + print "<h1>Verification failed</h1><p>"; + print("<p><a href=''><b>Try again</b>"); + header("Location: ".ROOT_URL."/otp_code?repeat"); + exit; + } + return(0); + case 1: // OTP match + $_SESSION['otp_set'] = 1; + Utils::debug(3, "setting session mfa_authenticated\n"); + $_SESSION['mfa_authenticated'] = 1; + $this->otp_succeeded = 1; + return(1); + case 2: // match but not verified + $_SESSION['unverified'] = 1; + $this->authOtp(); + return(2); + case 3: + $_SESSION['otp_set'] = 1; + return(3); + case 4: + eduGAIN_manage_gui::pageGraphics(); + print '<div style="margin-left: 2em; margin-top:4ex">'; + print "<h1>This code has already been used. Please wait for the new code and </h1><p>"; + unset($_SESSION['last_post']); + print("<p><a href=''><b>try again</b>"); + exit; + default: + exit; + } + + } + + private function setUserRole () { + if (in_array(EDUGAIN_OT, $this->groups)) { + $this->admin = true; + $this->role = "OT"; + } elseif (in_array(EDUGAIN_SEC, $this->groups)) { + $this->secretariat = true; + $this->role = "Secretariat"; + } else { + print("Sorry, no proper authorization"); + exit; + } + } + + public $user = []; + public $admin = 0; + public $secretariat = 0; + public $superadmin = 0; + public $otpRequired = 0; + public $role = ''; + public $groups = []; + public $criticalSave = 0; + public $otpCode = ''; + public $post_var = ''; + public $otp_succeeded = 0; + public $authType = 'eduteams'; + +} +// if not authenticated send to "authenticate" +Utils::debug(3,"Starting AUTH\n"); +if (!isset($_SESSION['auth_ok']) || $_SESSION['auth_ok'] != 'authenticated') { + Utils::debug(3,"AUTH required\n"); + header("Location: ".ROOT_URL."/authenticate"); + exit; +} +Utils::debug(3,$_SESSION['auth_ok'], "Authentication successfull: ", "\n"); +$mgm = new Management(); +Utils::debug(3,$mgm->role, "User role:", "\n"); + + +// the user needs to be MFA authenticated +if ($mgm->role == 'OT') { + $mgm->setMFA(); +} else { + $mgm->verifyMFASetup(); +} +$mgm->checkCriticalSave(); +$mgm->setPostVar(); + +if ($mgm->superadmin) + require(eduGAIN_config . "database_policy.php"); +else + require(eduGAIN_config . "database_rw.php"); + +require("../lib/eduGAIN.php"); + +$page_title = "eduGAIN database management tool"; +$edugain = new eduGAIN_manage(-1); +if (!empty($mgm->post_var['code'])) { + $edugain->update_EduGAINTables($mgm->post_var, $mgm->superadmin); + $edugain = new eduGAIN_manage(-1); +} + +$edugainGUI = new eduGAIN_manage_gui($mgm->role); +$federations = []; +$federationCodes = []; +foreach ($edugain->FEDS as $fedCode => $federation) { + $processedFederation = []; + $processedFederation['fed'] = $federation; + $processedFederation['searchString'] = $federation['searchString']; + $federations[] = $processedFederation; + $federationCodes[] = $federation['code']; +} +$data = []; + +$rows = []; + +$rows[] = $edugainGUI->row('Federation code', 'I', 'code', ''); +$rows[] = $edugainGUI->row('Areas', 'AREAS', 'country_code'); +$rows[] = $edugainGUI->row('Name', 'I', 'name', '', 0, 0, 0); +$rows[] = $edugainGUI->row('In Europe', 'C', 'europe'); +$rows[] = $edugainGUI->row('Status', 'ST', 'status', '', 1); +$rows[] = $edugainGUI->row('eduGAIN declaration', 'EDECL', 'edecl'); +$rows[] = $edugainGUI->row('Federation URL', 'I', 'url', '', 0, 0, 4); +$rows[] = $edugainGUI->row('Registration practice', 'POL', 'registration'); +$rows[] = $edugainGUI->row('Contact email', 'I', 'contact_email', '', 0, 0, 4); +$rows[] = $edugainGUI->row('TSG delegate', 'PERS', 'tsg_delegate'); +$rows[] = $edugainGUI->row('TSG deputy', 'PERS', 'tsg_deputy'); +$rows[] = $edugainGUI->row('Security contact', 'SIRTFI', 'security_contact'); +$rows[] = $edugainGUI->row('Policy', 'POL', 'policy'); +$rows[] = $edugainGUI->row('Policy in English', 'C', 'policy_english'); +$rows[] = $edugainGUI->row('Metadata URL', 'I', 'metadata_url', '', 2,0,6); +$rows[] = $edugainGUI->row('Registration Authority', 'I', 'reg_auth', '', 2,0,6); +$rows[] = $edugainGUI->row('Signing certificate', 'CERT', 'certificate'); + +$data['federations'] = $federations; +$data['rows'] = $rows; +$data['federation_codes'] = $federationCodes; +$data['role'] = $mgm->role; +echo "<script>var admin = $mgm->admin; var role = '$mgm->role';</script>"; + +echo $twig->render('manage.html', $data); diff --git a/page-logic/otp_code.php b/page-logic/otp_code.php new file mode 100644 index 0000000000000000000000000000000000000000..a0be4e89a9c4df46c9c21f3900ce53790f987880 --- /dev/null +++ b/page-logic/otp_code.php @@ -0,0 +1,39 @@ +<?php +eduGAIN_manage_gui::pageGraphics(); +$repeatText = ''; +if (isset($_GET['repeat'])) { + $repeatText = "<b>The last code you entered failed. </b><p>"; +} + + print '<div style="margin-left: 2em; margin-top:4ex">'; + print "<h1>Additional verification required</h1><p>"; + print '<form id="otpform" action="'.ROOT_URL.'/manage" method = POST>'; + print $repeatText."Remember that the code can only be used once, if you have already used it you need to wait for the new one to be generated by your device." + . "<p>Please enter your validation code: " + . '<input type="text" size=6 name="second_factor" id="second_factor"> ' + . '<button id="otpsubmit" class="ui-button ui-corner-all ui-widget">Submit</button>' + . ' ' + . '<button id="otpcancel" class="ui-button ui-corner-all ui-widget">Cancel</button>' + . '<input type="hidden" name ="otpstatus" id="otpstatus" value="1"/ >' + ; + print '</form></div>'; + print '<script> + window.onload = function() { + document.getElementById("second_factor").focus(); + }; + $("#otpsubmit").on("click", function(event) { + event.preventDefault(); + if(!$("#second_factor").val().match(/\d\d\d\d\d\d/)) { + alert("The code needs to be 6 digits"); + return false; + } + $("#otpform").submit(); + }); + $("#otpcancel").on("click", function(event) { + event.preventDefault(); + $("#second_factor").val(""); + $("#otpstatus").val("0"); + $("#otpform").submit(); + }); + </script>'; + diff --git a/page-logic/otp_start.php b/page-logic/otp_start.php new file mode 100644 index 0000000000000000000000000000000000000000..427f12a53010f368958b671fec4f4a8d585264c5 --- /dev/null +++ b/page-logic/otp_start.php @@ -0,0 +1,93 @@ +<?php +eduGAIN_manage_gui::pageGraphics(); +session_start(); +header("Cache-Control: no-store, no-cache, max-age=0, must-revalidate, proxy-revalidate"); +if (!isset($_SESSION['auth_ok']) || $_SESSION['auth_ok'] != 'authenticated') { + header("Location: ".ROOT_URL."/authenticate"); + exit; +} +$user = $_SESSION['user']['email']; +?> +<script src="third-party/jquery.js"></script> + +<div style="margin-left: 2em; margin-top:4ex"> +<h1>Registration for access to eduGAIN database management</h1> + +<?php + +require_once('../vendor/autoload.php'); +require_once('../lib/config.php'); +#require_once('../lib/autoload.php'); +$otpCode = filter_var($_POST['second_factor'], FILTER_SANITIZE_NUMBER_INT); +print("OTP:$otpCode"); + +use OTPHP\TOTP; + +$db_user = 'otp_init'; +$db_password = '1qaz2wsx'; + +$mysqli = new mysqli(DB_HOST, $db_user, $db_password, OTP_DATABASE); +if ($mysqli->connect_error) { + die("Not connected"); +} + +$mysqli->set_charset('utf8'); +$mysqli->query("SET time_zone='+00:00'"); +// first we generate a new secret and try to save it to the DB. if this fails then we just +// report that the user is already registered +$otp = TOTP::create(); +$secret = $otp->getSecret(); +$_SESSION['otp_set'] = 1; +$otp->setLabel($user); +$otp->setIssuer('eduGAIN management'); +$uri = $otp->getProvisioningUri(); +$query = "INSERT INTO otp VALUES('$user', '$secret', 0, 0)"; +$result = $mysqli->query($query); +if (!$result) { + print ("User already registered.<p>"); + print ("The secret access code may be regenerated for you by the eduGAIN Operations Team."); + print ("</div>"); + exit; +} + +?> + +One-time codes will be required for certain database update operations. +<p> + +Please scan this code into an application like Google Auth<p> + +<img src="<?php echo (new \chillerlan\QRCode\QRCode)->render($uri) ?>" alt="QR Code" /> +<p>or copy the code below into your application;<p> +<?php echo $secret; ?> +<p>Please save this info in a secure place (not your mailbox). + This code may be regenerated for you by the eduGAIN Operations Team. +</p> +<p>After you have done it, the aplication should generate the validation code - please paste it here: + +<form action="<?php echo ROOT_URL ?>/otp_start" method = POST> +<input type="text" size=6 name="second_factor" id="second_factor"> <button id="submit_otp" class="ui-button ui-corner-all ui-widget">Submit</button><p> +</form> +<p style="font-size:20px">When done <a href="<?php echo ROOT_URL."/manage"; ?>">CLICK HERE</a> to go to the management page. +</div> +<script> + window.onload = function() { + document.getElementById("second_factor").focus(); + } + + jQuery(document).ready(function () { + $("#submit_otp").on("click", function(event) { + event.preventDefault(); + $.post('<?php echo ROOT_URL."/verify-otp.php"; ?>', {otp: $('#second_factor').val()}, + function (data) { + if (data == 1 ) { + alert("Your registration was successful."); + window.location.replace("<?php echo ROOT_URL."/manage"; ?>"); + } else { + alert("Sorry this code was invalid, please try again"); + } + }, + 'json'); + }); + }); + </script> diff --git a/page-logic/update_maps.php b/page-logic/update_maps.php index 8389666f6945349e516f0a2d511f171458e29d1c..1a1311595830cc9d3fb1eb8ac3e25b134e9f84b2 100644 --- a/page-logic/update_maps.php +++ b/page-logic/update_maps.php @@ -13,9 +13,12 @@ if (isset($_POST['logout'])) { unset($_POST); unset($_SESSION); } -require("../lib/config.php"); +require_once("../lib/config.php"); $mapsDir = TECHNICAL_HOME."/web/maps/"; require(eduGAIN_config . "admin.php"); +// it might appear that we are opening a security hole publishing the password +// in the code, but it can only be used during the system installation therefore +// poses no threat if (isset($_POST['user']) && isset($_POST['passwd'])) { if ($_POST['user'] === 'init' && $_POST['passwd'] === 'edu9ain') { $mapsDirContent = scandir($mapsDir); diff --git a/scripts/CoCointerface.php b/scripts/CoCointerface.php new file mode 100644 index 0000000000000000000000000000000000000000..0625e0417609c27b3e006ed3d8baabe8ed67e448 --- /dev/null +++ b/scripts/CoCointerface.php @@ -0,0 +1,30 @@ +<?php +require_once(dirname(__FILE__)."/../lib/config.php"); +require(eduGAIN_config."coco_update.php"); +require_once(TECHNICAL_HOME.'/lib/autoload.php'); + + +class CoCointerface extends eduGAIN_core{ + public function load_coco() { + $j = file_get_contents(COCO_URL.'/json.php?all_sps=true&attributes=id_status;entityID;status'); + $e = json_decode($j); + foreach ($e as $r) + if($r->id_status != NULL) + $this->CoCo[$r->entityID] = array('id'=>$r->id,'code'=>$r->id_status,'msg'=>$r->status); + } + public $CoCo; +} + +$e = new CoCointerface(); + +$e->load_coco(); +$q = "DELETE FROM coco_stat"; +$e->databaseQuery($q); + +foreach ($e->CoCo as $e_id => $s) { + $q = "INSERT into coco_stat (entityid,status,msg,coco_id) VALUES ('$e_id'," . $s['code'] . ", \"" . $s['msg'] . "\", " . $s['id'] . ")"; + $e->databaseQuery($q); +} + +?> + diff --git a/scripts/mk_map.php b/scripts/mk_map.php index 7f0eb508635b660c9d57d84ab2bc70fcce9e68d2..6822cfed280e8da68aeba59cfcaadd48b7319908 100644 --- a/scripts/mk_map.php +++ b/scripts/mk_map.php @@ -4,7 +4,7 @@ * The same can be acheved wia web with update_maps */ require_once("../lib/config.php"); -require_once("../lib/eduGAIN_map.php"); +require_once('../lib/autoload.php'); eduGAIN_map::make_maps(); eduGAIN_map::make_maps(1); ?> diff --git a/scripts/update_coco.sh b/scripts/update_coco.sh new file mode 100644 index 0000000000000000000000000000000000000000..e65d1f78e6bcdb0fd37d6632975c1e25e2ebe69a --- /dev/null +++ b/scripts/update_coco.sh @@ -0,0 +1,4 @@ +#!/bin/bash +spath="$(readlink -n -f "$0")" +cd "$(dirname "$spath")" +/usr/bin/php CoCointerface.php 1> /dev/null 2>>/var/log/coco_error.log diff --git a/templates/js/entities.js b/templates/js/entities.js index cfbc4c340dfa1503a01fec451c692691700a3ffd..992512027360f621f8a578a8b12491b144fa7d40 100644 --- a/templates/js/entities.js +++ b/templates/js/entities.js @@ -15,11 +15,9 @@ $(function(){ autoOpen: false, width: 800, close: function(event, ui){ - $(".entity_links").css('color','#336699'); - $(".entity_links").css('font-weight','normal'); + $(".entity_links").css('color','#336699').css('background-color', 'inherit'); $("#entity_details_content").html(""); }, - position: { my: "left top", at: "left+50 top+50", of : window } }); }); @@ -241,23 +239,24 @@ function show_details(id,next,prev) { $("#show_prev").attr('href','javascript:show_next('+prev+')'); $("#show_next").attr('href','javascript:show_next('+next+')'); $("#loading").show(); - $("body").addClass("busy"); + $("body").addClass("busy"); current_id = id; coco_id = 0; - $(".entity_links").css('color','#336699'); - $(".entity_links").css('font-weight','normal'); - $("#link_"+id).css('color','green'); - $("#link_"+id).css('font-weight','bold'); + $(".entity_links").css('color','#336699').css('background-color', 'inherit'); + $("#link_"+id).css('color','white').css('background-color', '#F1893B'); + $.get("api.php",{action: 'show_entity_details', row_id: id, format: 'html', opt: 'internal'}, function(data) { $("#loading").hide(); $("body").removeClass("busy"); $("#link_"+id).removeClass("busy"); $("#entity_details").dialog("open"); + $("#entity_details").parent().css("top", $(window).scrollTop()+30).css("left", "30px"); $("#entity_details_content").html(data); if(coco_id != 0) $("#show_coco").show(); else $("#show_coco").hide(); + if(eccs_status != 0) $("#show_eccs").show(); else diff --git a/templates/manage.html b/templates/manage.html new file mode 100644 index 0000000000000000000000000000000000000000..fddc1a8a9abc3358862774a51e98f77c5fca7b11 --- /dev/null +++ b/templates/manage.html @@ -0,0 +1,296 @@ + + + +{% extends 'common/master.html' %} + +{% block title %}eduGAIN mangement inteface{% endblock title %} +{% block additional_head %} +<link rel="stylesheet" type="text/css" href="css/manage.css"> +{% endblock %} + +{% block main_body %} +{% block javasctipts %} +<script> + var code; + var declaration_version; + var flags = Object; + var changedFields = new Array(); + var _participants = {{ federations|json_encode()|raw() }}; + + var _participant_codes = JSON.parse('{{ federation_codes|json_encode|e('js') }}'); + + function quickSearch(input) { + return _participants.filter(function(federation){ + return federation.searchString.indexOf(input) !== -1; + }); + } + + function field(data, id, sub_id1, sub_id2) { + var obj; + if (sub_id2 != undefined) { + if (data[id] == undefined || data[id][sub_id1] == undefined || data[id][sub_id1][sub_id2] == undefined) + obj = null; + else + obj = data[id][sub_id1][sub_id2]; + id = id + '_' + sub_id1 + '_' + sub_id2; + } else if (sub_id1 != undefined) { + if (data[id] == undefined || data[id][sub_id1] == undefined) + obj = null; + else + obj = data[id][sub_id1]; + id = id + '_' + sub_id1; + } else { + if (data[id] == undefined) + obj = null; + else + obj = data[id]; + } + if ($("#" + id).is('div')) { + $("#" + id).html(obj); + $("#" + id).parent().show(); + } + if ($("#" + id).prop('type') == 'text') { + $("#" + id).val(obj); + $("#" + id).parent().show(); + } + if ($("#" + id).prop('type') == 'checkbox') { + if (obj == 1) + $("#" + id).prop('checked', true); + else + $("#" + id).prop('checked', false); + } + if ($("#" + id).prop('type') == 'select-one') { + $("#" + id + " option").each(function () { + if ($(this).val() == obj) { + $(this).prop('selected', true); + $(this).parents("tr").show(); + } else + $(this).prop('selected', false); + }); + } + + } + + function cleanup() { + $("#new_fed").val(0); + $("#status option:selected").prop('selected', false); + $("#status").prop('disabled', false); + $("#status").removeClass('ot'); + $(".additional").hide(); + $(".next_button").show(); + $("#next_country_button").show(); + $("#federation_details input").val(''); + $("#federation_details input:checked").prop('checked', false); + $("#federation_details option:selected").prop('selected', false); + if (admin == 1) { + $(".ot").prop('disabled', false); + } else { + $(".ot").prop('disabled', true); + } + } + + function discardChanges(event) { + event.preventDefault(); + loadFederation(); + } + + function testNewFed() { + if ($.inArray($("#code").val(), _participant_codes) >= 0) { + alert("Identfier "+$("#code").val()+" is already in use"); + return(false); + } + if ($("#code").val().match(/^[A-Z]+-?[A-Z]*$/) == null) { + alert("The codes must consist of capital letters and possibly a single hyphen inside"); + return(false); + } + return(true); + } + + function testSaveCompleteness(event) { + event.preventDefault(); + missing_fields = 0; + errors = new Array(); + status = $("#status option:selected").val(); + if ($("#code").val() == "") { + alert("Missing federation identifier"); + return(false); + } + if ($("#new_fed").val() == 1) { + if (testNewFed() == false) { + return(false); + }; + } + if (status == 6 && $("#certificate_0").text() == "") { + alert("You must upload the federation certificate first"); + return(false); + } + $("input[class*='nonempty_level_']").each(function(index) { + x = Number($(this).attr("class").substring(15,16)); + if (x <= status && ($(this).val() == '' || $(this).val() == '0000-00-00')) { + $(this).addClass("missing"); + missing_fields = 1; + } else { + $(this).removeClass("missing"); + } + }); + + if(missing_fields == 1) { + alert("The fields marked in light-red cannot be empty"); + return(false); + } + $("#changed_fields").val(JSON.stringify(changedFields)); + $("#main_form").submit(); + } + + function newFederation(event) { + event.preventDefault(); + cleanup(); + $("#code").removeAttr("readonly"); + if( admin == 1) { + $("#status").prop('disabled', false); + $("#status option").prop('disabled', false); + } else { + $("#status").prop('disabled', false); + $("#status").removeClass('critical'); + } + $("#federation_details").show(); + $("#save").show(); + $("#discard").show(); + $("#new_fed").val(1); + handlePolicyUploadDiv(); + } + + function handlePolicyUploadDiv() { + if ($("#policy_version").val() > 0) { + if ($("#declaration_file").val()) { + $("#pol_div").hide(); + $("#declaration_div").show(); + } else { + $("#pol_div").show(); + $("#declaration_div").hide(); + } + } else { + $("#pol_div").hide(); + $("#declaration_div").hide(); + } + } + + function loadFederation() { + $("#code").attr("readonly", "true"); + $.get('{{ constant('API_URL') }}', {action: 'show_federation', fed_id: $("#feds option:selected").attr('id')}, + function (data) { + cleanup(); + $("#federation_details").show(); + $("#save").show(); + $("#discard").show(); + $.each(data, function (key, element) { + if (typeof element == 'object' && element != null) { + $.each(element, function (k, e) { + if (typeof e == 'object') { + $.each(e, function (k1, e1) { + field(data, key, k, k1); + }); + } else { + field(data, key, k); + } + }); + } else { + field(data, key); + } + }); + handlePolicyUploadDiv(); + $("#fed_status").val(data.status); + if (data.status > 4 && admin != 1) { + $("#status").addClass('ot'); + $("#status").prop('disabled', true); + } + }, "json"); + } + +jQuery(document).ready(function () { + $("#new_pol").click(function (event) { + event.preventDefault(); + $("#pol_div").show(); + $("#new_pol").hide(); + }); + + $("#new_cert").click(function (event) { + event.preventDefault(); + $("#cert_div").show(); + $("#new_cert").hide(); + }); + + $("#next_country_button").click(function (event) { + event.preventDefault(); + $(this).parents("table").first().find("tr:hidden").first().show(); + if ($(this).parents("table").first().find("tr:hidden").size() == 0) + $(this).hide(); + }); + + $(".next_button").click(function (event) { + event.preventDefault(); + $("." + $(this).attr('id') + ":hidden").first().show(); + if ($("." + $(this).attr('id') + ":hidden").size() == 0) + $(this).hide(); + }); + + $('#fed_search_box').keyup(function(){ + var input = $(this).val().toLowerCase().replace('~', ''); + var results = quickSearch(input); + $('#feds').find('option').hide(); + + for(index in results) { + var federation_code = results[index].fed.code; + console.log(federation_code); + $('#' + federation_code).show(); + } + }); + $("#feds").change(loadFederation); + $("#main_fed_table").on('change', "select, input, file", function (event) { + changedFields.push($(this).attr("id")); + }); + + $("#main_fed_table").on('change', ".critical", function (event) { + $("#critical_save").val(1); + }); + + $("#save").on('click', testSaveCompleteness); + $("#discard").on('click', discardChanges); + $("#new_f").on('click', newFederation); + }); +</script> +{% endblock %} + +<div class="central-section"> + <form id="main_form" action="" method="post" enctype="multipart/form-data"> + <!-- <input type="submit" name="logout" value="Logout"> --> + <h1>Manage federations (your current role is {{role}})</h1> + <button id="new_f" class="ui-button ui-corner-all ui-widget">Create a new Federation</button><p> + + + Quick search: <input type="text" id="fed_search_box" title="type here"/><p> + + <select id="feds" size="4" style="width: 550px;"> + {% for F in federations %} + <option id='{{ F.fed.code }}'>{{ F.fed.name }} ({{ F.fed.countries|join(', ') }})</option> + {% endfor %} + </select> + <div id="federation_details" style="display:none"> + <h3>Fields with <span class="critical">yellow background</span> require second factor authentication; those with <span class="ot">grey</span> and can be changed only by the OT members and are disabled for other users.</h3> + <table id="main_fed_table"> + {% for row in rows %} + {{row|raw}} + {% endfor %} + </table> + </div> + <input type="hidden" name="critical_save" id="critical_save" value="0"> + <input type="hidden" name="changed_fields" id="changed_fields" value=""> + <input type="hidden" name="fed_status" id="fed_status" value="XXX"> + <input type="hidden" name="new_fed" id="new_fed" value="0"> + <button id="save" style="display:none; font-size: 20px" class="ui-button ui-corner-all ui-widget">Save changes</button> + <button id="discard" style="display:none; font-size: 20px" class="ui-button ui-corner-all ui-widget">Discard changes</button> + </form> +</div> + +{% endblock main_body %} + diff --git a/templates/metadata.html b/templates/metadata.html index 652783d99d644d512a3f45b7236f101f33af6448..b8920b43dd60da2057f45da4559f42820e138774 100644 --- a/templates/metadata.html +++ b/templates/metadata.html @@ -25,18 +25,9 @@ Should an error persist beyond the validUntil period of the copy, the data of th <dt>SHA-256</dt><dd> BD:21:40:48:9A:9B:D7:40:44:DD:68:05:34:F7:78:88:A9:C1:3B:0A:C1:7C:4F:3A:03:6E:0F:EC:6D:89:99:95 </dl> -<h1>The old feed</h1> -<p>Until the end of June 2022 the previous <a href="mds-v1.cer"><b>key</b></a> will still be used for signing of <a href="https://mds.edugain.org/edugain-v1.xml">https://mds.edugain.org/edugain-v1.xml</a></p>. -<h3>The old Signing certificate fingerprints</h3> -<dl> -<dt>SHA-1</dt><dd> 1A:D3:8C:97:3E:22:9C:84:59:C8:5C:BF:D7:FB:F1:8A:5E:61:07:CA</dd> -<dt>SHA-256</dt><dd> 3E:A8:A0:D2:9C:B4:0E:73:96:FA:B1:77:C5:BF:F2:21:81:4E:60:67:51:78:5D:5E:7B:FA:59:4E:36:2F:C6:A6</dd> -</dl> +<h1>The old feeds</h1> +The old feeds (using the previous and now expired certificate) were removed on My 31st 2023. -<h2>Other feeds</h2> -The feeds - <a href="https://mds.edugain.org">https://mds.edugain.org</a> and <a href="https://mds.edugain.org/feed-sha256.xml">https://mds.edugain.org/feed-sha256.xml</a> -are just copies of https://mds.edugain.org//edugain-v1.xml and are provided for backwards compalitibility but should be considered deprecated. <!-- <h2>Pre-production feed</h2> Whenever the aggregator software is updated in a way that introduces changes to the eduGAIN metadata aggregate, a pre-production feed will be made available for testing. Typically the testing period will be two weeks. The test feed will be availabe at <a href=https://mds.edugain.org/edugain-v1-1-beta.xml">https://mds.edugain.org/edugain-v1-1-beta.xml</a>. --> diff --git a/templates/status.html b/templates/status.html index f05b258f1f260f1fd6224f84e414662c4351a693..bcba572e95838f9e1a855d259887aef93a58ab9e 100644 --- a/templates/status.html +++ b/templates/status.html @@ -18,8 +18,13 @@ <input type="checkbox" class="type_filter" id="include_candidates" value="candidates_group" checked="checked" /> <label for="include_candidates">Candidates ({{ candidates | length }})</label> </div> + <div> + <input type="checkbox" id="problem_filter" /> + <label for="include_problems">Limit to current feed problems</label> + </div> </div> + <div class="central-section"> <div id="search_results" class="col-2-1"> @@ -177,24 +182,25 @@ function deselectAll(){ $dialog_test.dialog(); $dialog_test.dialog("destroy"); $dialog_test.dialog({ 'width' : w + 30, - 'height' : h + 70, - 'position' : { - my: 'right top', - at: 'right-50 top+50', - of: $('#breadcrumb') - }, - 'close': function(event, ui){ - deselectAll(); - } - - }); - + 'height' : h + 70, + 'position' : {my: 'right top', at: 'right-50 top+50', of: $('#breadcrumb')}, + 'close': function(event, ui){deselectAll(); + } + }); $dialog_test.html($memberInfo.html()); } showTime($dialog_test.find('span.timer')); } + + $('#problem_filter').on('change', function() { + if($('#problem_filter').prop('checked') ) { + $('.feed-ok').hide(); + } else { + $('.feed-ok').show(); + } + }); $(document).keyup(function(e){ if(e.keyCode == 27){ diff --git a/templates/status/status-members.html b/templates/status/status-members.html index 6a8c5774822d39d9443f98454f9b6a2d93bccd25..70c027b807755e2279e50cbd734181fdd8188a5b 100644 --- a/templates/status/status-members.html +++ b/templates/status/status-members.html @@ -6,9 +6,13 @@ {% set alert_icon = 'green_dot.png' %} {% set alert_text = 'status OK' %} {% set error_message = '' %} + {% set problem_class = ' feed-ok' %} {% if federation.fed.feed_problem %} {% set alert_icon = 'orange_dot.png' %} - {% set alert_text = 'metadata update problem' %} + {% set alert_text = 'metadata update problem' %} + {% if federation.fed.status == 6 %} + {% set problem_class = ' feed-problem' %} + {% endif %} {% endif %} {% if federation.information_missing %} {% set error_message = error_message ~ ' information missing' %} @@ -18,12 +22,12 @@ {% set alert_text = 'metadata dropped' %} {% elseif federation.fed.valid_sec <= constant('VALIDITY_ALERT_THRESHOLD') %} {% set alert_icon = 'orange_dot.png' %} - {% set alert_text = 'validUnitl under 3 hours' %} + {% set alert_text = 'validUnitl under ' ~ constant('VALIDITY_ALERT_THRESHOLD')/3600 ~ ' hours' %} {% elseif federation.fed.valid_sec < constant('VALIDITY_WARNING_THRESHOLD') %} {% set alert_icon = 'blue_dot.png' %} - {% set alert_text = 'validUnitl between 3 and 108 hours' %} + {% set alert_text = 'validUnitl between ' ~ constant('VALIDITY_ALERT_THRESHOLD')/3600 ~ ' and ' ~ constant('VALIDITY_WARNING_THRESHOLD')/3600 ~ ' hours' %} {% endif %} -<div id="{{ federation.fed.code }}_option_div" class="member-div" style="padding-left: 1em;{% +<div id="{{ federation.fed.code }}_option_div" class="member-div{{ problem_class }}" style="padding-left: 1em;{% if federation.information_missing %}color: red;{% endif diff --git a/web/css/manage.css b/web/css/manage.css new file mode 100644 index 0000000000000000000000000000000000000000..7fafa9142cdfb77449e16d9a87dff02d1e517ca5 --- /dev/null +++ b/web/css/manage.css @@ -0,0 +1,29 @@ + .additional { + display: none; + } + #main_fed_table { + background: rgb(214, 214, 224); + + } + + #main_fed_table td { + background: white; + padding: 2px; + padding-left: 5px; + } + + .critical { + background: #fffa90; + } + + .ot { + background: #AAB7B8; + } + + .critical.ot { + background: #AAB7B8; + } + .missing { + background: #F5B7B1; + } + diff --git a/web/css/master.css b/web/css/master.css index 15fbafa03bdbcde93db2f231b6c02863dfed9678..8b6823a57a6d69d7d8e9a229d668f5f124d68624 100644 --- a/web/css/master.css +++ b/web/css/master.css @@ -163,6 +163,10 @@ footer a { background-position: 90% 5px; background-image: url('../images/warning-icon.png'); } + +.entity_links { + padding: 5px; +} /* ------- Table Style 1 ------- */ .ts-1 { diff --git a/web/otp-server.php b/web/otp-server.php new file mode 100644 index 0000000000000000000000000000000000000000..429d275d96e2f4a5f2f180dd14f3e7a7b349736d --- /dev/null +++ b/web/otp-server.php @@ -0,0 +1,83 @@ +<?php +/* + * The server can dwo two things - it can test if the user is defined or it can + * validate the otp_code against the secret in the database. + * + * Return values: + * -1 - user not found in the DB + * 0 - user exists but there was a missmatch in the code + * 1 - there was a success in verification of the code against the user secret + * 2 - the code has not been provided - the user has not been verified yet + * 3 - the code has not been provided - just confirming that the user is verified + * 4 - the code has been used for a second time + */ +session_start(); + +require_once('../vendor/autoload.php'); +require_once('/var/www/html/technical_tw_new/edugain.config/otp_sec.php'); +use OTPHP\TOTP; + +$mysqli = new mysqli(DB_HOST, USER, PASSWORD, DB_DATABASE); +if ($mysqli->connect_error) { + die("Not connected"); +} +$mysqli->set_charset('utf8'); +$mysqli->query("SET time_zone='+00:00'"); + +if (empty($_GET['user'])) { + exit; +} + +$user = filter_var($_GET['user'], FILTER_SANITIZE_EMAIL); +$out = 0; + +$result = $mysqli->query("SELECT secret, last_code, verified from otp where user ='$user'"); + + +if ($result) { + if ($result->num_rows == 0) { + $out = -1; // the user is not defined + } else { + $r = $result->fetch_row(); + $otpSecret = $r[0]; + $otpLastCode = $r[1]; + $verified = $r[2]; + $out = 0; // the user exists in the database - this is a temporary code value + } +} else { + exit; +} + +$otpCode = filter_var($_GET['otp'], FILTER_SANITIZE_NUMBER_INT); + +// check if any code has been passed and if so update the result code accordingle - again this value is temporary + +if ($otpCode == '' && $out == 0) { + if ($verified == 1) { + $out = 3; + } else { + $out = 2; + } +} + + +if ($out == 0) { // the otp code must have been provided and the user exists in the DB, the secret is taken form the DB + $otpObject = TOTP::create($otpSecret); + $otpTestCode = $otpObject->now(); + if ($otpCode === $otpTestCode) { + if($otpCode === $otpLastCode) { + $out = 4; + } else { + $mysqli->query("UPDATE otp SET verified = 1, last_code = $otpCode where user = '$user'"); + $out = 1; + } + } else { + // there was a missmatch in the codes + $out = 0; + } +} +header('Content-type: application/json; charset=utf-8'); +header('Access-Control-Allow-Origin: *'); +print json_encode($out, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + + diff --git a/web/verify-otp.php b/web/verify-otp.php new file mode 100644 index 0000000000000000000000000000000000000000..34118acc32a72e542f522d5d1edce9adef5b146a --- /dev/null +++ b/web/verify-otp.php @@ -0,0 +1,16 @@ +<?php +ini_set( "session.cookie_lifetime", 3600 ); +session_start(); +require_once('../lib/config.php'); + +// this is a temporary solution and will be incorporated inyot the Management class +// in the future +if (isset($_POST['otp'])) { + $otpCode = filter_var($_POST['otp'], FILTER_SANITIZE_NUMBER_INT); + $user = $_SESSION['user']['email']; + $url = OTP_SERVER.'?user='.$user.'&otp='.$otpCode; + $out = file_get_contents($url); + header('Content-type: application/json; charset=utf-8'); + header('Access-Control-Allow-Origin: *'); + print($out); +}